Пятница, 18.07.2025, 20:45 Приветствую Вас Гость

On-line: Книги, учебники, статьи

Главная | Регистрация | Вход | RSS

Глава 5. Разрешение некоторых проблем программирования в Windows(2)

Трансляция программы на Рис. 3.5.1.
MASM32:

 ml /c /coff /DMASM tray.asm
 rc tray.rc
 link /subsystem:windows tray.obj tray.res
TASM32:
 tasm32 /ml tray.asm
 brcc32 tray.rc
 tlink32 -aa tray.obj,,,,,tray.res

В связи с программой на Рис. 3.5.1 хочу особо акцентировать Ваше внимание на сообщении WM_SIZE. Весьма полезное сообщение, я Вам скажу. Представьте, что у себя в окне Вы расположили какую-то информацию. Если окно допускает изменение размеров, то Вам придется решать проблему размещения информации в случае, если размер окна изменился. Так вот, аккуратно все перерисовать и отмасштабировать можно как раз, если использовать данное сообщение. Подчеркну, что сообщение посылается, когда размер окна уже изменился. При этом WPARAM содержит признак того, что произошло с окном, a LPARAM - новый размер окна (младшее слово - ширина, старшее - высота).

II

В. Есть ли дополнительные средства, упрощающие файловую обработку?

Да, таким средством, в частности, являются файлы, отображаемые в память. Побайтное чтение из файла действительно не всегда удобно. Конечно, можно подойти к этой проблеме несколько иначе. Можно выделить в памяти достаточно большой буфер и прочитать туда файл, а затем работать с ним, как с большим массивом. Так вот, файлы, отображаемые в память, - это нечто похожее, но здесь система берет на себя часть такой работы. Самое замечательное, однако, в этом механизме заключается в том, что файл, спроецированный в память, может быть разделенным между несколькими процессами, а это еще один способ обмена информацией между процессами.

У файлов, проецируемых в память, есть довольно существенный недостаток. После отображения их размер не может быть увеличен. В этом случае поступают следующим образом: заранее предполагая новый размер файла, проецируют его на большую область памяти.

Работу с файлами, отображаемыми в память, производят по следующему алгоритму:

  1. Открыть (или создать) файл с помощью обычной функции CreateFile. Функция, как известно, возвращает дескриптор открытого файла.
  2. Отобразить файл45 с помощью функции CreateFileMapping. Именно эта функция определяет размер отображения. Эта функция, как и предыдущая, возвращает дескриптор, только не обычного, а отображенного файла.
  3. Скопировать файл (или его часть) в созданную область при помощи функции MapViewOfFile. Эта функция возвращает указатель (вот оно!) на начало области, где будет расположен файл. После этого руки у Вас развязаны, и Вы можете делать с отображенным файлом все, что Вам заблагорассудится.
  4. При желании можно записать область памяти в файл при помощи функции FlushViewOfFile. Разумеется, сбрасываться на диск будет та область памяти, которую мы заказали при помощи функции CreateFileMapping. Записывать на диск можно, разумеется, и с помощью обычной функции WriteFile.
  5. Перед тем как закрывать отображенный файл, следует вначале сделать указатель недействительным. Это делается с помощью функции UnmapViewOfFile.
  6. Закрыть следует оба дескриптора. Вначале дескриптор, возвращенный функцией CreateFileMapping, а затем дескриптор, созданный функцией CreateFile.

Как видите, алгоритм работы с отображаемыми файлами весьма прост. Рассмотрим теперь новые для Вас функции.

Функция CreateFileMapping. Возвращает дескриптор отображаемого файла.

  • 1-й параметр. Дескриптор открытого файла.
  • 2-й параметр. Атрибут доступа, обычно полагают равным нулю.
  • 3-й параметр. Может принимать одно из следующих значений, и должен быть совместим с режимом разделения файла: PAGE_READONLY, PAGE_WRITECOPY, PAGE_READWRITE. Как Вы понимаете, этот атрибут определяет защиту отображаемого файла и не должен противоречить атрибуту файла, открытого с помощью CreateFile.
  • 4-й параметр. Старшая часть (32 бита) размера отображаемого файла.
  • 5-й параметр. Младшая часть размера отображаемого файла (как Вы понимаете, размер может и не совпадать с размером файла). Если оба параметра равны нулю, то размер полагается равным размеру открытого файла (1-й параметр).
  • 6-й параметр. Имя отображаемого файла. Необходимо только в том случае, если предполагается, что отображаемый файл будет использоваться несколькими процессами. В этом случае повторный вызов функции CreateFileMapping другими процессами с тем же именем приведет не к созданию нового отображаемого файла, а к возвращению уже созданного дескриптора.

