Replying to [C] Creiamo un Notepad

  • Create account

    • Nickname:
  • Enter your Post

    •              
           
       
      FFUpload  Huppy Pick colour  HTML Editor  Help
      .
    •      
       
      Clickable Smilies    Show All
      .
  • Clickable Smilies

    • :huh:^_^:o:;):P:D:lol::B)::rolleyes:-_-<_<:)
      :wub::angry::(:unsure::wacko::blink::ph34r::alienff::cry::sick::shifty::woot:
      <3:XD:*_*:];P:XP:(:=)X):D:>.<>_<
      =_=:|:?3_3:p:;_;^U^*^^*:=/::*::b::f:

  •   

Last 10 Posts [ In reverse order ]

  1. Posted 4/3/2013, 19:24
    In questo tutorial vedremo come creare un Notepad scrivendolo totalmente in C, utilizzando le Graph API del sistema (windows.h). Come IDE, per mera comodità, io utilizzerò Visual Studio settato per lavorare in C (dopo aver creato un nuovo progetto Win32 per C++, andate sulle proprietà del suddetto andate in C/C++>Advanced>Compile As e mettete C).

    Eliminate pure il primo file creato e inserite il vostro main.c (o come preferite chiamarlo).
    Dobbiamo in primis importare le librerie che ci serviranno e scrivere il main:
    CODICE
    #include <windows.h>
    #include <commctrl.h>

    const char g_szClassName[] = "noteClass";

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

           InitCommonControls();

           wc.cbSize                 = sizeof(WNDCLASSEX);
           wc.style                 = 0;
           wc.lpfnWndProc         = WndProc;
           wc.cbClsExtra         = 0;
           wc.cbWndExtra         = 0;
           wc.hInstance         = hInstance;
           wc.hIcon                 = LoadIcon(NULL, 0);
           wc.hCursor                 = LoadCursor(NULL, IDC_ARROW);
           wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
           wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MAINMENU);
           wc.lpszClassName = g_szClassName;
           wc.hIconSm                 = LoadIcon(NULL, 0);

           if(!RegisterClassEx(&wc))
           {
                   MessageBox(NULL, "Impossibile avviare il programma", "Errore!",
                           MB_ICONEXCLAMATION | MB_OK);
                   return 0;
           }

           hwnd = CreateWindowEx(
                   0,
                   g_szClassName,
                   "Saffo's Notepad",
                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                   CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
                   NULL, NULL, hInstance, NULL);

           if(hwnd == NULL)
           {
                   MessageBox(NULL, "Impossibile avviare il programma", "Errore!",
                           MB_ICONEXCLAMATION | MB_OK);
                   return 0;
           }

           ShowWindow(hwnd, nCmdShow);
           UpdateWindow(hwnd);

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

    (Ho messo il codice sotto spoiler di modo che i neofiti di C prendessero un coccolone, perdonate la crudeltà gratuita)
    What a terrible code? Sicuramente c'è molto da spiegare: le prime due librerie, ci consentono di interagire con la parte grafica del sistema, mentre la nuova dicitura per il main ci fa comprendere che ci troviamo in un ambiente grafico e non sulla console. Per i nostri scopi, vi spiego solamente l'argomento "hInstance", ovvero il nostro programma stesso.

    Per prima cosa, creiamo tre variabili:
    CODICE
    WNDCLASSEX wc;
           HWND hwnd;
           MSG Msg;

    La prima è la nostra finestra, la classe che la definisce, il secondo è il HWND, che serve per "registrarla sul sistema" e il MSG potete intenderlo come una sorta di event listener che vi spiego dopo.

    CODICE
    wc.cbSize                 = sizeof(WNDCLASSEX);
           wc.style                 = 0;
           wc.lpfnWndProc         = WndProc;
           wc.cbClsExtra         = 0;
           wc.cbWndExtra         = 0;
           wc.hInstance         = hInstance;
           wc.hIcon                 = LoadIcon(NULL, 0);
           wc.hCursor                 = LoadCursor(NULL, IDC_ARROW);
           wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
           wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MAINMENU);
           wc.lpszClassName = g_szClassName;
           wc.hIconSm                 = LoadIcon(NULL, 0);

    Questa è la parte che definisce la nostra finestra base: cbSize indica la grandezza in memoria dell'istanza, lpfnWndProc è il listener per gli eventi, hIcon e hIconSmall sono le due icone del programma, hCursor è il cursore da visualizzare, hbrBackground è il colore di sfondo, lpszMenuName è il nome del menu nelle resource (che vedremo dopo, lasciatelo così), lpszClassName è il nome della classe del programma, che si trova nella variabile precedentemente dichiarata.

    Fatto ciò possiamo registrare la finestra e inviare un errore in caso di non riuscita
    CODICE
    if(!RegisterClassEx(&wc))
           {
                   MessageBox(NULL, "Impossibile avviare il programma", "Errore!",
                           MB_ICONEXCLAMATION | MB_OK);
                   return 0;
           }


    Ora possiamo, appunto, creare la finestra
    CODICE
    hwnd = CreateWindowEx(
                   0,
                   g_szClassName,
                   "Saffo's Notepad",
                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
                   CW_USEDEFAULT, CW_USEDEFAULT, 480, 320,
                   NULL, NULL, hInstance, NULL);

           if(hwnd == NULL)
           {
                   MessageBox(NULL, "Impossibile avviare il programma", "Errore!",
                           MB_ICONEXCLAMATION | MB_OK);
                   return 0;
           }
    ShowWindow(hwnd, nCmdShow);
           UpdateWindow(hwnd);

    480 e 320 sono le dimensioni, "Saffo's Notepad" è il titolo. ShowWindow mostra la finestra e UpdateWindow ne esegue il refresh per assicurarsi l'avvenuta apertura.

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

    Con questo while infinito, chiediamo ogni volta al listener quali eventi si stanno verificando. GetMessage, se non si verifica nulla, si blocca, finché non arrivano eventi. Ci sono miriadi di eventi, ma molti, per nostra fortuna, sono gestiti dal computer e ora vedremo come.

    La nuova funzione che andiamo ad aggiungere, appunto, è quella per gestire gli eventi, WndProc:
    CODICE
    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
           switch(msg)
           {
                   case WM_CREATE:
                   {
                           HFONT hfDefault;
                           HWND hEdit;

                           HWND hTool;
                           TBBUTTON tbb[3];
                           TBADDBITMAP tbab;

                           HWND hStatus;
                           int statwidths[] = {100, -1};

                           hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
                                   WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
                                   0, 0, 100, 100, hwnd, (HMENU)IDC_MAIN_EDIT, GetModuleHandle(NULL), NULL);
                           if(hEdit == NULL)
                                   MessageBox(hwnd, "Impossibile avviare il programma!", "Errore", MB_OK | MB_ICONERROR);

                           hfDefault = GetStockObject(DEFAULT_GUI_FONT);
                           SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));

                           hTool = CreateWindowEx(0, TOOLBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
                                   hwnd, (HMENU)IDC_MAIN_TOOL, GetModuleHandle(NULL), NULL);
                           if(hTool == NULL)
                                   MessageBox(hwnd, "Could not create tool bar.", "Error", MB_OK | MB_ICONERROR);

                           SendMessage(hTool, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
                           
                           tbab.hInst = HINST_COMMCTRL;
                           tbab.nID = IDB_STD_SMALL_COLOR;
                           SendMessage(hTool, TB_ADDBITMAP, 0, (LPARAM)&tbab);

                           ZeroMemory(tbb, sizeof(tbb));
                           tbb[0].iBitmap = STD_FILENEW;
                           tbb[0].fsState = TBSTATE_ENABLED;
                           tbb[0].fsStyle = TBSTYLE_BUTTON;
                           tbb[0].idCommand = ID_FILE_NEW;

                           tbb[1].iBitmap = STD_FILEOPEN;
                           tbb[1].fsState = TBSTATE_ENABLED;
                           tbb[1].fsStyle = TBSTYLE_BUTTON;
                           tbb[1].idCommand = ID_FILE_OPEN;

                           tbb[2].iBitmap = STD_FILESAVE;
                           tbb[2].fsState = TBSTATE_ENABLED;
                           tbb[2].fsStyle = TBSTYLE_BUTTON;
                           tbb[2].idCommand = ID_FILE_SAVEAS;

                           SendMessage(hTool, TB_ADDBUTTONS, sizeof(tbb)/sizeof(TBBUTTON), (LPARAM)&tbb);

                           hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
                                   WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
                                   hwnd, (HMENU)IDC_MAIN_STATUS, GetModuleHandle(NULL), NULL);

                           SendMessage(hStatus, SB_SETPARTS, sizeof(statwidths)/sizeof(int), (LPARAM)statwidths);
                           SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM)"Pronto.");
                   }
                   break;
                   case WM_SIZE:
                   {
                           HWND hTool;
                           RECT rcTool;
                           int iToolHeight;

                           HWND hStatus;
                           RECT rcStatus;
                           int iStatusHeight;

                           HWND hEdit;
                           int iEditHeight;
                           RECT rcClient;

                           hTool = GetDlgItem(hwnd, IDC_MAIN_TOOL);
                           SendMessage(hTool, TB_AUTOSIZE, 0, 0);

                           GetWindowRect(hTool, &rcTool);
                           iToolHeight = rcTool.bottom - rcTool.top;

                           hStatus = GetDlgItem(hwnd, IDC_MAIN_STATUS);
                           SendMessage(hStatus, WM_SIZE, 0, 0);

                           GetWindowRect(hStatus, &rcStatus);
                           iStatusHeight = rcStatus.bottom - rcStatus.top;

                           GetClientRect(hwnd, &rcClient);

                           iEditHeight = rcClient.bottom - iToolHeight - iStatusHeight;

                           hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
                           SetWindowPos(hEdit, NULL, 0, iToolHeight, rcClient.right, iEditHeight, SWP_NOZORDER);
                   }
                   break;
                   case WM_CLOSE:
                           DestroyWindow(hwnd);
                   break;
                   case WM_DESTROY:
                           PostQuitMessage(0);
                   break;
                   case WM_COMMAND:
                           switch(LOWORD(wParam))
                           {
                                   case ID_FILE_EXIT:
                                           PostMessage(hwnd, WM_CLOSE, 0, 0);
                                   break;
                                   case ID_FILE_NEW:
                                           SetDlgItemText(hwnd, IDC_MAIN_EDIT, "");
                                   break;
                                   case ID_FILE_OPEN:
                                           DoFileOpen(hwnd);
                                   break;
                                   case ID_FILE_SAVEAS:
                                           DoFileSave(hwnd);
                                   break;
                                   case ID_INFO_ABOUT:
                                           MessageBox(NULL, "Creato da Saffo\r\nCopyright 2013", "About", MB_OK | MB_ICONINFORMATION);
                                   break;
                           }
                   break;
                   default:
                           return DefWindowProc(hwnd, msg, wParam, lParam);
           }
           return 0;
    }

    Eseguendo uno switch su msg, possiamo sapere che tipo di evento si sta verificando e in tal caso gestirlo.
    WM_CREATE è l'evento dell'avvio della finestra, infatti creiamo tutti i controlli.
    WM_SIZE si verifica se viene ridimensionata la finestra, in tal caso tutti gli elementi subiranno un ridimensionamento.
    WM_CLOSE e DESTROY sono di default per chiudere correttamente la finestra mentre WM_COMMAND gestisce gli eventi generati da un pulsante, inviato come wParam. Tutti i nomi che vedete andremo poi ad aggiungerli nei file resource.

    Abbiamo visto che noi gestiamo ben pochi eventi, ma chi gestisce, ad esempio, il mouse che si sposta sul programma? O il fatto che la finestra venga ridimensionata o ridotta ad icona? Per fortuna c'è DefWindowProc() che lo fa per noi ^.^

    Ok, per ora possiamo lasciare da parte questo file e passare alle resource, ovvero la definizione dei menu e dei pulsanti. Creiamo un nuovo file di risorse .rc (nomeprogramma.rc) dove inseriremo questo codice:
    CODICE
    #include "resource.h"

    IDR_MAINMENU MENU DISCARDABLE
    BEGIN
       POPUP "&File"
       BEGIN
           MENUITEM "&Nuovo",                        ID_FILE_NEW
           MENUITEM "&Apri...",                                        ID_FILE_OPEN
           MENUITEM "Salva Come...",                 ID_FILE_SAVEAS
           MENUITEM SEPARATOR
           MENUITEM "Esci",                       ID_FILE_EXIT
       END
           POPUP "&?"
           BEGIN
                   MENUITEM "About" ID_INFO_ABOUT
           END
    END

    Il codice non è molto complesso e infatti si vede che stiamo creando vari menu (File e About) con dei sottomenu. Al posto di "Begin" e "End" potete sostituire benissimo { e }.
    Il nome che viene dopo la dichiarazione dell'item è il nome della risorsa da usare nel programma.
    Avrete notato anche un'inclusione di resource.h: create quel file, che conterrà l'assegnazione degli id ai vari elementi:
    CODICE
    #define IDR_MAINMENU                    102
    #define ID_FILE_EXIT                    40001
    #define ID_FILE_OPEN                    40002
    #define ID_FILE_SAVEAS                  40003
    #define ID_FILE_NEW                     40004
    #define ID_INFO_ABOUT                                        40007

    Gli id possono essere qualsiasi, ma è bene dividerli con delle categorie di numeri (per me, gli 10x sono i principali, 4000x sono gli items del menu)

    Potete salvare tutto e riaprire il vostro file main.c: dobbiamo inserire le funzioni già citate per il salvataggio e l'apertura!
    CODICE
    BOOL LoadTextFileToEdit(HWND hEdit, LPCTSTR pszFileName)
    {
           HANDLE hFile;
           BOOL bSuccess = FALSE;

           hFile = CreateFile(pszFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
                   OPEN_EXISTING, 0, NULL);
           if(hFile != INVALID_HANDLE_VALUE)
           {
                   DWORD dwFileSize;

                   dwFileSize = GetFileSize(hFile, NULL);
                   if(dwFileSize != 0xFFFFFFFF)
                   {
                           LPSTR pszFileText;

                           pszFileText = GlobalAlloc(GPTR, dwFileSize + 1);
                           if(pszFileText != NULL)
                           {
                                   DWORD dwRead;

                                   if(ReadFile(hFile, pszFileText, dwFileSize, &dwRead, NULL))
                                   {
                                           pszFileText[dwFileSize] = 0; // Add null terminator
                                           if(SetWindowText(hEdit, pszFileText))
                                                   bSuccess = TRUE; // It worked!
                                   }
                                   GlobalFree(pszFileText);
                           }
                   }
                   CloseHandle(hFile);
           }
           return bSuccess;
    }

    BOOL SaveTextFileFromEdit(HWND hEdit, LPCTSTR pszFileName)
    {
           HANDLE hFile;
           BOOL bSuccess = FALSE;

           hFile = CreateFile(pszFileName, GENERIC_WRITE, 0, NULL,
                   CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
           if(hFile != INVALID_HANDLE_VALUE)
           {
                   DWORD dwTextLength;

                   dwTextLength = GetWindowTextLength(hEdit);
                   // No need to bother if there's no text.
                   if(dwTextLength > 0)
                   {
                           LPSTR pszText;
                           DWORD dwBufferSize = dwTextLength + 1;

                           pszText = GlobalAlloc(GPTR, dwBufferSize);
                           if(pszText != NULL)
                           {
                                   if(GetWindowText(hEdit, pszText, dwBufferSize))
                                   {
                                           DWORD dwWritten;

                                           if(WriteFile(hFile, pszText, dwTextLength, &dwWritten, NULL))
                                                   bSuccess = TRUE;
                                   }
                                   GlobalFree(pszText);
                           }
                   }
                   CloseHandle(hFile);
           }
           return bSuccess;
    }

    E poi le due funzioni che apriranno i fileDialog:
    CODICE
    void DoFileOpen(HWND hwnd)
    {
           OPENFILENAME ofn;
           char szFileName[MAX_PATH] = "";

           ZeroMemory(&ofn, sizeof(ofn));

           ofn.lStructSize = sizeof(ofn);
           ofn.hwndOwner = hwnd;
           ofn.lpstrFilter = "Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
           ofn.lpstrFile = szFileName;
           ofn.nMaxFile = MAX_PATH;
           ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
           ofn.lpstrDefExt = "txt";

           if(GetOpenFileName(&ofn))
           {
                   HWND hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
                   if(LoadTextFileToEdit(hEdit, szFileName))
                   {
                           SendDlgItemMessage(hwnd, IDC_MAIN_STATUS, SB_SETTEXT, 0, (LPARAM)"Opened...");
                           SendDlgItemMessage(hwnd, IDC_MAIN_STATUS, SB_SETTEXT, 1, (LPARAM)szFileName);
                   }
           }
    }

    void DoFileSave(HWND hwnd)
    {
           OPENFILENAME ofn;
           char szFileName[MAX_PATH] = "";

           ZeroMemory(&ofn, sizeof(ofn));

           ofn.lStructSize = sizeof(ofn);
           ofn.hwndOwner = hwnd;
           ofn.lpstrFilter = "File di Testo (*.txt)\0*.txt\0Tutti i files (*.*)\0*.*\0";
           ofn.lpstrFile = szFileName;
           ofn.nMaxFile = MAX_PATH;
           ofn.lpstrDefExt = "txt";
           ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;

           if(GetSaveFileName(&ofn))
           {
                   HWND hEdit = GetDlgItem(hwnd, IDC_MAIN_EDIT);
                   if(SaveTextFileFromEdit(hEdit, szFileName))
                   {
                           SendDlgItemMessage(hwnd, IDC_MAIN_STATUS, SB_SETTEXT, 0, (LPARAM)"Salvato...");
                           SendDlgItemMessage(hwnd, IDC_MAIN_STATUS, SB_SETTEXT, 1, (LPARAM)szFileName);
                   }
           }
    }


    Aggiungete ora alle inclusioni anche resource.h, il codice assomiglierà a questo:
    saffo_notepad.c
    NB: io ho aggiunto anche l'icona.

    Finalmente potete compilare e avviare il vostro progetto e vederlo funzionare. Ho calcolato che per fare una cavolata del genere sono richieste ~315 righe di codice... Immaginate per fare altro...

    Non vi ho descritto tutte le parti del codice, solo quelle salienti ed importanti, presumendo che voi abbiate già una base di developing in C e magari anche con le API di Windows.

    Grazie a tutti per la lettura ^.^

    PS: Download Archivio

Review the complete topic (launches new window)