Wywoływanie i obsługa standardowych okien Otwórz i Zapisz oraz zapis do pliku
Stronę tą wyświetlono już: 2292 razy
Wstęp
Na poprzedniej stronie omawiany został projekt, w którym dodane zostało menu, ikonka, kursor oraz akceleratory oraz dodana została pierwsza kontrolka typu EDIT. Był to początek programu o wdzięcznej nazwie myNotepad i wszystko byłoby pięknie, gdyby nie fakt, że program ten wcale nie wczytuje tekstu z pliku ani też go nie zapisuje. Na tej stronie będę kontynuował poprzednio zaczęty projekt omawiając tym samym sposób wykorzystania dwóch z pośród rodziny okien standardowych systemu Windows. Te standardowe okna istnieją dlatego, że bardzo często różne programy korzystają z tej samej lub podobnej funkcjonalności a więc aby nie powielać ciągle tego samego kodu przez różne programy część obowiązku za dostarczenie pewnych narzędzi spoczywa na systemie. Do takich standardowych okien należą okno dialogowe Otwórz oraz Zapisz, które zasadniczo wykorzystuje większość programów do wczytywania lub zapisywania jakiegoś pliku z dysku twardego komputera.
W WinApi w celu wywołania okna dialogowego Otwórz używa się funkcji GetOpenFileName, natomiast do wywołania okna Zapisz wykorzystuje się funkcję GetSaveFileName. Obie wspomniane wcześniej funkcje przyjmują tylko jeden argument, którym jest wskaźnik do prawidłowo wypełnionej struktury typu OPENFILENAME. Jeżeli struktura ta została prawidłowo wypełniona, wcześniej wymienione funkcje wywołają okna dialogowe.
Modyfikacja kodu programu myNotepad
Czas najwyższy zakasać rękawy i czym prędzej dodać do programu nowe elementy, ale zanim to najpierw na samym początku projektu należy załączyć plik nagłówkowy locale.h:
- #include <locale.h>
po czym niezwłocznie w ciele funkcji WinMain na samym początku dodać następujące wywołanie funkcji setlocale:
- setlocale(LC_CTYPE, "");
Taki zabieg jest konieczny, aby w oknach dialogowych poprawnie wczytywane były polskie znaki i aby program poprawnie odczytywał polskie znaki w plikach, choć możliwe jest, że skoro wykorzystują w projekcie tryb z unicodem to obsługa polskich znaków powinna być poprawna.
W ciele funkcji hwndProc na samym jej początku konieczne będzie dodanie kilku nowych zmiennych statycznych:
- LRESULT CALLBACK hwndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
- static RECT wndSize;
- static HWND hedit; // kontrolka edit do przechowywania tekstu
- static OPENFILENAME ofn; // struktura związana z obsługą okna otwierania pliku i zapisywania do pliku
- static TCHAR FileName[MAX_PATH]; // zmienna, która przechowywać będzie nazwę pliku wraz z ścieżką
- static TCHAR TitleName[MAX_PATH]; // zmienna, która będzie przechowywała nazwę pliku
Jak widać jest tu instancja struktury OPENFILENAME, oraz dwa łańcuchy znaków typu TCHAR*. Opis tych dodatkowych zmiennych zamieściłem w komentarzach powyższego kodu. W komunikacie WM_COMMAND należy dodać następujący kod programu:
- TitleName[0] = 0; // ustaiwam, żeby rzadnych śmieci nie było
- FileName[0] = 0; // to samo co poprzednio
- ZeroMemory(&ofn,sizeof(ofn)); // zeruję pola struktury
- ofn.lStructSize = sizeof(OPENFILENAME); // zapisuję rozmiar struktury
- ofn.hwndOwner = hWnd; // okno rodzić dla okna otwierania lub zapisywania
- ofn.hInstance = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE); // uchwyt instancji
- ofn.lpstrFilter = L"Plik tekstowy (*.txt) *.txt "; // filtr dostępnych rozszeżeń plików
- ofn.lpstrFile = FileName; // wskaźnik do bufora nazwy pliku
- ofn.nMaxFile = MAX_PATH; // rozmiar tego bufora
- ofn.lpstrFileTitle = TitleName; // wskaźnik do bufora ścieżki do pliku
- ofn.nMaxFileTitle = MAX_PATH; // rozmiar tego bufora
- ofn.nFileExtension = 0;
- ofn.lpstrDefExt = L" ";
- ofn.Flags = OFN_PATHMUSTEXIST;// | OFN_FILEMUSTEXIST; // flagi (ściażka musi istnieć
I tutaj wszystko zostało omówione w komentarzach kodu programu. Teraz w komunikacie WM_COMMAND w switch-u z identyfikatorem pozycji menu ID_PLIK_OTWORZ należy zastąpić dotychczas wstawiony tam MessageBox następującym kodem:
- case ID_PLIK_OTWORZ:
- {
- if(GetOpenFileName(&ofn)){ // Jeżeli wywołanie okna Otwórz zakończy się powodzeniem i wybrany zostanie plik tekstowy do otworzenia to
- HANDLE hfile; // uchwyt pliku
- if(INVALID_HANDLE_VALUE == (hfile = CreateFile(FileName,GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL))){ // jeżeli otwarcie pliku się nie powiedzie
- MessageBox(hWnd, L"Coś nie tak z plikiem", L"Informacja",MB_OK); // to płacz, że się nie powiodło
- }else{ // a jak nie to
- UINT filesize = GetFileSize(hfile, NULL); // pobieraj rozmiar pliku
- PBYTE buffer = new BYTE[filesize + 2]; // odpowiedni bufor danych rezerwuję
- DWORD dwRead; // ta zmienna otrzyma informację o liczbie bajtów wczytanych do zmiennej bufora
- ReadFile(hfile, buffer, filesize, &dwRead, NULL); // wczytywanie danych z pliku
- CloseHandle(hfile); // zamykanie pliku
- buffer[filesize] = buffer[filesize + 1] = L' '; // zerowanie nadmiarowych elementów
- wchar_t *text = new wchar_t[filesize + 1]; // tworzenie bufora na dane dla typu wchar_t
- MultiByteToWideChar(CP_ACP, 0, (LPCSTR)buffer, -1, text, filesize + 1); // zamiana z ASCII (z domyślnym kodowaniem systemowym) na UTF8
- SetWindowText(hedit, text); // wczytywanie tekstu do okna hedit
- delete [] text; // zwalniam bufor danych
- delete [] buffer; // to samo co poprzednio
- }
- }
- }
W powyższym kodzie wykorzystana została funkcja MultiByteToWideChar a to z tego względu, że kontrolka EDIT wykorzystuje w projekcie tekst unicode, natomiast ja tutaj założyłem, że wczytywane dane są zapisane w standardowym kodowaniu używanym przez system użytkownika.
Podobnie wstawić należy kod dla identyfikatora pozycji w menu ID_PLIK_ZAPISZ:
- case ID_PLIK_ZAPISZ:
- {
- if(GetSaveFileName(&ofn)){ // jeżeli wyświetlenie okna się powiedzie i użytkownik wybierze plik do zapisu to
- HANDLE hfile; // uchwyt pliku
- UINT textsize = GetWindowTextLength(hedit); // pobieram już zawczasu długość tekstu przechowywanego w oknie hedit
- if(INVALID_HANDLE_VALUE == (hfile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL))){ // jak plik się nie wczyta to
- MessageBox(hWnd, L"Nie udało się utworzyć pliku",L"Informacja",MB_OK); // płacz, że się nie wczytał
- }else if(textsize){ // a jak nie i tekst zawarty w kontrolce jest dłuższy niż 0 to
- wchar_t *text = new wchar_t[textsize + 1]; // rezerwuj pamięć
- GetWindowText(hedit, text, textsize + 1); // pobieraj dane
- char *text2 = new char[textsize + 1]; // przygotuj bufor pamięci do konwersji z UTF-8 na ASCII w kodowaniu standardowym Windowsa
- WideCharToMultiByte(CP_ACP,WC_COMPOSITECHECK,text, textsize,text2,textsize,NULL, NULL); // zamiana z UTF-8 na ASCII
- DWORD byteorder = 0xFF; // potrzebne do funkcji WriteFile
- text2[textsize] = ' '; // na końcu zero trzeba wstawić
- WriteFile(hfile, (LPVOID)text2, textsize+1,&byteorder,NULL); // zapis do pliku
- CloseHandle(hfile); // zamykanie pliku
- delete [] text2; // zwalnianie pamięci
- delete [] text; // i to samo
- }
- }
- }
W tej chwili już mamy jako - tako obsłużone wczytywanie danych z pliku i zapisywanie do pliku.
Spis nowych funkcji i struktur użytych w programie
Oto spis nowych funkcji:
- GetOpenFileName - wyświetla okno dialogowe Otwórz, funkcja ta związana jest ściśle z strukturą OPENFILENAME;
- GetSaveFileName - wyświetla okno dialogowe Zapisz, funkcja ta związana jest ściśle z strukturą OPENFILENAME;
- CloseHande - zwalnia uchwyt w kodzie programu funkcja została użyta do zamknięcia uchwytu pliku pozyskanego funkcją CreateFile;
- CreateFile - funkcja wykorzystana do utworzenia pliku i pozyskania jego uchwytu. Z tą funkcją powiązane są funkcje ReadFile, WriteFile oraz CloseHandle;
- GetWindowLong - umożliwia pozyskiwanie informacji związanych z danym uchwytem okna. W kodzie użyto tej funkcji do pozyskania uchwytu instancji programu typu HINSTANCE;
- MultiByteToWideChar - zamiana tekstu zapisanego z standardowym kodowaniem ustawionym w systemie na utf-8;
- ReadFile - czytanie danych z pliku, związane z funkcją CreateFile;
- setlocale - funkcja ustawia pewne parametry związane z lokalnymi ustawieniami wyświetlania i przetwarzania tekstu;
- SetWindwText - funkcja ustawia tekst wyświetlany w danym oknie programu. W kodzie funkcji tej użyto do ustawienia tekstu zawartego w kontrolce hedit;
- WideCharToMultiByte - zamiana z utf-8 na standardowe kodowanie Windowsa;
- WriteFile - zapisywanie danych do pliku, związane z funkcją CreateFile;
Wykorzystane w kodzie struktury:
- OPENFILENAME - struktura związana z wywoływaniem okien Otwórz i Zapisz za pomocą funkcji GetOpenFileName oraz GetSaveFileName.

