65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.94/5 (11投票s)

2008年6月30日

CPOL

3分钟阅读

viewsIcon

40953

downloadIcon

537

想从一些 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 中,随后将其分配给 PictureBoxImage 属性。这一切都发生在非常短的 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 函数只是使用 PictureBoxImage.Save 将其保存到文件。

private void SaveBmp(string FileName)
{
    File.Delete(FileName);
    System.Drawing.Imaging.ImageFormat imgfmt = ImageFormat.Bmp;
    pictureBox1.Image.Save(FileName, imgfmt);
}

使用我从本网站上的另一个酷项目获取的 AnimatedGifEncoderLZWEncoderNeuQuant 类,我制作了一个“保存动画 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();
}

我确实希望这能帮助一些人。此示例并非旨在成为一个现成的应用程序,只是发布来展示一种对我有效且我找不到其他任何方法的技术。

祝你好运,希望它对你有所帮助。

© . All rights reserved.