Powrót do strony wyboru lekcji

Programowanie okien 3

Struktura OPENFILENAME



W systemie Windows, często zachodzi potrzeba skorzystania z okna dialogu definiującego ścieżkę dostępu do pliku. Wyświetlany jest on przede wszystkim podczas operacji wczytania pliku do programu, bądź jego zapisania. - Rys1.

Nie można wyświetlić obrazu
Rys.1.Okno dialogowe dostępu do pliku.

Aby w naszym programie posłużyć się takim oknem dialogowym, należy skorzystać ze struktury OPENFILENAME. Struktura ta jest następnie parametrem odpowiedniej funkcji zapisującej, bądź otwierającej plik do programu.

Przykład

Tworzymy prosty edytor tekstowy, podobny w funkcjonowaniu do notatnika. Program ma umożliwić otwieranie plików i tworzenie nowych plików poprzez wyczyszczenie pola edycyjnego po wybraniu opcji-nowy plik. Interfejs programu będzie definiowany przez plik zasobów, przedstawiony poniżej. Na początku definiujemy standardowo typ instrukcji procesora, określamy model pamięci jako płaski z wywołaniem procedur typu STDCALL. Dołączamy także odpowiednie biblioteki: user32.lib i kernel32.lib oraz dodatkowo, w porównaniu z poprzednimi programami wykorzystującymi okna, bibliotekę Comdlg32.lib:


.586 ;typ instrukcji procesora

.MODEL FLAT,STDCALL

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\Comdlg32.lib

Definiujemy stałe określające kontrolki wykorzystywane w programie. Kontrolkami będą poszczególne opcje menu:


ID_NOWY      EQU   1010
ID_OTWORZ    EQU   1020
ID_ZAKONCZ   EQU   1050
IDC_EDT1     EQU   101

Następnie definiujemy wszystkie stałe potrzebne do poprawnego funkcjonowania programu:


OPEN_EXISTING       EQU   3
GENERIC_READ        EQU   80000000h
GENERIC_WRITE       EQU   40000000h
WM_SETICON          EQU   80h
WM_COMMAND          EQU   111h
WM_SETTEXT          EQU   0Ch
WM_GETTEXT          EQU   0Dh
WM_CLOSE            EQU   10h
WM_INITDIALOG       EQU   110h

OFN_ENABLESIZING          EQU   00800000h
OFN_FORCESHOWHIDDEN       EQU   10000000h
OFN_EXPLORER              EQU   00080000h
OFN_PATHMUSTEXIST         EQU   00000800h
OFN_OVERWRITEPROMPT       EQU   00000002h
OFN_NODEREFERENCELINKS    EQU   00100000h
OFN_FILEMUSTEXIST         EQU   00001000h
OFN_HIDEREADONLY          EQU   00000004h

Pierwsze 8 stałych zostało już omawiane w poprzednich programach. Nowościami są flagi, które przekazujemy w 4-bajtowym polu Flags struktury OPENFILENAME. Flaga OFN_ENABLESIZING pozwala na zmianę wielkości wyświetlanego okna dialogowego. Flaga OFN_FORCESHOWHIDDEN wymusza pokazywanie plików ukrytych i systemowych. Flaga OFN_EXPLORER określa standardowy wygląd okna dialogowego. OFN_PATHMUSTEXIST wymusza wpisywanie tylko prawidłowych ścieżek plików w oknie dialogu. OFN_OVERWRITEPROMPT generuje komunikat jeżeli nadpisujemy jakiś plik podczas zapisu. OFN_NODEREFERENCELINKS zwraca plik, który możemy nadpisywać. OFN_FILEMUSTEXIST określa iż użytkownik może wpisywać nazwy tylko istniejących plików w polu Plik wejścia Name . OFN_HIDEREADONLY ukrywa pole wyboru Read Only. Następnie definiujemy funkcje użyte w programie:


EXTERN lstrlenA@4:NEAR

EXTERN CreateFileA@28:NEAR

EXTERN SendMessageA@16:NEAR

EXTERN SendDlgItemMessageA@20:NEAR

EXTERN EndDialog@8:NEAR