Функция MapViewOfFile. Возвращает указатель на область памяти, где помещается файл или его часть.

  • 1-й параметр. Дескриптор, возвращенный функцией CreateFileMapping.
  • 2-й параметр. Определяет операцию, которую мы будем делать. Например, FILE_MAP_READ означает - только чтение, a FILE_MAP_WRITE - чтение и запись.
  • 3-й параметр. Старшая часть (32-байта) смещения в файле, откуда начинается копирование в память.
  • 4-й параметр. Младшая часть смещения в файле, откуда начинается копирование.
  • 5-й параметр. Определяет количество копируемых байт. Если вы хотите скопировать весь файл, то положите три последних параметра равными 0.

Функция FlushViewOfFile.

  • 1-й параметр. Указывает на область, записываемую в файл.
  • 2-й параметр. Определяет количество записываемых байт.

Функция UnmapViewOfFile.

  • 1-й параметр. Дескриптор отображаемого файла.

Вот, собственно, и вся теория отображаемых файлов. Материал весьма прост для программирования, и мы не приводим здесь программы. Я думаю, что читатель, добравшийся до данной главы сего повествования, без труда напишет свою программу, воспользовавшись изложенными выше материалами.


45 Возможно, несколько правильнее сказать, что функция создает объект, под названием отображаемый файл.


III

В.Можно ли контролировать ввод информации в окне редактирования?

Да, в главе 2.4 мы видели, как можно это корректно делать при помощи горячих клавиш. Однако с помощью горячих клавиш легко блокировать приход в окно редактирования некоторых символов. Речь же в некоторых случаях может идти о том, чтобы преобразовывать вводимую информацию, что называется, "налету". Таким механизмом может быть использование подклассов. Для демонстрации его мы возьмем программу на Рис. 1.3.2, видоизменим ее так, чтобы можно было контролировать ввод информации.

На самом деле в этом механизме нет ничего нового. Еще в операционной системе MS DOS можно было перехватывать прерывание и встраивать туда свою процедуру. В результате при вызове данного прерывания вызывалась вначале Ваша процедура, а потом уже та, которая была установлена ранее. Впрочем, можно было поступать и по-другому: вначале вызывается процедура, существовавшая ранее, а потом, в последнюю очередь, вызывается Ваша процедура (см. [1]). Поскольку перехватывать прерывание можно было многократно, в результате могла образоваться целая цепочка процедур, выполняющихся одна за другой. Мне уже приходилось сравнивать вызов процедур окна с вызовом прерываний. Это очень похоже, не правда ли? С точки зрения объектного программирования это является ни чем иным как созданием класса-родителя.

В основе рассматриваемого механизма лежит использование функции SetWindowLong, которая может менять атрибуты уже созданного окна. Вот параметры этой функции.

  • 1-й параметр. Дескриптор окна, которое было создано текущим процессом.
  • 2-й параметр. Величина, определяющая, какой атрибут следует изменить. Вообще говоря, это всего лишь смещение в некоторой области памяти. Нас будет интересовать только величина, определяющая адрес процедуры окна. Эта величина определяется константой DWL_DLGPROC = 4 для диалогового окна и GWL_WNDPROC = -4 для обычного окна. Отсюда, кстати, следует, что данное действо можно производить не только над простыми, но и над диалоговыми окнами.
  • 3-й параметр. Новое значение атрибута окна.

Следует также отметить, что данная функция возвращает старое значение атрибута. Здесь я должен еще обратить внимание читателя на то, что все элементы, создаваемые на окне, являясь тоже окнами, имеют, разумеется, и свои функции окна. В повседневной практике мы довольствуемся только сообщениями, приходящими на процедуру основного окна.

Вопрос следующий: как вызвать старую процедуру окна. Обычная команда CALL не подходит. Нужно использовать специальную функцию CallWindowProc. Параметры этой функции следующие:

  • 1-й параметр. Адрес вызываемой процедуры.
  • 2-й параметр. Дескриптор окна.
  • 3-й параметр. Код сообщения.
  • 4-й параметр. Параметр WPARAM.
  • 5-й параметр. Параметр LPARAM.
