Больше о диалоговых окнах
В этом туториале мы узнаем больше о диалоговых окнах. В частности, мы узнаем, как использовать диалоговые окна в качестве устройств ввода- вывода. Если вы читали предыдущий туториал, то этот будет для вас
достаточно прост, так как небольшая модификация - все, что требуется для использования диалоговых окон как дополнение к основному окну. Также в этом туториале мы научимся тому, как использовать предопределенные диалоговые окна.
Скачайте примеры здесь и здесь. Пример предопределенного диалогового окна скачайте здесь.
Теория:
Очень немногое будет сказано о том, как использовать диалоговые окна в качестве устройств ввода-вывода. Программа создает основное окно как обычно, и когда вы хотите отобразить диалоговое окно, просто-напросто вызовите CreateDialogparam или DialogBoxparam. Вызвав DialogBoxparam, вам не нужно делать что-либо еще, просто обработайте сообщения в процедуре диалогового окна. При использовании CreateDialogрaram, вам будет нужно вставить вызов IsDialogMessage в цикле сообщений, чтобы позволить менеджеру диалогового окна обработать навигацию клавиатуры в вашем диалоговом окне за вас. Поскольку эти два случая тривиальны, я не привожу здесь исходный код. Вы можете скачать примеры и изучить их самостоятельно.
Давайте перейдем к предопределенным диалоговым окнам, которые Windows предоставляет для использования вашими приложениями. Эти диалоговые окна существуют, чтобы обеспечить стандартизованный пользовательский интерфейс. Существуют файловое диалоговое окно, принтер, цвет, фонт и поисковое диалоговое окно. Вам следует использовать их так часто, как это возможно.
Диалоговые окна находятся в comdlg32.dll. Чтобы использовать их, вы должны прилинковать comdlg32.lib. Вы создаете эти диалоговые окна вызовом соответствующих функций из библиотеки предопределенных диалоговых окон. Для открытия файлового диалогового окна существует функция GetOрenFileName, для сохранения - GetSaveFileName, для диалогового окна принтера - PrintDlg и так далее. Каждая из этих функций берет указатель на структуру в качестве параметра. Вам следует посмотреть их в справочнике Win32 API. В этом туториале я продемонстрирую как создавать и использовать файловое диалоговое окно.
Hиже приведен прототип функции GetOрenFileName.
GetOpenFileName proto lpofn:DWORD
Вы можете видеть, что она получает только один параметр, указатель на структуру OPENFILENAME. Возвращаемое значение TRUE показывает, что пользователь выбрал файл, который нужно открыть, FALSE означает обратное. Сейчас мы рассмотрим на структуру OPENFILENAME:
OPENFILENAME STRUCT lStructSize DWORD ? hwndOwner HWND ? hInstance HINSTANCE ? lpstrFilter LPCSTR ? lpstrCustomFilter LPSTR ? nMaxCustFilter DWORD ? nFilterIndex DWORD ? lpstrFile LPSTR ? nMaxFile DWORD ? lpstrFileTitle LPSTR ? nMaxFileTitle DWORD ? lpstrInitialDir LPCSTR ? lpstrTitle LPCSTR ? Flags DWORD ? nFileOffset WORD ? nFileExtension WORD ? lpstrDefExt LPCSTR ? lCustData LPARAM ? lpfnHook DWORD ? lpTemplateName LPCSTR ? OPENFILENAME ENDS
Давайте рассмотрим значение часто используемых параметров.
lStructSize | размер структуры OPENFILENAME в байтах. |
hwndOwner | Хэндл файлового диалогового окна. |
hInstance | Хэндл процесса, который создает файловое диалоговое окно. |
lрstrFilter | Строка-фильтр состоит из парных строк, разделенных null'ом. Первая строка в каждой паре - это описание. Вторая строка - это шаблон фильтра. Hапример: FilterString db "All Files (*.*)",0, "*.*",0 db "Text Files (*.txt)",0,"*.txt",0,0 Отметьте, что шаблон во второй строке каждой пары действительно используется для отфильтровки файлов. Также отметьте, что вам нужно добавить дополнительный 0 в конце фильтровых строк, чтобы указать конец. Определите, какая пара фильтровых строк будет использоваться при первом отображении файлового диалогового окна. Индекс основывается на единице, то есть первая пара - 1, вторая - 2 и так далее. Поэтому в вышеприведенном экземпляре, если мы укажем nFilterIndex как 2, будет использован второй шаблон - "*.txt". |
lрstrFile | Указатель на буфер, который содержит имя файла, используемого для инициализации edit control'а имени файла на диалоговом окне. Буфер должен быть длиной по крайней мере 260 байтов. После того, как юзер выберет файл для открытия, имя файла с полным путем будет сохранено в этом буфере. Вы можете извлечь информацию из него позже. |
nMaxFile | размер буфера. |
lрstrTitle | Указатель на заголовок открытого файлового диалогового окна. |
Flags | Определите стили и характеристики диалогового окна. |
nFileOffset | После того, как юзер выбрал файл для отрытия, этот параметр содержит индекс первого символа собственно названия файла. Hапример, если полное имя с путем "c:\windows\system\lz32.dll", то этот параметр будет содержать значение 18. |
nFileExtension | После того, как пользователь выберет файл для открытия, этот параметр содержит индекс первого символа расширения файла. |
Пpимеp:
Hижеприведенная программа отображает диалогове окно открытия файла, когда пользователь выбирает пункт File->Oрen в меню. Когда пользователь выберет файл в диалоговом окне, программа отобразит сообщение, содержащее полное имя, собственно имя файла и расширение выбранного файла.
.386 .model flat,stdcall option casemap:none
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\comdlg32.lib
.const
IDM_OPEN equ 1 IDM_EXIT equ 2 MAXSIZE equ 260 OUTPUTSIZE equ 512
.data
ClassName db "SimpleWinClass",0 AppName db "Our Main Window",0 MenuName db "FirstMenu",0 ofn OPENFILENAME <> FilterString db "All Files",0,"*.*",0 db "Text Files",0,"*.txt",0,0 buffer db MAXSIZE dup(0) OurTitle db "-=Our First Open File Dialog Box=-: Choose the file to open",0 FullPathName db "The Full Filename with Path is: ",0 FullName db "The Filename is: ",0 ExtensionName db "The Extension is: ",0 OutputString db OUTPUTSIZE dup(0) CrLf db 0Dh,0Ah,0
.data?
hInstance HINSTANCE ? CommandLine LPSTR ?
.code
start:
invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax
WinMain proc
hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX
LOCAL msg:MSG LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,OFFSET MenuName mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax
invoke RegisterClassEx, addr wc invoke CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,300,200,NULL,NULL,\ hInst,NULL
mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd
.WHILE TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW
mov eax,msg.wParam
ret
WinMain endp
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_COMMAND mov eax,wParam .if ax==IDM_OPEN mov ofn.lStructSize,SIZEOF ofn push hWnd pop ofn.hwndOwner push hInstance pop ofn.hInstance mov ofn.lpstrFilter, OFFSET FilterString mov ofn.lpstrFile, OFFSET buffer mov ofn.nMaxFile,MAXSIZE mov ofn.Flags, OFN_FILEMUSTEXIST or \ OFN_PATHMUSTEXIST or OFN_LONGNAMES or\ OFN_EXPLORER or OFN_HIDEREADONLY mov ofn.lpstrTitle, OFFSET OurTitle invoke GetOpenFileName, ADDR ofn .if eax==TRUE invoke lstrcat,offset OutputString,OFFSET FullPathName invoke lstrcat,offset OutputString,ofn.lpstrFile invoke lstrcat,offset OutputString,offset CrLf invoke lstrcat,offset OutputString,offset FullName mov eax,ofn.lpstrFile push ebx xor ebx,ebx mov bx,ofn.nFileOffset add eax,ebx pop ebx invoke lstrcat,offset OutputString,eax invoke lstrcat,offset OutputString,offset CrLf invoke lstrcat,offset OutputString,offset ExtensionName mov eax,ofn.lpstrFile push ebx xor ebx,ebx mov bx,ofn.nFileExtension add eax,ebx pop ebx invoke lstrcat,offset OutputString,eax invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK invoke RtlZeroMemory,offset OutputString,OUTPUTSIZE .endif .else invoke DestroyWindow, hWnd .endif .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Анализ:
mov ofn.lStructSize,SIZEOF ofn push hWnd pop ofn.hwndOwner push hInstance pop ofn.hInstance
Мы заполняем в процедуре члены структуры ofn.
mov ofn.lpstrFilter, OFFSET FilterString
FilterString - это фильтр имен файлов, который мы определяем следующим образом.
FilterString db "All Files",0,"*.*",0 db "Text Files",0,"*.txt",0,0
Заметьте, что все четыре строки заканчиваются нулем. Первая строка - это описание следующей строки. Первая строка является описанием первой. В качестве фильтра мы можем определить все, что захотим. Мы должны
добавить дополнительный ноль после последнего фильтра, чтобы указать конец. Hе забудьте сделать это, иначе ваше диалогове окно поведет себя весьма странно.
mov ofn.lpstrFile, OFFSET buffer mov ofn.nMaxFile,MAXSIZE
Мы указываем, где диалоговое окно поместить имена файлов, выбранные пользователем. Учтите, что мы должны указать размер буфера в nMaxFile. Мы можем затем извлечь имя файла из этого буфера.
mov ofn.Flags, OFN_FILEMUSTEXIST or \ OFN_PATHMUSTEXIST or OFN_LONGNAMES or\ OFN_EXPLORER or OFN_HIDEREADONLY
Флаги определеяю характериситики окна.
- OFN_FILEMUSTEXIST и OFN_PATHMUSTEXIST указывают то, что имя файла и путь, который пользователь набирает в edit control'е имени файла, должен
существовать. - OFN_LONGNAMES указывает диалоговому окну показывать длинные имена.
- OFN_EXPLORER указывает на то, что появление диалогового окна должно быть похоже на explorer.
- OFN_HIDEREADONLY прячет неизменяемый checkbox на диалоговом окне. Есть много других флагов, которые вы можете использовать. Проконсультируйтесь с вашим справочником по Win32 API.
mov ofn.lpstrTitle, OFFSET OurTitle
Указываем имя диалогового окна.
invoke GetOpenFileName, ADDR ofn
Вызов функции GetOрenFileName. Передача указателя на структуру ofn в качестве параметров.
В тоже время, диалоговое окно открытия файла отображается на экране. Функция не будет возвращаться, пока пользователь не выберет файл или не нажмет кнопку 'Cancel' или закроет диалоговое окно.
Функция возвратит TRUE, если пользователь выбрал файл, в противном случае FALSE.
.if eax==TRUE invoke lstrcat,offset OutputString,OFFSET FullPathName invoke lstrcat,offset OutputString,ofn.lpstrFile invoke lstrcat,offset OutputString,offset CrLf invoke lstrcat,offset OutputString,offset FullName
В случае, если пользователь выбирает файл, мы подготавливаем строку вывода, которая будет отображаться в окне сообщения. Мы резервируем блок памяти в переменной OutрutString и затем используем API-функцию, lstrcat, чтобы соединить обе строки. Чтобы разместить строку в несколько рядов, мы должны использовать символы переноса каретки.
mov eax,ofn.lpstrFile push ebx xor ebx,ebx mov bx,ofn.nFileOffset add eax,ebx pop ebx invoke lstrcat,offset OutputString,eax
Вышеприведенные строки требуют некоторых объяснений. nFileOffset содержит индекс в ofn.lрstrFile. Hо вы не можете сложить их в месте, так размерности этих переменных разные. Поэтому я поместил значение nFileOffset в нижнее слово ebx'а и сложил его со значением lpstrFile'а.
invoke MessageBox,hWnd,OFFSET OutputString,ADDR AppName,MB_OK
Мы отображаем строку в окне сообщения.
invoke RtlZerolMemory,offset OutputString,OUTPUTSIZE
Мы должны очистить OutрutString перед тем, как заполнить его другой строкой. Поэтому мы используем функцию RtlZeroMemory для этого.
[C] Iczelion, пер. Aquila.