Search code examples
c++windowswinapifindwindowspy++

Why did I fail to find window use parent specified in Spy++?


Imagine there is a web page with a <input type="file" />. I open this page in Firefox, and click on the file button. Then a dialog will popup.

I want to set the file name edit of the popup dialog programmatically in C++:

enter image description here

First I use Spy++ to check window class, the Firefox window and popup dialog properties in Spy++ look like:

Firefox window:
Handle:     001E013E
Caption:    Table with objects - Mozilla Firefox
Class:      MozillaWindowClass

Popup dialog:
Handle:     004508BE
Caption:    File Upload
Class:      #32770 (Dialog)

Spy++ also shows browser window is the parent of popup dialog.

My code looks like:

#include <Windows.h>
#include <stdio.h>

int main()
{
    HWND hBrowser = FindWindow(L"MozillaWindowClass", NULL);
    printf("Browser hwnd=%X\n", hBrowser);

    HWND hDialog = FindWindowEx(hBrowser, NULL, L"#32770 (Dialog)", NULL);
    printf("Dialog hwnd=%X\n", hDialog);

    system("pause");
}

But the value of hBrowser does not equal the value in Spy++ dialog, and value of hDialog is NULL. I have only one Firefox window opened, with only one tab.

Then I tried to change my code to:

// 0x001E013E is the handle in Spy++
HWND hDialog = FindWindowEx((HWND)0x001E013E, NULL, L"#32770 (Dialog)", NULL);

hDialog still outputs as NULL.

Questions:

  1. Why the handle in Spy++ and the one I get in program is not the same?
  2. Does the "parent window" in Spy++ have the same meaning with the parent parameter in FindWindowEx?

Note: I can't use window title to do the find, due to localization issue (Firefox may be installed in languages other than English).


Solution

  • MozillaWindowClass is the owner of the open dialog, it is not a parent you can use with FindWindowEx. There can also be more than one MozillaWindowClass window so it is better to look for the dialog first:

    BOOL CALLBACK FindMozillaOpenFilenameDialogEnumProc(HWND hWnd, LPARAM param)
    {
        HWND*pData = (HWND*) param;
        if (GetClassLongPtr(hWnd, GCW_ATOM) == 32770) // Found a dialog?
        {
            HWND hOwner = GetWindow(hWnd, GW_OWNER);
            if (hOwner)
            {
                WCHAR buf[100];
                GetClassName(hOwner, buf, 100);
                if (0 == lstrcmp(buf, TEXT("MozillaWindowClass"))) // The dialog is owned by a Mozilla window?
                {
                    HWND hCombo = GetDlgItem(hWnd, 0x047c); // cmb13
                    GetClassName(hCombo, buf, 100);
                    buf[8] = '\0'; // Some Windows versions use ComboBox and some use ComboBoxEx32, we only care if it is some type of combobox
                    if (0 == lstrcmp(buf, TEXT("ComboBox"))) // The dialog contains a ComboBox with the expected ID?
                    {
                        *pData = hWnd;
                        return false;
                    }
                }
            }
        }
        return true;
    }
    
    
    int main()
    {
    
        HWND hDialog = NULL;
        EnumWindows(FindMozillaOpenFilenameDialogEnumProc, (LPARAM) &hDialog);
        printf("Dialog hwnd=%X\n", hDialog);
        if (hDialog)
        {
            HWND hCombo = GetDlgItem(hDialog, 0x047c);
            SendMessage(hCombo, WM_SETTEXT, 0, (LPARAM) TEXT("c:\\foo\\bar.exe")); // Could also use CDM_SETCONTROLTEXT?
        }
        return 0;
    }
    

    This code relies on undocumented and internal names and window relationships, it could break at any time.

    Keep in mind that "MozillaWindowClass" is a internal Mozilla name and could change at any time. The documented cmb13 id of the filename control is only documented for GetOpenFileName and GetSaveFileName but not for IFileDialog based dialogs. You really should use UI Automation when you interact with the open dialog in another application!