; файл edit.inc
; константы
WM_CHAR equ 102h
WM_SETFOCUS equ 7h
; сообщение приходит при закрытии окна
WM_DESTROY equ 2
; сообщение приходит при создании окна
WM_CREATE equ 1
; сообщение, если что-то происходит с элементами на окне
WM_COMMAND equ 111h
; сообщение, позволяющее получить строку
WM_GETTEXT equ 0Dh
; константа для функции SetWindowLong
GWL_WNDPROC equ -4
; свойства окна
CS_VREDRAW equ 1h
CS_HREDRAW equ 2h
CS_GLOBALCLASS equ 4000h
WS_TABSTOP equ 10000h
WS_SYSMENU equ 80000h
WS_OVERLAPPEDWINDOW equ 0 + WS_TABSTOP + WS_SYSMENU
STYLE equ CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS

;CS_HREDRAW equ 2h
BS_DEFPUSHBUTTON equ 1h
WS_VISIBLE equ 10000000h
WS_CHILD equ 40000000h
WS_BORDER equ 800000h
STYLBTN equ WS_CHILD + BS_DEFPUSHBUTTON + WS_VISIBLE + WS_TABSTOP
STYLEDT equ WS_CHILD + WS_VISIBLE + WS_BORDER + WS_TABSTOP
; идентификатор стандартной иконки
IDI_APPLICATION equ 32512
; идентификатор курсора
IDC_ARROW equ 32512
; режим показа окна - нормальный
SW_SHOWNORMAL equ 1
; прототипы внешних процедур
IFDEF MASM
 EXTERN CallWindowProcA@20:NEAR
 EXTERN SetWindowLongA@12:NEAR
 EXTERN SetFocus@4:NEAR
 EXTERN SendMessageA@16:NEAR
 EXTERN MessageBoxA@16:NEAR
 EXTERN CreateWindowExA@48:NEAR
 EXTERN DefWindowProcA@16:NEAR
 EXTERN DispatchMessageA@4:NEAR
 EXTERN ExitProcess@4:NEAR
 EXTERN GetMessageA@16:NEAR
 EXTERN GetModuleHandleA@4:NEAR
 EXTERN LoadCursorA@8:NEAR
 EXTERN LoadIconA@8:NEAR
 EXTERN PostQuitMessage@4:NEAR
 EXTERN RegisterClassA@4:NEAR
 EXTERN ShowWindow@8:NEAR
 EXTERN TranslateMessage@4:NEAR
 EXTERN UpdateWindow@4:NEAR
ELSE
 EXTERN CallWindowProcA:NEAR
 EXTERN SetWindowLongA:NEAR
 EXTERN SetFocus:NEAR
 EXTERN SendMessageA:NEAR
 EXTERN MessageBoxA:NEAR
 EXTERN CreateWindowExA:NEAR
 EXTERN DefWindowProcA:NEAR
 EXTERN DispatchMessageA:NEAR
 EXTERN ExitProcess:NEAR
 EXTERN GetMessageA:NEAR
 EXTERN GetModuleHandleA:NEAR
 EXTERN LoadCursorA:NEAR
 EXTERN LoadIconA:NEAR
 EXTERN PostQuitMessage:NEAR
 EXTERN RegisterClassA:NEAR
 EXTERN ShowWindow:NEAR
 EXTERN TranslateMessage:NEAR
 EXTERN UpdateWindow:NEAR
 CallWindowProcA@20 = CallWindowProcA
 SetWindowLongA@12 = SetWindowLongA
 SetFocus@4 = SetFocus
 SendMessageA@16 = SendMessageA
 MessageBoxA@16 = MessageBoxA
 CreateWindowExA@48 = CreateWindowExA
 DefWindowProcA@16 = DefWindowProcA
 DispatchMessageA@4 = DispatchMessageA
 ExitProcess@4 = ExitProcess
 GetMessageA@16 = GetMessageA
 GetModuleHandleA@4 = GetModuleHandleA
 LoadCursorA@8 = LoadCursorA
 LoadIconA@8 = LoadIconA
 PostQuitMessage@4 = PostQuitMessage
 RegisterClassA@4 = RegisterClassA
 ShowWindow@8 = ShowWindow
 TranslateMessage@4 = TranslateMessage
 UpdateWindow@4 = UpdateWindow
ENDIF

; структуры
; структура сообщения
MSGSTRUCT STRUC
 MSHWND DD ?
 MSMESSAGE DD ?
 MSWPARAM DD ?
 MSLPARAM DD ?
 MSTIME DD ?
 MSPT DD ?
MSGSTRUCT ENDS


