user14092875
user14092875

Reputation: 165

How to use winapi on visual studio 2019 to create buttons to open file explorer

=================================SOLVED=================================

As Ken White mentioned in the comments. I found here a code that helped me. I've edited the code a in some places to adjust it to Visual Studio 2019:

#include <iostream>
#include <Windows.h>

// FUNCTION PROTOTYPES =========================================================
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

// entry point for a Windows application =======================================
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR szCmdLine, int nWinMode) {
    // define some variables
    const wchar_t     szWinName[] = L"Win32App";
    const wchar_t     szAppTitle[] = L"Win32 API Skeletal Application";
    HWND     hwnd;
    MSG      msg;
    WNDCLASS wc;

    // define a window class
    wc.hInstance = hInstance;                         // handle to this instance
    wc.lpszClassName = szWinName;                         // window class name
    wc.lpfnWndProc = WndProc;                           // pointer to window proc
    wc.style = 0;                                 // default style
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);   // predefined icon
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);       // predefined cursor
    wc.lpszMenuName = NULL;                              // no class menu
    wc.cbClsExtra = 0;                                 // no extra info needed
    wc.cbWndExtra = 0;                                 // no extra info needed
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);       // predefined color brush

    // register the defined window class
    if (RegisterClass(&wc) == 0)
    {
        // an error occurred, abort the program
        MessageBox(NULL, L"Couldn't Register the Window Class!", L"ERROR", MB_OK | MB_ICONERROR);
        return 0;
    }

    // now that a window class has been registered, create the main window
    hwnd = CreateWindow(szWinName,           // name of window class to create
        szAppTitle,          // window title bar caption
        WS_OVERLAPPEDWINDOW, // window style - normal
        CW_USEDEFAULT,       // X coordinate - let Windows decide
        CW_USEDEFAULT,       // Y coordinate - let Windows decide
        CW_USEDEFAULT,       // width - let Windows decide
        CW_USEDEFAULT,       // height - let Windows decide
        NULL,                // no parent window
        NULL,                // no menu
        hInstance,           // handle to this instance
        NULL);               // no additional arguments

    // check to see if window was successfully created
    if (hwnd == NULL)
    {
        // an error occurred, abort
        MessageBox(NULL, L"Couldn't Create the Main Window!", L"ERROR", MB_OK | MB_ICONERROR);
        return 0;
    }

    // display (and update) the newly created window
    ShowWindow(hwnd, nWinMode);
    UpdateWindow(hwnd);

    // create the message loop
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);  // translate keyboard messages
        DispatchMessage(&msg);   // return control to Windows
    }

    return msg.wParam;
}


// processes the messages that Windows sends to the application ================
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    // choose which Windows messages you want to use
    switch (message)
    {
        // the window is being destroyed, so terminate the application
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }

    // let Windows process any messages not specified in the switch statement
    return DefWindowProc(hwnd, message, wParam, lParam);
}

Problem Description: Create new window using Visual Studio 2019 in C++

I'm writing user interface for USB device using Visual Studio 2019, in a console app type of project in cpp. I want to add two features - I want to create buttons which will appear on the screen and will enable the user to:

  1. by clicking on a button "store data" the file explorer will be opened and they will be able to choose the file where to store the data read from the endpoint buffer
  2. by clicking on a button "read data" the file explorer will be opened and they will be able to choose the file from which they want to read data and write it to the endpoint. I want to let the user choose the path to the file, I don't want the user to insert the path into the terminal using the keyboard, I want to create a button which will open the file explorer and will let the user access to the desired file\location.

For example, to store data from an endpoint buffer into a file, the user will click on "store data" and will navigate to the desired file\location. after the location has been chosen, libusb functions will read from the endpoint and will store the data in a temporary struct, from which I will write it into the file.

The main problem for me is to create those buttons and make them display the file explorer. I've started with some short code to create a new window, as written in this guide:

  1. button creation - https://learn.microsoft.com/en-us/windows/win32/controls/create-a-button
  2. window creation - https://learn.microsoft.com/en-us/windows/win32/learnwin32/your-first-windows-program

the problem is, when I run the program the window opens and collapses immediately. in addition, I cannot find what functions can help me to make the pressing on a button to open the file explorer.

I'm using windows.h header to create the window. this is my code:

''''

 #include <Windows.h>

 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return 
 DefWindowProc(hwnd, uMsg, wParam, lParam); }

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";

WNDCLASS wc = { };

wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;

RegisterClass(&wc);

HWND hwnd = CreateWindowEx(
    0,                              // Optional window styles.
    CLASS_NAME,                     // Window class
    L"Learn to Program Windows",    // Window text
    WS_OVERLAPPEDWINDOW,            // Window style

    // Size and position
    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

    NULL,       // Parent window    
    NULL,       // Menu
    hInstance,  // Instance handle
    NULL        // Additional application data
);

if (hwnd == NULL)
{
    return 0;
}

ShowWindow(hwnd, nCmdShow);

return 0;
}

''''

