繁体   English   中英

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

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

Snoop 是间谍实用程序,它使用一些强大的技术(可能是某种反射)来检查正在运行的 WPF 应用程序。 最有趣的是,Snnop 能够读出整个对象结构。

前几天下载了Snoop源码,花了一些时间研究内部行为。 不幸的是,我还无法了解 Snoop 是如何做这些事情的,所以我希望任何人都可以帮助我。

在工作中,我目前正在编写一个编码的 UI 测试框架,如果我能够访问应用程序的对象结构,那就太棒了,因为这不仅可以让我断言 UI 状态。

更新:

这是所需的代码:

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;

这对我来说已经是一个很大的帮助,但找出 Snoop 如何将自己注入另一个进程也很有趣。

您可以使用 WPF VisualTreeHelper 和/或 LogicalTreeHelper 来完成 Snoop 所做的事情。 一旦你掌握了任何视觉元素,你几乎可以遍历它的整个视觉树来查看它包含的所有元素。 可视化树助手在这里

因此,在您的 UI 测试中,抓取主窗口并遍历其可视化树以找到您想要的任何元素,然后对该元素执行您想要的任何验证或操作。

此外,您可以使用 System.Diagnostics.Process.MainWindowHandle 从现有进程获取窗口句柄,然后使用窗口句柄创建 wpf 窗口。 已经有一段时间了,所以如果没有做更多研究,我就不记得具体细节了。 下面的代码可能会有所帮助:

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

更新:

好的,我找到了基本的代码位置,这是 Snoop 用来提供注入能力的。 令我惊讶的是,代码是用 C++/CLI 编写的。 大概是有原因的。

这就是代码(我希望可以在这里发布它):

//-----------------------------------------------------------------------------
//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 不会从外部检查 WPF。 它将自身注入应用程序并实际向其添加放大或窥探窗口。 这也是为什么当您退出窥探时检查窗口实际上保持打开状态的原因。

所以“检查”代码只是检查它想要的窗口,它可以使用所有可用的 WPF 函数来这样做。 就像前面提到的 VisualTreeHelper 和 LogicalTreeHelper 一样。

对于我构建的小型测试框架,我注入了代码以添加一个小型代理对象,以便我可以轻松控制应用程序(按下按钮、更改值、在视图模型上执行功能等)。

上面的答案对我不起作用。 好像有点含糊。 我用这段代码稍微扩展了接受的答案:

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

如果接受的答案适用于任何人,则此答案似乎更完整,并且适用于其他人。 但是,这段代码的最后一行对我来说返回 null。

暂无
暂无

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

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