;----структура класса окон
WNDCLASS STRUC
 CLSSTYLE DD ?
 CLWNDPROC DD ?
 CLSCBCLSEX DD ?
 CLSCBWNDEX DD ?
 CLSHINST DD ?
 CLSHICON DD ?
 CLSHCURSOR DD ?
 CLBKGROUND DD ?
 CLMENNAME DD ?
 CLNAME DD ?
WNDCLASS ENDS


; файл edit.asm
.386P
; плоская модель
.MODEL FLAT, stdcall
include edit.inc
; директивы компоновщику для подключения библиотек
IFDEF MASM
 includelib c:\masm32\lib\user32.lib
 includelib c:\masm32\lib\kernel32.lib
ELSE
 includelib c:\tasm32\lib\import32.lib
ENDIF

;-------------------------------------------------
; сегмент данных
_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
 NEWHWND DD 0
 MSG MSGSTRUCT <?>
 WC WNDCLASS <?>
 HINST DD 0 ; дескриптор приложения
 TITLENAME DB 'Контроль окна редактирования',0
 CLASSNAME DB 'CLASS32',0
 CPBUT DB 'Выход',0 ; выход
 CPEDT DB ' ',0
 CLSBUTN DB 'BUTTON',0
 CLSEDIT DB 'EDIT',0
 HWNDBTN DWORD 0
 HWNDEDT DWORD 0
 CAP BYTE 'Сообщение',0
 MES BYTE 'Конец работы программы',0
 TEXT DB 100 DUP (0)
 OLDWND DD 0
 CHAR DD ?
_DATA ENDS

; сегмент кода
_TEXT SEGMENT DWORD PUBLIC USE32 'CODE'
START:
; получить дескриптор приложения
 PUSH 0
 CALL GetModuleHandleA@4
 MOV [HINST], EAX
REG_CLASS:
; заполнить структуру окна
; стиль
 MOV [WC.CLSSTYLE],STYLE
; процедура обработки сообщений
 MOV [WC.CLWNDPROC], OFFSET WNDPROC
 MOV [WC.CLSCBCLSEX],0
 MOV [WC.CLSCBWNDEX],0
 MOV EAX, [HINST]
 MOV [WC.CLSHINST], EAX
;----------иконка окна
 PUSH IDI_APPLICATION
 PUSH 0
 CALL LoadIconA@8
 MOV [WC.CLSHICON], EAX
;----------курсор окна
 PUSH IDC_ARROW
 PUSH 0
 CALL LoadCursorA@8
 MOV [WC.CLSHCURSOR], EAX
;——————————
 MOV [WC.CLBKGROUND], 17 ; цвет окна
 MOV DWORD PTR [WC.CLMENNAME],0
 MOV DWORD PTR [WC.CLNAME],OFFSET CLASSNAME
 PUSH OFFSET WC
 CALL RegisterClassA@4
; создать окно зарегистрированного класса
 PUSH 0
 PUSH [HINST]
 PUSH 0
 PUSH 0
 PUSH 150 ; DY - высота окна
 PUSH 400 ; DX - ширина окна
 PUSH 100 ; Y - координата левого верхнего угла
 PUSH 100 ; X - координата левого верхнего угла
 PUSH WS_OVERLAPPEDWINDOW
 PUSH OFFSET TITLENAME ; имя окна
 PUSH OFFSET CLASSNAME ; имя класса
 PUSH 0
 CALL CreateWindowExA@48
; проверка на ошибку
 CMP EAX,0
 JZ _ERR
 MOV [NEWHWND], EAX ; дескриптор окна
;-----------------------------------------
 PUSH SW_SHOWNORMAL
 PUSH [NEWHWND]
 CALL ShowWindow@8 ; показать созданное окно
;-----------------------------------------
 PUSH [NEWHWND]
 CALL UpdateWindow@4 ; команда перерисовать видимую
 ; часть окна, сообщение WM_PAINT
; петля обработки сообщений
MSG_LOOP:
 PUSH 0
 PUSH 0
 PUSH 0
 PUSH OFFSET MSG
 CALL GetMessageA@16
 CMP AX, 0
 JE END_LOOP
 PUSH OFFSET MSG
 CALL TranslateMessage@4
 PUSH OFFSET MSG
 CALL DispatchMessageA@4
 JMP MSG_LOOP
END_LOOP:
; выход из программы (закрыть процесс)
 PUSH [MSG.MSWPARAM]
 CALL ExitProcess@4
_ERR:
 JMP END_LOOP
