Search code examples
c++windowsuser-interfacewinapidrag-and-drop

Drag&drop event (WM_DROPFILES) in C++ GUI


I am trying to create a simple application where the user can drag and drop files from outside of the window (usually Explorer) into an area inside of the window. My ultimate purpose is to get the file path to later on process it.

Currently I can drag and drop files into the area but I never receive the WM_DROPFILES event. I have tried with some related functions (DoDragDrop, RegisterDragDrop, CDropSource), but they all have been either impossible to compile or unsuccessful.

Could anyone tell me if I am missing setting any property?

Many thanks in advance

LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
    switch (Message)
    {
        case WM_CREATE:
        {
            CreateWindowEx(
                WS_EX_ACCEPTFILES,
                TEXT("static"),
                TEXT("Drag and drop your file to this area"),
                WS_VISIBLE | WS_CHILD,
                20, // x
                20, // y
                120, // w
                60, // h
                hwnd, // parent window
                (HMENU) 1, // unique label
                NULL, NULL);
        }
        case WM_DROPFILES:
        {
            MessageBox(hwnd, "Dragged!", "Title", MB_OK | MB_ICONINFORMATION);
        }

        case WM_DESTROY:
        {
            PostQuitMessage(0);
            break;
        }

        default:
        {
            return DefWindowProc(hwnd, Message, wParam, lParam);
        }
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG msg;

    memset(&wc,0,sizeof(wc));
    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.lpfnWndProc   = WndProc;
    wc.hInstance     = hInstance;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW); 
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszClassName = "WindowClass";
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }

    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        "WindowClass",
        "MySimpleApp",
        WS_VISIBLE | WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        WINDOW_W,
        WINDOW_H,
        NULL,NULL,hInstance,NULL);

    if (hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
        return 0;
    }

    while (GetMessage(&msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}

Solution

  • You are not receiving the WM_DROPFILES message because you are not subclassing the STATIC control you create to receive messages that are sent to it. You are assuming you can catch the message in the control's parent window, but that is not where the message goes. It is sent to the window that you actually drop onto - the STATIC control.

    Try this instead:

    LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
    {
        switch (uMsg) {
            case WM_NCDESTROY: {
                RemoveWindowSubclass(hwnd, &StaticWndProc, uIdSubclass);
                break;
            }
            case WM_DROPFILES: {
                MessageBox(hwnd, "Dragged!", "Title", MB_OK | MB_ICONINFORMATION);
                break;
            }
        }
            
        return DefSubclassProc(hwnd, uMsg, wParam, lParam);
    }
    
    LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            case WM_CREATE: {
                HWND hStatic = CreateWindowEx(
                    WS_EX_ACCEPTFILES,
                    TEXT("static"),
                    TEXT("Drag and drop your file to this area"),
                    WS_VISIBLE | WS_CHILD,
                    20, // x
                    20, // y
                    120, // w
                    60, // h
                    hwnd, // parent window
                    (HMENU) 1, // unique label
                    NULL, NULL);
                SetWindowSubclass(hStatic, &StaticWndProc, 0, 0);
                break;
            }
    
            case WM_DESTROY: {
                PostQuitMessage(0);
                break;
            }
    
            default: {
                return DefWindowProc(hwnd, Message, wParam, lParam);
            }
        }
    
        return 0;
    }
    

    DoDragDrop() and RegisterDragDrop() (which you should be using instead of WM_DROPFILES) have nothing to do with receiving WM_DROPFILES.