EXTERN GetModuleHandleA@4:NEAR

EXTERN DialogBoxParamA@20:NEAR

EXTERN SetDlgItemTextA@12:NEAR

EXTERN GetOpenFileNameA@4:NEAR

EXTERN CloseHandle@4:NEAR

EXTERN ReadFile@20:NEAR

EXTERN ExitProcess@4:NEAR

Funkcja SetDlgItemTextA@12 wstawia tekst w określoną kontrolkę np. w pole edycyjne. GetOpenFileNameA@4 inicjuje okno dialogowe otwarcia pliku, jej jedynym parametrem jest adres struktury OPENFILENAME. Pozostałe funkcje były już omawiane w poprzednich częściach kursu. Struktura OPENFILENAME jest przedstawiona poniżej:


OPENFILENAMEA STRUC
lStructSize        DWORD      ? ;długość struktury w bajtach
hwndOwner          DWORD      ? ;uchwyt do okna które posiada okno dialogowe
hInstance          DWORD      ? ;uchwyt do modułu który zawiera szablon okna
lpstrFilter        DWORD      ? ;filtry
lpstrCustomFilter  DWORD      ?
nMaxCustFilter     DWORD      ? ;rozmiar filtru w bajtach
nFilterIndex       DWORD      ? ;uchwyt kursora z funkcji LoadCursor
lpstrFile          DWORD      ? ;nazwa inicjowana pliku
nMaxFile           DWORD      ? ;maksymalny rozmiar pliku inicjowanego
lpstrFileTitle     DWORD      ? ;nazwa wybranego pliku i rozszerzenie
nMaxFileTitle      DWORD      ? ;rozmiar wybranego pliku
lpstrInitialDir    DWORD      ? ;algorytm wyboru początkowego katalogu
lpstrTitle         DWORD      ? ;ciąg tekstowy wyświetlany na pasku tytułowym okna
Flags              DWORD      ? ;dodatkowe informacje w postaci odpowiednich flag
nFileOffset         WORD      ? ;liczba bajtów na ścieżkę do pliku
nFileExtension      WORD      ? ;liczba bajtów na rozszerzenie pliku
lpstrDefExt        DWORD      ? ;domyślne rozszerzenie pliku
lCustData          DWORD      ? ;dane dla procedury haka
lpfnHook           DWORD      ? ;wskaźnik do procedury haka
lpTemplateName     DWORD      ? ;nazwa okna szablonu źródłowego
OPENFILENAMEA ENDS

OPENFILENAME       EQU        <OPENFILENAMEA>

Sekcja danych programu wygląda następująco:


.DATA?
OTWORZS   OPENFILENAME <?>
HFILEO        DD   ? ;uchwyt pliku
ReadBytes     DD   ? ;ilość przeczytanych bajtów
WIELKOSCB     DD   ?
HINST         DD   ?

.DATA
NOWY          DB    0
PA            DB   'IDD_DLG1',   0 ;nazwa dialogu
OTFILTR       DB   'Pliki tekstowe',   0,   '*.txt',   0
              DB   'Wszystkie pliki',   0,   '*.*',   0
              DB    0
BUFOR         DB    60000  DUP (0)
OTCUSTFILTR   DB    256  DUP (0)
OTPNAZWA      DB   'tekst.txt',   0
              DB    256  DUP (0)
OFTYTUL       DB    256  DUP (0)
OINITDIR      DB    'C:\',   0
ODLGTYTUL     DB   'Otwórz plik',   0

Większość danych to dane inicjalizacyjne, które kopiujemy do odpowiednich pól struktury. Następnie przechodzimy do sekcji kodu programu:


.CODE

START:

PUSH  0
CALL  GetModuleHandleA@4
MOV  [HINST],EAX

PUSH  0
PUSH  OFFSET  WNDPROC
PUSH  0
PUSH  OFFSET  PA
PUSH  [HINST]
CALL  DialogBoxParamA@20

CMP  EAX,-1
JNE KOL

KOL:
PUSH  0
CALL  ExitProcess@4

