With help from @SimonMourier and some googling, I finally solved it. First, I need to get the first dwThreadId from my dwProcessId and I do that using the function GetFirstThreadId:
// Found this function (original name was ListProcessModules) on
// https://learn.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
static DWORD GetFirstThreadId(DWORD dwProcessId) {
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE) {
return(FALSE);
}
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
// Retrieve information about the first thread,
// and exit if unsuccessful
if (!Thread32First(hThreadSnap, &te32)) {
wprintf(L"Thread32First"); // show cause of failure
CloseHandle(hThreadSnap); // clean the snapshot object
return(FALSE);
}
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
do {
if (te32.th32OwnerProcessID == dwProcessId) {
// wprintf(L"\n\n THREAD ID = 0x%08X", te32.th32ThreadID);
// wprintf(L"\n Base priority = %d", te32.tpBasePri);
// wprintf(L"\n Delta priority = %d", te32.tpDeltaPri);
// wprintf(L"\n");
CloseHandle(hThreadSnap); // clean the snapshot object
return te32.th32ThreadID;
}
} while (Thread32Next(hThreadSnap, &te32));
CloseHandle(hThreadSnap); // clean the snapshot object
return 0;
}
Then it's just a matter of calling EnumThreadWindows and ElementFromHandle:
static UIA_HWND g_hwnd;
static int numCallbackCallsLeft;
int _tmain(int argc, _TCHAR* argv[]) {
const std::wstring processName = L"myExecutable.exe";
DWORD dwProcessID = FindProcessId(processName);
DWORD dwThreadID = GetFirstThreadId(dwProcessID);
HWND hTop = NULL;
numCallbackCallsLeft = 1;
BOOL bResult = EnumThreadWindows(dwThreadID, EnumWindowsCallBackFnc, (LPARAM)&hTop);
while (0 < numCallbackCallsLeft) {
Sleep(1);
}
if (SUCCEEDED(CoInitialize(NULL))) {
if (SUCCEEDED(g_pAutomation.CoCreateInstance(CLSID_CUIAutomation8))) { // or CLSID_CUIAutomation
CComPtr<IUIAutomationElement> pElement;
if (SUCCEEDED(g_pAutomation->ElementFromHandle(g_hwnd, &pElement))) {
// Hooray, we now have pElement!!!
}
}
}
CoUninitialize();
return 0;
}
// Pointer to this function is passed as parameter to EnumThreadWindows function
static BOOL CALLBACK EnumWindowsCallBackFnc(HWND hwnd, LPARAM lParam) {
wprintf(L"EnumWindowsCallBackFnc. hwnd: %d\n", (int)hwnd);
g_hwnd = hwnd;
// This function is called for all siblings windows owned by given thread
// So, store hwnd for the later use...
numCallbackCallsLeft--;
return (0 < numCallbackCallsLeft); // return TRUE if you want to continue windows enumeration.
// If you found the proper window, just return FALSE and windows enumeration will stop
}