Tytuł:
Architektura oprogramowania bez tajemnic. Wykorzystaj język C++ do tworzenia wydajnych aplikacji i systemów
Autor:
Adrian Ostrowski, Piotr Gaczkowski

Tytuł:
Opus magnum C++ 11. Programowanie w języku C++. Wydanie II poprawione (komplet)
Autor:
Jerzy Grębosz

Tytuł:
Programowanie wieloplatformowe z C++ i wxWidgets 3
Autor:
Bartosz W. Warzocha

Tytuł:
Język C++ i przetwarzanie współbieżne w akcji. Wydanie II
Autor:
Anthony Williams

Tytuł:
C++ dla bystrzaków. Wydanie VII
Autor:
Stephen R. Davis

Tytuł:
Tablice informatyczne. Podstawy C++
Autor:
Radosław Sokół

Tytuł:
Opus magnum C++11. Programowanie w języku C++ (komplet)
Autor:
Jerzy Grębosz

Tytuł:
OpenCV 3. Komputerowe rozpoznawanie obrazu w C++ przy użyciu biblioteki OpenCV
Autor:
Adrian Kaehler, Gary Bradski

Tytuł:
C++ w 24 godziny. Wydanie VI
Autor:
Rogers Cadenhead, Jesse Liberty

Tytuł:
C++ Optymalizacja kodu. Sprawdzone techniki zwiększania wydajności
Autor:
Guntheroth Kurt