;parametry:
;[EBP +14H] LPARAM
;[EBP +10H] WPARAM
;[EBP +0CH] MES
;[EBP +8] HWND

WNDPROC   PROC


PUSH  EBP
MOV  EBP,ESP
PUSH  EBX
PUSH  ESI
PUSH  EDI

CMP  DWORD  PTR  [EBP + 0CH],WM_CLOSE;komunikat związany ze zniszczeniem okna
JNE  OMIN

PUSH  0
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  EndDialog@8
JMP  FINISH

OMIN:

CMP  DWORD  PTR  [EBP + 0CH],WM_COMMAND;komunikat związany z kontrolkami
JNE  OMINCOMMAND

Początek to standardowe uruchomienie programu korzystającego z okna dialogowego i zdefiniowanie procedury. W sekcji obsługi zdarzeń kontrolek definiujemy reakcje na poszczególne opcje w menu, które utworzymy w edytorze zasobów:


CMP  WORD  PTR  [EBP + 10H],ID_NOWY ;wybranie opcji 'Nowy'
JNE  OMINNOWY

PUSH  OFFSET  NOWY
PUSH  IDC_EDT1
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  SetDlgItemTextA@12

OMINNOWY:

Funkcja SetDlgItemTextA@12 za pomocą ustawienia bufora w kontrolce nie zawierającego żadnych znaków, powoduje usunięcie wpisanych wcześniej. Dzięki temu mamy utworzony nowy plik do edycji. Następnie sprawdzamy czy użytkownik nie wybrał opcji 'Otwórz'. Jeśli tak inicjalizujemy odpowiednie pola struktury danymi, które też wcześniej już zdefiniowaliśmy:


CMP  WORD  PTR  [EBP + 10H],ID_OTWORZ ;wybranie opcji 'Otworz'
JNE  OMINOTWORZ

MOV  OTWORZS.lStructSize,SIZEOF OTWORZS

PUSH  DWORD  PTR  [EBP+ 08H]
POP  OTWORZS.hwndOwner

PUSH  HINST
POP  OTWORZS.hInstance

MOV  OTWORZS.lpstrFilter,OFFSET OTFILTR

MOV  OTWORZS.lpstrCustomFilter,OFFSET OTCUSTFILTR

MOV  OTWORZS.nMaxCustFilter,SIZEOF OTCUSTFILTR

MOV  OTWORZS.nFilterIndex,0

MOV  OTWORZS.lpstrFile,OFFSET OTPNAZWA

MOV  OTWORZS.nMaxFile,256

MOV  OTWORZS.lpstrFileTitle,OFFSET OFTYTUL

MOV  OTWORZS.nMaxFileTitle,SIZEOF OFTYTUL

MOV  OTWORZS.lpstrInitialDir,OFFSET OINITDIR

MOV  OTWORZS.lpstrTitle,OFFSET ODLGTYTUL

MOV  OTWORZS.Flags,OFN_ENABLESIZING  OR \
                   OFN_EXPLORER   OR \
                   OFN_FORCESHOWHIDDEN   OR \
                   OFN_PATHMUSTEXIST   OR \
                   OFN_OVERWRITEPROMPT   OR \
                   OFN_HIDEREADONLY   OR \
                   OFN_FILEMUSTEXIST  OR \
                   OFN_NODEREFERENCELINKS

MOV  OTWORZS.nFileOffset,0

MOV  OTWORZS.lpfnHook,0

MOV  OTWORZS.lpTemplateName,0

Po wypełnieniu struktury OPENFILENAME. Podajemy ją jako parametr do funkcji GetOpenFileNameA@4 . Wywołując tym samym okno dialogowe w celu otwarcia pliku. Jeżeli funkcja zwróci zero nie otwieramy pliku. W przeciwnym wypadku korzystamy już ze znanych nam funkcji: CreateFileA@28 - w celu otwarcia pliku i ReadFile@20 -odczytania jego zawartości:


PUSH   OFFSET OTWORZS
CALL  GetOpenFileNameA@4
CMP  EAX ,0
JE  IST