;-----------------------------------------
; процедура окна
; расположение параметров в стеке
; [EBP+014Н] ; LPARAM
; [EBP+10H] ; WAPARAM
; [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_DESTROY
 JE WMDESTROY
 CMP DWORD PTR [EBP+0CH],WM_CREATE
 JE WMCREATE
 CMP DWORD PTR [EBP+0CH],WM_COMMAND
 JE WMCOMMND
 JMP DEFWNDPROC
WMCOMMND:
 MOV EAX,HWNDBTN
 CMP DWORD PTR [EBP+14H],EAX
 JNE NODESTROY
 JMP WMDESTROY
NODESTROY:
 MOV EAX, 0
 JMP FINISH
WMCREATE:
; создать окно-кнопку
 PUSH 0
 PUSH [HINST]
 PUSH 0
 PUSH DWORD PTR [EBP+08H]
 PUSH 20 ; DY
 PUSH 60 ; DX 
 PUSH 10 ; Y 
 PUSH 10 ; X
 PUSH STYLBTN
 PUSH OFFSET CPBUT ; имя окна
 PUSH OFFSET CLSBUTN ; имя класса
 PUSH 0
 CALL CreateWindowExA@48
 MOV HWNDBTN,EAX ; запомнить дескриптор кнопки
 ; создать окно редактирования
 PUSH 0
 PUSH [HINST]
 PUSH 0
 PUSH DWORD PTR [EBP+08H]
 PUSH 20 ; DY
 PUSH 350; DX
 PUSH 50 ; Y
 PUSH 10 ; X
 PUSH STYLEDT
 PUSH OFFSET CPEDT ; имя окна
 PUSH OFFSET CLSEDIT ; имя класса
 PUSH 0
 CALL CreateWindowExA@48
 MOV HWNDEDT,EAX
; установить фокус на окне редактирования
 PUSH HWNDEDT
 CALL SetFocus@4
; установить свою собственную процедуру обработки
 PUSH OFFSET WNDEDIT
 PUSH GWL_WNDPROC
 PUSH [HWNDEDT]
 CALL SetWindowLongA@12
 MOV OLDWND,EAX
 MOV EAX, 0
 JMP FINISH
DEFWNDPROC:
 PUSH DWORD PTR [EBP+14H]
 PUSH DWORD PTR [EBP+10H]
 PUSH DWORD PTR [EBP+0CH]
 PUSH DWORD PTR [EBP+08H]
 CALL DefWindowProcA@16
 JMP FINISH
WMDESTROY:
; получить отредактированную строку
 PUSH OFFSET TEXT
 PUSH 150
 PUSH WM_GETTEXT
 PUSH HWNDEDT
 CALL SendMessageA@16
; показать эту строку
 PUSH 0
 PUSH OFFSET CAP
 PUSH OFFSET TEXT
 PUSH DWORD PTR [EBP+08H] ; дескриптор окна
 CALL MessageBoxA@16
; на выход
 PUSH 0
 CALL PostQuitMessage@4 ; сообщение WM_QUIT
 MOV EAX, 0
FINISH:
 POP EDI
 POP ESI
 POP EBX
 POP EBP
 RET 16
WNDPROC ENDP
;-----------------------------------------------
; новая процедура обработки сообщений окну редактирования
WNDEDIT PROC
 PUSH EBP
 MOV EBP,ESP
 MOV EAX,DWORD PTR [EBP+10H]
 MOV CHAR,EAX
 CMP DWORD PTR [EBP+0CH],WM_CHAR
 JNE _OLD
; проверка вводимого символа
 CMP AL,13
 JNE _OLD
; послать сообщение о закрытии основного окна
 PUSH 0
 PUSH 0
 PUSH WM_DESTROY
 PUSH [NEWHWND]
 CALL SendMessageA@16
_OLD:
; вызвать старую процедуру
 PUSH DWORD PTR [EBP+014H]
 PUSH DWORD PTR [EBP+10H]
 PUSH DWORD PTR [EBP+0CH]
 PUSH DWORD PTR [EBP+8H]
 PUSH [OLDWND]
 CALL CallWindowProcA@20
FIN:
 POP EBP
 RET 16
WNDEDIT ENDP
_TEXT ENDS
END START

Вход на сайт
Поиск
Календарь
«  Июль 2025  »
ПнВтСрЧтПтСбВс
 123456
78910111213
14151617181920
21222324252627
28293031
Архив записей
Наш опрос
Как Вам удобнее??
Всего ответов: 341
Мини-чат
Друзья сайта
  • Официальный блог
  • Сообщество uCoz
  • FAQ по системе
  • Инструкции для uCoz
  • Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0