What technique does Snoop uses to inspect a WPF application
You can accomplish what Snoop does by using the WPF VisualTreeHelper and/or the LogicalTreeHelper. 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.
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. 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. To my astonishment that code is written 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);
}
}