PUSH  0
PUSH  0
PUSH  OPEN_EXISTING
PUSH  0
PUSH  0
PUSH  GENERIC_READ
PUSH  OFFSET   OTPNAZWA
CALL  CreateFileA@28

MOV  HFILEO,EAX

JE  NIEODCZYTUJ

PUSH 0
PUSH OFFSET ReadBytes
PUSH SIZEOF  BUFOR
PUSH OFFSET  BUFOR
PUSH HFILEO
CALL ReadFile@20

CMP  EAX ,0
JE  NIEODCZYTUJ

PUSH  OFFSET  BUFOR
PUSH  IDC_EDT1
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  SetDlgItemTextA@12

NIEODCZYTUJ:

PUSH  HFILEO
CALL  CloseHandle@4

IST:

OMINOTWORZ:

Następnie wyświetlamy zawartość pliku za pomocą SetDlgItemTextA@12 i zamykamy plik funkcją CloseHandle@4. Końcowa część kodu programu przedstawia się następująco:


CMP  WORD  PTR  [EBP + 10H],ID_ZAKONCZ ;wybranie opcji 'Zakończ'
JNE  OMINZAKONCZ

PUSH  0
PUSH  0
PUSH  WM_CLOSE
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  SendMessageA@16

OMINZAKONCZ:

OMINCOMMAND:

FINISH:

MOV  EAX,0

POP  EDI
POP  ESI
POP  EBX
POP  EBP
RET
WNDPROC  ENDP

END Start

Uruchomiony program przedstawiony jest na Rys.2.

Nie można wyświetlić obrazu
Rys.2. Interfejs programu .

Całość programu przedstawiona jest na poniższym listingu:


.586 ;typ instrukcji procesora

.MODEL FLAT,STDCALL

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\Comdlg32.lib

ID_NOWY      EQU   1010
ID_OTWORZ    EQU   1020
ID_ZAKONCZ   EQU   1050
IDC_EDT1     EQU   101

OPEN_EXISTING       EQU   3
GENERIC_READ        EQU   80000000h
GENERIC_WRITE       EQU   40000000h
WM_SETICON          EQU   80h
WM_COMMAND          EQU   111h
WM_SETTEXT          EQU   0Ch
WM_GETTEXT          EQU   0Dh
WM_CLOSE            EQU   10h
WM_INITDIALOG       EQU   110h

OFN_ENABLESIZING          EQU   00800000h
OFN_FORCESHOWHIDDEN       EQU   10000000h
OFN_EXPLORER              EQU   00080000h
OFN_PATHMUSTEXIST         EQU   00000800h
OFN_OVERWRITEPROMPT       EQU   00000002h
OFN_NODEREFERENCELINKS    EQU   00100000h
OFN_FILEMUSTEXIST         EQU   00001000h
OFN_HIDEREADONLY          EQU   00000004h

EXTERN lstrlenA@4:NEAR

EXTERN CreateFileA@28:NEAR

EXTERN SendMessageA@16:NEAR

EXTERN SendDlgItemMessageA@20:NEAR

EXTERN EndDialog@8:NEAR

EXTERN GetModuleHandleA@4:NEAR

EXTERN DialogBoxParamA@20:NEAR

EXTERN SetDlgItemTextA@12:NEAR

EXTERN GetOpenFileNameA@4:NEAR

EXTERN CloseHandle@4:NEAR

EXTERN ReadFile@20:NEAR

EXTERN ExitProcess@4:NEAR