I added in the linker's additional dependencies the flag "user32.lib" and I also don't have any linker errors, so I guess that's not it. What could be the problem? I've tried looking at other similar questions, but couldn't find anything helpful. also, is there any library which provides functions to open the file explorer after clicking on the button?

I hope my question hasn't been asked before, and is written properly. If there's something wrong with it, please let me know so I can edit it.

Thank you very much for your time and attention.

Upvotes: 0

Views: 689

Answers (1)

Zeus
Zeus

Reputation: 3890

The reason you run the program the window opens and collapses immediately is that you did not create a message loop. The message loop is not just the WindowProc function you mentioned. You also need to add a message processing loop function.

According to the MSDN document:

  1. wWinMain is the program entry point. When the program starts, it registers some information about the behavior of the application window. One of the most important items is the address of a function, named WindowProc in this example. This function defines the behavior of the window—its appearance, how it interacts with the user, and so forth.

  2. Next, the program creates the window and receives a handle that uniquely identifies the window.

  3. If the window is created successfully, the program enters a while loop. The program remains in this loop until the user closes the window and exits the application.

Notice that the program does not explicitly call the WindowProc function, even though we said this is where most of the application logic is defined. Windows communicates with your program by passing it a series of messages. The code inside the while loop drives this process. Each time the program calls the DispatchMessage function, it indirectly causes Windows to invoke the WindowProc function, once for each message.

Then you can create a button through CreateWindow as the following code(But you need to set a unique identifier for subsequent operations):

#define IDB_BTN 1001
HWND btn = CreateWindow("BUTTON", "OPEN", WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, 500, 200, 100, 100, hwnd, (HMENU)IDB_BTN, (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE), NULL);

And you can open the Explorer in two ways.

You can open the explorer directly through ShellExecuteA:

ShellExecute(NULL, "open", "explorer.exe", NULL, NULL, SW_NORMAL);

Of course maybe the best way to do this is with the SHOpenFolderAndSelectItems() function.

LPCWSTR pszPathToOpen = L"C:\\Windows";
PIDLIST_ABSOLUTE pidl;
if (SUCCEEDED(SHParseDisplayName(pszPathToOpen, 0, &pidl, 0, 0)))
{
    ITEMIDLIST idNull = { 0 };
    LPCITEMIDLIST pidlNull[1] = { &idNull };
    SHOpenFolderAndSelectItems(pidl, 1, pidlNull, 0);
    ILFree(pidl);
}

Here is the complete sample:

#include <Windows.h>
#include <shlobj_core.h>
#define IDB_BTN 1001
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
    // Register the window class.
    TCHAR CLASS_NAME[] = "Sample Window Class";
    MSG msg;
    WNDCLASS wc = { };

    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = CLASS_NAME;

    RegisterClass(&wc);

    HWND hwnd = CreateWindowEx(
        0,                              // Optional window styles.
        CLASS_NAME,                     // Window class
        "Learn to Program Windows",    // Window text
        WS_OVERLAPPEDWINDOW,            // Window style

        // Size and position
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

        NULL,       // Parent window    
        NULL,       // Menu
        hInstance,  // Instance handle
        NULL        // Additional application data
    );
    HWND btn = CreateWindow(
        "BUTTON",
        "OPEN",
        WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON,
        500,
        200,
        100,
        100,
        hwnd,
        (HMENU)IDB_BTN,
        (HINSTANCE)GetWindowLongPtr(hwnd, GWLP_HINSTANCE),
        NULL);
    if (hwnd == NULL)
    {
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);

    while (GetMessageW(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessageW(&msg);
    }
    return msg.wParam;
}


LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDB_BTN:
        {
            //ShellExecute(NULL, "open", "explorer.exe", NULL, NULL, SW_NORMAL);
            LPCWSTR pszPathToOpen = L"C:\\Windows";
            PIDLIST_ABSOLUTE pidl;
            if (SUCCEEDED(SHParseDisplayName(pszPathToOpen, 0, &pidl, 0, 0)))
            {
                ITEMIDLIST idNull = { 0 };
                LPCITEMIDLIST pidlNull[1] = { &idNull };
                SHOpenFolderAndSelectItems(pidl, 1, pidlNull, 0);
                ILFree(pidl);
            }
            return 0;
        }
        default:
            break;
        };
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

Upvotes: 2

Related Questions