如何从正在运行的应用程序的子窗口中提取图像






2.94/5 (11投票s)
想从一些 Windows 应用程序中获取酷炫的图像,但找不到?这个应用程序会帮助你。还能获取动画 GIF。
引言
我经常想复制另一个应用程序的外观和感觉,但很难找到匹配的精美图形。这个小应用程序只是我找到的其他代码示例的汇编,并做了一些我自己的调整,但我从未找到任何能做到它所做的事情,所以把它发布在这里供其他人使用。它只是让你输入桌面上可见的窗口的名称,然后枚举该窗口的所有子窗口,并在找到时从中提取图像并显示它们。然后,您可以保存它们。我还做了一个从这些图像中提取动画 GIF 的功能,虽然它只是一次偷取一个帧,然后重新组装它们,因为我找不到直接提取资源的方法。我只是获取每个窗口的图像捕获。它非常简单,非常 hacky,但它运行良好,最终帮助我在几种情况下获得了我想要的东西。希望它有所帮助!
注意:可执行文件附在上面,供那些没有时间阅读描述的人使用,所以你可以直接点击并运行,看看它做了什么。如果你想打开项目,它需要 Visual Studio 2008。您可以完全免费下载 2008 的试用版。
背景
一些其他人的公开代码包含在代码片段中。对不起,我不知道他们是谁。GIF 编码器和动画器是由这里其他的人编写的。
使用代码
它非常不言自明,只有几个功能,所以你可以直接下载并使用它,或者复制它的部分内容供你使用。如果我的解释很稀疏,请原谅,但我认为对于少数搜索并需要它的人来说,拥有一些东西比一无所有更好,而且当我搜索时,我找不到它。
无论如何,为了稍微解释一下代码,它使用 Win32 中的 FindWindowEx
来获取标题与您在文本框中输入的字符串匹配的第一个窗口。
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
string lpszClass, string lpszWindow);
public delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.Dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern IntPtr EnumChildWindows(IntPtr parentHandle,
Win32Callback callback, IntPtr lParam);
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target " +
"could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want
// to cancel the operation, then return a null here
return true;
}
没有任何错误检查,只是为了演示目的而完成。然后,它使用 Win32 EnumChildWindows
与声明的委托 EnumWindowProc
来枚举该窗口中的所有子“窗口”,这些窗口是表单上的所有按钮、控件、图像等。它只是将每个子窗口的句柄值插入列表框控件中。然后,当您单击列表框控件时,它会获取您单击的句柄,并使用从该句柄生成的 Graphics
.NET 对象并复制子窗口的内容,将它们 BitBlt
到位图的 HDC 中,随后将其分配给 PictureBox
的 Image
属性。这一切都发生在非常短的 ShowImgFromHandle()
函数中
private const int SRCCOPY = 0x00CC0020;
private const int CAPTUREBLT = 0x40000000;
[DllImport("gdi32.dll")]
private static extern bool BitBlt(
IntPtr hdcDest,
int nXDest,
int nYDest,
int nWidth,
int nHeight,
IntPtr hdcSrc,
int nXSrc,
int nYSrc,
int dwRop
);
private void ShowImgFromHandle(IntPtr hdlChild)
{
RECT rct;
GetWindowRect(hdlChild, out rct);
Rectangle wndRct = rct;
Graphics gr = Graphics.FromHwnd(hdlChild);
pictureBox1.Width = wndRct.Width;
pictureBox1.Height = wndRct.Height;
if (wndRct.Height == 0 || wndRct.Width == 0)
MessageBox.Show("This child window doesn't have a valid image.");
else
{
lblResourceInfo.Text = "Width: " + wndRct.Width.ToString() +
", Height: " + wndRct.Height.ToString();
Bitmap bmp = new Bitmap(wndRct.Width, wndRct.Height);
Graphics grBmp = Graphics.FromImage(bmp);
IntPtr bmpHdc = grBmp.GetHdc();
BitBlt(bmpHdc, 0, 0, wndRct.Width, wndRct.Height,
gr.GetHdc(), 0, 0, CAPTUREBLT | SRCCOPY);
grBmp.ReleaseHdc();
pictureBox1.Image = bmp;
}
}
最后,如果您单击“保存位图”,则 SaveBmp
函数只是使用 PictureBox
的 Image.Save
将其保存到文件。
private void SaveBmp(string FileName)
{
File.Delete(FileName);
System.Drawing.Imaging.ImageFormat imgfmt = ImageFormat.Bmp;
pictureBox1.Image.Save(FileName, imgfmt);
}
使用我从本网站上的另一个酷项目获取的 AnimatedGifEncoder
、LZWEncoder
和 NeuQuant
类,我制作了一个“保存动画 GIF”按钮。这只是在 1 秒内调用上面的 ShowImgFromHandle
函数 15 次,并使用 AnimatedGif
类中的代码将每个图像捕获到它创建的动画 GIF 中,每次只捕获一个屏幕截图。它将间隔设置为与捕获的周期(1 秒)相同,因此最终的 GIF 中没有跳跃。我不知道 AnimatedGifEncoder
是如何工作的,只是直接从另一个项目中获取了它,并感谢它的发布。它工作得很好。
private void SaveAnimatedGif(string FileName)
{
String outputFilePath = FileName;
AnimatedGifEncoder e = new AnimatedGifEncoder();
e.Start(outputFilePath);
e.SetDelay(1000/15);
//-1:no repeat,0:always repeat
e.SetRepeat(0);
for (int i = 0; i < 15; i++)
{
ShowImgFromHandle((IntPtr)listBox1.Items[listBox1.SelectedIndex]);
System.Threading.Thread.Sleep(1000 / 15);
e.AddFrame(pictureBox1.Image);
}
e.Finish();
}
我确实希望这能帮助一些人。此示例并非旨在成为一个现成的应用程序,只是发布来展示一种对我有效且我找不到其他任何方法的技术。
祝你好运,希望它对你有所帮助。