OPENFILENAMEA STRUC
lStructSize        DWORD      ? ;długość struktury w bajtach
hwndOwner          DWORD      ? ;uchwyt do okna które posiada okno dialogowe
hInstance          DWORD      ? ;uchwyt do modułu który zawiera szablon okna
lpstrFilter        DWORD      ? ;filtry
lpstrCustomFilter  DWORD      ?
nMaxCustFilter     DWORD      ? ;rozmiar filtru w bajtach
nFilterIndex       DWORD      ? ;uchwyt kursora z funkcji LoadCursor
lpstrFile          DWORD      ? ;nazwa inicjowana pliku
nMaxFile           DWORD      ? ;maksymalny rozmiar pliku inicjowanego
lpstrFileTitle     DWORD      ? ;nazwa wybranego pliku i rozszerzenie
nMaxFileTitle      DWORD      ? ;rozmiar wybranego pliku
lpstrInitialDir    DWORD      ? ;algorytm wybory początkowego katalogu
lpstrTitle         DWORD      ? ;ciąg tekstowy wyświetlany na pasku tytułowym okna
Flags              DWORD      ? ;dodatkowe informacje w postaci odpowiednich flag
nFileOffset         WORD      ? ;liczba bajtów na ścieżkę do pliku
nFileExtension      WORD      ? ;liczba bajtów na rozszerzenie pliku
lpstrDefExt        DWORD      ? ;domyślne rozszerzenie pliku
lCustData          DWORD      ? ;dane dla procedury haka
lpfnHook           DWORD      ? ;wskaźnik do procedury haka
lpTemplateName     DWORD      ? ;nazwa okna szablonu źródłowego
OPENFILENAMEA ENDS

OPENFILENAME       EQU        <OPENFILENAMEA>

.DATA?
OTWORZS   OPENFILENAME <?>
HFILEO        DD   ? ;uchwyt pliku
ReadBytes     DD   ? ;ilość przeczytanych bajtów
WIELKOSCB     DD   ?
HINST         DD   ?

.DATA
NOWY          DB    0
PA            DB   'IDD_DLG1',   0 ;nazwa dialogu
OTFILTR       DB   'Pliki tekstowe',   0,   '*.txt',   0
              DB   'Wszystkie pliki',   0,   '*.*',   0
              DB    0
BUFOR         DB    60000  DUP (0)
OTCUSTFILTR   DB    256  DUP (0)
OTPNAZWA      DB   'tekst.txt',   0
              DB    256  DUP (0)
OFTYTUL       DB    256  DUP (0)
OINITDIR      DB    'C:\',   0
ODLGTYTUL     DB   'Otwórz plik',   0

.CODE

START:

PUSH  0
CALL  GetModuleHandleA@4
MOV  [HINST],EAX

PUSH  0
PUSH  OFFSET  WNDPROC
PUSH  0
PUSH  OFFSET  PA
PUSH  [HINST]
CALL  DialogBoxParamA@20

CMP  EAX,-1
JNE KOL

KOL:
PUSH  0
CALL  ExitProcess@4

;parametry:
;[EBP +14H] LPARAM
;[EBP +10H] WPARAM
;[EBP +0CH] MES
;[EBP +8] HWND

WNDPROC   PROC


PUSH  EBP
MOV  EBP,ESP
PUSH  EBX
PUSH  ESI
PUSH  EDI

CMP  DWORD  PTR  [EBP + 0CH],WM_CLOSE;komunikat związany ze zniszczeniem okna
JNE  OMIN

PUSH  0
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  EndDialog@8
JMP  FINISH

OMIN:

CMP  DWORD  PTR  [EBP + 0CH],WM_COMMAND;komunikat związany z kontrolkami
JNE  OMINCOMMAND

CMP  WORD  PTR  [EBP + 10H],ID_NOWY ;wybranie opcji 'Nowy'
JNE  OMINNOWY

PUSH  OFFSET  NOWY
PUSH  IDC_EDT1
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  SetDlgItemTextA@12

OMINNOWY:

CMP  WORD  PTR  [EBP + 10H],ID_OTWORZ ;wybranie opcji 'Otworz'
JNE  OMINOTWORZ

MOV  OTWORZS.lStructSize,SIZEOF OTWORZS

PUSH  DWORD  PTR  [EBP+ 08H]
POP  OTWORZS.hwndOwner

PUSH  HINST
POP  OTWORZS.hInstance

MOV  OTWORZS.lpstrFilter,OFFSET OTFILTR

MOV  OTWORZS.lpstrCustomFilter,OFFSET OTCUSTFILTR

MOV  OTWORZS.nMaxCustFilter,SIZEOF OTCUSTFILTR

MOV  OTWORZS.nFilterIndex,0

MOV  OTWORZS.lpstrFile,OFFSET OTPNAZWA

