简体   繁体   English

Snoop 使用什么技术来检查 WPF 应用程序

[英]What technique does Snoop uses to inspect a WPF application

Snoop, the spy utility, uses some powerful technique (probably some sort of reflection) to inspect a running WPF application. Snoop 是间谍实用程序,它使用一些强大的技术(可能是某种反射)来检查正在运行的 WPF 应用程序。 Most interesting is the fact, that Snnop is able to readout the entire object structure.最有趣的是,Snnop 能够读出整个对象结构。

A few days ago I downloaded the Snoop source code and spent some time on studying the internal behavior.前几天下载了Snoop源码,花了一些时间研究内部行为。 Unfortunately, I couldn't find out yet how Snoop is doing these things, so I hope that anybody can help me out.不幸的是,我还无法了解 Snoop 是如何做这些事情的,所以我希望任何人都可以帮助我。

At work I am currently writing a Coded UI Testing-Framework and it would be fantastic if I had access to the application's object structures because this would allow me to not only assert the UI state.在工作中,我目前正在编写一个编码的 UI 测试框架,如果我能够访问应用程序的对象结构,那就太棒了,因为这不仅可以让我断言 UI 状态。

UPDATE:更新:

This is the code needed:这是所需的代码:

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;

This is already a big help for me, but it is also interesting to find out, how Snoop injects itself into another process.这对我来说已经是一个很大的帮助,但找出 Snoop 如何将自己注入另一个进程也很有趣。

You can accomplish what Snoop does by using the WPF VisualTreeHelper and/or the LogicalTreeHelper.您可以使用 WPF VisualTreeHelper 和/或 LogicalTreeHelper 来完成 Snoop 所做的事情。 Once you get a hold of any visual element, you can pretty much traverse its entire visual tree to see all the elements it contains.一旦你掌握了任何视觉元素,你几乎可以遍历它的整个视觉树来查看它包含的所有元素。 Visual tree helper here可视化树助手在这里

So in your UI test, grab the main window and traverse its visual tree to find any element you want and then perform any validations or operations you want on that element.因此,在您的 UI 测试中,抓取主窗口并遍历其可视化树以找到您想要的任何元素,然后对该元素执行您想要的任何验证或操作。

Furthermore, you may be able to use System.Diagnostics.Process.MainWindowHandle to get the windows handle from an existing process and then use the window's handle to create a wpf window.此外,您可以使用 System.Diagnostics.Process.MainWindowHandle 从现有进程获取窗口句柄,然后使用窗口句柄创建 wpf 窗口。 Its been a while so I dont remember the specifics without doing more research.已经有一段时间了,所以如果没有做更多研究,我就不记得具体细节了。 The code below may help:下面的代码可能会有所帮助:

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

UPDATE:更新:

Okay, I found the basic code location, that is used by Snoop to provide the injection ability.好的,我找到了基本的代码位置,这是 Snoop 用来提供注入能力的。 To my astonishment that code is written C++/CLI.令我惊讶的是,代码是用 C++/CLI 编写的。 Probably there is a reason for.大概是有原因的。

And that is the code (I hope that it is okay to post it here):这就是代码(我希望可以在这里发布它):

//-----------------------------------------------------------------------------
//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 doesn't inspect a WPF from the outside. Snoop 不会从外部检查 WPF。 It injects itself into the application and actually adds the magnify or snoop window to it.它将自身注入应用程序并实际向其添加放大或窥探窗口。 Thats also why when you exit snoop the inspection windows actually stay open.这也是为什么当您退出窥探时检查窗口实际上保持打开状态的原因。

So the 'inspection' code simply inspects the window it wants and it can use all avaible WPF functions to do so.所以“检查”代码只是检查它想要的窗口,它可以使用所有可用的 WPF 函数来这样做。 Like the VisualTreeHelper and LogicalTreeHelper as mentioned here earlier.就像前面提到的 VisualTreeHelper 和 LogicalTreeHelper 一样。

For a small test framework i build i injected code to add a small proxy object so i can control the application easily (press buttons, change values, execute functions on viewmodels etc).对于我构建的小型测试框架,我注入了代码以添加一个小型代理对象,以便我可以轻松控制应用程序(按下按钮、更改值、在视图模型上执行功能等)。

The answer above doesn't work for me.上面的答案对我不起作用。 It seems a bit vague.好像有点含糊。 I expanded on accepted answer a little with this code:我用这段代码稍微扩展了接受的答案:

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

This answer seems more complete and will work for others if the accepted answer works for anyone.如果接受的答案适用于任何人,则此答案似乎更完整,并且适用于其他人。 However, this the last line of this code returns null for me.但是,这段代码的最后一行对我来说返回 null。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM