Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Kurs WinAPI - okna dialogowe

Ostatnio zmodyfikowano 2016-07-17 20:07
Autor Wiadomość
mlodymichal1990
Temat założony przez niniejszego użytkownika
Kurs WinAPI - okna dialogowe
» 2016-07-16 13:36:59
Ostatnio czytam kurs: http://cpp0x.pl/kursy​/Kurs-WinAPI-C++/167 . Na rozdziałach o oknach dialogowych zaczęły się schody, których nie mogę przezwyciężyć. Wiem, że na dole każdego rozdziału widnieje info, że kody mogą być nieaktualne, więc może to jest powodem. Część kodów mi się w ogóle nie kompiluje, a część się kompiluje, ale nie działa. Do rzeczy.

Rozdział 9.
Kod nie działał mi, tzn. okno dialogowe się nie wyświetlało. W końcu znalazłem rozwiązanie, należało w linijce:
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION

dołożyć: WS_VISIBLE. Tutaj moja sugestia do administratorów, aby poprawić ten kod, coby podobni do mnie początkujący programiści niepotrzebnie nie irytowali się i nie tracili swojego czasu :)

Rozdział 12.
Podobna sytuacja - kompilacja zakończona sukcesem, niestety dialog nie wyświetla się. Wklejam cały kod.
C/C++
#include <windows.h>
#include "pasek.h"

// Prototypy funkcji obsługujących komunikaty.
// Sztuk dwie, jedna dla okna głównego, jedna dla dialogu ;-)
LPSTR NazwaKlasy = "Klasa Okienka";
MSG msg;
LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
BOOL CALLBACK DlgProc( HWND, UINT, WPARAM, LPARAM );
HWND hpasek;
HFONT g_hfnNowy;
LPSTR bufor, bufor2;
UINT WM_FINDREPLACE;
FINDREPLACE fr;

int WINAPI WinMain( HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument,
int nFunsterStil )
{
    // WYPEŁNIANIE STRUKTURY
    WNDCLASSEX wc;
   
    wc.cbSize = sizeof( WNDCLASSEX );
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hThisInstance;
    wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hbrBackground =( HBRUSH )( COLOR_WINDOW + 1 );
    wc.lpszMenuName = NULL;
    wc.lpszClassName = NazwaKlasy;
    wc.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
   
    // REJESTROWANIE KLASY OKNA
    if( !RegisterClassEx( & wc ) )
    {
        MessageBox( NULL, "Wysoka Komisja odmawia rejestracji tego okna!", "Niestety...",
        MB_ICONEXCLAMATION | MB_OK );
        return 1;
    }
    WM_FINDREPLACE = RegisterWindowMessage( FINDMSGSTRING );
    // TWORZENIE OKNA
    HWND hwnd;
   
    hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, NazwaKlasy, "Oto okienko", WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, NULL, NULL, hThisInstance, NULL );
    RECT rcClient;
    GetClientRect( hwnd, & rcClient );
    // ...lub GetSaveFileName (&ofn), jeśli zapisujemy
   
    // Tworzymy jakiś przycisk do wywołania dialogu
    #define ID_PLIK 501
    hpasek = CreateWindowEx( 0, "BUTTON", "Dialog", WS_CHILD | WS_VISIBLE | WS_BORDER,
    0, rcClient.bottom - 27, 50, 25, hwnd,( HMENU ) ID_PLIK, hThisInstance, NULL );
   
    if( hwnd == NULL )
    {
        MessageBox( NULL, "Okno odmówiło przyjścia na świat!", "Ale kicha...", MB_ICONEXCLAMATION );
        return 1;
    }
   
    // Pokazujemy główne okno
    ShowWindow( hwnd, nFunsterStil );
   
    // No i standardowo - pętelka
    while( GetMessage( & msg, NULL, 0, 0 ) )
    {
        TranslateMessage( & msg );
        DispatchMessage( & msg );
    }
   
    return msg.wParam;
}

// Procedura okna
LRESULT CALLBACK WndProc( HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam )
{
    switch( mesg )
    {
    case WM_COMMAND:
        {
            if( wParam != ID_PLIK ) break;
            //  if(( HWND ) lParam != hpasek ) break;
            ZeroMemory( & fr, sizeof( FINDREPLACE ) );
            fr.lStructSize = sizeof( FINDREPLACE );
            fr.hwndOwner = hwnd;
            fr.lpstrFindWhat = bufor;
            fr.wFindWhatLen = 80;
            fr.lpstrReplaceWith = bufor2; ///
            fr.wReplaceWithLen = 80; ///
            fr.Flags = FR_HIDEUPDOWN | FR_NOMATCHCASE | FR_HIDEWHOLEWORD;
           
            lstrcpy( bufor, "tekst" );
            lstrcpy( bufor2, "inny tekst" ); ///
            FindText( & fr );
            ShowWindow( hwnd, SW_SHOW );
           
        }
        break;
       
    case WM_DESTROY:
        PostQuitMessage( 0 );
        break;
       
        default:
        if( mesg == WM_FINDREPLACE )
        {
            FINDREPLACE * fr =( FINDREPLACE * ) lParam;
           
            if( fr->Flags & FR_FINDNEXT )
            {
                MessageBox( hwnd, "Nic nie znaleziono.", NULL, MB_ICONINFORMATION );
            }
        }
        else
        {
            return DefWindowProc( hwnd, mesg, wParam, lParam );
        }
    }
    return 0;
}

// Procedura dialogowa
BOOL CALLBACK DlgProc( HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    switch( Msg )
    {
    case WM_COMMAND:
        {
           
        }
        break;
       
        default: return FALSE;
    }
    return TRUE;
}

Rozdział 13.
W procedurze hakowej pojawia się instrukcja:
SendMessage( hdlg, CDM_GETFILEPATH, sizeof( buffer ),( LPARAM ) & buffer );

buffer nigdzie powyżej nie jest zdeklarowane i jest błąd kompilacji. Z kontekstu całości wnioskuję, że jest to string zawierający nazwę pliku i że "pobieramy" go ze struktury OPENFILENAME. Nie mam natomiast pojęcia, jak to "wykodzić". Podobnie instrukcja:
assert( g_hDCMem );

Mój kompilator nie zna funkcji assert, nigdzie powyżej nie jest zdefiniowana.

Rozdział 14.
Kompilacja zakończona sukcesem - dialog nie wyświetla się, WS_VISIBLE nie pomogło.

C/C++
#include <windows.h>

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure( HWND, UINT, WPARAM, LPARAM );
BOOL CALLBACK DialogProc( HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam );

