Pergunta

Snoop, o utilitário espião, usa uma técnica poderosa (provavelmente algum tipo de reflexão) para inspecionar um aplicativo WPF em execução.O mais interessante é o fato de que o Snnop é capaz de ler toda a estrutura do objeto.

Há alguns dias baixei o código-fonte do Snoop e passei algum tempo estudando o comportamento interno.Infelizmente, ainda não consegui descobrir como o Snoop está fazendo essas coisas, então espero que alguém possa me ajudar.

No trabalho, estou escrevendo uma estrutura de teste de UI codificada e seria fantástico se eu tivesse acesso às estruturas de objetos do aplicativo, porque isso me permitiria não apenas afirmar o estado da UI.

ATUALIZAR:

Este é o código necessário:

string filePath = "WpfApp.exe";
AppDomain appDomain = AppDomain.CurrentDomain;
byte[] bytes = System.IO.File.ReadAllBytes(filePath);
Assembly ass = appDomain.Load(bytes);
ass.EntryPoint.Invoke(null, new object[] { });
IntPtr handle = Process.GetCurrentProcess().MainWindowHandle;
Window w = System.Windows.Interop.HwndSource.FromHwnd(handle).RootVisual as Window;

Isso já é uma grande ajuda para mim, mas também é interessante descobrir como o Snoop se injeta em outro processo.

Foi útil?

Solução

Você pode realizar o que o Snoop faz usando o WPF VisualTreeHelper e/ou o LogicalTreeHelper.Depois de controlar qualquer elemento visual, você pode percorrer praticamente toda a sua árvore visual para ver todos os elementos que ele contém.Ajudante de árvore visual aqui

Portanto, em seu teste de UI, pegue a janela principal e percorra sua árvore visual para encontrar qualquer elemento desejado e, em seguida, execute quaisquer validações ou operações desejadas nesse elemento.

Além disso, você pode usar System.Diagnostics.Process.MainWindowHandle para obter o identificador do Windows de um processo existente e, em seguida, usar o identificador da janela para criar uma janela wpf.Já faz um tempo, então não me lembro dos detalhes sem fazer mais pesquisas.O código abaixo pode ajudar:

Window window = (Window)System.Windows.Interop.HwndSource.FromHwnd(process.MainWindowHandle).RootVisual;

Outras dicas

ATUALIZAR:

Ok, encontrei a localização básica do código, que é usado pelo Snoop para fornecer a capacidade de injeção.Para minha surpresa, esse código está escrito em C.Provavelmente há uma razão para isso.

E esse é o código (espero que não haja problema em publicá-lo aqui):

//-----------------------------------------------------------------------------
//Spying Process functions follow
//-----------------------------------------------------------------------------
void Injector::Launch(System::IntPtr windowHandle, System::String^ assembly, System::String^ className, System::String^ methodName)
{
    System::String^ assemblyClassAndMethod = assembly + "$" + className + "$" + methodName;
    pin_ptr<const wchar_t> acmLocal = PtrToStringChars(assemblyClassAndMethod);

    HINSTANCE hinstDLL; 

    if (::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)&MessageHookProc, &hinstDLL))
    {
        LogMessage("GetModuleHandleEx successful", true);
        DWORD processID = 0;
        DWORD threadID = ::GetWindowThreadProcessId((HWND)windowHandle.ToPointer(), &processID);

        if (processID)
        {
            LogMessage("Got process id", true);
            HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
            if (hProcess)
            {
                LogMessage("Got process handle", true);
                int buffLen = (assemblyClassAndMethod->Length + 1) * sizeof(wchar_t);
                void* acmRemote = ::VirtualAllocEx(hProcess, NULL, buffLen, MEM_COMMIT, PAGE_READWRITE);

                if (acmRemote)
                {
                    LogMessage("VirtualAllocEx successful", true);
                    ::WriteProcessMemory(hProcess, acmRemote, acmLocal, buffLen, NULL);

                    _messageHookHandle = ::SetWindowsHookEx(WH_CALLWNDPROC, &MessageHookProc, hinstDLL, threadID);

                    if (_messageHookHandle)
                    {
                        LogMessage("SetWindowsHookEx successful", true);
                        ::SendMessage((HWND)windowHandle.ToPointer(), WM_GOBABYGO, (WPARAM)acmRemote, 0);
                        ::UnhookWindowsHookEx(_messageHookHandle);
                    }

                    ::VirtualFreeEx(hProcess, acmRemote, 0, MEM_RELEASE);
                }

                ::CloseHandle(hProcess);
            }
        }
        ::FreeLibrary(hinstDLL);
    }
}

Snoop não inspeciona um WPF externamente.Ele se injeta no aplicativo e, na verdade, adiciona a janela de ampliação ou espionagem a ele.É também por isso que quando você sai do Snoop, as janelas de inspeção permanecem abertas.

Portanto, o código de 'inspeção' simplesmente inspeciona a janela desejada e pode usar todas as funções WPF disponíveis para fazer isso.Como VisualTreeHelper e LogicalTreeHelper conforme mencionado aqui anteriormente.

Para uma pequena estrutura de teste que construo, injetei código para adicionar um pequeno objeto proxy para que eu possa controlar o aplicativo facilmente (pressionar botões, alterar valores, executar funções em modelos de visualização, etc.).

A resposta acima não funciona para mim.Parece um pouco vago.Ampliei um pouco a resposta aceita com este código:

    var allProcesses = Process.GetProcesses();
    var filteredProcess = allProcesses.Where(p => p.ProcessName.Contains(ProcessSearchText)).First();
    var windowHandle = filteredProcess.MainWindowHandle;
    var hwndSource = HwndSource.FromHwnd(windowHandle);

Esta resposta parece mais completa e funcionará para outras pessoas se a resposta aceita funcionar para alguém.No entanto, esta última linha deste código retorna nulo para mim.

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