4

定義された時間間隔で特定のウィンドウのグラフィックスコンテンツをキャプチャしたいサードパーティのWin32アプリケーションを自動化しようとしています。私はこれの初期段階にあり、現在、C#を介してMicrosoft UI Automation APIを使用して、クライアントアプリと外部アプリ間のほとんどのやり取りを実行しようとしています。これで、外部アプリにやりたいことを実行させることができますが、サードパーティの所有者が作成したコントロールのように見える特定のウィンドウからグラフィックをキャプチャしたいと思います。これどうやってするの?キャプチャしたいウィンドウは、この画像の赤い長方形でマークされたウィンドウです。

赤い長方形の中にあるものが必要です

私はそのような動作をする実装を持っていますが、それは外部アプリのUIが一番上にあることに依存しており、それは私にとって保証されていないので、もっと一般的なものを見つけたいと思います。

var p = Process.Start("c:\myapp.exe");
var mainForm = AutomationElement.FromHandle(p.MainWindowHandle);
// "workspace" below is the window whose content I want to capture.
var workspace = mainForm.FindFirst(TreeScope.Descendents,
                    new PropertyCondition(AutomationElement.ClassNameProperty, "AfxFrameOrView70u"));
var rect = (Rect) workspace.GetCurrentPropertyValue(AutomationElement.BoundingRectangleProperty);
using (var bmp = new Bitmap((int)rect.Width, (int)rect.Height))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.CopyFromScreen((int)rect.Left, (int)rect.Top, 0, 0, new Size((int)rect.Width, (int)rect.Height));
        bmp.Save(@"c:\screenshot.png", ImageFormat.Png);
    }
}

上記は自動アプリが上にある場合は十分に機能しますが、画面を長方形に盲目的にコピーするだけなので、私のコードはマシンで実行されているものに翻弄され、アプリのウィンドウを覆う可能性があります。

WM_PRINTメッセージをウィンドウに送信するためのいくつかの提案を読みました。数か月前のこの質問/回答は有望に思えましたが、このコードを使用すると、コントロールの実際の内容がまったく含まれていない白い長方形が表示されます。

var prop = (int)workspace.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
var hwnd = new IntPtr(prop);
using ( var bmp2 = new Bitmap((int)rect.Width, (int)rect.Height))
{
    using (Graphics g = Graphics.FromImage(bmp2))
    {
        g.FillRectangle(SystemBrushes.Control, 0, 0, (int)rect.Width, (int)rect.Height);
        try
        {
            SendMessage(hwnd, WM_PRINT, g.GetHdc().ToInt32(), (int)(DrawingOptions.PRF_CHILDREN | DrawingOptions.PRF_CLIENT | DrawingOptions.PRF_OWNED));
        }
        finally
        {
            g.ReleaseHdc();
        }
        bmp2.Save(@"c:\screenshot.bmp");
    }
}

それで、最初に、ウィンドウのコンテンツのビットマップを確実に保存することさえ可能ですか?もしそうなら、最善の方法は何ですか、そして私WM_PRINTSendMessage試みの何が悪いのですか?

4

2 に答える 2

6

pinvoke.netサイトのPrintWindowAPIの例をこのように変更したことで、うまくいったようです。

Bitmap bmp = new Bitmap((int)rect.Width, (int)rect.Height);
Graphics memoryGraphics = Graphics.FromImage(bmp);
IntPtr dc = memoryGraphics.GetHdc();
bool success = PrintWindow(hwnd, dc, 0);
memoryGraphics.ReleaseHdc(dc);
bmp.Save(@"c:\screenshot.bmp");

これは、アプリが別のウィンドウで覆われている場合は機能しますが、アプリが最小化されている場合は機能しません。私はそれと一緒に暮らせると思います。

于 2010-05-14T18:41:42.227 に答える
1

別のアプリが最上位にない場合、そのアプリからビットマップを取得する信頼できる方法はありません。これは、アプリが表示されていない場合でもアプリのコントロールがレンダリングされないためです。また、Windowsは、コントロールがzオーダーの最上位を失った後、コントロールの最後の既知のコンテンツが何であったかを必ずしも記憶していません。

最善の策は、スクリーンショットを撮る必要があるときにターゲットアプリをZオーダーの前に移動し、オプションで画像をキャプチャした後に元のZオーダーを復元することです。

于 2010-05-14T16:26:11.453 に答える