/*  Make the class name into a global variable  */
char szClassName[] = "CodeBlocksWindowsApp";
HGLOBAL hgbl;
DLGTEMPLATE * pdt;
DLGITEMTEMPLATE * pdit;
WORD * pw;
LPWSTR pws;
int nchar;
void PixelsToDialogUnits( RECT & rc );
WORD * lpwAlign( WORD * in );

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 */
    "Code::Blocks Template Windows App", /* 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 */
    );
   
    hgbl = GlobalAlloc( GMEM_ZEROINIT, 1024 );
    pdt =( DLGTEMPLATE * ) GlobalLock( hgbl );
    pdt->style = WS_POPUP | WS_BORDER | WS_SYSMENU | DS_MODALFRAME | WS_CAPTION | WS_VISIBLE;
    pdt->cdit = 2;
    RECT rcDlg = { 10, 10, 200, 150 };
    PixelsToDialogUnits( rcDlg );
   
    pdt->x =( short ) rcDlg.left;
    pdt->y =( short ) rcDlg.top;
    pdt->cx =( short ) rcDlg.right;
    pdt->cy =( short ) rcDlg.bottom;
   
    pw =( WORD * )( pdt + 1 );
    * pw++ = 0x0; // brak menu
    * pw++ = 0x0; // predefiniowana klasa dialogu
   
    pws =( LPWSTR ) pw;
    nchar = 1 + MultiByteToWideChar( CP_ACP, 0, "Mój dialog", - 1, pws, 50 );
    pw += nchar;
   
    pw = lpwAlign( pw );
    pdit =( DLGITEMTEMPLATE * ) pw;
   
    RECT rcBtn = { 50, 100, 100, 25 };
    PixelsToDialogUnits( rcBtn );
   
    pdit->x =( short ) rcBtn.left;
    pdit->y =( short ) rcBtn.top;
    pdit->cx =( short ) rcBtn.right;
    pdit->cy =( short ) rcBtn.bottom;
   
    pdit->id = IDOK;
    pdit->style = WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON;
   
    pw =( WORD * )( pdit + 1 );
    * pw++ = 0xffff;
    * pw++ = 0x0080; // przycisk
   
    pws =( LPWSTR ) pw;
    nchar = 1 + MultiByteToWideChar( CP_ACP, 0, "OK", - 1, pws, 50 );
    pw += nchar;
    * pw++ = 0; // brak dodatkowych danych
    // DRUGA KONTROLKA
    pw = lpwAlign( pw );
    pdit =( DLGITEMTEMPLATE * ) pw;
   
    RECT rcEdit = { 25, 10, 150, 25 };
    PixelsToDialogUnits( rcEdit );
   
    pdit->x =( short ) rcEdit.left;
    pdit->y =( short ) rcEdit.top;
    pdit->cx =( short ) rcEdit.right;
    pdit->cy =( short ) rcEdit.bottom;
   
    pdit->id = 1000;
    pdit->style = WS_CHILD | WS_VISIBLE | WS_BORDER;
   
    pw =( WORD * )( pdit + 1 );
    * pw++ = 0xffff;
    * pw++ = 0x0081; // edit box
   
    pws =( LPWSTR ) pw;
    nchar = 1 + MultiByteToWideChar( CP_ACP, 0, "Tekst", - 1, pws, 50 );
    pw += nchar;
    * pw++ = 0; // brak dodatkowych danych
   
    GlobalUnlock( hgbl );
    DialogBoxIndirect( hThisInstance,( DLGTEMPLATE * ) hgbl, hwnd,( DLGPROC ) DialogProc );
    ShowWindow( hwnd, SW_SHOW );
    //   GlobalFree( hgbl );
   
    /* 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_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;
}

BOOL CALLBACK DialogProc( HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{
    return false;
}

void PixelsToDialogUnits( RECT & rc )
{
    LONG base = GetDialogBaseUnits();
    int baseX = LOWORD( base );
    int baseY = HIWORD( base );
   
    rc.left = MulDiv( rc.left, 4, baseX );
    rc.right = MulDiv( rc.right, 4, baseX );
    rc.top = MulDiv( rc.top, 8, baseY );
    rc.bottom = MulDiv( rc.bottom, 8, baseY );
}

WORD * lpwAlign( WORD * in )
{
    ULONG_PTR ul =( ULONG_PTR ) in;
    ul += 3;
    ul >>= 2;
    ul <<= 2;
    return( WORD * ) ul;
}

Zastanawiam się, czy mam problemy, bo kurs jest nieaktualny, czy ja coś źle robię. Jeżeli to pierwsze, to czy jest ktoś w stanie polecić jakiś inny dobry kurs? Proszę o pomoc.
P-150023
marcolo2307
» 2016-07-16 16:27:41
Z tego co pamiętam, to też mi kiedyś nie działały kody z tutejszego poradnika, głównie pliki *.rc. Ogólnie wydaje mi się, że nie warto uczyć się WinAPI, co najwyżej można go używać dla niektórych funkcji. Lepiej ucz się Qt: http://cpp0x.pl/kursy​/Kurs-Qt-C++/503.
P-150025
Monika90
» 2016-07-16 16:51:59

W procedurze hakowej pojawia się instrukcja:
SendMessage( hdlg, CDM_GETFILEPATH, sizeof( buffer ),( LPARAM ) & buffer );

buffer nigdzie powyżej nie jest zdeklarowane i jest błąd kompilacji. Z kontekstu całości wnioskuję, że jest to string zawierający nazwę pliku i że "pobieramy" go ze struktury OPENFILENAME. Nie mam natomiast pojęcia, jak to "wykodzić".

Ten bufor musisz sobie sam zadeklarować
C/C++
char buffer[ MAX_PATH ];



Podobnie instrukcja:
assert( g_hDCMem );

Mój kompilator nie zna funkcji assert, nigdzie powyżej nie jest zdefiniowana.

Żeby mieć assert w C
#include <assert.h>

W C++
#include <cassert>




Rozdział 12.
Podobna sytuacja - kompilacja zakończona sukcesem, niestety dialog nie wyświetla się. Wklejam cały kod.

C/C++
LPSTR bufor, bufor2;
Te wskaźniki mają wartość null, zastąp je tablicami znaków.
P-150026
mlodymichal1990
Temat założony przez niniejszego użytkownika
» 2016-07-16 22:48:42
Dzięki! To, co napisałaś, poprawiłem. Jednak w kodzie z rozdziału 13 (ach, ta pechowa 13!) pojawiły się 2 kolejne problemy. Kompilator nie zna stałej
IDC_BMPPREVIEW
 i funkcji
SelectBitmap


Kilka lekcji dalej jest też problem z funkcją
ComboBox_SetCurSel
 , także i jej kompilator nie zna.
P-150048
mlodymichal1990
Temat założony przez niniejszego użytkownika
» 2016-07-17 12:45:48
Problem
SelectBitmap
 i
ComboBox_SetCurSel
 rozwiązałem sam -
#include<windowsx.h>
 . W rozwiązaniu problemu pomógł mi rozdział 19. Ale jak widzę, w kursie tym jest sporo niedopowiedzeń i początkującym naprawdę trudno się połapać.
Dlaczego
IDC_BMPPREVIEW
 nie działa - nadal nie wiem.
P-150055
Luq
» 2016-07-17 14:39:25
Zamień IDC_BMPPREVIEW na numer identyfikacyjny, który nadałeś checkboxowi w pliku .rc. Jeśli masz taki sam kod, jak w tym kursie, to powinien to być IDC_PREVIEW.
P-150065
mlodymichal1990
Temat założony przez niniejszego użytkownika
» 2016-07-17 19:43:51
Faktycznie, dzięki! Teraz cały kod mi się kompiluje. Niestety - nie działa tak jak trzeba. Udało mi się wydedukować, że błąd występuje w tym miejscu:
C/C++
UINT APIENTRY OFNHookProc( HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uiMsg )
    {
       
    case WM_NOTIFY:
        {
            NMHDR * pnmhdr =( NMHDR * ) lParam;
            OFNOTIFY * ofnot =( OFNOTIFY * ) lParam;
           
            if( pnmhdr->code == CDN_SELCHANGE )
            {
                char buffer[ MAX_PATH ];
                SendMessage( hdlg, CDM_GETFILEPATH, sizeof( buffer ),( LPARAM ) & buffer );
                LPSTR filename = buffer;
               
                FreeBitmap();
                g_hBitmap =( HBITMAP ) LoadImage( GetModuleHandle( NULL ), filename, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
               
            }
Tutaj chodzi o to, żeby po kliknięciu na plik *.bmp wczytywało go do uchwytu g_hBitmap. Niestety nie robi tego i nie mam pojęcia, jak sprawić, żeby robiło. Załóżmy, że mam na dysku plik
rysunek.bmp
. Gdy zmodyfikuję ostatnią linijkę do:
g_hBitmap =( HBITMAP ) LoadImage( GetModuleHandle( NULL ), "rysunek.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
 , to wczytuje poprawnie (choć oczywiście jest uniezależnione od kliknięcia na plik). Wie ktoś może, jak to należy napisać?
P-150077
Luq
» 2016-07-17 20:07:15
Przy SendMessage zamiast hdlg wpisz GetParent(hdlg) - wiadomość musisz wysłać do okna-rodzica
P-150078
« 1 »
  Strona 1 z 1