Pergunta

Estou tentando automatizar um aplicativo Win32 de terceiros, onde quero capturar o conteúdo gráfico de uma janela específica em intervalos de tempo definidos. Estou nas fases iniciais disso e atualmente estou tentando usar o Automação da UI da Microsoft API via C# para fazer a maior parte da interação entre meu aplicativo cliente e o aplicativo externo. Agora posso fazer com que o aplicativo externo faça o que eu quero, mas agora quero capturar os gráficos de uma janela específica que parece ser algum controle de terceiros. Como posso fazer isso? A janela que quero capturar é a marcada pelo retângulo vermelho nesta imagem:

I need what's in the red rectangle

Eu tenho uma implementação que funciona, mas depende da interface do usuário do aplicativo externo estar no topo, e isso não é garantido para mim, então eu prefiro encontrar algo mais geral.

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);
    }
}

O exposto acima funciona bem o suficiente quando o aplicativo automatizado está no topo, mas apenas copia cegamente a tela no retângulo, então meu código está à mercê do que acontecer para estar em execução na máquina e pode cobrir a janela do meu aplicativo.

Eu li algumas sugestões para enviar o WM_PRINT mensagem para a janela. este Resposta da questão De alguns meses atrás, parecia promissor, mas quando uso esse código, apenas recebo um retângulo branco com nenhum dos conteúdos reais do meu controle.

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");
    }
}

Então, primeiro, é possível até eu economizar um bitmap de bitmap de maneira confiável? Se sim, qual é a melhor maneira e o que há de errado com o meu WM_PRINT com SendMessage tentar?

Foi útil?

Solução

Esta modificação do PrintWindow Exemplo de API No site Pinvoke.net, parece ter feito o truque.

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");

Isso funciona se o aplicativo for coberto por outra janela, mas não funcionar se o aplicativo for minimizado. Eu acho que posso viver com isso.

Outras dicas

Não há confiável Maneira de obter um bitmap de um aplicativo diferente se esse aplicativo não estiver no topo. Isso ocorre porque os controles do aplicativo nem sequer renderizam se o aplicativo não estiver visível, e o Windows nem sequer se lembra necessariamente de qual o último conteúdo conhecido do controle após esse controle perde a posição mais alta na ordem Z.

Sua melhor aposta é mover o aplicativo de destino para a frente da ordem Z no momento em que você precisa tirar a captura de tela e, em seguida, restaurar opcionalmente a ordem Z original depois de capturar a imagem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top