Tworzenie przycisku w okienku dialogowym Windows

0

Witam. Mam generalnie problem ze stworzeniem przycisku w oknie dialogowym. Problem polega na tym, ze po uruchomieniu programu, nie pojawia się przycisk. I wynik tego jest pokazanie na poniżej załączonym screenie. Mimo ze próbowałem wieku sposobów, zawsze działa w ten sam sposób. Nie wiem czy gdzieś popełniłem błąd, czy to wina Windowsa, ale już meczę się już kilka godzin. Proszę o pomoc.

include <windows.h>
#include <iostream>

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

int WINAPI WinMain(HINSTANCE currentInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd){
    LPCSTR CLASS_NAME = "unqiueClassWindow";
    LPCSTR NAME = "Windows App";

    WNDCLASS wc = {};
    
    wc.hInstance = currentInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.lpfnWndProc = WindowProcessMessages;
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hIcon = LoadIcon(nullptr, IDI_APPLICATION); 
    wc.hbrBackground = (HBRUSH) COLOR_WINDOW;

    RegisterClass(&wc);

    HWND hwnd = CreateWindow(CLASS_NAME, NAME, WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, currentInstance, nullptr);
    if(hwnd == NULL) return 0;

    MSG msg = {};
    while(GetMessage(&msg, NULL, 0, 0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){
    switch(msg){
        case WM_CREATE: {
            HWND btn1 = CreateWindow(TEXT("Button1"), TEXT("Start"), WS_OVERLAPPEDWINDOW | WS_CHILD , 10, 100, 80, 30, hwnd, (HMENU) 1, nullptr, nullptr);
            if(btn1 == NULL) std::cout << "Button1 ERROR!";
            return 0;
        }

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

     return DefWindowProc(hwnd, msg, wparam, lparam);
}

screenshot-20220912215341.png

Jeżeli ktoś zauważył czerwone błędy na edytorze to są tu one:
screenshot-20220912220010.png
screenshot-20220912220036.png

i mimo tego ze zmieniam zmienne na odpowiedni typ to i tak zwraca mi błąd w kompilatorze:
screenshot-20220912220241.png
więc żeby nie było, ze jest to tym spowodowane. Tak rozumiem sens błędów oraz próbowałem na tej funkcji co podana jest w kompilatorze, ale jak mówiłem, wynik jest taki sam. Na jakiekolwiek bym użył funkcji CreateWindow, CreateWindowW, CreateWindowExA, CreateWindowExW, to wynik się zwraca taki sam.

0

bo WNDCLASS oczekuje LPCSTR a CreateWindow oczekuje LPCWSTR. Jak zmieniasz typ to tu zaczyna pasować a przestaje gdzie indziej.
Użyj WNDCLASSW.
Skoro się nie kompiluje to po prostu odpalasz ostatnio poprawnie skompilowaną wersję i stąd pewnie nie ma przycisku.

0
obscurity napisał(a):

bo WNDCLASS oczekuje LPCSTR a CreateWindow oczekuje LPCWSTR. Jak zmieniasz typ to tu zaczyna pasować a przestaje gdzie indziej.
Użyj WNDCLASSW.

Poprawiłem to teraz, ale wychodzi mi teraz inny błąd. Teraz pokazuje mi tylko 1 znak nazwy aplikacji. Ale błąd z głównego wątku nadal zostaje
screenshot-20220913073104.png

Skoro się nie kompiluje to po prostu odpalasz ostatnio poprawnie skompilowaną wersję i stąd pewnie nie ma przycisku.

Być moze. Ale nie wiem dlaczego tak się robi. Podczas kompilowania, wyskakuje mi ze wszystko jest poprawne i ze kod został poprawnie skompilowany.
Wysyłam jeszcze raz kod po poprawkach:

#include <windows.h>
#include <iostream>

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

int WINAPI WinMain(HINSTANCE currentInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd){
    const wchar_t CLASS_NAME[] = L"unqiueClassWindow";

    WNDCLASSW wc{};
    
    wc.hInstance = currentInstance;
    wc.lpszClassName = CLASS_NAME;
    wc.lpfnWndProc = WindowProcessMessages;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
    wc.hbrBackground = (HBRUSH) COLOR_WINDOW;

    RegisterClassW(&wc);

    HWND hwnd = CreateWindowW(CLASS_NAME, L"WindowsApp", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL);
    if(hwnd == NULL) return 0;

    MSG msg{};
    while(GetMessage(&msg, NULL, 0, 0)){
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

LRESULT CALLBACK WindowProcessMessages(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam){
    switch(msg){
        case WM_CREATE: {
            HWND btn1 = CreateWindowW(L"Button1", L"Start", WS_CHILD | WS_VISIBLE , 10, 100, 80, 30, hwnd, (HMENU) 1, NULL, NULL);
            if(btn1 == NULL) std::cout << "Button1 ERROR!";
            return 0;
        }

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

     return DefWindowProc(hwnd, msg, wparam, lparam);
}


0
// Code::Blocks 20.03 -std=c++17 or higher
// Windows Xp 32bit, Windows 7 or higher 64bit

#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

#include <iostream>
#include <tchar.h>
#include <windows.h>
#include <cstdlib>
#include <ctime>
#include <cstring>
#include <wininet.h>

using namespace std;

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("Windows App 32");

//tworzymy button
HWND g_hPrzycisk;



int WINAPI WinMain (HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl))
        return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           _T("Button Close Form"),       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           544,                 /* The programs width */
           375,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    //tworzymy przycisk
    g_hPrzycisk = CreateWindowEx( 0, "BUTTON", "Zamknij Okno", WS_CHILD | WS_VISIBLE,
100, 100, 150, 30, hwnd, NULL, hThisInstance, NULL );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

    switch (message)                  /* handle the messages */
    {

        case WM_COMMAND:
            if(( HWND ) lParam == g_hPrzycisk )
            PostQuitMessage (0);
        break;
        case WM_DESTROY:
            PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
            break;
        default:                      /* for messages that we don't deal with */
            return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

1
reich napisał(a):
/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("Windows App 32");

Można się obyć bez globala, bo oba użycia są wewnątrz WinMain. Można też do wincl zapisać po prostu literał, a w CreateWindow jako nazwę klasy podać MAKEINTATOM(atom) gdzie atom jest wynikiem funkcji RegisterClass. W ten sposób w ogóle można się pozbyć szClassName, a także sprawdzania wyniku RegisterClass, bo jeśli tam jest zwracany null to dalej sfailuje CreateWindow - której wynik z kolei wypadałoby sprawdzić, a nie jest to robione.

/* Use Windows's default colour as the background of the window */
wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

Częsty błąd. Dokumentacja domaga się dodania 1 jeśli użyta jest stała COLOR_. Czyli powinno być (HBRUSH)(COLOR_BACKGROUND + 1). W tej chwili rysuje się jakiś inny kolor, zamiast COLOR_BACKGROUND.

       HWND_DESKTOP,        /* The window is a child-window to desktop */

Nigdy nie powinno się używać HWND_DESKTOP jako parenta, chyba że robimy jakiegoś hakierskiego toola który rysuje po pulpicie. Normalne główne okna powinny mieć tu NULL (a właściwie nullptr jeśli to C++11 lub nowszy).

/* The program return-value is 0 - The value that PostQuitMessage() gave */
return messages.wParam;

Komentarz powinien raczej brzmieć "The value passed to PostQuitMessage", bo to jest to 0 które jest niżej przekazywane do PostQuitMessage.

    case WM_COMMAND:
        if(( HWND ) lParam == g_hPrzycisk )
        PostQuitMessage (0);
    break;

Można tak, ale dałbym tu DestroyWindow zamiast PostQuitMessage. DestroyWindow wyśle WM_DESTROY które zrobi PostQuitMessage.

    case WM_DESTROY:
        PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
        break;
0

Dobra. Główny wątek został rozwiązany. Ale nadal nie rozumiem, czemu w nazwie okna nie wczytuje mi pełnej nazwy. Mimo ze w kodzie jest inaczej: CreateWindowW(CLASS_NAME, L"WindowsApp", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); gdzie CLASS_NAME jest zdefiniowany w ten sposób const wchar_t CLASS_NAME[] = L"unqiueClassWindow";

screenshot-20220913105346.png

0

Nie robisz przypadkiem RegisterClassA a potem CreateWindowW? Tu raczej powinna być zgodność.

Jeszcze powinieneś dodać manifest, bo bez niego Windows nie zaaplikuje systemowej skórki i masz buttona z Windows 95.

Dwa sposoby podałem tutaj ale oba dotyczą VC++. Nie wiem co to za toolset tam masz (jakieś MinGW?) ale powinien mieć narzędzie do dodania manifestu do exeka...

2

Dobra. Główny wątek został rozwiązany. Ale nadal nie rozumiem, czemu w nazwie okna nie wczytuje mi pełnej nazwy. Mimo ze w kodzie jest inaczej: CreateWindowW(CLASS_NAME, L"WindowsApp", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, NULL, NULL); gdzie CLASS_NAME jest zdefiniowany w ten sposób const wchar_t CLASS_NAME[] = L"unqiueClassWindow";

dopisz na początku pliku

#define UNICODE
#define _UNICODE

#include <windows.h>
#include <iostream>
#include <cwchar>

1 użytkowników online, w tym zalogowanych: 0, gości: 1