MOV  OTWORZS.nMaxFile,256

MOV  OTWORZS.lpstrFileTitle,OFFSET OFTYTUL

MOV  OTWORZS.nMaxFileTitle,SIZEOF OFTYTUL

MOV  OTWORZS.lpstrInitialDir,OFFSET OINITDIR

MOV  OTWORZS.lpstrTitle,OFFSET ODLGTYTUL

MOV  OTWORZS.Flags,OFN_ENABLESIZING  OR \
                   OFN_EXPLORER   OR \
                   OFN_FORCESHOWHIDDEN   OR \
                   OFN_PATHMUSTEXIST   OR \
                   OFN_OVERWRITEPROMPT   OR \
                   OFN_HIDEREADONLY   OR \
                   OFN_FILEMUSTEXIST  OR \
                   OFN_NODEREFERENCELINKS

MOV  OTWORZS.nFileOffset,0

MOV  OTWORZS.lpfnHook,0

MOV  OTWORZS.lpTemplateName,0

PUSH   OFFSET OTWORZS
CALL  GetOpenFileNameA@4
CMP  EAX ,0
JE  IST

PUSH  0
PUSH  0
PUSH  OPEN_EXISTING
PUSH  0
PUSH  0
PUSH  GENERIC_READ
PUSH  OFFSET   OTPNAZWA
CALL  CreateFileA@28

MOV  HFILEO,EAX

JE  NIEODCZYTUJ

PUSH 0
PUSH OFFSET ReadBytes
PUSH SIZEOF  BUFOR
PUSH OFFSET  BUFOR
PUSH HFILEO
CALL ReadFile@20

CMP  EAX ,0
JE  NIEODCZYTUJ

PUSH  OFFSET  BUFOR
PUSH  IDC_EDT1
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  SetDlgItemTextA@12

NIEODCZYTUJ:

PUSH  HFILEO
CALL  CloseHandle@4

IST:

OMINOTWORZ:

CMP  WORD  PTR  [EBP + 10H],ID_ZAKONCZ ;wybranie opcji 'Zakończ'
JNE  OMINZAKONCZ

PUSH  0
PUSH  0
PUSH  WM_CLOSE
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  SendMessageA@16

OMINZAKONCZ:

OMINCOMMAND:

FINISH:

MOV  EAX,0

POP  EDI
POP  ESI
POP  EBX
POP  EBP
RET
WNDPROC  ENDP

END Start

Pobierz kod

Plik zasobów programu wygląda następująco:


#include "..\include\resource.h"

#define IDC_EDT1 101
#define ID_NOWY 1010
#define ID_OTWORZ 1020
#define ID_ZAKONCZ 1050

IDD_DLG1 DIALOGEX 10,10,297,190
CAPTION "Edytor"
FONT 8,"MS Sans Serif", 0, 0, 0
MENU 1000
STYLE  WS_POPUP|WS_VISIBLE|WS_OVERLAPPEDWINDOW|DS_CENTER|DS_SETFOREGROUND|DS_3DLOOK
EXSTYLE   WS_EX_STATICEDGE
BEGIN
  CONTROL "",IDC_EDT1,"Edit",WS_VISIBLE|WS_VSCROLL|WS_HSCROLL|ES_WANTRETURN|ES_NOHIDESEL
|ES_AUTOHSCROLL|ES_AUTOVSCROLL|ES_MULTILINE,15,12,256,162,WS_EX_CLIENTEDGE
END

1000 MENU
BEGIN
POPUP   "Plik"
   BEGIN
      MENUITEM   "Plik",ID_NOWY
      MENUITEM   "Otwórz",ID_OTWORZ
      MENUITEM   SEPARATOR
      MENUITEM   "Zakończ",ID_ZAKONCZ
   END
END

Pobierz kod

Ćwiczenie 5

Do przykładu dodać również opcję zapisu do pliku z wyświetlaniem okna dialogowego.

Pobierz szablon

Nie można wyświetlić obrazu
Rys.3. Przykładowe wywołanie programu.

<Poprzednia lekcjaKolejna lekcja>