Inter Process Communication Inter Process Communication или IPC используется для обмена данными между двумя приложениями или процессами. Процессы могут быть запущенными либо на одном и том же компьютере, либо на разных компьютерах объединенных в сеть. Операционная система Windows поддерживает различные методы IPC: Буфер обмена: когда пользователь использует команду копирования или вырезания в любом приложении/окне, скопированные данные сохраняются в буфере обмена окнами (временное хранилище). Другое приложение может получить доступ к данным из буфера обмена. COM: модель компонентных объектов предлагает платформу для взаимодействия между процессами в шаблонах сервера и клиента. COM-сервер может быть локальным сервером или внутрипроцессным сервером. Также может быть несколько COM-клиентов, которые взаимодействуют с COM-сервером и обмениваются данными. WM_COPYDATA: Windows предоставляет сообщение, то есть WM_COPYDATA, которое позволяет процессу обмениваться данными с другим процессом. Можно использовать с SendMessage или SendMessageTimeout, у которого в качестве одного из параметров используется адрес структуры COPYDATASTRUCT. Сообщение WM_COPYDATA используется для обмена данными внутри локального компьютера. DDE: динамический обмен данными — это протокол, который содержит набор рекомендаций и правил для отправки данных между процессами. Процесс использует функцию SendMessage с сообщением WM_DDE_INITIATE или WM_DDE_ACK, отправленным в ответ на сообщение WM_DDE_INITIATE. Для обмена данными используется общая память. Memory Mapped File и Paging File: быстрые механизмы связи между процессами, обеспечивают эффективный способ использования содержимого файла в виртуальной памяти или путем доступа к общей области в памяти. В этих IPC данные файла рассматриваются как часть адресного пространства процесса, так что процесс может легко получить доступ к адресу содержимого. Любой другой процесс, имеющий доступ к общей памяти, должен реализовать синхронизацию, чтобы снизить риск повреждения данных. Сопоставление файлов выполняется в той же системе/машине и недоступно для сетевых процессов. Pipe-каналы: можно использовать как в качестве однонаправленного, так и двунаправленного механизма обмена данными. Windows поддерживает два типа каналов: именованный и анонимный канал. Анонимные каналы могут использоваться в одной сети или только между связанными процессами, Именованные каналы могут использоваться в сети внутри разных процессов. Pipe -канал рассматривают как очередь FIFO, где один конец канала действует как сервер, а другой конец канала — как клиент. Mailslot-канал: обеспечивает только одностороннюю связь. Один процесс может создать mailslot-канал в качестве сервера, а другой процесс — клиентский mailslot-канал. Клиент mailslot отправляет сообщение на сервер mailslot, записывая сообщение, и сообщения добавляются к серверу mailslot до тех пор, пока сервер не прочитает их. Процесс может иметь mailslot как на сервере, так и на клиенте, и это помогает в многонаправленной связи. Mailslot предоставляет возможность транслировать сообщение по сети. Sockets: эффективный способ отправки и получения данных по сети и на локальном компьютере. Использует несколько протоколов, таких как TCP/IP и UDP. Используется с комбинацией IP-адреса машины и адреса порта, по которому могут передаваться данные. RPC: Remote Procedure Call (удаленный вызов процедур) обеспечивает способ связи по сети, так что процесс может вызывать функцию в другом процессе. RPC поддерживает тесную связь между клиентом и сервером с высокой производительностью. LPC: Local Procedure Call (локальный вызов процедур). LPC используется операционной системой для передачи сообщений между подсистемами через порты. Основные достоинства LPC: Высокая производительность; Простота и функциональность; Доступность для программ режима пользователя и для драйверов режима ядра. LPC использует клиент-серверную архитектуру и предоставляет API схожий с интерфейсом сокетов. Во взаимодействии участвуют поток-сервер и один или несколько потоков-клиентов: каждый клиент предварительно соединяется с сервером, посылает одно или несколько сообщений, на некоторые из этих сообщений получает ответ и разрывает соединение. Во вложении приложения для иллюстрации IPC (Mailslot, WM_COPYDATA, DDE, Clipboard, Paging File, Memory Mapped File, Named Pipe). От одного приложения к другому передаются: текстовое сообщение картинки звук из wav-файла Картинки и wav-файл находятся во вложении в папке Images.
01. WM_COPYDATAПеред отправкой сообщения WM_COPYDATA структура COPYDATASTRUCT инициализируется информацией о типе пересылаемых данных, объеме данных и указателем на блок данных. Затем с помощью функции SendMessage сообщение WM_COPYDATA пересылается в принимающую программу; параметр wParam содержит дескриптор окна программы, а lParam ― адрес структуры COPYDATASTRUCT. Для передачи данных должна использоваться только функция синхронной отправки сообщения SendMessage, а не PostMessage, которая передает сообщение асинхронным образом. Память под передаваемые данные резервируется и удерживается до тех пор, пока приложение-приемник не обработает сообщение и не оповестит об этом систему. До этого момента приложение-отправитель переходит в режим ожидания. Если нужно ограничивать время ожидания, пока целевое приложение по какой-то причине может не ответить на сообщение, тогда используют функцию SendMessageTimeout. Когда сообщение поступает обработчику WM_COPYDATA принимающей программы, указатель на данные копируется из структуры COPYDATASTRUCT и используется, как любой другой указатель. В принимающем процессе размер блока данных, адрес которого содержит lpData, извлекается из элемента cbData. Значение, считываемое из элемента lpData принимающей программой, будет отличаться от значения, помещенного туда отправляющей программой. Перед передачей сообщения WM_COPYDATA операционная система выделяет в адресном пространстве принимающего процесса блок памяти, копирует данные из отправляющего процесса и обновляет значение lpData. В адресных пространствах этих процессов адреса размещении блока не совпадут. В структуре COPYDATASTRUCT имеется поле, dwData, в котором можно передать 32-разрядное значение, определяемое в программе. Можно поместить туда любое значение, но я помещаю туда константы определенные для работы с буфером обмена (CF_TEXT, CF_DIB, CF_WAVE) (добавлены некоторые форматы из сообщения SadKo) КонстантаhexОписание формата данныхCF_TEXT1Текстовые данные в виде массив символов. Каждая строка завершается комбинацией символов 0Dh и 0Ah, весь массив закрыт нулемCF_BITMAP2Битовое изображение в формате, который зависит от устройства отображения (Device Dependent Bitmap)CF_METAFILEPICT3МетафайлCF_SYLK4Формат Microsoft Symbolic Link. Этот формат предназначен для передачи текстовых данных, причем каждая строка заканчивается комбинацией символов 0Dh и 0Ah. Используется в некоторых программных продуктах MicrosoftCF_DIF5Формат Data Interchange Format. Аналогично формату CF_SYLK, формат CF_DIF предназначен для передачи текстовых данныхCF_TIFF6Графическое изображение в формате Tag Image File Format, который был разработан Microsoft, Aldus Corporation и Hewlett-PackardCF_OEMTEXT7Аналогично предыдущему, однако буфер содержит символы в кодировке OEMCF_DIB8Битовое изображение в формате, который не зависит от устройства отображения (Device Independent Bitmap)CF_PALETTE9Палитра цветов. Используется при записи в Clipboard битовых изображений DIBCF_PENDATAAФормат данных используется в расширении операционной системы Windows, предназначенном для работы с перьевым вводомCF_RIFFBФормат Resource Interchange File FormatCF_WAVECЗвуковые данные. Подмножество формата Resource Interchange File FormatCF_UNICODETEXTDТекст в формате UTF-16, аналогично CF_TEXT и CF_OEMTEXTCF_ENHMETAFILEEДескриптор (HANDLE) к EMF (Enhanced Metafile) ― HENHMETAFILECF_HDROPFДескриптор к структуре HDROP, которая содержит список путей к файлам, используемая при Drag&Drop. Приложение может запросить информацию о файлах, вызвав функцию DragQueryFileCF_LOCALE10Объект HGLOBAL, содержащий информацию о локали текста в буфере обменаCF_DIBV511Объект в памяти, содержащий структуру BITMAPV5HEADER, за которой следует информация о цветовом пространстве и данные картинкиCF_OWNERDISPLAY80Формат данных определяется приложением, записавшим такие данные в Clipboard. Это же приложение отвечает за отображение данныхCF_DSPTEXT81Текстовое представление данных, формат которых определен приложениемCF_DSPBITMAP82Представление данных, формат которых определен приложением, в виде битового изображенияCF_DSPMETAFILEPICT83Представление данных, формат которых определен приложением, в виде метафайлаCF_DSPENHMETAFILE8EФормат EMF (Enhanced Metafile)При использовании метода WM_COPYDATA помните, что сообщения должны пересылаться, а не просто регистрироваться. Чтобы освободить память, выделенную в адресном пространстве принимающего процесса, операционную систему нужно проинформировать о моменте завершении пересылки. Получатель сообщения WM_COPYDATA обращается с блоком данных как будто он предназначен только для чтения. Для внесения изменении в полученные данные, в принимающей программе необходимо подготовить их локальную копию. Объект памяти, который должен быть помещен в буфер обмена, должен быть размещен, при помощи использования функции GlobalAlloc с флажками GMEM_DDESHARE и GMEM_MOVEABLE. Как только объект памяти помещен в буфер обмена, монопольное использование этого дескриптора памяти переходит к системе. Когда буфер обмена очищается, а объект памяти имеет один из следующих форматов буфера обмена, система освобождает объект памяти, обращаясь к обозначенной функции: Наименование функции, которая освобождает объектФормат буфера обменаDeleteMetaFileCF_DSPENHMETAFILECF_DSPMETAFILEPICTCF_ENHMETAFILECF_METAFILEPICTDeleteObjectCF_BITMAPCF_DSPBITMAPCF_PALETTEGlobalFreeCF_DIBCF_DSPTEXTCF_OEMTEXTCF_TEXTCF_UNICODETEXTВ том случае, если буфер обмена освобожден от объекта памяти, чей формат не показывается в предшествующем списке, прикладная программа сама должна освободить объект памяти. Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 COPYDATASTRUCT struct dwData dq ? cbData dd ?,? lpData dq ? COPYDATASTRUCT ends .code WinMain proc enter 30h,0 mov r9d,256 mov [rbp-10h],r9 mov qword ptr [rbp-8],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON mov r9d,offset DialogProc mov qword ptr[rbp-10h],rax invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local buffer:qword local cdata:COPYDATASTRUCT local FSize:dword local hFile:dword local p:qword local szReadWrite:qword ;number of bytes actually read or write local hResource:qword local pResource:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG: invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam jmp wmBYE wmSEND_TXT:invoke GlobalAlloc,GPTR,256 mov buffer,rax mov cdata.lpData,rax invoke GetDlgItemText,hWnddlg,ID_TXT,eax,256 or eax,eax jz wmBYE ; Заполнить структуру COPYDATASTRUCT mov cdata.dwData,CF_TEXT ; Учитываем закрывающий строку 0 inc eax mov cdata.cbData,eax jmp @f wmSEND_ICO:; Отправить изображение второму приложению mov cdata.dwData,CF_BITMAP mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov cdata.cbData,eax mov FSize,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov cdata.lpData,rax mov p,rax ;------------------------------------------------- mov ecx,256 and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov [rsp+20h],rcx mov [rsp+28h],rcx mov edx,FSize invoke CreateIconFromResourceEx,p,,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b ;-------------------------------------------------- jmp @f wmSEND_WAV:mov ecx,offset wav_file invoke CreateFile,,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,\ FILE_ATTRIBUTE_ARCHIVE,0 mov hFile,eax invoke GetFileSize,eax,0 mov FSize,eax mov cdata.dwData,CF_WAVE mov cdata.cbData,eax invoke GlobalAlloc,GPTR,eax mov cdata.lpData,rax lea r9d,szReadWrite and qword ptr[rsp+20h],0 invoke ReadFile,hFile,eax,FSize invoke CloseHandle,hFile @@:; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю lea r9d,cdata mov qword ptr[rsp+20h],SMTO_ABORTIFHUNG mov qword ptr[rsp+28h],500 and qword ptr[rsp+30h],0 invoke SendMessageTimeout,eax,WM_COPYDATA,hWnddlg jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;--------------------------------------- .data szWin db 'WM_COPYDATA Reciever',0 wav_file db '..\Images\03.wav',0 p1 dd 1 end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_DIALOG 200 #define IDC_ICON1 500 #define IDC_IMG1 104 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "WM_COPYDATA Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc IDC_DIALOG equ 200 IMAGE_BASE equ 400000h ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 COPYDATASTRUCT struct dwData dq ? ;дескриптор типа передаваемых данных cbData dd ?,? ;размер данных lpData dq ? ;указатель на данные COPYDATASTRUCT ends .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local p:qword local FSize:dword local lpwiocb:WAVEHDR local hWaveOut:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COPYDATA ; Пришло сообщение WM_COPYDATA? je wmCOPYDATA cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmCOPYDATA:; Получить строку от первого приложения ; В lParam приходит указатель на структуру COPYDATASTRUCT cmp [r9+COPYDATASTRUCT.cbData],0; Размер данных нулевой? je wmBYE; Да, пропускаем cmp [r9+COPYDATASTRUCT.dwData],CF_TEXT; Это нужный тип данных? je COPYDATA_TEXT; Нет, пропускаем cmp [r9+COPYDATASTRUCT.dwData],CF_BITMAP; Это нужный тип данных? je COPYDATA_ICON; Нет, пропускаем cmp [r9+COPYDATASTRUCT.dwData],CF_WAVE; Это нужный тип данных? jne wmBYE COPYDATA_MUSIC:mov eax,[r9+COPYDATASTRUCT.cbData] mov FSize,eax mov r8,[r9+COPYDATASTRUCT.lpData] mov p,r8 add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода xor eax,eax mov ecx,(sizeof WAVEHDR)/8 lea edx,lpwiocb mov edi,edx rep stosq mov rax,p add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,FSize sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb invoke waveOutWrite,hWaveOut,,sizeof WAVEHDR @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut jmp wmBYE COPYDATA_TEXT: invoke SetDlgItemText,,ID_TXT,[r9+COPYDATASTRUCT.lpData] jmp wmBYE COPYDATA_ICON:; Создать HICON напрямую из памяти and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,[r9+COPYDATASTRUCT.lpData],\ [r9+COPYDATASTRUCT.cbData],TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON;,eax jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "WM_COPYDATA Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL 1,ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END
02. ClipboardФункции для работы с ClipboardВ Windows предусмотрено несколько функций для работы с Clipboard. Перед тем как выполнить запись или чтение данных, приложение должно получить доступ к Clipboard, открыв ее при помощи функции OpenClipboard: Code (C): BOOL WINAPI OpenClipboard(HWND hwnd); В качестве параметра этой функции передается дескриптор окна, которое будет "владеть" Clipboard. При необходимости функция этого окна будет получать сообщения, извещающие о необходимости выполнения определенных операций с Clipboard. Если доступ к Clipboard получен, OpenClipboard = TRUE. Если же Clipboard уже открыт другим приложением, OpenClipboard вернет FALSE. После использования приложение должно закрыть Clipboard, вызвав функцию CloseClipboard: Code (C): BOOL WINAPI CloseClipboard(void); Функция в случае успеха возвращает TRUE, в случае ошибки ― FALSE. Содержимое открытого Clipboard можно сбросить функцией EmptyClipboard: Code (C): BOOL WINAPI EmptyClipboard(void); EmptyClipboard возвращает TRUE при нормальном завершении или FALSE при ошибке. Для выполнения записи данных в Clipboard используют функцию SetClipboardData: Code (C): HANDLE WINAPI SetClipboardData(UINT uFormat, HANDLE hData); У SetClipboardData два параметра. Первый параметр определяет формат запоминаемых в Clipboard данных, второй параметр передается дескриптор незафиксированного глобального блока памяти, содержащего запоминаемые данные. Для параметра uFormat используют константу, соответствующую одному из предопределенных форматов, или константу полученную от функции RegisterClipboardFormat. Функция RegisterClipboardFormat позволяет регистрировать собственные форматы данных для Clipboard. Список предопределенных форматов данных для Clipboard в разделе об WM_COPYDATA. Для чтения данных из Clipboard используют функцию GetClipboardData: Code (C): HANDLE WINAPI GetClipboardData(UINT uFormat); Единственный параметр задает требуемый формат данных. Если Clipboard содержит данные в указанном формате, функция GetClipboardData возвращает дескриптор незафиксированного глобального блока памяти, содержащего требуемые данные. Запись данных в Clipboard Для записи данных в Clipboard, выполняют следующую последовательность действий: Открыть Clipboard функцией OpenClipboard Сбросить содержимое Clipboard функцией EmptyClipboard Заказать с помощью GlobalAlloc глобальный блок памяти с размером, достаточным для размещения записываемых в Clipboard данных Зафиксировать полученный блок памяти функцией GlobalLock Записать в зафиксированный блок памяти данные Расфиксировать блок памяти функцией GlobalUnlock Вызвать функцию SetClipboardData, передав ей через первый параметр формат данных, а через второй ― дескриптор расфиксированного блока памяти, содержащего данные Закрыть Clipboard функцией CloseClipboard Блок памяти, полученный для данных, не уничтожается функцией GlobalFree, после того как его дескриптор был использован в вызове функции SetClipboardData. При записи в Clipboard перемещения данных не происходит. Функция SetClipboardData изменяет атрибуты передаваемого ей блока памяти, блок памяти меняет своего "хозяина", переходит в собственность операционной системы Windows и приобретает атрибут GMEM_DDESHARE. Если приложение заказало глобальный блок памяти без атрибута GMEM_DDESHARE, этот блок памяти принадлежит приложению. Оно отвечает за уничтожение такого блока памяти. Если же приложение "забыло" уничтожить заказанный ей глобальный блок памяти, блок памяти уничтожает Windows при завершении приложения. Блок памяти, предназначенный для хранения данных Clipboard, не уничтожается при завершении работы приложения, создавшего его. Если приложение снабдит блок памяти атрибутом GMEM_DDESHARE, Windows не будет автоматически уничтожать его при завершении работы приложения. Приложение обязано уничтожить его самостоятельно, как только этот блок памяти перестанет быть нужным. Блоки памяти с атрибутом GMEM_DDESHARE используются в Windows для организации общего поля памяти, доступного всем приложениям. В Windows для адресации памяти все приложения обращаются к одной локальной таблице дескрипторов LDT, все приложения могут адресоваться к одному блоку памяти, если будут знать его селектор. Память Clipboard в Windows устроена в виде набора блоков памяти "общего пользования" (по одному на каждый формат данных). Функция SetClipboardData получает у приложения "личный" глобальный блок памяти с данными, и отдает его в "коллективное пользование", добавляя атрибут GMEM_DDESHARE. Если данные записаны в Clipboard с передачей дескриптора глобального блока памяти функции SetClipboardData, больше нельзя использовать этот дескриптор для адресации данных. В любой момент времени пользователь может уничтожить такой блок памяти записью в Clipboard новых данных. В процессе записи новых данных приложение вызовет функцию EmptyClipboard, которая уничтожит все блоки памяти, распределенные Clipboard раньше. Если операция записи данных в Clipboard выполняется при обработке одного сообщения, никакое другое приложение не будет работать с Clipboard во время записи данных. Если этот обработчик попутно создает диалоговые панели, пользователь может записать в Clipboard содержимое, например, однострочного текстового редактора, реализованного в виде органа управления диалоговой панели. Для исключения конфликтных ситуаций, связанных с попыткой одновременного доступа к Clipboard со стороны нескольких приложений, используют следующее правило: Все операции с Clipboard от его открытия и до закрытия должны выполняться в одном обработчике сообщения. Нельзя открывать Clipboard в обработчике одного сообщения, а закрывать в обработчике другого сообщения. Между открытием и закрытием Clipboard нельзя создавать диалоговые панели или выполнять другие действия, которые могут повлечь за собой создание диалоговых панелей (например, вызов функций SendMessage или PeekMessage) Операционная система Windows создает для каждого приложения отдельную локальную таблицу дескрипторов LDT и отдельное адресное пространство. Никакое приложение ни при каких обстоятельствах не может иметь доступа к адресному пространству другого приложения. Если для передачи данных между приложениями использовать функции Clipboard, гарантирована совместимость на уровне исходных текстов с операционной системой Windows. Чтение данных из Clipboard Процедура чтения данных из Clipboard. Приложение должно сделать следующее. Открыть Clipboard функцией OpenClipboard Вызвать GetClipboardData, передав ей через единственный параметр требуемый формат данных. Если Clipboard содержит данные в указанном формате, функция GetClipboardData возвратит дескриптор незафиксированного блока памяти, содержащего нужные данные. Если в Clipboard нет данных в указанном формате, будет возвращено значение NULL Зафиксировать блок памяти, дескриптор которого получен от функции GetClipboardData, функцией GlobalLock Переписать данные из зафиксированного буфера данных Clipboard в буфер, заказанный специально для этого приложением Расфиксировать блок памяти, дескриптор которого получен от функции GetClipboardData, функцией GlobalUnlock Закрыть Clipboard функцией CloseClipboard Чтение содержимого Clipboard, как и запись в Clipboard, нужно выполнять в обработчике одного сообщения. Приложение должно переписать данные из блока памяти Clipboard в свой, созданный специально, а не пользоваться блоком памяти, дескриптор которого был получен от функции GetClipboardData. В любой момент времени пользователь может уничтожить этот блок памяти, перезаписав содержимое Clipboard новыми данными. Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 WM_CLIPBOARDUPDATE equ 31Dh .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local buffer[96]:BYTE local hGin:qword local p:qword local FSize:dword local lpwiocb:WAVEHDR local hWaveOut:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_CLIPBOARDUPDATE je wmCLIPBOARDUPDATE cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmCLIPBOARDUPDATE:; Получить строку от первого приложения ; В lParam приходит указатель на тип данных? invoke OpenClipboard,hWnddlg ;открываем буфер обмена invoke __imp_GetClipboardData,lParam;извлекаем текст из буфера обмена mov hGin,rax invoke GlobalLock,eax ;блокируем память cmp lParam,CF_WAVE; Это нужный тип данных? jz WAVE cmp lParam,CF_DIB; Это нужный тип данных? jz DIB cmp lParam,CF_TEXT jnz wmBYE ;--------------------------------------------------- TXT: mov ecx,96 mov esi,eax lea edi,buffer mov r8d,edi rep movsb invoke SetDlgItemText,hWnddlg,ID_TXT jmp @0 DIB: and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,eax,lParam,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON;,eax jmp @0 WAVE: mov p,rax mov r8,rax;p mov eax,[r8+4] mov FSize,eax add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,p add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,FSize sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb invoke waveOutWrite,hWaveOut,,sizeof WAVEHDR @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut @0: invoke GlobalLock,hGin invoke CloseClipboard jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Clipboard Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 WM_CLIPBOARDUPDATE equ 31Dh extern __imp_SetClipboardData:qword .code WinMain proc enter 30h,0 mov r9d,256;cx mov [rbp-10h],r9 mov qword ptr [rbp-8],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rbp-10h],rax;30h-10h=+20h invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD size_of_buffer equ 96 local buffer[size_of_buffer]:BYTE local hGout:qword local p:qword local FSize:dword local hFile:dword local szReadWrite:qword ;number of bytes actually read or write local hResource:qword local pResource:qword local type0:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG:; invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam jmp wmBYE wmSEND_TXT:lea r8d,buffer invoke GetDlgItemText,,ID_TXT,,size_of_buffer;255 or eax,eax jz wmBYE mov FSize,eax invoke GlobalAlloc,GPTR,size_of_buffer;GHND or GMEM_DDESHARE mov hGout,rax invoke GlobalLock,eax mov p,rax mov edi,eax lea esi,buffer mov ecx,FSize rep movsb and byte ptr[rdi],0 mov type0,CF_TEXT ;---------------------------------------------------- jmp @f wmSEND_ICO:; Отправить изображение второму приложению mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov FSize,eax invoke GlobalAlloc,GPTR,eax; or GMEM_DDESHARE,eax mov hGout,rax invoke GlobalLock,eax mov p,rax mov edi,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov esi,eax mov ecx,FSize rep movsb ;------------------------------------------------ mov ecx,256 and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov [rsp+20h],rcx mov [rsp+28h],rcx mov edx,FSize invoke CreateIconFromResourceEx,p,,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON mov type0,CF_DIB xor p1,11b ;-------------------------------------------------- jmp @f wmSEND_WAV:mov ecx,offset wav_file invoke CreateFile,,GENERIC_READ,0,0,OPEN_EXISTING,\ FILE_ATTRIBUTE_ARCHIVE,0 mov hFile,eax invoke GetFileSize,eax,0 mov FSize,eax invoke GlobalAlloc,GPTR,eax mov hGout,rax invoke GlobalLock,eax mov p,rax lea r9d,szReadWrite invoke ReadFile,hFile,eax,FSize,,0 invoke CloseHandle,hFile mov type0,CF_WAVE ;-------------------------------------------------------- @@: invoke GlobalUnlock,hGout invoke OpenClipboard,hWnddlg or eax,eax jz wmBYE invoke EmptyClipboard invoke __imp_SetClipboardData,type0,hGout invoke CloseClipboard ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю invoke PostMessage,eax,WM_CLIPBOARDUPDATE,hWnddlg,type0 jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;--------------------------------------- .data szWin db 'Clipboard Reciever',0 wav_file db '..\Images\03.wav',0 p1 dd 1 end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_DIALOG 200 #define IDC_ICON1 500 #define IDC_IMG1 104 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Clipboard Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END
04. Paging File (страничный файл)Страничный файл входит в общую физическую память вычислительной системы увеличивая ее объем. В физической памяти создается проекция страничного файла, которая отображается на виртуальное адресное пространство нескольких процессов. Первый процесс создает в памяти проекцию страничного файла и отображает ее на свое адресное пространство, записывает в эту память некоторые данные. Второй процесс отображает ту же проекцию страничного файла на свое адресное пространство, читает эти данные. При создании проекции файла в разделе Memory Mapped File в качестве первого параметра функции CreateFileMapping указывают дескриптор открытого файла на диске. Чтобы создать проекцию страничного файла, первый параметр функции CreateFileMapping должен быть равным -1 (INVALID_HANDLE_VALUE). Предпоследний параметр функции задает размер проекции, а последний ― произвольное имя объекта, которое должно быть известно во втором процессе. С помощью функции MapViewOfFile в виртуальной памяти процесса выделяется область в памяти того же размера и проекция страничного файла отображается на эту же область памяти. Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 .code WinMain proc dummy:qword mov r9d,256 mov [rsp+20h],r9 mov qword ptr [rsp+28],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON mov r9d,offset DialogProc mov qword ptr[rsp+20h],rax invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD size_of_buffer equ 96 local buffer[size_of_buffer]:BYTE local hFile:dword local FSize:dword local hMapping:qword local pMapping:qword local hResource:qword local pResource:qword local szReadWrite:qword local type0:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG:; invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam jmp wmBYE wmSEND_TXT: lea r8d,buffer invoke GetDlgItemText,,ID_TXT,,size_of_buffer or eax,eax jz wmBYE inc eax mov FSize,eax ;создание в памяти объекта "проекция файла" sub esp,30h movr qword ptr[rsp+28h],szMapName mov eax,FSize mov [rsp+20h],rax or rcx,INVALID_HANDLE_VALUE invoke CreateFileMapping,,0,PAGE_READWRITE,0 mov hMapping,rax ;отображение проекции файла на адресное пространство процесса mov ecx,eax mov eax,FSize mov [rsp+20h],rax invoke MapViewOfFile,,FILE_MAP_ALL_ACCESS,0,0 mov pMapping,rax mov ecx,FSize mov edi,eax lea esi,buffer rep movsb mov type0,WM_USER+1 ;-------------------------------------------------- jmp @f wmSEND_ICO:sub esp,30h mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov FSize,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax mov esi,eax ;созддание в памяти объекта "проекция файла" movr qword ptr[rsp+28h],szMapName mov eax,FSize mov [rsp+20h],rax or rcx,INVALID_HANDLE_VALUE invoke CreateFileMapping,,0,PAGE_READWRITE,0 mov hMapping,rax ;отображение проекции файла на адресное пространство процесса mov ecx,eax mov eax,FSize mov [rsp+20h],rax invoke MapViewOfFile,,FILE_MAP_ALL_ACCESS,0,0 mov pMapping,rax mov ecx,FSize mov edi,eax;rdi,pMapping rep movsb ;-------------------------------------------------- mov ecx,256 and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov [rsp+20h],rcx mov [rsp+28h],rcx mov edx,FSize invoke CreateIconFromResourceEx,pMapping,,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b mov type0,WM_USER+2 ;------------------------------------------------------- jmp @f wmSEND_WAV:sub esp,40h mov ecx,offset wav_file xor r9d,r9d mov qword ptr[rsp+30h],r9 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_ARCHIVE mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,\ FILE_SHARE_READ or FILE_SHARE_WRITE mov hFile,eax invoke GetFileSize,eax,0 mov FSize,eax ;созддание в памяти объекта "проекция файла" movr qword ptr[rsp+28h],szMapName mov eax,FSize mov [rsp+20h],rax or rcx,INVALID_HANDLE_VALUE invoke CreateFileMapping,,0,PAGE_READWRITE,0 mov hMapping,rax ;отображение проекции файла на адресное пространство процесса xor r8d,r8d mov [rsp+20h],r8 invoke MapViewOfFile,eax,FILE_MAP_ALL_ACCESS,,0,FSize mov pMapping,rax ;------------------------------------------------- and qword ptr[rsp+20h],0 lea r9,szReadWrite invoke ReadFile,hFile,pMapping,FSize mov type0,WM_USER+3 @@:; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov qword ptr[rsp+20h],SMTO_ABORTIFHUNG mov qword ptr[rsp+28h],5000 and qword ptr[rsp+30h],0 invoke SendMessageTimeout,eax,type0,hWnddlg,FSize invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;--------------------------------------- .data szWin db 'Paging File Reciever',0 wav_file db '..\Images\03.wav',0 szMapName db "13-15=Mapping",0 p1 dd 1 end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_DIALOG 200 #define IDC_ICON1 500 #define IDC_IMG1 104 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Paging File Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc IDC_DIALOG equ 200 IMAGE_BASE equ 400000h ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local pMapping:qword local hMapping:qword local FSize:dword local lpwiocb:WAVEHDR local hWaveOut:qword local type0:dword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_USER+3 je wmUSER_3 cmp edx,WM_USER+1 je wmUSER_1 cmp edx,WM_USER+2 je wmUSER_2 cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmUSER_3:; Получить музыку от первого приложения mov type0,CF_WAVE jmp @f wmUSER_1:; Получить строку от первого приложения mov type0,CF_TEXT jmp @f wmUSER_2:; Получить картинку от первого приложения mov type0,CF_DIB @@: mov r8d,offset lpszName invoke OpenFileMapping,FILE_MAP_ALL_ACCESS,FALSE mov hMapping,rax mov ecx,eax mov rax,lParam mov [rsp+20h],rax invoke MapViewOfFile,,FILE_MAP_ALL_ACCESS,0,0 mov pMapping,rax cmp type0,CF_TEXT je TXT cmp type0,CF_DIB je DIB WAVE: mov r8,pMapping add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,pMapping mov ecx,eax add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4] sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: cmp lpwiocb.dwFlags,WHDR_DONE or WHDR_PREPARED jnz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut jmp @f TXT: invoke SetDlgItemText,hWnddlg,ID_TXT,pMapping jmp @f DIB: and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,pMapping,lParam,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON @@: invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp lpszName db "13-15=Mapping",0 end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Paging File Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END
06a. Named PipeСредство передачи данных между параллельно работающими процессами. Позволяет организовать передачу данных между локальными процессами, а также между процессами, запущенными на различных рабочих станциях в сети. Pipe-каналы похожи на файлы. Pipe просты в использовании. Через Pipe-канал можно передавать данные только между двумя процессами. Один из процессов создает канал, другой открывает его. После этого оба процесса могут передавать данные через канал в одну или обе стороны, используя для этого функции, предназначенные для работы с файлами ReadFile и WriteFile. Приложения могут выполнять над Pipe-каналами синхронные или асинхронные операции. При использовании асинхронных операций необходимо обеспечить синхронизацию. Именованные и анонимные каналыСуществуют две разновидности pipe-каналов ― именованные (Named Pipes) и анонимные (Anonymous Pipes). Именованным каналам при создании присваивается имя, которое доступно для других процессов. Зная имя какой-либо рабочей станции в сети, процесс может получить доступ к каналу, созданному на этой рабочей станции. Анонимные каналы используются для организации передачи данных между родительскими и дочерними процессами, запущенными на одном компьютере.Имена каналовИмена каналов имеют следующий вид: Code (C): \\Имя_Сервера\pipe\Имя_Канала Если процесс открывает канал, созданный на другой рабочей станции нужно указать имя сервера. Если процесс создает канал или открывает канал на своей рабочей станции, вместо имени указывается символ точки: Code (C): \\.\pipe\Имя_Канала Процесс может создать канал только на той рабочей станции, где он запущен, поэтому при создании канала имя сервера никогда не указывается.Реализации каналовОдин серверный процесс создает один канал (одну реализацию канала) для работы с одним клиентским процессом. Если требуется организовать взаимодействие одного серверного процесса с несколькими клиентскими. Например, сервер базы данных должен принимать от клиентов запросы и рассылать ответы на них. Тогда серверный процесс создает несколько реализаций канала, по одной реализации для каждого клиентского процесса.Функции для работы с каналамиНаиболее важные функции предназначенные для работы с Pipe-каналами. Более подробная информация в документации MSDN.Создание каналаДля создания именованных и анонимных pipe-каналов используются функции CreatePipe и CreateNamedPipe.Функция CreatePipeАнонимный канал создается функцией CreatePipe Code (C): BOOL CreatePipe( PHANDLE hReadPipe,// адрес переменной, в которую будет записан дескриптор канала для чтения данных PHANDLE hWritePipe,// адрес переменной, в которую будет записан дескриптор канала для записи данных LPSECURITY_ATTRIBUTES lpPipeAttributes, // адрес переменной для атрибутов защиты DWORD nSize);// количество байт памяти, зарезервированной для канала Канал может использоваться как для записи в него данных, так и для чтения. Поэтому при создании канала функция CreatePipe возвращает два дескриптора, записывая их по адресу, заданному в параметрах hReadPipe и hWritePipe. дескриптор, записанный по адресу hReadPipe, передается как параметр функции ReadFile или ReadFileEx для выполнения операции чтения. дескриптор, записанный по адресу hWritePipe, передается функции WriteFile или WriteFileEx для выполнения операции записи. Через параметр lpPipeAttributes передается адрес переменной, содержащей атрибуты защиты для создаваемого канала. Чаще этот параметр указывают как NULL. В результате канал будет иметь атрибуты защиты, принятые по умолчанию. Параметр nSize определяет размер буфера для создаваемого канала. Если этот размер указан как нуль, создается буфер с размером, принятым по умолчанию. При необходимости система может изменить указанный размер буфера. В случае успеха функция CreatePipe возвращает значение TRUE, при ошибке ― FALSE. Для уточнения причины возникновения ошибки используют функцию GetLastError.Функция CreateNamedPipeДля создания именованного канала Pipe используют функцию CreateNamedPipe. Прототип этой функции: Code (C): HANDLE CreateNamedPipe( LPCTSTR lpName, // адрес строки имени канала DWORD dwOpenMode, // режим открытия канала DWORD dwPipeMode, // режим работы канала DWORD nMaxInstances, // максимальное количество реализаций канала DWORD nOutBufferSize, // размер выходного буфера в байтах DWORD nInBufferSize, // размер входного буфера в байтах DWORD nDefaultTimeOut, // время ожидания в миллисекундах LPSECURITY_ATTRIBUTES lpSecurityAttributes); // адрес переменной для атрибутов защиты Через параметр lpName передается адрес строки имени канала в форме \\.\pipe\Имя_Канала (при создании канала имя сервера не указывается, так как канал можно создать только на той рабочей станции, где запущен процесс, создающий канал). Параметр dwOpenMode задает режим, в котором открывается канал. Pipe-канал может быть ориентирован либо на передачу потока байт, либо на передачу сообщений. В первом случае данные через канал передаются по байтам, во втором ― отдельными блоками заданной длины. Режим работы канала (ориентированный на передачу байт или сообщений) задается константами PIPE_TYPE_BYTE или PIPE_TYPE_MESSAGE, которые указываются в параметре dwOpenMode. По умолчанию используют режим PIPE_TYPE_BYTE. Помимо способа передачи данных через канал, с помощью параметра dwOpenMode можно указать, будет ли данный канал использован только для чтения данных, только для записи или одновременно для чтения и записи. Способ использования канала задается указанием одной из следующих констант: КонстантаhexbinИспользование каналаPIPE_ACCESS_INBOUND100000001Только для чтенияPIPE_ACCESS_OUTBOUND200000010Только для записиPIPE_ACCESS_DUPLEX300000011Для чтения и записиПеречисленные параметры должны быть одинаковы для всех реализаций канала. Параметры, которые могут отличаться для разных реализаций канала: Константаhex Использование каналаPIPE_READMODE_BYTE0Канал открывается на чтение в режиме последовательной передачи отдельных байтPIPE_READMODE_MESSAGE2Канал открывается на чтение в режиме передачи отдельных сообщений указанной длиныPIPE_WAIT0Канал будет работать в блокирующем режиме, когда процесс переводится в состояние ожидания до завершения операций в каналеPIPE_NOWAIT1Неблокирующий режим работы канала. Если операция не может быть выполнена немедленно, в неблокирующем режиме функция завершается с ошибкойFILE_FLAG_OVERLAPPED40000000 Использование асинхронных операций (ввод и вывод с перекрытием). Данный режим позволяет процессу выполнять полезную работу параллельно с проведением операций в каналеFILE_FLAG_WRITE_THROUGH80000000В этом режиме функции, работающие с каналом, не возвращают управление до тех пор, пока не будет полностью завершена операция на удаленном компьютере. Используется только с каналом, ориентированном на передачу отдельных байт и только в том случае, когда канал создан между процессами, запущенными на различных станциях сетиДополнительно через параметр dwOpenMode можно передавать флаги защиты: ФлагhexОписаниеWRITE_DAC40000 Вызывающий процесс должен иметь права доступа на запись к произвольному управляющему списку доступа именованного канала access control list (ACL)WRITE_OWNER80000 Вызывающий процесс должен иметь права доступа на запись к процессу, владеющему именованным каналом PipeACCESS_SYSTEM_SECURITY1000000 Вызывающий процесс должен иметь права доступа на запись к управляющему списку доступа именованного канала access control list (ACL)Параметр dwPipeMode определяет режим работы канала. В этом параметре можно указать константы PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE, PIPE_READMODE_BYTE, PIPE_READMODE_MESSAGE, PIPE_WAIT и PIPE_NOWAIT. Для всех реализаций канала необходимо указывать один и тот же набор констант. Параметр nMaxInstances определяет максимальное количество реализаций, которые могут быть созданы для канала. Можно указывать значения от 1 до PIPE_UNLIMITED_INSTANCES. В случае максимальное количество реализаций ограничивается только наличием свободных системных ресурсов. Если один серверный процесс использует несколько реализаций канала для связи с несколькими клиентскими процессами, общее количество реализаций может быть меньше, чем потенциальное максимальное количество клиентов. Это связано с тем, что клиенты могут использовать реализации по очереди, если только они не пожелают связаться с серверным процессом все одновременно. Параметры nOutBufferSize и nInBufferSize определяют размер буферов, используемых для записи в канал и чтения из канала. При необходимости система может использовать буферы других, по сравнению с указанными, размеров. Параметр nDefaultTimeOut определяет время ожидания для реализации канала. Для всех реализаций необходимо указывать одинаковое значение этого параметра. Через параметр lpPipeAttributes передается адрес переменной, содержащей атрибуты защиты для создаваемого канала. Обычно этот параметр равен NULL. В результате канал будет иметь атрибуты защиты, принятые по умолчанию. В случае успеха функция CreateNamedPipe возвращает дескриптор созданной реализации канала, который можно использовать в операциях чтения и записи, выполняемых с помощью функций ReadFile и WriteFile. При ошибке CreateNamedPipe возвращает значение INVALID_HANDLE_VALUE. Код ошибки можете уточнить, вызвав функцию GetLastError.Использование функции CreateFileФункция CreateFile, предназначена для работы с файлами, может использоваться для создания новых каналов и открытия существующих. Вместо имени файла указывают имя pipe-канала. Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc WM_CLIPBOARDUPDATE equ 31Dh ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 .code WinMain proc dummy:qword mov r9d,256 mov [rsp+20h],r9 mov qword ptr [rsp+28],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rsp+20h],rax invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD size_of_buffer equ 256 local cbWritten:dword;Количество байт, переданных через канал local FSize:dword; local hNamePipe:qword; дескриптор канала Mailslot local hFile:dword local p:qword local hResource:qword local type0:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG:; invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam jmp wmBYE wmSEND_TXT:invoke GlobalAlloc,GPTR,size_of_buffer mov p,rax;Буфер для передачи данных через канал invoke GetDlgItemText,hWnddlg,ID_TXT,eax,size_of_buffer;255 or eax,eax jz wmBYE inc eax mov FSize,eax mov type0,WM_USER+1 jmp @f wmSEND_ICO:mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov FSize,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov p,rax ;------------------------------------------------- mov ecx,256 and qword ptr[rsp+30h],LR_DEFAULTCOLOR mov [rsp+20h],rcx mov [rsp+28h],rcx mov edx,FSize invoke CreateIconFromResourceEx,p,,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b mov type0,WM_USER+2 jmp @f wmSEND_WAV:mov ecx,offset wav_file xor r9d,r9d mov qword ptr[rsp+30h],r9 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_ARCHIVE mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,\ FILE_SHARE_READ or FILE_SHARE_WRITE mov hFile,eax invoke GetFileSize,eax,0 mov FSize,eax invoke GlobalAlloc,GPTR,eax mov p,rax lea r9d,cbWritten and qword ptr[rsp+20h],0 invoke ReadFile,hFile,eax,FSize invoke CloseHandle,hFile mov type0,WM_USER+3 @@:; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov qword ptr[rsp+20h],SMTO_ABORTIFHUNG mov qword ptr[rsp+28h],250 and qword ptr[rsp+30h],0 invoke SendMessageTimeout,eax,type0,hWnddlg,FSize ; Создаем пайп mov ecx,offset szPipeName xor r9d,r9d mov qword ptr[rsp+20h],OPEN_EXISTING mov [rsp+28h],r9 mov [rsp+30h],r9 invoke CreateFile,,GENERIC_WRITE,FILE_SHARE_READ mov hNamePipe,rax mov rdx,p lea r9d,cbWritten and qword ptr[rsp+20h],0 invoke WriteFile,eax,,FSize ;Закрываем дескриптор канала invoke CloseHandle,hNamePipe jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;--------------------------------------- szWin db 'Pipe Reciever',0 szPipeName db "\\.\pipe\MyPipe",0; Имя создаваемого пайпа p1 dd 1 wav_file db '..\Images\03.wav',0 end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_DIALOG 200 #define IDC_ICON1 500 #define IDC_IMG1 104 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Pipe Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc WM_CLIPBOARDUPDATE equ 31Dh IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local cbRead:DWORD;Количество байт данных, принятых через канал local buffer:qword;Буфер для передачи данных через канал local lpwiocb:WAVEHDR local hWaveOut:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_USER+1 je wmUSER_1 cmp edx,WM_USER+2 je wmUSER_2 cmp edx,WM_USER+3 je wmUSER_3 cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmUSER_1:; Получить строку от первого приложения mov type0,CF_TEXT jmp @f wmUSER_2:mov type0,CF_DIB jmp @f wmUSER_3:mov type0,CF_WAVE @@: invoke GlobalAlloc,GMEM_FIXED,lParam mov buffer,rax mov ecx,offset lpszPipeName invoke CreateNamedPipe,,PIPE_ACCESS_INBOUND,PIPE_TYPE_BYTE,1,0,lParam,0,0 mov hNamedPipe,rax invoke ConnectNamedPipe,eax,NULL lea r9d,cbRead and qword ptr[rsp+20h],0 invoke ReadFile,hNamedPipe,buffer,lParam ;---------------------------------------------------- cmp type0,CF_TEXT je TEXT cmp type0,CF_DIB je DIB WAVE: mov r8,buffer add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,buffer mov ecx,eax add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4] sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut jmp @0 ;------------------------------------------------------------ TEXT: invoke SetDlgItemText,hWnddlg,ID_TXT,buffer jmp @0 ;---------------------------------------------------- DIB: and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,buffer,lParam,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON ;----------------------------------------------------------- @0: invoke GlobalFree,buffer invoke DisconnectNamedPipe,hNamedPipe invoke CloseHandle,hNamedPipe jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:;Перед завершением приложения закрываем дескриптор канала Mailslot invoke EndDialog,hWnddlg,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp type0 db ? lpszPipeName db "\\.\pipe\MyPipe",0;Имя создаваемого пайпа hNamedPipe dq ?;дескриптор пайпа end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Pipe Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END
03. Memory Mapped FileОбмен данными между процессами через файлы, отображенных на память. Обладает высоким быстродействием, так как данные передаются между процессами непосредственно через виртуальную память. Отображение создается функцией CreateFileMapping. Фрагмент кода в котором создается отображение файла, а затем выполняется отображение этого файла в память: Code (C): hFileMapping = CreateFileMapping(hSrcFile, NULL,PAGE_READWRITE,0,dwFileSize,NULL); if(hFileMapping == NULL) return; lpFileMap = MapViewOfFile(hFileMapping, FILE_MAP_WRITE, 0, 0, 0); if(lpFileMap == 0) return; В качестве первого параметра для функции CreateFileMapping передают дескриптор файла, открытого функцией CreateFile. Последний параметр указан как NULL, поэтому отображение не имеет имени. Если отображение используется для передачи данных между процессами, указывают имя. Пользуясь этим именем, другие процессы смогут открыть отображение функцией OpenFileMapping. дескриптора файла, передаваемого функции CreateFileMapping через первый параметр. Если создается отображение только для того чтобы обеспечить передачу данных между процессами, не нужно создавать файл на диске компьютера. В качестве дескриптора файла указывается значение 0FFFFFFFFh (-1), создается отображение непосредственно в виртуальной памяти без использования дополнительного файла. Code (C): CHAR lpFileShareName[] = "$MyVerySpecialFileShareName$"; hFileMapping=CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0, 100,lpFileShareName); После того как создан объект-отображение, выполняется отображение файла в память при помощи функции MapViewOfFile. В случае успеха эта функция вернет указатель на отображенную область памяти. Первый процесс создал отображение. Второй процесс, который выполняет обмен данными с первым процессом, открывает это отображение по имени при помощи функции OpenFileMapping: Code (C): hFileMapping = OpenFileMapping (FILE_MAP_READ | FILE_MAP_WRITE, FALSE, lpFileShareName); Далее второе приложение выполняет отображение, вызывая функцию MapViewOfFile: Code (C): lpFileMap = MapViewOfFile(hFileMapping,FILE_MAP_READ| FILE_MAP_WRITE,0,0,0); Используя значением, полученное от функции MapViewOfFile, второе приложение получает указатель на отображенную область памяти. Эта область находится в тех же страницах виртуальной памяти, что и область, созданная первым процессом. Два процесса получили указатели на общие страницы памяти. Перед завершением работы процессы должны отменить отображение файла и освободить дескриптор созданного объекта-отображения: Code (C): UnmapViewOfFile(lpFileMap); CloseHandle(hFileMapping); Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 .code WinMain proc dummy:qword mov r9d,256 mov [rsp+20h],r9 mov qword ptr [rsp+28],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rsp+20h],rax invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD size_of_buffer equ 96 local buffer[size_of_buffer]:BYTE local FSize:dword local hResource:qword local pResource:qword local szReadWrite:qword local type0:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG:; invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam jmp wmBYE wmSEND_TXT:mov type0,WM_USER+1 lea r8d,buffer invoke GetDlgItemText,,ID_TXT,,size_of_buffer;255 or eax,eax jz wmBYE inc eax mov FSize,eax ;созддание файла sub esp,40h xor r8,r8 mov qword ptr[rsp+20h],OPEN_ALWAYS mov [rsp+28h],r8 mov [rsp+30h],r8 mov ecx,offset szMapName0 invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,,0 mov hFile,rax lea edx,buffer lea r9d,szReadWrite and qword ptr[rsp+20h],0 invoke WriteFile,eax,,FSize invoke Func,hFile,0 jmp @0 wmSEND_ICO:sub esp,30h mov type0,WM_USER+2 mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov FSize,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax mov esi,eax ;созддание в памяти объекта "проекция файла" и ;отображение проекции файла на адресное пространство процесса invoke Func,INVALID_HANDLE_VALUE,FSize ;----------------------------------------------------------------- mov ecx,FSize mov edi,eax;rdi,pMapping rep movsb ;-------------------------------------------------- and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 mov edx,FSize invoke CreateIconFromResourceEx,pMapping,,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b jmp @0 ;------------------------------------------------------- wmSEND_WAV:sub esp,40h mov type0,WM_USER+3 mov ecx,offset wav_file xor r9d,r9d mov qword ptr[rsp+30h],r9 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_ARCHIVE mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,\ FILE_SHARE_READ or FILE_SHARE_WRITE mov hFile,rax invoke Func,rax,0 @0:; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov qword ptr[rsp+20h],SMTO_ABORTIFHUNG mov qword ptr[rsp+28h],5000 and qword ptr[rsp+30h],0 invoke SendMessageTimeout,eax,type0,hWnddlg,0 invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping invoke CloseHandle,hFile cmp type0,WM_USER+1 jnz wmBYE mov ecx,offset szMapName0 invoke DeleteFile jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;---------------------------------------- Func proc hFile:qword,FSize:qword mov FSize,rdx ;созддание в памяти объекта "проекция файла" movr qword ptr[rsp+28h],szMapName mov [rsp+20h],rdx invoke CreateFileMapping,,0,PAGE_READWRITE,0 mov hMapping,rax ;отображение проекции файла на адресное пространство процесса mov rax,FSize mov [rsp+20h],rax invoke MapViewOfFile,hMapping,FILE_MAP_ALL_ACCESS,0,0 mov pMapping,rax leave ret Func endp ;--------------------------------------- szWin db 'Memory-Mapped File Reciever',0 wav_file db '..\Images\03.wav',0 szMapName db "13-15=Mapping",0 szMapName0 db "15.txt",0 p1 dd 1 hMapping dq ? pMapping dq ? hFile dq ? end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_DIALOG 200 #define IDC_ICON1 500 #define IDC_IMG1 104 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Memory-Mapped File Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc WM_CLIPBOARDUPDATE equ 31Dh IDC_DIALOG equ 200 IMAGE_BASE equ 400000h ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local pMapping:qword local hMapping:qword local FSize:dword local lpwiocb:WAVEHDR local hWaveOut:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_USER+3 je wmUSER_3 cmp edx,WM_USER+1 je wmUSER_1 cmp edx,WM_USER+2 je wmUSER_2 cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmUSER_2:; Получить иконку от первого приложения mov type0,CF_DIB jmp @f wmUSER_1:; Получить строку от первого приложения mov type0,CF_TEXT jmp @f wmUSER_3:; Получить музыку от первого приложения mov type0,CF_WAVE jmp @f @@: mov r8d,offset lpszName invoke OpenFileMapping,FILE_MAP_READ,FALSE mov hMapping,rax xor r8,r8 mov [rsp+20h],r8 invoke MapViewOfFile,eax,FILE_MAP_READ,,0 mov pMapping,rax ;------------------------------------------- cmp type0,CF_DIB je DIB cmp type0,CF_TEXT je TEXT WAVE: mov r8,pMapping add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,pMapping mov ecx,eax add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4] sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: cmp lpwiocb.dwFlags,WHDR_DONE or WHDR_PREPARED jnz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut jmp @f;wmBYE ;-------------------------------------------------- TEXT: invoke SetDlgItemText,hWnddlg,ID_TXT,pMapping jmp @f;wmBYE ;------------------------------------------------------ DIB: and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,pMapping,lParam,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON @@: invoke UnmapViewOfFile,pMapping invoke CloseHandle,hMapping jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp align 10h lpszName db "13-15=Mapping",0 type0 db ? end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Memory-Mapped File Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END
07. Dynamic Data ExchangeМеханизм динамического обмена данными DDE (Dynamic Data Exchange), позволяет создать постоянно действующие каналы между несколькими одновременно работающими приложениями Windows. Каналы могут создаваться автоматически при запуске приложения или при необходимости и по явному запросу пользователя. После создания каналов, они будут работать без вмешательства пользователя. В Windows механизм DDE реализован через передачу сообщений с помощью функции SendMessage с использованием глобальных блоков памяти, доступных всем приложениям. При создании DDE-приложений программист вынужден вникать во все детали процесса создания канала связи, придерживаясь определенного в SDK протокола. Начиная с Windows 3.1 появилось расширение ― управляющая библиотека динамического обмена данными DDEML, выполненная как DLL. Библиотека DDEML упрощает DDE-приложения и избавляет программиста от учета деталей протокола обмена сообщениями. В Windows динамический обмен данных организуется с помощью библиотеки DDEML. Сетевые возможности Windows базируются на сетевом динамическом обмене данными Network DDE. Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 WM_USER_INITIATE equ WM_USER + 1 extern __imp_DdeGetData:qword .code WinMain proc dummy:qword mov r9d,256 mov [rsp+20h],r9 mov qword ptr [rsp+28],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rsp+20h],rax invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local hFile:dword local cbWritten:dword local hResource:qword local pResource:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG cmp edx,WM_USER_INITIATE je wmUSER_INITIATE xor eax,eax jmp exit0 wmUSER_INITIATE: mov edx,offset szService invoke DdeCreateStringHandle,idInst,,CP_WINANSI mov hszService,rax mov edx,offset szTopic invoke DdeCreateStringHandle,idInst,,CP_WINANSI mov hszTopic,rax mov edx,offset szItem invoke DdeCreateStringHandle,idInst,,CP_WINANSI mov hszItem,rax ; Регистрируем сервис invoke DdeNameService,idInst,hszService,NULL,DNS_REGISTER jmp wmBYE wmINITDIALOG: ; mov hwnd,rcx invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam ; Выполняем инициализацию mov ecx,offset idInst mov edx,offset DDEServerCallback invoke DdeInitialize,,,APPCLASS_STANDARD,0 or eax,eax jz @f mov edx,offset Error0 mov edx,offset szAppName invoke MessageBox,hWnddlg,,,MB_ICONEXCLAMATION or MB_OK jmp wmCLOSE ; После успешной инициализации получаем дескрипторы строк для сервиса, раздела и элемента данных @@: invoke SendMessage,hWnddlg,WM_USER_INITIATE,0,0 mov eax,TRUE jmp wmBYE wmSEND_ICO:mov edx,p1 mov type0,(WM_USER+3) invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov FSize,rax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov p,rax mov ecx,eax and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,,FSize,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b jmp @f wmSEND_WAV:mov ecx,offset wav_file xor r9d,r9d mov type0,(WM_USER+4) mov qword ptr[rsp+30h],r9 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_ARCHIVE mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,\ FILE_SHARE_READ or FILE_SHARE_WRITE mov hFile,eax invoke GetFileSize,eax,0 mov FSize,rax invoke GlobalAlloc,GPTR,eax mov p,rax lea r9d,cbWritten and qword ptr[rsp+20h],0 invoke ReadFile,hFile,eax,FSize invoke CloseHandle,hFile jmp @f wmSEND_TXT:invoke GlobalAlloc,GPTR,256 mov p,rax mov type0,(WM_USER+2) invoke GetDlgItemText,hWnddlg,ID_TXT,eax,256 inc eax mov FSize,rax ; Найти окно получателя @@: mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю invoke PostMessage,eax,type0,hWnddlg,FSize jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:;Завершение работы канала связи cmp hConvApp,NULL jnz @f invoke DdeDisconnect,hConvApp @@:; Сервис больше не доступен invoke DdeNameService,idInst,hszService,NULL,DNS_UNREGISTER ; Освобождение дескрипторов строк invoke DdeFreeStringHandle,idInst, hszService invoke DdeFreeStringHandle,idInst, hszTopic invoke DdeFreeStringHandle,idInst, hszItem invoke EndDialog,hWnddlg,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp DDEServerCallback proc wType:qword,wFmt:qword,hConv:qword,hsz1:qword,hsz2:qword,hData:qword,dwData1:qword,dwData2:qword mov hConv,r8 mov hsz1,r9 cmp ecx,XTYP_CONNECT je xtypCONNECT cmp ecx,XTYP_REQUEST je xtypREQUEST cmp ecx,XTYP_EXECUTE je xtypEXECUTE cmp ecx,XTYP_ERROR je xtypERROR cmp ecx,XTYP_DISCONNECT je xtypDISCONNECT cmp ecx,XTYP_CONNECT_CONFIRM je xtypCONNECT_CONFIRM xor eax,eax jmp wmBYE xtypCONNECT:;Создание канала передачи данных ; Если сервис поддерживается, возвращаем признак успешного создания канала mov rax,hsz2 cmp rax,hszService jne xtypEXECUTE mov eax,TRUE jmp wmBYE xtypDISCONNECT:; Завершение работы канала and hConvApp,NULL xtypERROR:; Ошибка ; Запрос на выполнение команды, отрабатывается вхолостую xtypEXECUTE: xor eax,eax jmp wmBYE xtypREQUEST:; Создаем дескриптор данных cmp edx,CF_TEXT jz @f cmp edx,CF_BITMAP jz @f cmp edx,CF_WAVE jnz wmBYE @@: xor r9d,r9d mov rax,hszItem mov [rsp+20h],rax mov [rsp+28h],rdx mov [rsp+30h],r9 invoke DdeCreateDataHandle,idInst,p,FSize ; В случае успеха возвращаем созданный дескриптор или ноль mov hData,rax jmp wmBYE ; Подтверждение создания канала xtypCONNECT_CONFIRM: mov hConvApp,r8 @@: xor eax,eax wmBYE: leave retn DDEServerCallback endp ;--------------------------------------- szWin db 'DDE Reciever',0 wav_file db '..\Images\03.wav',0 Error0 db "Could not initialize server!",0 ; дескриптор приложения, полученный после регистрации в библиотеке DDEML idInst dq 0 hszService dq 0 hszTopic dq 0 hszItem dq 0 ; дескриптор канала hConvApp dq 0 ; Буфер для приема данных p dq ? szService db "07s",0 szTopic db "Brer Rabbit",0 szItem db "DDEData",0 szAppName db "DDEML Server",0 ;hwnd dq ? FSize dq ? type0 dq ? p1 dd 1 end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_IMG1 104 #define IDC_DIALOG 200 #define IDC_ICON1 500 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "DDE Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 TXT equ 10 ICO equ 11 WAV equ 12 WM_USER_INITIATE equ WM_USER + 1 extern __imp_DdeGetData:qword .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local buffer:qword local lpwiocb:WAVEHDR local hWaveOut:qword local dwResult:dword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_USER+4 je MUSIC_FROM_SERVER cmp edx,WM_USER+2 je MSG_FROM_SERVER cmp edx,WM_USER+3 je ICON_FROM_SERVER cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_USER_INITIATE je wmUSER_INITIATE cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG:; Выполняем инициализацию xor esi,esi mov ecx,offset idInst mov edx,offset DDEServerCallback invoke DdeInitialize,,,APPCMD_CLIENTONLY or APPCLASS_STANDARD,0 or eax,eax jnz Error ; После успешной инициализации получаем дескрипторы строк для сервиса, раздела и элемента данных @@: invoke SendMessage,hWnddlg,WM_USER_INITIATE,0,0 mov eax,TRUE jmp wmBYE ICON_FROM_SERVER:mov wFmt,CF_BITMAP jmp @f MSG_FROM_SERVER:mov wFmt,CF_TEXT jmp @f MUSIC_FROM_SERVER:mov wFmt,CF_WAVE @@: invoke GlobalAlloc,GMEM_FIXED,lParam mov buffer,rax ; Запускаем транзакцию чтения данных mov rax,hConv mov esi,4 or eax,eax jz Error mov r8,rax mov rax,wFmt mov [rsp+20h],rax mov qword ptr[rsp+28h],XTYP_REQUEST mov qword ptr[rsp+30h],TIMEOUT_ASYNC;5000 lea eax,dwResult mov [rsp+38h],rax invoke DdeClientTransaction,0,0,,hszItem ; Получаем строку от сервера mov esi,5 or eax,eax;if(hData != NULL) jz Error invoke __imp_DdeGetData,eax,buffer,lParam,0 mov esi,6 or eax,eax jz Error cmp wFmt,CF_BITMAP jnz @f and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,buffer,eax,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON jmp @0 @@: cmp wFmt,CF_TEXT jnz @f invoke SetDlgItemText,hWnddlg,ID_TXT,buffer jmp @0 ;----------------------------------------------------------- @@: mov r8,buffer add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,buffer mov ecx,eax add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4] sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut ;------------------------------------------------------------ @0: invoke GlobalFree,buffer jmp wmBYE wmUSER_INITIATE:; Инициализация DDEML и создание канала связи invoke DDEClientOpen or eax,eax jnz wmBYE mov edx,offset Error1 mov r8d,offset szAppName invoke MessageBox,hWnddlg,,,MB_ICONEXCLAMATION or MB_YESNO cmp eax,IDNO jz wmBYE mov ecx,offset szService invoke WinExec,,SW_SHOW cmp eax,32 mov esi,2 jb Error ; После удачного запуска повторяем попытку инициализации DDEML и создания канала связи invoke DDEClientOpen jmp wmBYE Error: mov r8d,offset szAppName invoke MessageBox,hWnddlg,ErrorMsg[rsi*8],,MB_ICONEXCLAMATION jmp aExit wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:; Завершаем работу с DDEML. Закрываем канал связи cmp hConv,0 jz @f invoke DdeDisconnect,hConv @@:; Освобождаем дескрипторы строк invoke DdeFreeStringHandle,idInst,hszService invoke DdeFreeStringHandle,idInst,hszTopic invoke DdeFreeStringHandle,idInst,hszItem aExit: invoke EndDialog,hWnddlg,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp DDEServerCallback proc wType:qword,wFmt:qword,hConv:qword,hsz1:qword,hsz2:qword,hData:qword,dwData1:qword,dwData2:qword xor eax,eax wmBYE: leave retn DDEServerCallback endp DDEClientOpen proc local dummy:qword ; При успешной инициализации создаем дескрипторы строк для сервиса, ; раздела и элемента данных mov edx,offset szService invoke DdeCreateStringHandle,idInst,,CP_WINANSI mov hszService,rax mov edx,offset szTopic invoke DdeCreateStringHandle,idInst,,CP_WINANSI mov hszTopic,rax mov edx,offset szItem invoke DdeCreateStringHandle,idInst,,CP_WINANSI mov hszItem,rax ; Устанавливаем канал связи invoke DdeConnect,idInst, hszService, hszTopic,0 mov hConv,rax ; Возвращаем дескриптор созданного канала связи leave ret DDEClientOpen endp ;----------------------------------- szAppName db "DDEML Client",0 szService db "07s",0 szTopic db "Brer Rabbit",0 szItem db "DDEData",0 ErrorMsg dq Error0,Error1,Error2,Error3,Error4,Error5,Error6 Error0 db "Could not initialize client!",0 Error1 db "Сервер не запущен",10,"Запустить?",0 Error2 db "Невозможно запустить сервер",0 Error3 db "Сервер не отвечает",0 Error4 db "hConv = 0",0 Error5 db "hData = 0",0 Error6 db "lParam = 0",0 ; дескриптор приложения для DDEML hConv dq 0 idInst dq 0 hszService dq ? hszTopic dq ? hszItem dq ? wFmt dq ? end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "DDE Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END
05. MailslotMailslot-каналы позволяют выполнять одностороннюю передачу данных от одного или нескольких клиентов к одному или нескольким серверам. Особенность mailslot-каналов в том, что они позволяют передавать данные в широковещательном режиме. На компьютере или в сети могут работать несколько серверных процессов, способных получать сообщения через mailslot-каналы. При этом один клиентский процесс может посылать сообщения сразу всем этим серверным процессам. Создание mailslot-канала Mailslot-канал создается серверным процессом с помощью функции CreateMailslot. После создания серверный процесс получает дескриптор mailslot-канала. Используя этот дескриптор, сервер может читать сообщения, посылаемые в канал клиентскими процессами. Сервер не может выполнять над mailslot-каналом операцию записи, так как этот канал предназначен только для односторонней передачи данных ― от клиента к серверу. Прототип функции CreateMailslot: Code (C): HANDLE CreateMailslot( LPCTSTR lpName, // адрес имени канала Mailslot DWORD nMaxMsgSize, // максимальный размер сообщения DWORD lReadTimeout, // время ожидания для чтения LPSECURITY_ATTRIBUTES lpSecurityAttributes); // адрес структуры защиты Через параметр lpName передают функции CreateMailslot адрес строки символов с именем mailslot-канала. Имя имеет следующий вид: Code (C): \\.\mailslot\[Путь]Имя_Канала В этом имени путь необязательная компонента. Но можно указать его аналогично тому, как это делается для файлов. Имя mailslot-канала задается аналогично имени pipe-канала. Параметр nMaxMsgSize ― максимальный размер сообщений, передаваемых через mailslot-канал. Можно указать нулевое значение, при этом размер сообщений не будет ограничен. Есть одно исключение ― размер широковещательных сообщений, передаваемых всем рабочим станциям и серверам домена не должен превышать 400 байт. С помощью параметра lReadTimeout серверное приложение задает время ожидания для операции чтения в миллисекундах, по истечении которого функция чтения вернет код ошибки. Если в этом параметре указать значение MAILSLOT_WAIT_FOREVER, ожидание будет бесконечным. Параметр lpSecurityAttributes задает адрес структуры защиты, который можно указать как NULL. При ошибке функцией CreateMailslot возвращается значение INVALID_HANDLE_VALUE. Код ошибки можно определить при помощи функции GetLastError. Пример использования функции CreateMailslot в серверном приложении: Code (C): LPSTR lpszMailslotName = "\\\\.\\mailslot\\$MailslotName$"; hMailslot = CreateMailslot(lpszMailslotName, 0, MAILSLOT_WAIT_FOREVER, NULL); В примере задан максимальный размер сообщения, поэтому на эту величину нет ограничений (кроме ограничения в 400 байт для сообщений, передаваемых всем компьютерам домена в широковещательном режиме). Время ожидания указано как MAILSLOT_WAIT_FOREVER, поэтому функции, работающие с данным mailslot-каналом , будут работать в блокирующем режиме. Открытие mailslot-канала Сперва клиентский процесс должен открыть mailslot-канал. Для этого используют функцию CreateFile Code (C): LPSTR lpszMailslotName = "\\\\.\\mailslot\\$MailslotName$"; hMailslot = CreateFile( lpszMailslotName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); Здесь в качестве первого параметра функции CreateFile передается имя mailslot-канала. Вы можете открыть mailslot-канал, созданный на другой рабочей станции в сети. Для этого строка имени канала, передаваемая функции CreateFile, должна иметь следующий вид: Code (C): \\Имя_Рабочей_Станции\mailslot\[Путь]Имя_Канала Можно открыть канал для передачи сообщений всем рабочим станциям заданного домена. Для этого необходимо задать имя по следующему образцу: Code (C): \\Имя_Домена\mailslot\[Путь]Имя_Канала Для передачи сообщений одновременно всем рабочим станциям сети первичного домена имя задается следующим образом Code (C): \\*\mailslot\[Путь]Имя_Канала В качестве второго параметра функции CreateFile используют константу GENERIC_WRITE. Эта константа определяет, что над открываемым каналом будет выполняться операция записи. Клиентский процесс может только посылать сообщения в mailslot-канал, но не читать их оттуда. Чтение сообщений из mailslot-канала ― задача для серверного процесса. Третий параметр FILE_SHARE_READ, так как сервер может читать сообщения, посылаемые одновременно несколькими клиентскими процессами. Константа OPEN_EXISTING используется потому, что функция CreateFile открывает существующий канал, а не создает новый. Запись сообщений в канал Mailslot Запись сообщений в mailslot-канал выполняет клиентский процесс, вызывая для этого функцию WriteFile. Code (C): HANDLE hMailslot; char szBuf[512]; DWORD cbWritten; WriteFile(hMailslot, szBuf, strlen(szBuf) + 1, &cbWritten, NULL); В качестве первого параметра этой функции необходимо передать дескриптор mailslot-канала, полученный от функции CreateFile. Второй параметр определяет адрес буфера с сообщением, третий ― размер сообщения. Если сообщение передается в виде текстовой строки, тогда закрыть строку нужно нулем, для определения длины сообщения можно воспользоваться функцией strlen. Чтение сообщений из канала Mailslot Серверный процесс может читать сообщения из созданного им mailslot-канала при помощи функции ReadFile Code (C): HANDLE hMailslot; char szBuf[512]; DWORD cbRead; ReadFile(hMailslot, szBuf, 512, &cbRead, NULL); Через первый параметр функции ReadFile передается дескриптор созданного mailslot-канала, полученный от функции CreateMailslot. Второй и третий параметры задают, адрес буфера сообщения и его размер. Перед выполнением операции чтения следует проверить состояние mailslot-канала. Если в mailslot-канале нет сообщений, то функцию ReadFile вызывать не следует. Для проверки состояния канала воспользуйтесь функцией GetMailslotInfo. Определение состояния канала Mailslot Серверный процесс может определить текущее состояние mailslot-канала по его дескриптору с помощью функции GetMailslotInfo Code (C): BOOL GetMailslotInfo( HANDLE hMailslot, // дескриптор канала Mailslot LPDWORD lpMaxMessageSize, // адрес максимального размера сообщения LPDWORD lpNextSize, // адрес размера следующего сообщения LPDWORD lpMessageCount, // адрес количества сообщений LPDWORD lpReadTimeout); // адрес времени ожидания Через параметр hMailslot передается дескриптор mailslot-канала , состояние которого необходимо определить. Остальные параметры ― указатели на переменные типа DWORD, в которые будут записаны параметры состояния mailslot-канала. В переменную, адрес которой передается через параметр lpMaxMessageSize, после возвращения из функции GetMailslotInfo будет записан максимальный размер сообщения. Это значение можно использовать для динамического получения буфера памяти, в который это сообщение будет прочитано функцией ReadFile. В переменную, адрес которой указан через параметр lpNextSize, записывается размер следующего сообщения, если оно есть в канале. Если в канале больше нет сообщений, в эту переменную будет записана константа MAILSLOT_NO_MESSAGE. С помощью параметра lpMessageCount можно определить количество сообщений, записанных в канал клиентскими процессами. Если это количество равно нулю ― не следует вызывать функцию ReadFile для чтения несуществующего сообщения. В переменную, адрес которой задается в параметре lpReadTimeout, записывается текущее время ожидания, установленное для канала (в миллисекундах). Если вся информация, которую можно получить с помощью функции GetMailslotInfo не нужна, некоторые из ее параметров (первого) можно указать как NULL. В случае успешного завершения функция GetMailslotInfo возвращает значение TRUE, а при ошибке ― FALSE. Код ошибки можно получить, вызвав функцию GetLastError. Пример использования функции GetMailslotInfo: Code (C): BOOL fReturnCode; DWORD cbMessages; DWORD cbMsgNumber; fReturnCode = GetMailslotInfo(hMailslot, NULL, &cbMessages, &cbMsgNumber, NULL); Изменение состояния канала Mailslot С помощью функции SetMailslotInfo серверный процесс может изменить время ожидания для mailslot-канала уже после его создания. Прототип функции SetMailslotInfo Code (C): BOOL SetMailslotInfo( HANDLE hMailslot, // дескриптор канала Mailslot DWORD dwReadTimeout); // время ожидания Через параметр hMailslot функции SetMailslotInfo передается дескриптор mailslot-канала, для которого нужно изменить время ожидания. Новое значение времени ожидания в миллисекундах задается через параметр dwReadTimeout. Можно указать здесь значение 0 или MAILSLOT_WAIT_FOREVER. Если dwReadTimeout=0 функции, работающие с каналом, вернут управление немедленно, dwReadTimeout=MAILSLOT_WAIT_FOREVER ― будут находиться в состоянии ожидания до тех пор, пока не завершится выполняемая операция. Текст приложения-сервера Code (ASM): ; GUI # include win64a.inc WM_CLIPBOARDUPDATE equ 31Dh ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 .code WinMain proc dummy:qword mov r9d,256 mov [rsp+20h],r9 mov qword ptr [rsp+28],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rsp+20h],rax invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local cbWritten:dword;Количество байт, переданных через канал local FSize:dword; local p:qword local hMailslot:qword; дескриптор канала Mailslot local hResource:qword local hFile:dword local szReadWrite:qword ;number of bytes actually read or write local lpwiocb:WAVEHDR local hWaveOut:qword local type0:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_INITDIALOG je wmINITDIALOG xor eax,eax jmp exit0 wmINITDIALOG:; invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam jmp wmBYE wmSEND_TXT:invoke GlobalAlloc,GPTR,256 mov p,rax;Буфер для передачи данных через канал invoke GetDlgItemText,hWnddlg,ID_TXT,eax,256 or eax,eax jz wmBYE inc eax mov FSize,eax mov type0,WM_USER+1 jmp @f wmSEND_ICO:mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov FSize,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov p,rax ;------------------------------------------------- and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 mov edx,FSize invoke CreateIconFromResourceEx,p,,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b mov type0,WM_USER+2 jmp @f wmSEND_WAV:sub esp,40h mov ecx,offset wav_file xor r9d,r9d mov qword ptr[rsp+30h],r9 mov qword ptr[rsp+28h],FILE_ATTRIBUTE_ARCHIVE mov qword ptr[rsp+20h],OPEN_EXISTING invoke CreateFile,,GENERIC_READ or GENERIC_WRITE,\ FILE_SHARE_READ or FILE_SHARE_WRITE mov hFile,eax invoke GetFileSize,eax,0 mov FSize,eax invoke GlobalAlloc,GPTR,eax mov p,rax mov type0,WM_USER+3 lea r9d,cbWritten and qword ptr[rsp+20h],0 invoke ReadFile,hFile,eax,FSize invoke CloseHandle,hFile @@:; Создаем канал с процессом MSLOTS mov ecx,offset szMailslotName xor r9d,r9d mov qword ptr[rsp+20h],OPEN_EXISTING mov [rsp+28h],r9 mov [rsp+30h],r9 invoke CreateFile,,GENERIC_WRITE,FILE_SHARE_READ mov hMailslot,rax lea r9d,cbWritten and qword ptr[rsp+20h],0 invoke WriteFile,hMailslot,p,FSize ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю invoke PostMessage,eax,type0,hWnddlg,FSize ;Закрываем дескриптор канала invoke CloseHandle,hMailslot jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;--------------------------------------- szWin db 'Mailslot Reciever',0 szMailslotName db "\\.\mailslot\brer_Rabbit",0; Имя создаваемого канала wav_file db '..\Images\03.wav',0 p1 dd 1 end ресурсы Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_DIALOG 200 #define IDC_ICON1 500 #define IDC_IMG1 104 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Mailslot Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Напишите что-нибудь и нажмите 'Отправить текст'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Отправить текст",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Отправить ICO",ID_SEND_ICO, 149,45,60,15 PUSHBUTTON "Отправить WAV",ID_SEND_WAV, 149,65,60,15 PUSHBUTTON "Выход", IDCANCEL, 149,85,60,15 END Текст приложения-клиента Code (ASM): ; GUI # include win64a.inc WM_CLIPBOARDUPDATE equ 31Dh IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 IDC_ICON1 equ 500 TXT equ 10 ICO equ 11 WAV equ 12 .code WinMain proc dummy:qword mov r9d,offset DialogProc and qword ptr[rsp+20h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword local cbRead:DWORD;Количество байт данных, принятых через канал local cbMessages:DWORD;Размер сообщения в байтах local cbMsgNumber:DWORD;Количество сообщений в канале Mailslot local buffer:QWORD local lpwiocb:WAVEHDR local hWaveOut:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_INITDIALOG je wmINITDIALOG cmp edx,WM_USER+1 je wmUSER_1 cmp edx,WM_USER+2 je wmUSER_2 cmp edx,WM_USER+3 je wmUSER_3 cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmUSER_1:mov type0,CF_TEXT jmp @f wmUSER_2:mov type0,CF_DIB jmp @f wmUSER_3:mov type0,CF_WAVE ; Определяем состояние канала Mailslot @@: lea r8d,cbMessages lea r9d,cbMsgNumber xor edx,edx mov [rsp+20h],rdx invoke GetMailslotInfo,hMailslot ; Выполняем задержку на 500 миллисекунд invoke Sleep,500 ; Если в канале есть Mailslot сообщения, читаем первое из них и выводим на экран cmp cbMsgNumber,0 jz wmBYE invoke GlobalAlloc,GMEM_FIXED,lParam mov buffer,rax mov edx,eax lea r9d,cbRead and qword ptr[rsp+20h],0 invoke ReadFile,hMailslot,,lParam cmp type0,CF_TEXT je TEXT cmp type0,CF_DIB je DIB WAVE: mov r8,buffer add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,buffer mov ecx,eax add rax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4] sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut invoke GlobalFree,buffer jmp wmBYE TEXT: invoke SetDlgItemText,hWnddlg,ID_TXT,buffer invoke GlobalFree,buffer jmp wmBYE DIB: and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,buffer,lParam,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,ID_ICON,STM_SETIMAGE,IMAGE_ICON invoke GlobalFree,buffer jmp wmBYE wmINITDIALOG:; Создаем канал Mailslot, имеющий имя lpszMailslotName mov ecx,offset lpszMailslotName invoke CreateMailslot,,0,MAILSLOT_WAIT_FOREVER, NULL mov hMailslot,rax jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:;Перед завершением приложения закрываем дескриптор канала Mailslot invoke CloseHandle,hMailslot invoke EndDialog,hWnddlg,0 wmBYE: mov eax,TRUE exit0: leave ret DialogProc endp lpszMailslotName db "\\.\mailslot\brer_Rabbit",0;Имя создаваемого канала Mailslot hMailslot dq ?;дескриптор канала Mailslot type0 db ? end ресурсы Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Mailslot Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Выход",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,23,128,128 END
OLE/ActiveXУниверсальная технология, одно из ее применений ― межпроцессный обмен данными. OLE создавалась на смену DDE. Для обмена данными существует интерфейс IDataObject. Для обмена данными по сети используется DCOM, ее можно рассматривать как объединение ActiveX и RPC.Windows SocketsОтвечает за обмен данными в глобальной сети. Сокеты используются в локальных сетях. Взаимодействие происходит через так называемые разъемы-"сокеты", которые представляют собой абстракцию конечных точек коммуникационной линии, соединяющей два приложения. С этими объектами программа работает, ждет соединения, посылает данные и так далее. Microsoft Message Queue (MSMQ)Этот протокол обеспечивает посылку сообщений между приложениями с помощью очереди сообщений. Отличие от стандартной очереди сообщений Windows ― он может работать с удаленными процессами и с процессами, которые на данный момент недоступны (например, не запущены). Доставка сообщения по адресу гарантируется. Оно ставится в специальную очередь сообщений и находится там до тех пор, пока не появляется возможность его доставить.Удаленный вызов процедур (Remote Procedure Call, RPC)С помощью этой технологии общение через сеть прозрачно как для сервера, так и для клиента. И серверу, и клиенту кажется, что "собеседник" расположен локально по отношению к ним.Distributed Component Object Model (DCOM)Программная архитектура, разработанная компанией Microsoft для распределения приложений между несколькими компьютерами в сети. Программный компонент на одной из машин может использовать DCOM для передачи сообщения (его называют удаленным вызовом процедуры) к компоненту на другой машине. DCOM автоматически устанавливает соединение, передает сообщение и возвращает ответ удаленного компонента. Remote Method Invocation (RMI)Механизм, который позволяет вызывать метод удаленного объекта. Все операции по подготовке и передаче данных инкапсулируются в вызываемом методе клиентского объекта-заглушки (stub). Вызов метода не отличается от вызова метода обычного локального объекта, за небольшим исключением: локальные объекты передаются по значению (копии) при передаче удаленного (Remote) объекта, если он экспортирован, передается stub этого объекта передаваемые объекты должны быть Serializable при вызове удаленного метода может возбуждаться исключение RemoteException (ошибки маршализации/демаршализации, передачи данных и другие возможные ошибки протокола) При вызове метода работают с удаленным интерфейсом, а не с удаленным классом. Common Object Request Broker Architecture (CORBA)Общая архитектура брокера объектных запросов; типовая архитектура опосредованных запросов к объектам) — технологический стандарт написания распределенных приложений, продвигаемый консорциумом (рабочей группой) OMG и соответствующая ему информационная технология. CORBA обеспечивает взаимодействие между системами, работающих под разными операционными системами, написанными на разных языках программирования и запущенных на разном вычислительном оборудовании. CORBA использует объектно-ориентированную модель, системы, использующие CORBA, не обязательно должны быть объектно-ориентированными. CORBA — пример парадигмы распределенной объектной системы. DLLВажной особенностью использования динамических библиотек является возможность одновременного использования одной библиотеки несколькими модулями. Динамическая библиотека становится продолжением запустившего ее приложения, загружаясь в адресное пространство процесса. Данные процесса доступны из динамической библиотеки и данные динамической библиотеки доступны для процесса. При компоновке динамической библиотеки сегмент данных объявлен как секция с общим доступом. При запуске приложения, которое будет загружать динамическую библиотеку, эта область памяти будет общей для всех приложений.
Code (C): static void SendString (LPCSTR string) { HANDLE handles[257]; char name[32]; int i, length; for (i=0; i<257; i++) { sprintf (name, "Mutex#u", i); handles[i] = CreateMutex (NULL, TRUE, name); } length = lstrlen (string); for (i=0; i<length; i++) { ReleaseMutex (handles[string[i]]); WaitForSingleObject (handles[string[i]], INFINITE); } ReleaseMutex (handles[256]); for (i=0; i<257; i++) CloseHandle (handles[i]); } static LPSTR ReceiveString () { LPSTR string; HANDLE handles[257]; char name[32]; int i, length, result; string = malloc (1024); length = 0; for (i=0; i<257; i++) { sprintf (name, "Mutex#u", i); handles[i] = CreateMutex (NULL, FALSE, name); } while (TRUE) { result = WaitForMultipleObjects (257, handles, FALSE, INFINITE); if (result < 0 || result > 255) break; string[length++] = (char)result; ReleaseMutex (handles[result]); } string[length] = 0; for (i=0; i<257; i++) CloseHandle (handles[i]); return string; }
Не может. Это надо на потоках разруливать. Основной поток записывает в массив хендлов MAX-1 дескрипторов, а последним ― хендл вспомогательного потока, который ждет на следующих MAX-1 дескрипторах и опционально дополнительном вспомогательном потоке.
Объекты синхронизации08. Evet PipesWindows позволяет создавать объекты синхронизации, которые называются событиями (event object). Объекты-события могут находиться в signaled состоянии или nonsignaled состоянии. Сигнальное состояние объекта (signaled) соответствует моменту времени, когда объект не принадлежит ни одному потоку и его можно "захватить". Состояние "сброшен" (nonsignaled) соответствует моменту, когда какой-либо поток уже владеет этим объектом. Доступ к объекту разрешается, когда поток, владеющий объектом, освободит его. Установка состояния выполняется вызовом соответствующей функции. Одна из задач создает объект-событие, вызывая для этого функцию CreateEvent. У события есть имя, которое доступно всем задачам активных процессов. В процессе создания или позже эта задача устанавливает событие в исходное состояние (signaled или nonsignaled). Вызывая функции WaitForSingleObject или WaitForMultipleObjects, задача ожидает момент, когда событие перейдет в signaled состояние. Другая задача, принадлежащая тому же самому или другому процессу, получает дескриптор события по его имени с помощью функции OpenEvent. Дальше используя функции SetEvent, ResetEvent или PulseEvent, приложение изменяет состояние события. После создания nonsignaled/неотмеченного события первая задача переходит в состояние ожидания, пока вторая задача не подготовит для нее данные. Как только это произойдет, вторая задача отмечает и затем сбрасывает событие, что приводит к завершению ожидания первой задачей. Отобразив подготовленные данные, первая задача опять входит в состояние ожидания, пока вторая задача не подготовит данные и не отметит событие. С помощью объекта-события две задачи синхронизируют свою работу.Создание событияДля создания события приложение вызывает функцию CreateEvent Code (C): HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // атрибуты защиты BOOL bManualReset, // флаг ручного сброса события BOOL bInitialState, // флаг начального состояния события LPCTSTR lpName // адрес имени объекта-события ); Параметр lpEventAttributes задает атрибуты защиты и в большинстве случаев может быть указан как NULL. Параметр bManualReset выбирает один из двух режимов работы объекта-события: ручной или автоматический. Если bManualReset=TRUE, событие нужно сбрасывать вручную при помощи функции ResetEvent. Если bManualReset=FALSE, событие будет сброшено (переведено в nonsignaled состояние) автоматически сразу после того как задача завершит ожидание этого события. Параметр bInitialState определяет начальное состояние события. Если bInitialState =TRUE, объект-событие создается в signaled/отмеченном состоянии, если bInitialState=FALSE ― в nonsignaled. Чтобы событием могли использовать задачи, созданные разными процессами, необходимо с помощью параметра lpName задать имя объекта-события. В качестве имени можно выбрать любое имя размером не более MAX_PATH=260 символов, не содержащее символ “\”. Если событие используется задачами, работающими в рамках одного процесса, имя события можно не задавать (lpName=NULL). Будет создано безымянное событие. В случае успешного завершения функция CreateEvent возвращает дескриптор события, который используется при выполнении операций над объектом-событием. При ошибке возвращается NULL (код ошибки получают при помощи GetLastError). Если при создании события указано имя уже существующего в системе события, созданного другой задачей ― тогда функция GetLastError возвратит значение ERROR_ALREADY_EXISTS=0B7h.Открытие событияЕсли событие используется задачами, созданными в рамках одного процесса, его не нужно открывать. В качестве параметра функциям, изменяющим состояние объекта-события, можно передавать дескриптор события, полученный при создании от функции CreateEvent. Если событие используется для синхронизации задач, принадлежащих разным процессам, нужно при создании события задать его имя. Задача, изменяющая состояние события и принадлежащая другому процессу, должна открыть объект-событие с помощью функции OpenEvent, передав ей имя этого объекта. Прототип функции OpenEvent: Code (C): HANDLE OpenEvent( DWORD fdwAccess, // флаги доступа BOOL fInherit, // флаг наследования LPCTSTR lpszEventName // адрес имени объекта-события ); Флаги доступа, передаваемые через параметр fdwAccess, определяют требуемый уровень доступа к объекту-событию. Этот параметр может быть комбинацией следующих значений: Значениеhexbin Описание EVENT_QUERY_STATE SEMAPHORE_QUERY_STATE10.0000.0000.0000.0000.0001Право на запрос состояния объекта события EVENT_MODIFY_STATE SEMAPHORE_MODIFY_STATE20.0000.0000.0000.0000.0010 Полученный дескриптор можно использовать функциями SetEvent и ResetEvent40.0000.0000.0000.0000.010080.0000.0000.0000.0000.1000DELETE100000.0001.0000.0000.0000.0000Право на удаление объектаREAD_CONTROL200000.0010.0000.0000.0000.0000Право на чтение информации в дескрипторе безопасности для объекта, не включая информацию в SACL. Для чтения или записи SACL необходимо запросить право доступа ACCESS_SYSTEM_SECURITYWRITE_DAC400000.0100.0000.0000.0000.0000Право на изменение DACL в дескрипторе безопасности для объекта. Право доступа на запись к произвольному управляющему списку доступа объекта access control list (ACL)WRITE_OWNER800000.1000.0000.0000.0000.0000Право на изменение владельца в дескрипторе безопасности объекта. SYNCHRONIZE1000001.0000.0000.0000.0000.0000 Полученный дескриптор можно использовать в любых функциях ожидания событияMUTEX_ALL_ACCESS1F00011.1111.0000.0000.0000.0001Указаны все возможные для Mutex флаги доступаEVENT_ALL_ACCESS SEMAPHORE_ALL_ACCESS1F00031.1111.0000.0000.0000.0011Указаны все возможные для Event и Semaphore флаги доступаПараметр fInherit определяет возможность наследования полученного дескриптора. Если fInherit=TRUE, дескриптор может наследоваться дочерними процессами. Если fInherit=FALSE, наследование не допускается. Через параметр lpszEventName передают имени объекта-события. С помощью OpenEvent несколько задач могут открыть один и тот же объект-событие и выполнять одновременное ожидание этого объекта.Установка событияДля установки объекта-события в отмеченное состояние используется функция SetEvent: Code (C): BOOL SetEvent(HANDLE hEvent); В качестве параметра функции необходимо передать дескриптор объекта-события, полученного от CreateEvent или OpenEvent. При успешном завершении возвращается TRUE, при ошибке ― FALSE. Код ошибки получают через GetLastError.Cброс событияСброс события (установка в неотмеченное состояние) выполняется функцией ResetEvent: Code (C): BOOL ResetEvent(HANDLE hEvent); Если задача создала событие, работающее в автоматическом режиме, оно будет сбрасываться без помощи ResetEvent, если какая-либо задача выполняла ожидание этого события и событие произошло.Функция PulseEventФункция PulseEvent выполняет установку объекта-события в signaled состояние с последующим сбросом события в nonsignaled состояние: Code (C): BOOL PulseEvent(HANDLE hEvent); Если эта функция вызвана для события, работающего в ручном режиме, то все задачи, ожидающие это событие, завершат ожидание и продолжат свою работу. Событие при этом будет установлено в nonsignaled состояние. Для автоматических объектов-событий выполняются аналогичные действия, но функция возвращает управление сразу как только одна из ожидающих задач перейдет в активное состояние. Текст приложения-сервера (08s.asm) Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 EVENT_DATA_COUNT = 16 size_of_buffer = 96 time_interval = 10 EventPipeObjectStruct struct hEventDataHigh dq EVENT_DATA_COUNT dup(?) hEventDataLow dq EVENT_DATA_COUNT dup(?) hEventAck dq ? EventPipeObjectStruct ends .code WinMain proc enter 30h,0 mov r9d,256;cx mov [rbp-10h],r9 mov qword ptr [rbp-8],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rbp-10h],rax;30h-10h=+20h invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local hFile:qword local szEventName[32]:byte local szReadWrite:qword ;number of bytes actually read or write local hResource:qword local pResource:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_INITDIALOG je wmINITDIALOG cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmINITDIALOG:invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam xor edi,edi;for(DWORD i = 0; i < 16; i++) @@:; set current event name invoke sprintf,&szEventName,&Format1,edi ; create new object invoke CreateEvent,0,0,0,&szEventName mov EventPipeObject.hEventDataHigh[rdi*8],rax ; check for errors or eax,eax;if(EventPipeObject.hEventDataHigh[i] == NULL) jz wmBYE;exit0; ; create "low" data event handles ; set current event name invoke sprintf,&szEventName,&Format2,edi ; create new object invoke CreateEvent,0,0,0,&szEventName mov EventPipeObject.hEventDataLow[rdi*8],rax ; check for errors or eax,eax;if(EventPipeObject.hEventDataLow[i] == NULL) jz wmBYE;exit0 inc edi cmp edi,EVENT_DATA_COUNT jb @b ; create acknowledgement event ; create new object invoke CreateEvent,0,0,0,&Format3 mov EventPipeObject.hEventAck,rax jmp wmBYE wmSEND_TXT:invoke GlobalAlloc,GPTR,size_of_buffer mov p,rax invoke GetDlgItemText,hWnddlg,ID_TXT,eax,size_of_buffer or eax,eax jz wmBYE mov dwLength,eax mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE mov edx,WM_USER+1 jmp @0 ;----------------------------------------------------------- wmSEND_ICO:; Отправить изображение второму приложению mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov dwLength,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov p,rax ;------------------------------------------------- and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 mov edx,dwLength invoke CreateIconFromResourceEx,eax,,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b ; Отправить данные получателю mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov edx,WM_USER+2 jmp @0 ;----------------------------------------------------------------------- wmSEND_WAV:mov ecx,offset wav_file invoke CreateFile,,GENERIC_READ,0,0,OPEN_EXISTING,\ FILE_ATTRIBUTE_ARCHIVE,0 mov hFile,rax invoke GetFileSize,eax,0 mov dwLength,eax invoke GlobalAlloc,GPTR,eax invoke GlobalLock,eax mov p,rax lea r9d,szReadWrite mov r8d,dwLength invoke ReadFile,hFile,eax,,,0 invoke CloseHandle,hFile ;------------------------------------------------------- ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov edx,WM_USER+3 ;------------------------------------------------ @0:; Отправить данные получателю mov r9d,dwLength invoke PostMessage,eax,,hWnddlg or eax,eax jz wmBYE;if(EventPipeObject.hEventAck == NULL) ; --------------------------------------------------------------------------- ; send block data mov rsi,p xor edi,edi @@: movzx eax,byte ptr[rdi+rsi] ;set "high" data event shr al, 4 invoke SetEvent,EventPipeObject.hEventDataHigh[rax*8] test eax, eax jz wmBYE ; set "low" data event movzx eax,byte ptr[rdi+rsi] and al,0Fh invoke SetEvent,EventPipeObject.hEventDataLow[rax*8] test eax, eax jz wmBYE ; wait for acknowledgement invoke WaitForSingleObject,EventPipeObject.hEventAck,INFINITE test eax, eax jnz wmBYE inc edi cmp edi,dwLength jb @b jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 ; Закрываем дескрипторы объектов-событий xor edi,edi @@: invoke CloseHandle,EventPipeObject.hEventDataHigh[rdi*8] inc edi cmp edi,2*EVENT_DATA_COUNT+1 jb @b wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;------------------------------------------------------------ Format1 db "Song_of_the_South_H%u",0 Format2 db "Song_of_the_South_L%u",0 Format3 db "Song_of_the_South_A",0 p1 dd 1 wav_file db '..\Images\03.wav',0 align 16 EventPipeObject EventPipeObjectStruct <> szWin db 'EventPipe Reciever',0 p dq ? dwLength dd ? end rc-файл (08s.rc) Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_IMG1 104 #define IDC_DIALOG 200 #define IDC_ICON1 500 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "EventPipe Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Write something and click 'Send Text'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Send Text",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Send ICO",ID_SEND_ICO,149,45,60,15 PUSHBUTTON "Send WAV",ID_SEND_WAV,149,65,60,15 PUSHBUTTON "Exit", IDCANCEL,149,85,60,15 END Текст приложения-клиента (08r.asm) Code (ASM): ; GUI # include win64a.inc EVENT_DATA_COUNT = 16 ;time_interval = 10 EventPipeObjectStruct struct hEventDataHigh dq EVENT_DATA_COUNT dup(?) hEventDataLow dq EVENT_DATA_COUNT dup(?) hEventAck dq ? EventPipeObjectStruct ends IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 ID_PROGRESS equ 106 IDC_ICON1 equ 500 .code WinMain proc enter 30h,0 mov r9d,offset DialogProc and qword ptr[rbp-10h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_INITDIALOG jne wmBYE wmINITDIALOG:mov hWnd,rcx mov edx,GWL_STYLE invoke GetWindowLongPtr or eax,WS_THICKFRAME OR WS_MINIMIZEBOX OR WS_MAXIMIZEBOX mov edx,GWL_STYLE invoke SetWindowLongPtr,hWnddlg,,rax mov r8d,offset WndProc mov edx,GWL_WNDPROC invoke SetWindowLongPtr,hWnddlg ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jnz @f;wmBYE mov edx,offset Error1 mov r8d,offset szAppName invoke MessageBox,hWnddlg,,,MB_ICONEXCLAMATION or MB_YESNO cmp eax,IDNO jz @f;wmBYE mov ecx,offset szService invoke WinExec,,SW_SHOW ; create "high" data event handles xor edi,edi;for(DWORD i = 0; i < ; i++) ; set current event name @@: invoke sprintf,&szEventName,&Format1,edi ; open existing object invoke OpenEvent,EVENT_ALL_ACCESS,0,&szEventName mov EventPipeObject.hEventDataHigh[rdi*8],rax or eax,eax jz wmBYE invoke sprintf,&szEventName,&Format2,edi ; open existing object invoke OpenEvent,EVENT_ALL_ACCESS,0,&szEventName mov EventPipeObject.hEventDataLow[rdi*8],rax ; check for errors or eax,eax;if(EventPipeObject.hEventDataLow[i] == NULL) jz wmBYE inc edi cmp edi,EVENT_DATA_COUNT jb @b ; create acknowledgement event invoke OpenEvent,EVENT_ALL_ACCESS,0,&Format3 mov EventPipeObject.hEventAck,rax ;-------------------------------------------------- ; Шрифт текста pushaddr szFont push FIXED_PITCH+FF_DONTCARE push PROOF_QUALITY push CLIP_DEFAULT_PRECIS push OUT_RASTER_PRECIS push ANSI_CHARSET push FALSE push FALSE push FALSE push FW_BOLD sub esp,20h invoke CreateFont,32,12,0,0 mov hFont,rax invoke GetDlgItem,hWnddlg,ID_PROGRESS mov hProgress,rax ; Установить наш собственный обработчик mov r8d,offset ProgressWindowProc invoke SetWindowLongPtr,hProgress,GWL_WNDPROC ; Сохранить хэндл предыдущего обработчика mov OldProc,rax wmBYE: xor eax,eax leave retn DialogProc endp WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local text:dword local ps:PAINTSTRUCT local hDC:dword local rcClient:RECT mov hWnd,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_USER+1 je wmUSER_1 cmp edx,WM_USER+2 je wmUSER_2 cmp edx,WM_USER+3 je wmUSER_3 leave jmp NtdllDialogWndProc_ ;----------------------------------------------------- wmUSER_1:mov rax,lParam mov DataLength,rax inc eax invoke GlobalAlloc,GPTR,eax mov p_Text,rax invoke EventPipeRecieve,eax and byte ptr[rdi],0 invoke SetDlgItemText,hWnd,ID_TXT,p_Text invoke GlobalFree,p_Text jmp wmBYE wmUSER_2:and percent,0 xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread2 mov r8d,offset TimerFuncProgress invoke CreateThread,,0,,13 invoke CloseHandle,hThread2 ;------------------------------------------------------ mov rax,lParam mov DataLength,rax invoke GlobalAlloc,GPTR,eax xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread3 mov r8d,offset TimerFuncICO invoke CreateThread,,0,,eax invoke CloseHandle,hThread3 jmp wmBYE wmUSER_3:and percent,0 xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread4 mov r8d,offset TimerFuncProgress invoke CreateThread,,0,,128 invoke CloseHandle,hThread4 ;----------------------------------------------- mov rax,lParam mov DataLength,rax invoke GlobalAlloc,GPTR,eax xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread5 mov r8d,offset TimerFuncWAV invoke CreateThread,,0,,eax invoke CloseHandle,hThread5 jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 ; Закрываем дескрипторы объектов-событий xor edi,edi @@: invoke CloseHandle,EventPipeObject.hEventDataHigh[rdi*8] inc edi cmp edi,2*EVENT_DATA_COUNT+1 jb @b wmBYE: mov eax,TRUE exit0: leave ret WndProc endp TimerFuncICO proc p:qword mov p,rcx invoke EventPipeRecieve ; Создать HICON напрямую из памяти and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,p,DataLength,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnd,ID_ICON,STM_SETIMAGE,IMAGE_ICON invoke GlobalFree,p exit2: leave ret TimerFuncICO endp TimerFuncWAV proc p:qword local lpwiocb:WAVEHDR local hWaveOut:qword mov p,rcx invoke EventPipeRecieve WAVE: mov r8,p ;"RIFF"+(File size)+(File Type Header)+(Format chunk marker)+(Length of format data) = 14h add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,p mov ecx,eax ;"RIFF"+(File size)+(File Type Header)+(Format chunk marker)+(Length of format data)+\ ;(Type of format)+(Number of Channels)+(Sample Rate)+(Sample Rate*BitsPerSample*Channels/8)+\ ;(BitsPerSample*Channels/8)+(Bits per sample)+(“data” chunk header)+(Size of the data section)=2Ch add eax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4];размер блока данных sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut @1: invoke GlobalFree,p exit2: leave ret TimerFuncWAV endp TimerFuncProgress proc lParam:qword mov lParam,rcx ; Сформировать строку для отрисовки @@: mov r8d,percent mov edx,offset szText mov ecx,offset buff invoke wsprintf ; Назначить строку свойством ProgressBar mov r8d,offset buff mov edx,offset szProp invoke SetProp,hProgress ; Назначить значение процента свойством ProgressBar mov r8d,percent mov edx,offset szPerc invoke SetProp,hProgress ; Перерисовать содержимое окна invoke InvalidateRect,hProgress,0,FALSE inc percent cmp percent,101 je exit0 invoke Sleep,lParam jmp @b exit0: leave ret TimerFuncProgress endp EventPipeRecieve proc p:qword local dwEventDataLowValue:byte mov edi,ecx add rcx,DataLength mov EndDataLength,ecx @@: lea edx,EventPipeObject.hEventDataLow invoke WaitForMultipleObjects,EVENT_DATA_COUNT,,0,2 mov dwEventDataLowValue,al;eax cmp eax,EVENT_DATA_COUNT;if(dwEventDataLowValue >= EVENT_DATA_COUNT) jnb exit1 ; wait for "high" data value lea edx,EventPipeObject.hEventDataHigh invoke WaitForMultipleObjects,EVENT_DATA_COUNT,,0,0 ; calculate byte value from high/low value shl al,4;dwEventDataHighValue * 16 add al,dwEventDataLowValue ; store byte value stosb ; set acknowledgement event invoke SetEvent,EventPipeObject.hEventAck ;------------------------------------------------------------------ ; increase ptr exit1: cmp edi,EndDataLength jb @b;for(DWORD i = 0; i < EndDataLength; i++) leave ret EventPipeRecieve endp ProgressWindowProc proc hEdit:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local text:dword local ps:PAINTSTRUCT local hDC:dword local rcClient:RECT mov hEdit,rcx mov uMsg,rdx mov wParam,r8 mov lParam,r9 cmp edx,WM_PAINT je wmPAINT mov [rsp+20h],r9;lParam mov r9,wParam mov r8,uMsg mov rdx,hEdit invoke CallWindowProc,OldProc jmp wmBYE wmPAINT:; Размеры окна lea edx,rcClient invoke GetClientRect,hEdit;,edi ; Получить текст из свойств mov edx,offset szProp invoke GetProp,hEdit;hProgress mov text,eax ; Начать рисование lea edx,ps invoke BeginPaint,hEdit;hProgress mov hDC,eax ; Шрифт invoke SelectObject,hDC,hFont invoke SetBkMode,hDC,TRANSPARENT ; Отрисовать общий фон invoke GetSysColorBrush,COLOR_WINDOW lea edx,rcClient invoke FillRect,hDC,,eax ; Текст для отрисовки есть? cmp text,0 je @f invoke GetSysColor,COLOR_WINDOWTEXT invoke SetTextColor,hDC,eax mov edx,offset buff mov qword ptr[rsp+20h],DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER or DT_CENTER lea r9d,rcClient invoke DrawText,hDC,,-1 @@:; Вычислить размеры области для заливки mov edx,offset szPerc invoke GetProp,hEdit;hProgress mov ecx,eax lea r9d,rcClient mov eax,[r9].RECT.right sub eax,[r9].RECT.left mul ecx xor edx,edx mov ecx,100 div ecx ; Позиция выполненного прогресса add eax,[r9].RECT.left ; Наложить выполненный прогресс mov ecx,[r9].RECT.left mov edx,[r9].RECT.top mov r9d,[r9].RECT.bottom invoke CreateRectRgn,,,eax invoke SelectClipRgn,hDC,eax invoke GetSysColorBrush,COLOR_HIGHLIGHT lea edx,rcClient invoke FillRect,hDC,,eax ; Текст для отрисовки есть? cmp text,0 je @f invoke GetSysColor,COLOR_HIGHLIGHTTEXT invoke SetTextColor,hDC,eax mov qword ptr[rsp+20h],DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER or DT_CENTER mov edx,offset buff lea r9d,rcClient invoke DrawText,hDC,,-1 @@: lea edx,ps invoke EndPaint,hDC xor eax,eax wmBYE: leave retn ProgressWindowProc endp ;----------------------------------------------------------------------- .data hWnd dq ? Format1 db "Song_of_the_South_H%u",0 Format2 db "Song_of_the_South_L%u",0 Format3 db "Song_of_the_South_A",0 p_Text dq ? p_ICO dq ? p_WAV dq ? szWin db "EventPipe Sender",0 Error1 db "The server is not running.",10,"Run?",0 szAppName db "EventPipe Reciever",0 szService db "08s",0 hThread2 dq ? hThread3 dq ? hThread4 dq ? hThread5 dq ? szEventName db 32 dup(?) buff db 100 dup(?) szText db '%u%% completed',0 percent dd 0 hProgress dq ? hFont dq ? szProp db 'text',0 szPerc db 'percent',0 szFont db "Verdana",0 OldProc dq ? align 16 EventPipeObject EventPipeObjectStruct <> DataLength dq ? EndDataLength dd ? end rc-файл (08r.rc) Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 #define ID_PROGRESS 106 IDC_DIALOG DIALOG 0,0,212,154 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "EventPipe Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -3, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,5,199,13 CONTROL "",ID_PROGRESS,"STATIC",WS_VISIBLE | 3 | WS_DISABLED | WS_BORDER,2,22,146,19 DEFPUSHBUTTON "Exit",IDCANCEL,149,25,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,3,40,128,128 END
в аттаче ICO/WAV/ASM/RC/EXE-файлы. Текстовые строки между приложениями передаются мгновенно, рисунки и медиа-файлы передаются продолжительное время, поэтому (чтобы пользователь не закрыл клиентское приложение раньше времени) к клиентскому приложению пришлось добавить progress-bar. Механизм progress bar'а с надписью заимствован на сайте manhunter'a https://www.manhunter.ru/ assembler/ 1753 kak sdelat progressbar s nadpisyu.html за что ему огромное спасибо Краткое пояснение: стандартный элемент msctls_progress32 не используется, вместо него взят элемент STATIC. Полоса индикатора и надпись рисуется на канве элемента диалогового окна, которое выделено под индикатор. При рисовании текста поверх индикатора прогресса, учтена разница в цвете фона и полосы выполненной части. Взаимодействие основного потока приложения и индикатора реализуется через сабклассирование.10. SemaphoresСемафоры позволяют обеспечить доступ к ресурсу для заранее определенного, ограниченного приложением количества задач. Все остальные задачи, пытающиеся получить доступ сверх установленного лимита, будут переведены при этом в состояние ожидания до тех пор, пока какая либо задача, получившая доступ к ресурсу раньше, не освободит ресурс, связанный с данным семафором. Задачи, пытающиеся получить доступ к ресурсу, должны быть переведены в состояние ожидания и пребывать в нем до тех пор, пока не завершит свою работу одна из задач, уже получившая доступ к ресурсу. Семафор может находиться в signaled (отмеченном) или unsignaled (неотмеченном) состоянии. Приложение выполняет ожидание семафора при помощи WaitForSingleObject или WaitForMultipleObject. Если семафор находится в неотмеченном состоянии, задача, вызвавшая для него функцию WaitForSingleObject, находится в состоянии ожидания. Если состояние семафора становится отмеченным, работа задачи возобновляется. С каждым семафором связывается счетчик, начальное и максимальные значения которого задаются при создании семафора. Значение этого счетчика уменьшается, когда задача вызывает для семафора функцию WaitForSingleObject или WaitForMultipleObject, и увеличивается при вызове другой, специально предназначенной для этого функции. Если значение счетчика семафора равно нулю, он находится в неотмеченном состоянии. Если же это значение больше нуля, семафор переходит в отмеченное состояние. Допустим приложение создало семафор, указав для него максимальное значение счетчика, равное трем. Пусть начальное значение этого счетчика также будет равно трем. Если в этой ситуации несколько запускаемых по очереди задач будут выполнять с помощью функции WaitForSingleObject ожидание семафора, то первые три запущенные задачи будут работать, а все остальные перейдут в состояние ожидания. Первые три вызова функции WaitForSingleObject приведут к последовательному уменьшению значения счетчика семафора до нуля, в результате чего семафор переключится в неотмеченное состояние. Задача, запущенная четвертой, вызовет функцию WaitForSingleObject для неотмеченного семафора, в результате чего она будет ждать. Точно также, задачи, запущенные после запуска четвертой задачи, будут выполнять ожидание для того же семафора. Как долго продлится ожидание? До тех пор, пока одна из первых трех задач не освободит семафор, увеличив его счетчик на единицу вызовом специальной функции. Сразу после этого будет запущена одна из задач, ожидающих наш семафор. Функции для работы с семафорами Создание семафора Для создания семафора приложение должно вызвать функцию CreateSemaphore Code (C): HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, // атрибуты защиты LONG lInitialCount, // начальное значение счетчика семафора LONG lMaximumCount, // максимальное значение счетчика семафора LPCTSTR lpName // адрес строки с именем семафора ); В качестве атрибутов защиты вы можете передать значение NULL. Через параметры lInitialCount и lMaximumCount передается, начальное и максимальное значение счетчика, связанного с создаваемым семафором. Начальное значение счетчика lInitialCount должно быть больше или равно нулю и не должно превосходить максимальное значение счетчика, передаваемое через параметр lMaximumCount. Имя семафора указывается с помощью параметра lpName. В случае удачного создания CreateSemaphore возвращает дескриптор семафора. В случае ошибки ― NULL, код ошибки можно узнать с помощью GetLastError. Имена семафоров доступны всем приложениям в системе, возможна ситуация, когда приложение пытается создать семафор с уже использованным именем. Тогда новый семафор не создается, а приложение получает дескриптор существующего семафора. При такой ситуации GetLastError возвращает значение ERROR_ALREADY_EXISTS=0B7h. Уничтожение семафора Для уничтожения семафора его дескриптор передается CloseHandle. При завершении процесса все созданные им семафоры уничтожаются автоматически. Открытие семафора Если семафор используется для синхронизации задач, созданных в одном приложении, можно создать безымянный семафор, указав lpName=0. Если необходимо синхронизировать задачи разных процессов, определяют имя семафора. Один процесс создает семафор с помощью функции CreateSemaphore, другой открывает семафор, получая дескриптор для уже существующего семафора. Существующий семафор открывают функцией OpenSemaphore Code (C): HANDLE OpenSemaphore( DWORD fdwAccess, // требуемый доступ BOOL fInherit, // флаг наследования LPCTSTR lpszSemaphoreName // адрес имени семафора ); Флаги доступа, передаваемые через параметр fdwAccess, определяют требуемый уровень доступа к семафору. Этот параметр может быть комбинацией следующих значений: ЗначениеhexОписаниеSEMAPHORE_ALL_ACCESS1F0003Указаны все возможные флаги доступаSEMAPHORE_QUERY_STATE1SEMAPHORE_MODIFY_STATE2 Возможно изменение значение счетчика семафора функцией ReleaseSemaphore SYNCHRONIZE100000 Полученный дескриптор можно будет использовать в любых функциях ожидания событияПараметр fInherit определяет возможность наследования полученного дескриптора. Если fInherit=TRUE, дескриптор может наследоваться дочерними процессами. Если fInherit=FALSE, наследование не допускается. Через lpszSemaphoreName функции передают адрес имени семафора. Если семафор открыт, функция OpenSemaphore возвращает его дескриптор. При ошибке возвращается значение NULL. Код ошибки определяют при помощи GetLastError.Увеличение значения счетчика семафораДля увеличения значения счетчика семафора приложение должно использовать функцию ReleaseSemaphore Code (C): BOOL ReleaseSemaphore( HANDLE hSemaphore, // дескриптор семафора LONG cReleaseCount, // значение инкремента LPLONG lplPreviousCount // адрес переменной для записи предыдущего значения счетчика семафора ); ReleaseSemaphore увеличивает значение счетчика семафора, дескриптор которого передается ей через параметр hSemaphore, на значение, указанное в параметре cReleaseCount. Через параметр cReleaseCount передают значение большее нуля. Если в результате увеличения новое значение счетчика должно будет превысить максимальное значение, заданное при создании семафора, ReleaseSemaphore возвращает признак ошибки и не изменяет значение счетчика. Предыдущее значение счетчика, которое было до использования функции ReleaseSemaphore, записывается в переменную. Адрес этой переменной передается через параметр lplPreviousCount. Если ReleaseSemaphore завершилась успешно, возвращается TRUE. При ошибке возвращается FALSE. Код ошибки можно определить при помощи GetLastError. ReleaseSemaphore используется для решения двух задач: с помощью ReleaseSemaphore задачи освобождают ресурс, доступ к которому регулируется семафором. Они могут делать это после использования ресурса или перед своим завершением. ReleaseSemaphore может быть использована на этапе инициализации мультизадачного приложения. Создавая семафор с начальным значением счетчика, равным нулю, главная задача блокирует работу задач, выполняющих ожидание этого семафора. После завершения инициализации главная задача с помощью функции ReleaseSemaphore может увеличить значение счетчика семафора до максимального, в результате чего известное количество ожидающих задач будет активизировано. Уменьшение значения счетчика семафораВ Windows нет функции, специально предназначенной для уменьшения значения счетчика семафора. Этот счетчик уменьшается, когда задача вызывает функции ожидания, такие как WaitForSingleObject или WaitForMultipleObject. Если задача вызывает несколько раз функцию ожидания для одного и того же семафора, содержимое его счетчика каждый раз будет уменьшаться.Определение текущего значения счетчика семафораЕдинственная возможность определения текущего значения счетчика семафора заключается в увеличении этого значения функцией ReleaseSemaphore. Значение счетчика, которое было до увеличения, будет записано в переменную, адрес которой передается функции ReleaseSemaphore через параметр lplPreviousCount. В Windows не предусмотрено средств, с помощью которых можно было бы определить текущее значение семафора, не изменяя его. Нельзя задать функции ReleaseSemaphore нулевое значение инкремента, так как в этом случае указанная функция просто вернет соответствующий код ошибки.Текст приложения-сервера (10s.asm) Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 ID_SEND_ICO equ 102 ID_SEND_WAV equ 103 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 SEMAPHORE_DATA_COUNT = 16 size_of_buffer = 96 time_interval = 10 SemaphoreObjectStruct struct hSemaphoreDataHigh dq SEMAPHORE_DATA_COUNT dup(?) hSemaphoreDataLow dq SEMAPHORE_DATA_COUNT dup(?) hSemaphoreAck dq ? SemaphoreObjectStruct ends .code WinMain proc enter 30h,0 mov r9d,256;cx mov [rbp-10h],r9 mov qword ptr [rbp-8],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rbp-10h],rax;30h-10h=+20h invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local hFile:qword local szSemaphoreName[32]:byte local szReadWrite:qword ;number of bytes actually read or write local hResource:qword local pResource:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_INITDIALOG je wmINITDIALOG cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND xor eax,eax jmp exit0 wmINITDIALOG:invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam xor edi,edi;for(DWORD i = 0; i < 16; i++) @@:; set current Semaphore name invoke sprintf,&szSemaphoreName,&Format1,edi ; create new object invoke CreateSemaphore,0,0,1,&szSemaphoreName mov SemaphoreObject.hSemaphoreDataHigh[rdi*8],rax ; check for errors or eax,eax;if(SemaphoreObject.hSemaphoreDataHigh[i] == NULL) jz wmBYE;exit0; ; create "low" data Semaphore handles ; set current Semaphore name invoke sprintf,&szSemaphoreName,&Format2,edi ; create new object invoke CreateSemaphore,0,0,1,&szSemaphoreName mov SemaphoreObject.hSemaphoreDataLow[rdi*8],rax ; check for errors or eax,eax;if(SemaphoreObject.hSemaphoreDataLow[i] == NULL) jz wmBYE;exit0 inc edi cmp edi,SEMAPHORE_DATA_COUNT jb @b ; create acknowledgement Semaphore ; create new object invoke CreateSemaphore,0,0,1,&Format3 mov SemaphoreObject.hSemaphoreAck,rax jmp wmBYE wmSEND_TXT:invoke GlobalAlloc,GPTR,size_of_buffer mov p,rax invoke GetDlgItemText,hWnddlg,ID_TXT,eax,size_of_buffer or eax,eax jz wmBYE mov dwLength,eax mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE mov edx,WM_USER+1 jmp @0 ;----------------------------------------------------------- wmSEND_ICO:; Отправить изображение второму приложению mov edx,p1 invoke FindResource,0,,RT_ICON; find the resource mov hResource,rax invoke SizeofResource,0,eax ; get its size mov dwLength,eax invoke LoadResource,0,hResource ; load the resource invoke LockResource,eax;pResource mov p,rax ;------------------------------------------------- and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 mov edx,dwLength invoke CreateIconFromResourceEx,eax,,TRUE,30000h;270376,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnddlg,IDC_IMG1,STM_SETIMAGE,IMAGE_ICON xor p1,11b ; Отправить данные получателю mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov edx,WM_USER+2 jmp @0 ;----------------------------------------------------------------------- wmSEND_WAV:mov ecx,offset wav_file invoke CreateFile,,GENERIC_READ,0,0,OPEN_EXISTING,\ FILE_ATTRIBUTE_ARCHIVE,0 mov hFile,rax invoke GetFileSize,eax,0 mov dwLength,eax invoke GlobalAlloc,GPTR,eax invoke GlobalLock,eax mov p,rax lea r9d,szReadWrite mov r8d,dwLength invoke ReadFile,hFile,eax,,,0 invoke CloseHandle,hFile ;------------------------------------------------------- ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jz wmBYE ; Отправить данные получателю mov edx,WM_USER+3 ;------------------------------------------------ @0:; Отправить данные получателю mov r9d,dwLength invoke PostMessage,eax,,hWnddlg or eax,eax jz wmBYE;if(SemaphoreObject.hSemaphoreAck == NULL) ; --------------------------------------------------------------------------- ; send block data mov rsi,p xor edi,edi @@: movzx eax,byte ptr[rdi+rsi] ;set "high" data Semaphore shr al, 4 invoke ReleaseSemaphore,SemaphoreObject.hSemaphoreDataHigh[rax*8],1,0 test eax, eax jz wmBYE ; set "low" data Semaphore movzx eax,byte ptr[rdi+rsi] and al,0Fh invoke ReleaseSemaphore,SemaphoreObject.hSemaphoreDataLow[rax*8],1,0 test eax, eax jz wmBYE ; wait for acknowledgement invoke WaitForSingleObject,SemaphoreObject.hSemaphoreAck,INFINITE test eax, eax jnz wmBYE inc edi cmp edi,dwLength jb @b jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_ICO je wmSEND_ICO cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + ID_SEND_WAV je wmSEND_WAV cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 ; Закрываем дескрипторы объектов-событий xor edi,edi @@: invoke CloseHandle,SemaphoreObject.hSemaphoreDataHigh[rdi*8] inc edi cmp edi,2*SEMAPHORE_DATA_COUNT+1 jb @b wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;------------------------------------------------------------ Format1 db "Song_of_the_South_H%u",0 Format2 db "Song_of_the_South_L%u",0 Format3 db "Song_of_the_South_A",0 p1 dd 1 wav_file db '..\Images\03.wav',0 align 16 SemaphoreObject SemaphoreObjectStruct <> szWin db 'Semaphore Reciever',0 p dq ? dwLength dd ? end ресурсы сервера (10s.rc) Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define ID_SEND_ICO 102 #define ID_SEND_WAV 103 #define IDC_IMG1 104 #define IDC_DIALOG 200 #define IDC_ICON1 500 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_ICON2 ICON "..\\Images\\icon2.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Semaphore Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Write something and click 'Send Text'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Send Text",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Send ICO",ID_SEND_ICO,149,45,60,15 PUSHBUTTON "Send WAV",ID_SEND_WAV,149,65,60,15 PUSHBUTTON "Exit", IDCANCEL,149,85,60,15 END Текст приложения-клиента (10s.asm) Code (ASM): ; GUI # include win64a.inc SEMAPHORE_DATA_COUNT = 16 SemaphoreObjectStruct struct hSemaphoreDataHigh dq SEMAPHORE_DATA_COUNT dup(?) hSemaphoreDataLow dq SEMAPHORE_DATA_COUNT dup(?) hSemaphoreAck dq ? SemaphoreObjectStruct ends IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 ID_PROGRESS equ 106 IDC_ICON1 equ 500 .code WinMain proc enter 30h,0 mov r9d,offset DialogProc and qword ptr[rbp-10h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_INITDIALOG jne wmBYE wmINITDIALOG:mov hWnd,rcx mov edx,GWL_STYLE invoke GetWindowLongPtr or eax,WS_THICKFRAME OR WS_MINIMIZEBOX OR WS_MAXIMIZEBOX mov edx,GWL_STYLE invoke SetWindowLongPtr,hWnddlg,,rax mov r8d,offset WndProc mov edx,GWL_WNDPROC invoke SetWindowLongPtr,hWnddlg ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jnz @f;wmBYE mov edx,offset Error1 mov r8d,offset szAppName invoke MessageBox,hWnddlg,,,MB_ICONEXCLAMATION or MB_YESNO cmp eax,IDNO jz @f;wmBYE mov ecx,offset szService invoke WinExec,,SW_SHOW ; create "high" data event handles xor edi,edi;for(DWORD i = 0; i < ; i++) ; set current event name @@: invoke sprintf,&szSemaphoreName,&Format1,edi ; open existing object invoke OpenSemaphore,SEMAPHORE_ALL_ACCESS,0,&szSemaphoreName mov SemaphoreObject.hSemaphoreDataHigh[rdi*8],rax or eax,eax jz wmBYE invoke sprintf,&szSemaphoreName,&Format2,edi ; open existing object invoke OpenSemaphore,SEMAPHORE_ALL_ACCESS,0,&szSemaphoreName mov SemaphoreObject.hSemaphoreDataLow[rdi*8],rax ; check for errors or eax,eax;if(SemaphoreObject.hSemaphoreDataLow[i] == NULL) jz wmBYE inc edi cmp edi,SEMAPHORE_DATA_COUNT jb @b ; create acknowledgement event invoke OpenSemaphore,SEMAPHORE_ALL_ACCESS,0,&Format3 mov SemaphoreObject.hSemaphoreAck,rax ;-------------------------------------------------- ; Шрифт текста pushaddr szFont push FIXED_PITCH+FF_DONTCARE push PROOF_QUALITY push CLIP_DEFAULT_PRECIS push OUT_RASTER_PRECIS push ANSI_CHARSET push FALSE push FALSE push FALSE push FW_BOLD sub esp,20h invoke CreateFont,32,12,0,0 mov hFont,rax invoke GetDlgItem,hWnddlg,ID_PROGRESS mov hProgress,rax ; Установить наш собственный обработчик mov r8d,offset ProgressWindowProc invoke SetWindowLongPtr,hProgress,GWL_WNDPROC ; Сохранить дескриптор предыдущего обработчика mov OldProc,rax wmBYE: xor eax,eax leave retn DialogProc endp WndProc proc hWnd:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local text:dword local ps:PAINTSTRUCT local hDC:dword local rcClient:RECT mov hWnd,rcx mov lParam,r9 cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_USER+1 je wmUSER_1 cmp edx,WM_USER+2 je wmUSER_2 cmp edx,WM_USER+3 je wmUSER_3 leave jmp NtdllDialogWndProc_ ;----------------------------------------------------- wmUSER_1:mov rax,lParam mov DataLength,rax inc eax invoke GlobalAlloc,GPTR,eax mov p_Text,rax invoke SemaphoreRecieve,eax and byte ptr[rdi],0 invoke SetDlgItemText,hWnd,ID_TXT,p_Text invoke GlobalFree,p_Text jmp wmBYE wmUSER_2:and percent,0 xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread2 mov r8d,offset TimerFuncProgress invoke CreateThread,,0,,13 invoke CloseHandle,hThread2 ;------------------------------------------------------ mov rax,lParam mov DataLength,rax invoke GlobalAlloc,GPTR,eax xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread3 mov r8d,offset TimerFuncICO invoke CreateThread,,0,,eax invoke CloseHandle,hThread3 jmp wmBYE wmUSER_3:and percent,0 xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread4 mov r8d,offset TimerFuncProgress invoke CreateThread,,0,,128 invoke CloseHandle,hThread4 ;----------------------------------------------- mov rax,lParam mov DataLength,rax invoke GlobalAlloc,GPTR,eax xor ecx,ecx mov [rsp+20h],rcx movr qword ptr[rsp+28h],hThread5 mov r8d,offset TimerFuncWAV invoke CreateThread,,0,,eax invoke CloseHandle,hThread5 jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 ; Закрываем дескрипторы объектов-событий xor edi,edi @@: invoke CloseHandle,SemaphoreObject.hSemaphoreDataHigh[rdi*8] inc edi cmp edi,2*SEMAPHORE_DATA_COUNT+1 jb @b wmBYE: mov eax,TRUE exit0: leave ret WndProc endp TimerFuncICO proc p:qword mov p,rcx invoke SemaphoreRecieve ; Создать HICON напрямую из памяти and qword ptr[rsp+30h],LR_DEFAULTCOLOR;LR_DEFAULTCOLOR=0 mov qword ptr[rsp+20h],256 mov qword ptr[rsp+28h],256 invoke CreateIconFromResourceEx,p,DataLength,TRUE,30000h mov [rsp+20h],rax invoke SendDlgItemMessage,hWnd,ID_ICON,STM_SETIMAGE,IMAGE_ICON invoke GlobalFree,p exit2: leave ret TimerFuncICO endp TimerFuncWAV proc p:qword local lpwiocb:WAVEHDR local hWaveOut:qword mov p,rcx invoke SemaphoreRecieve WAVE: mov r8,p ;"RIFF"+(File size)+(File Type Header)+(Format chunk marker)+(Length of format data) = 14h add r8d,14h lea ecx,hWaveOut xor r9,r9 mov [rsp+20h],r9 mov qword ptr[rsp+28h],WAVE_ALLOWSYNC or edx,WAVE_MAPPER invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,p mov ecx,eax ;"RIFF"+(File size)+(File Type Header)+(Format chunk marker)+(Length of format data)+\ ;(Type of format)+(Number of Channels)+(Sample Rate)+(Sample Rate*BitsPerSample*Channels/8)+\ ;(BitsPerSample*Channels/8)+(Bits per sample)+(“data” chunk header)+(Size of the data section)=2Ch add eax,2Ch mov [rdx].WAVEHDR.lpData,rax ;адрес блока данных mov eax,[rcx+4];размер блока данных sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax ;размер блока данных invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb mov rcx,hWaveOut mov r8d,sizeof WAVEHDR invoke waveOutWrite @@: test lpwiocb.dwFlags,WHDR_DONE jz @b lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR invoke waveOutClose,hWaveOut @1: invoke GlobalFree,p exit2: leave ret TimerFuncWAV endp TimerFuncProgress proc lParam:qword mov lParam,rcx ; Сформировать строку для рисования @@: mov r8d,percent mov edx,offset szText mov ecx,offset buff invoke wsprintf ; Назначить строку свойством ProgressBar mov r8d,offset buff mov edx,offset szProp invoke SetProp,hProgress ; Назначить значение процента свойством ProgressBar mov r8d,percent mov edx,offset szPerc invoke SetProp,hProgress ; Перерисовать содержимое окна invoke InvalidateRect,hProgress,0,FALSE inc percent cmp percent,101 je exit0 invoke Sleep,lParam jmp @b exit0: leave ret TimerFuncProgress endp SemaphoreRecieve proc p:qword local dwSemaphoreDataLowValue:byte mov edi,ecx add rcx,DataLength mov EndDataLength,ecx @@: lea edx,SemaphoreObject.hSemaphoreDataLow invoke WaitForMultipleObjects,SEMAPHORE_DATA_COUNT,,0,2 cmp eax,SEMAPHORE_DATA_COUNT;if(dwSemaphoreDataLowValue >= SEMAPHORE_DATA_COUNT) jnb exit1 ; wait for "high" data value mov dwSemaphoreDataLowValue,al lea edx,SemaphoreObject.hSemaphoreDataHigh invoke WaitForMultipleObjects,SEMAPHORE_DATA_COUNT,,0,0 ; calculate byte value from high/low value shl al,4;dwSemaphoreDataHighValue * 16 add al,dwSemaphoreDataLowValue ; store byte value stosb ; set acknowledgement event invoke ReleaseSemaphore,SemaphoreObject.hSemaphoreAck,1,0 ;------------------------------------------------------------------ ; increase ptr exit1: cmp edi,EndDataLength jb @b;for(DWORD i = 0; i < EndDataLength; i++) leave ret SemaphoreRecieve endp ProgressWindowProc proc hEdit:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local text:dword local ps:PAINTSTRUCT local hDC:dword local rcClient:RECT mov hEdit,rcx mov uMsg,rdx mov wParam,r8 mov lParam,r9 cmp edx,WM_PAINT je wmPAINT mov [rsp+20h],r9;lParam mov r9,wParam mov r8,uMsg mov rdx,hEdit invoke CallWindowProc,OldProc jmp wmBYE wmPAINT:; Размеры окна lea edx,rcClient invoke GetClientRect,hEdit;,edi ; Получить текст из свойств mov edx,offset szProp invoke GetProp,hEdit;hProgress mov text,eax ; Начать рисование lea edx,ps invoke BeginPaint,hEdit;hProgress mov hDC,eax ; Шрифт invoke SelectObject,hDC,hFont invoke SetBkMode,hDC,TRANSPARENT ; Нарисовать общий фон invoke GetSysColorBrush,COLOR_WINDOW lea edx,rcClient invoke FillRect,hDC,,eax ; Текст для рисования есть? cmp text,0 je @f invoke GetSysColor,COLOR_WINDOWTEXT invoke SetTextColor,hDC,eax mov edx,offset buff mov qword ptr[rsp+20h],DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER or DT_CENTER lea r9d,rcClient invoke DrawText,hDC,,-1 @@:; Вычислить размеры области для заливки mov edx,offset szPerc invoke GetProp,hEdit;hProgress mov ecx,eax lea r9d,rcClient mov eax,[r9].RECT.right sub eax,[r9].RECT.left mul ecx xor edx,edx mov ecx,100 div ecx ; Позиция выполненного прогресса add eax,[r9].RECT.left ; Наложить выполненный прогресс mov ecx,[r9].RECT.left mov edx,[r9].RECT.top mov r9d,[r9].RECT.bottom invoke CreateRectRgn,,,eax invoke SelectClipRgn,hDC,eax invoke GetSysColorBrush,COLOR_HIGHLIGHT lea edx,rcClient invoke FillRect,hDC,,eax ; Текст для рисования есть? cmp text,0 je @f invoke GetSysColor,COLOR_HIGHLIGHTTEXT invoke SetTextColor,hDC,eax mov qword ptr[rsp+20h],DT_NOPREFIX or DT_SINGLELINE or DT_VCENTER or DT_CENTER mov edx,offset buff lea r9d,rcClient invoke DrawText,hDC,,-1 @@: lea edx,ps invoke EndPaint,hDC xor eax,eax wmBYE: leave retn ProgressWindowProc endp ;----------------------------------------------------------------------- .data hWnd dq ? Format1 db "Song_of_the_South_H%u",0 Format2 db "Song_of_the_South_L%u",0 Format3 db "Song_of_the_South_A",0 p_Text dq ? p_ICO dq ? p_WAV dq ? szWin db "Semaphore Sender",0 Error1 db "The server is not running.",10,"Run?",0 szAppName db "Semaphore Reciever",0 szService db "10s",0 hThread2 dq ? hThread3 dq ? hThread4 dq ? hThread5 dq ? szSemaphoreName db 32 dup(?) buff db 100 dup(?) szText db '%u%% completed',0 percent dd 0 hProgress dq ? hFont dq ? szProp db 'text',0 szPerc db 'percent',0 szFont db "Verdana",0 OldProc dq ? align 16 SemaphoreObject SemaphoreObjectStruct <> DataLength dq ? EndDataLength dd ? end ресурсы клиента (10r.rc) Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 #define ID_PROGRESS 106 IDC_DIALOG DIALOG 0,0,212,154 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Semaphore Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -3, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,5,199,13 CONTROL "",ID_PROGRESS,"STATIC",WS_VISIBLE | 3 | WS_DISABLED | WS_BORDER,2,22,146,19 DEFPUSHBUTTON "Exit",IDCANCEL,149,25,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,3,40,128,128 END в аттаче ICO/WAV/ASM/RC/EXE-файлы
Receiver.c Code (C): #include <win/Common.h> #include <win/Manifest.h> #include <win/Window.h> #include <win/Dialog.h> #include <win/RichEdit.h> #include <rtl/memory.h> #include <rtl/string.h> #include <rtl/StaticLib.h> #include <win/StaticLib.h> #include "resource.h" /* -------------------------------------------------------------------------- */ static LPCWSTR MUTEX_DATA = L"MtxData"; static LPCWSTR EVENT_BEGIN_TRANSMISSION = L"EvBeginTransmission"; static LPCWSTR EVENT_END_TRANSMISSION = L"EvEndTransmission"; static LPCWSTR EVENT_RECEIVER_READY = L"EvReceiverReady"; /* -------------------------------------------------------------------------- */ typedef struct MainDialog { HWND hwnd; HWND hwndOutput; HANDLE eventBegin; HANDLE eventEnd; HANDLE eventReceiverReady; HANDLE eventStop; HANDLE threadHandle; DWORD threadId; }MainDialog; /* -------------------------------------------------------------------------- */ static void AppendLine (HWND hwndOutput, LPCWSTR format, ...) { va_list args; wstring_t text; CHARRANGE cr; if (format && format[0]) { va_start (args, format); wstring_init (&text, NULL); wstring_vformat (&text, format, args); cr.cpMin = -1; cr.cpMax = -1; RichEdit_ExGetSel (hwndOutput, &cr); Edit_ReplaceSel (hwndOutput, text.cstr); wstring_clear (&text); } } /* -------------------------------------------------------------------------- */ static DWORD WINAPI ReceiveMessageThread (LPVOID param) { MainDialog* dlg; HANDLE handles[18]; wchar_t name[256]; uint32_t i, index; string_t buffer; wstring_t text; uint8_t value; dlg = (MainDialog*)param; for (i=0; i<16; i++) { rtl_wsprintf (name, 256, L"%s:%u", MUTEX_DATA, i); handles[i] = OpenMutexW (MUTEX_ALL_ACCESS, FALSE, name); if (!handles[i]) { while (i--) CloseHandle (handles[i]); return 0; } } handles[16] = dlg->eventEnd; handles[17] = dlg->eventStop; string_init (&buffer, NULL); value = 0; i = 0; SetEvent (dlg->eventReceiverReady); while (TRUE) { index = WaitForMultipleObjects (18, handles, FALSE, INFINITE); if (index >= WAIT_OBJECT_0 && index < WAIT_OBJECT_0 + 18) index -= WAIT_OBJECT_0; else if (index >= WAIT_ABANDONED_0 && index < WAIT_ABANDONED_0 + 18) index -= WAIT_ABANDONED_0; else break; if (index > 15) break; ReleaseMutex (handles[index]); value = (value << 4) | (uint8_t)index; if (i++) { string_append_data (&buffer, (const char*)&value, 1); value = 0; i = 0; } } ResetEvent (dlg->eventReceiverReady); if (!string_empty (&buffer)) { wstring_init_data (&text, (const wchar_t*)buffer.cstr, buffer.length / sizeof(wchar_t)); AppendLine (dlg->hwndOutput, L"%s\n", text.cstr); wstring_clear (&text); } string_clear (&buffer); for (i=0; i<16; i++) CloseHandle (handles[i]); return 0; } /* -------------------------------------------------------------------------- */ static DWORD WINAPI ReceiverThread (LPVOID param) { MainDialog* dlg; HANDLE threadHandle; DWORD threadId; HANDLE handles[2]; uint32_t index; dlg = (MainDialog*)param; handles[0] = dlg->eventBegin; handles[1] = dlg->eventStop; while (TRUE) { index = WaitForMultipleObjects (2, handles, FALSE, INFINITE); if (index != 0) break; threadHandle = CreateThread (NULL, 0, ReceiveMessageThread, dlg, 0, &threadId); if (threadHandle) { WaitForSingleObject (threadHandle, INFINITE); CloseHandle (threadHandle); } } return 0; } /* -------------------------------------------------------------------------- */ static BOOL OnInitDialog (MainDialog* dlg, HWND hwndFocus, LPARAM lParam) { Dialog_EnableTabTexture (dlg->hwnd); dlg->hwndOutput = GetDlgItem (dlg->hwnd, IDC_OUTPUT); dlg->eventStop = CreateEvent (NULL, TRUE, FALSE, NULL); dlg->eventBegin = CreateEventW (NULL, TRUE, FALSE, EVENT_BEGIN_TRANSMISSION); dlg->eventEnd = CreateEventW (NULL, TRUE, TRUE, EVENT_END_TRANSMISSION); dlg->eventReceiverReady = CreateEventW (NULL, TRUE, FALSE, EVENT_RECEIVER_READY); dlg->threadHandle = CreateThread (NULL, 0, ReceiverThread, dlg, 0, &dlg->threadId); return TRUE; } /* -------------------------------------------------------------------------- */ static void OnCommand (MainDialog* dlg, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDCANCEL: if (codeNotify == BN_CLICKED) EndDialog (dlg->hwnd, 0); break; } } /* -------------------------------------------------------------------------- */ static void OnDestroy (MainDialog* dlg) { SetEvent (dlg->eventStop); if (dlg->threadHandle) { WaitForSingleObject (dlg->threadHandle, INFINITE); CloseHandle (dlg->threadHandle); } CloseHandle (dlg->eventStop); CloseHandle (dlg->eventBegin); CloseHandle (dlg->eventEnd); CloseHandle (dlg->eventReceiverReady); Window_RemoveStruct (dlg->hwnd); } /* -------------------------------------------------------------------------- */ static INT_PTR WINAPI DlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { MainDialog* dlg; dlg = Window_EnterWndProc (MainDialog, hwnd, msg, wParam, lParam); if (dlg) { switch (msg) { HANDLE_MSG (dlg, WM_INITDIALOG, OnInitDialog); HANDLE_MSG (dlg, WM_COMMAND, OnCommand); HANDLE_MSG (dlg, WM_DESTROY, OnDestroy); } } return FALSE; } /* -------------------------------------------------------------------------- */ int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) { INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_STANDARD_CLASSES; InitCommonControlsEx (&icc); RichEdit_Initialize(); DialogBoxParam (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_MAIN), NULL, DlgProc, 0l); return 0; } /* -------------------------------------------------------------------------- */ Transmitter.c Code (C): #include <win/Common.h> #include <win/Manifest.h> #include <win/Window.h> #include <win/Dialog.h> #include <win/Edit.h> #include <rtl/memory.h> #include <rtl/string.h> #include <rtl/StaticLib.h> #include <win/StaticLib.h> #include "resource.h" /* -------------------------------------------------------------------------- */ static LPCWSTR MUTEX_DATA = L"MtxData"; static LPCWSTR EVENT_BEGIN_TRANSMISSION = L"EvBeginTransmission"; static LPCWSTR EVENT_END_TRANSMISSION = L"EvEndTransmission"; static LPCWSTR EVENT_RECEIVER_READY = L"EvReceiverReady"; /* -------------------------------------------------------------------------- */ typedef struct MainDialog { HWND hwnd; HWND hwndMessage; HANDLE mutexes[16]; HANDLE eventBegin; HANDLE eventEnd; HANDLE eventReceiverReady; }MainDialog; /* -------------------------------------------------------------------------- */ static void CreateSyncObjects (MainDialog* dlg) { wchar_t name[256]; uint32_t i; for (i=0; i<16; i++) { rtl_wsprintf (name, 256, L"%s:%u", MUTEX_DATA, i); dlg->mutexes[i] = CreateMutexW (NULL, TRUE, name); } dlg->eventBegin = CreateEventW (NULL, TRUE, FALSE, EVENT_BEGIN_TRANSMISSION); dlg->eventEnd = CreateEventW (NULL, TRUE, TRUE, EVENT_END_TRANSMISSION); dlg->eventReceiverReady = CreateEventW (NULL, TRUE, FALSE, EVENT_RECEIVER_READY); } /* -------------------------------------------------------------------------- */ static void CloseSyncObjects (MainDialog* dlg) { uint32_t i; SetEvent (dlg->eventEnd); CloseHandle (dlg->eventBegin); CloseHandle (dlg->eventEnd); CloseHandle (dlg->eventReceiverReady); for (i=0; i<16; i++) CloseHandle (dlg->mutexes[i]); } /* -------------------------------------------------------------------------- */ static void SendData (MainDialog* dlg, LPCVOID data, uint32_t size) { uint8_t* ptr; if (data && size) { ResetEvent (dlg->eventEnd); SetEvent (dlg->eventBegin); if (WaitForSingleObject (dlg->eventReceiverReady, 5000) == WAIT_OBJECT_0) { for (ptr = (uint8_t*)data; size; ptr++, size--) { ReleaseMutex (dlg->mutexes[(*ptr) >> 4]); WaitForSingleObject (dlg->mutexes[(*ptr) >> 4], INFINITE); ReleaseMutex (dlg->mutexes[(*ptr) & 0x0F]); WaitForSingleObject (dlg->mutexes[(*ptr) & 0x0F], INFINITE); } } ResetEvent (dlg->eventBegin); SetEvent (dlg->eventEnd); } } /* -------------------------------------------------------------------------- */ static void OnSendMessage (MainDialog* dlg) { wstring_t text; wstring_init (&text, NULL); Edit_GetTextW (dlg->hwndMessage, &text); SendData (dlg, text.cstr, text.length * sizeof(wchar_t)); wstring_clear (&text); SetWindowTextW (dlg->hwndMessage, L""); SetFocus (dlg->hwndMessage); } /* -------------------------------------------------------------------------- */ static BOOL OnInitDialog (MainDialog* dlg, HWND hwndFocus, LPARAM lParam) { Dialog_EnableTabTexture (dlg->hwnd); dlg->hwndMessage = GetDlgItem (dlg->hwnd, IDC_MESSAGE); CreateSyncObjects (dlg); return TRUE; } /* -------------------------------------------------------------------------- */ static void OnCommand (MainDialog* dlg, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDCANCEL: if (codeNotify == BN_CLICKED) EndDialog (dlg->hwnd, 0); break; case IDC_SEND: OnSendMessage (dlg); break; } } /* -------------------------------------------------------------------------- */ static void OnDestroy (MainDialog* dlg) { CloseSyncObjects (dlg); Window_RemoveStruct (dlg->hwnd); } /* -------------------------------------------------------------------------- */ static INT_PTR WINAPI DlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { MainDialog* dlg; dlg = Window_EnterWndProc (MainDialog, hwnd, msg, wParam, lParam); if (dlg) { switch (msg) { HANDLE_MSG (dlg, WM_INITDIALOG, OnInitDialog); HANDLE_MSG (dlg, WM_COMMAND, OnCommand); HANDLE_MSG (dlg, WM_DESTROY, OnDestroy); } } return FALSE; } /* -------------------------------------------------------------------------- */ int WINAPI _tWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd) { INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_STANDARD_CLASSES; InitCommonControlsEx (&icc); DialogBoxParam (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_MAIN), NULL, DlgProc, 0l); return 0; } /* -------------------------------------------------------------------------- */
09. MutexsПри необходимости обеспечения последовательного использования ресурсов задачами, созданными в рамках разных процессов, вместо критических секций используют объекты синхронизации Mutex (mutually exclusive ― взаимно исключающий). Объект Mutex может находится в сигнальном или несигнальном состоянии. Когда задача, принадлежащая любому процессу, становится владельцем объекта Mutex, он переключается в несигнальное состояние. Если задача «отказывается» от владения Mutex, его состояние ― сигнальное. Организация последовательного доступа к ресурсам с использованием Mutex возможна потому, что в каждый момент только одна задача может владеть этим объектом. Все остальные задачи для того чтобы завладеть объектом, который уже захвачен, должны ждать, с помощью функции WaitForSingleObject. Чтобы Mutex был доступен задачам, принадлежащим различным процессам, при создании ему присваивается имя (аналогично тому, как это делают для объекта-события или семафора). Объект Mutex находится в сигнальном или несигнальном состоянии до тех пор, пока последняя программа, использующая Mutex, не закроет его дескриптор, поэтому Mutex может оставаться в памяти и после того, как исходное приложение, создавшее его, завершило работу. Поскольку Mutex широко используется, Mutex'у должны быть предоставлены явные разрешения, позволяющие любому процессу использовать его. Создание Mutex Для создания Mutex используют CreateMutex, Code (C): HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes, // атрибуты защиты BOOL bInitialOwner, // начальное состояние LPCTSTR lpName); // имя объекта Mutex В качестве lpMutexAttributes можно указать NULL либо права Mutex нужно указывать явным образом. В случае lpMutexAttributes=0 права, которые получит Mutex будут зависеть от прав того, кто этот Mutex создает ВладелецПраваhexСистемаMUTEX_ALL_ACCESS1F0001администраторыMUTEX_ALL_ACCESS1F0001все остальныеSYNCHRONIZE | READ_CONTROL | MUTEX_QUERY_STATE120001Приложению, желающему отправлять отладочные сообщения, требуется только возможность дождаться и захватить Mutex, и это представлено правом SYNCHRONIZE. bInitialOwner ― начальное состояние Mutex. Если bInitialOwner=TRUE, задача, создающая Mutex, будет им владеть сразу после создания. Если bInitialOwner=FALSE, после создания Mutex не будет принадлежать ни одной задаче, пока не будет захвачен ими явным образом. lpName ― указатель на имя Mutex, действуют те же правила, что и для имени объекта-события. Имя не должно содержать символ «\», его длина не должна превышать значение MAX_PATH. Если Mutex будет использован задачами только одного процесса, вместо lpName можно указать NULL ― создается «безымянный» Mutex. CreateMutex возвращает дескриптор созданного Mutex или NULL при ошибке. Когда приложение пытается создать Mutex с именем, которое уже используется в системе другим Mutex, тогда CreateMutex вернет дескриптор существующего объекта Mutex, а GetLastError, вызванная сразу после вызова функции CreateMutex, вернет значение ERROR_ALREADY_EXISTS. Если при вызове CreateMutex именованный Mutex уже существует и захвачен другим потоком, функция возвращает управление сразу, не дожидаясь его освобождения, даже если параметр bInitialOwner =TRUE. Если, по логике кода, создающий Mutex поток должен стать его владельцем, необходимо явно вызвать WaitForSingleObject, если GetLastError вернула ERROR_ALREADY_EXISTS Функция создания объектов-событий CreateEvent ведет себя в данной ситуации аналогично.Освобождение дескриптора MutexЕсли Mutex больше не нужен, нужно освободить его дескриптор при помощи CloseHandle. При завершении процесса освобождаются дескрипторы всех Mutex, созданных для него. Если ни одно приложение не владеет объектом Mutex, функция WaitForSingleObject возвращает значение WAIT_OBJECT_0. В том случае когда какое-либо приложение владеет объектом Mutex, данное приложение перейдет в состояние ожидания. После окончания потребности в Mutex приложение переводит объект Mutex в несигнальное состояние с помощью функции ReleaseMutexОткрытие MutexЗная имя Mutex (lpszMutexName), можно его открыть с помощью OpenMutex, Code (C): HANDLE OpenMutex( DWORD fdwAccess, // требуемый доступ BOOL fInherit, // флаг наследования LPCTSTR lpszMutexName ); // адрес имени Mutex Флаги доступа, передаваемые через параметр fdwAccess, определяют требуемый уровень доступа к Mutex. Параметр может быть комбинацией значений: ЗначениеhexОписаниеMUTEX_ALL_ACCESS1F0001Указаны все возможные флаги доступа. полный доступ (разрешены все возможные виды доступа) Доступ к объекту Mutex. Для использования Mutex требуется только право доступа SYNCHRONIZE; чтобы изменить безопасность Mutex, укажите MUTEX_ALL_ACCESS. Функция завершается ошибкой, если дескриптор безопасности указанного объекта не разрешает запрошенный доступ для вызывающего процесса.MUTEX_MODIFY_STATE1определяет возможность изменения значение счетчика Mutex функцией ReleaseCREATE_MUTEX_INITIAL_OWNER1Полученный дескриптор можно будет использовать в любых функциях ожидания событияDELETE10000Разрешено удаление объектаREAD_CONTROL20000Разрешено чтение информации в дескрипторе безопасности для объекта, не включая информацию в SACL (System Access Control List ― список управления доступом к объектам). Для чтения или записи SACL необходимо запросить право доступа ACCESS_SYSTEM_SECURITY.WRITE_DAC40000Разрешено изменение DACL (Discretionary Access Control List — список избирательного управления доступом, контролируемый владельцем объекта и регламентирующий права пользователей и групп на действия с объектом: чтение, запись, удаление и т.д.) в дескрипторе безопасности для объектаWRITE_OWNER80000Разрешено изменение владельца в дескрипторе безопасности объектаSYNCHRONIZE100000позволяет использовать дескриптор объекта Mutex в любой из функций ожиданияПараметр fInherit определяет возможность наследования полученного дескриптора. Если fInherit=TRUE, дескриптор наследуется дочерними процессами Если fInherit=FALSE, наследование не допускается Через параметр lpszEventName передают OpenMutex адрес символьной строки, содержащей имя объекта Mutex С помощью функции OpenMutex несколько задач могут открыть один и тот же объект Mutex и затем выполнять одновременное ожидание для этого объекта. Функция OpenMutex завершается ошибкой, если объект Mutex с указанным именем не существует.Как завладеть Mutex?Зная дескриптор Mutex, полученный от CreateMutex или OpenMutex, задача может завладеть объектом при помощи функций ожидания событий для одного объекта: SignalObjectAndWait WaitForSingleObject WaitForSingleObjectEx для нескольких объектов: WaitForMultipleObjects WaitForMultipleObjectsEx MsgWaitForMultipleObjects MsgWaitForMultipleObjectsEx Любой поток с дескриптором для объекта Mutex может использовать одну из функций ожидания для запроса владения объектом Mutex. Если объект Mutex принадлежит другому потоку, функция ожидания блокирует запрашивающий поток, пока собственный поток не выпустит объект Mutex с помощью функции ReleaseMutex. Функция WaitForSingleObject возвращает управление, как только дескриптор объекта, передаваемый ей в качестве параметра, перейдет в сигнальное состояние. Если Mutex не принадлежит ни одной задаче, его состояние будет сигнальным. При вызове функцию WaitForSingleObject для Mutex, который никому не принадлежит, WaitForSingleObject сразу возвращает управление. Задача, вызвавшая функцию WaitForSingleObject, становится владельцем Mutex. Если другая задача вызовет функцию WaitForSingleObject для этого Mutex, то она будет переведена в состояние ожидания до тех пор, пока первая задача не «откажется от своих прав» на данный Mutex. Освобождение Mutex выполняется функцией ReleaseMutex. Захват Mutex аналогичен входу в критическую секцию.Освобождение MutexДля отказа от владения Mutex (для его освобождения) используется функция ReleaseMutex: Code (C): BOOL ReleaseMutex(HANDLE hMutex); Единственный параметр этой функции ― дескриптор Mutex. Функция возвращает TRUE при успешном завершении и FALSE при ошибке. Освобождение Mutex соответствует выходу из критической секции.Рекурсивное использование MutexОбъекты Mutex допускают рекурсивное использование. Задача может выполнять рекурсивные попытки завладеть одним и тем же Mutex и при этом она не будет переводиться в состояние ожидания. В случае рекурсивного использования каждому вызову функции ожидания должен соответствовать вызов функции освобождения Mutex ReleaseMutex. Функция ReleaseMutex уменьшает счетчик рекурсии в объекте Mutex на 1. Если данный объект передавался во владение потоку неоднократно, поток обязан вызвать ReleaseMutex столько раз, сколько необходимо для обнуления счетчика рекурсии. Как только счетчик станет равен 0, переменная, хранящая идентификатор потока, тоже обнулится, и объект Mutex освободится. После этого система проверит, ожидают ли освобождения Mutex какие-нибудь другие потоки. Если да, система «по-честному» выберет один из ждущих потоков и передаст ему во владение объект Mutex. Уничтожение MutexДля уничтожения Mutex его дескриптор передается CloseHandle. При завершении процесса все созданные им Mutex'ы не уничтожаются автоматически, а «висят» в памяти. Благодарности Огромное спасибо rmn за подсказки и замечания Блин #1 (неудачная реализация)Сделал, но передача через Mutex (в отличие от событий и семафоров) работает крайне неустойчиво, даже при передаче текстовой строки возможно появление неверных символов, чего уж мечтать о передаче картинок или звукового файла. Кнопку «Send Text» нужно нажать несколько раз, прежде чем появится заданный текст. Текст приложения-сервера (09s.asm) Code (ASM): ; GUI # include win64a.inc ID_TXT equ 100 ID_SEND_TXT equ 101 IDC_DIALOG equ 200 IDC_ICON1 equ 500 IDC_IMG1 equ 104 MUTEX_DATA_COUNT = 16 size_of_buffer = 96 MUTEX_ALL_ACCESS equ 1F0001h .code WinMain proc enter 30h,0 mov r9d,256;cx mov [rbp-10h],r9 mov qword ptr [rbp-8],LR_DEFAULTCOLOR invoke LoadImage,IMAGE_BASE,IDC_ICON1,IMAGE_ICON;,256,256,LR_DEFAULTCOLOR mov r9d,offset DialogProc mov qword ptr[rbp-10h],rax;30h-10h=+20h invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD local MutexName[256]:word local SizeString:dword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_INITDIALOG je wmINITDIALOG cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_DESTROY je wmDESTROY xor eax,eax jmp exit0 wmINITDIALOG:mov hWnd,rcx invoke GetDlgItem,,IDC_IMG1 invoke SendMessage,eax,STM_SETIMAGE,IMAGE_ICON,lParam ;Create Sync Objects xor edi,edi @@: lea ecx,MutexName mov edx,offset fmt invoke wsprintfW,,,edi lea r8d,MutexName invoke CreateMutexW,0,TRUE mov mutexes[rdi*8],rax inc edi cmp edi,MUTEX_DATA_COUNT jb @b mov r9d,offset EVENT_BEGIN_TRANSMISSION invoke CreateEventW,NULL,TRUE,FALSE mov eventBegin,rax mov r9d,offset EVENT_END_TRANSMISSION invoke CreateEventW,NULL,TRUE,TRUE mov eventEnd,rax mov r9d,offset EVENT_RECEIVER_READY invoke CreateEventW,NULL,TRUE,FALSE mov eventReceiverReady,rax jmp wmBYE wmSEND_TXT:lea r8d,MutexName invoke GetDlgItemText,hWnd,ID_TXT,,256 mov SizeString,eax ;Send Data-------------------------------------- invoke ResetEvent,eventEnd invoke SetEvent,eventBegin invoke WaitForSingleObject,eventReceiverReady,5000 cmp eax,WAIT_OBJECT_0 jnz @0 lea esi,MutexName @@: movzx edi,byte ptr[rsi] shr edi,4 invoke ReleaseMutex,mutexes[rdi*8] invoke WaitForSingleObject,mutexes[rdi*8],INFINITE movzx edi,byte ptr[rsi] and edi,0Fh invoke ReleaseMutex,mutexes[rdi*8] invoke WaitForSingleObject,mutexes[rdi*8],INFINITE inc esi dec SizeString jnz @b @0: invoke ResetEvent,eventBegin invoke SetEvent,eventEnd jmp wmBYE wmDESTROY:;Close Sync Objects invoke SetEvent,eventEnd invoke CloseHandle,eventBegin invoke CloseHandle,eventEnd invoke CloseHandle,eventReceiverReady xor edi,edi @@: invoke CloseHandle,mutexes[rdi*8] inc edi cmp edi,MUTEX_DATA_COUNT jb @b jmp wmBYE ; --------------------------------------------------------------------------- wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + ID_SEND_TXT je wmSEND_TXT cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ;--------------------------------------- .data szWin db "Mutex Reciever",0 hWnd dq ? align 10h EVENT_BEGIN_TRANSMISSION dw "E","v","B","e","g","i","n","T","r","a","n","s","m","i","s","s","i","o","n",0 EVENT_END_TRANSMISSION dw "E","v","E","n","d","T","r","a","n","s","m","i","s","s","i","o","n",0 EVENT_RECEIVER_READY dw "E","v","R","e","c","e","i","v","e","r","R","e","a","d","y",0 fmt dw "M","t","x","D","a","t","a",":","%","u",0 mutexes dq 16 dup(?) eventBegin dq ? eventEnd dq ? eventReceiverReady dq ? end ресурсы сервера (09s.rc) Code (C): #include "resource.h" #define ID_TXT 100 #define ID_SEND_TXT 101 #define IDC_IMG1 104 #define IDC_DIALOG 200 #define IDC_ICON1 500 IDC_ICON1 ICON "..\\Images\\icon1.ico" IDC_DIALOG DIALOG 0, 0, 212, 140 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Mutex Sender" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",IDC_IMG1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_ICON,5,23,128,128 CONTROL "Write something and click 'Send Text'",ID_TXT,"EDIT",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Send Text",ID_SEND_TXT,149,27,60,15 PUSHBUTTON "Exit", IDCANCEL,149,85,60,15 END Текст приложения-клиента (09r.asm) Code (ASM): ; GUI # include win64a.inc MUTEX_DATA_COUNT = 16 MUTEX_ALL_ACCESS = 1F0001h size_of_buffer = 96 IDC_DIALOG equ 200 ID_TXT equ 100 ID_ICON equ 102 ID_PROGRESS equ 106 IDC_ICON1 equ 500 .code WinMain proc enter 30h,0 mov r9d,offset DialogProc and qword ptr[rbp-10h],0 invoke DialogBoxParam,IMAGE_BASE,IDC_DIALOG,HWND_DESKTOP invoke RtlExitUserProcess,NULL WinMain endp DialogProc proc hWnddlg:qword,msg:qword,wParam:qword,lParam:qword mov hWnddlg,rcx mov lParam,r9 cmp edx,WM_INITDIALOG je wmINITDIALOG cmp edx,WM_CLOSE je wmCLOSE cmp edx,WM_COMMAND je wmCOMMAND cmp edx,WM_DESTROY je wmDESTROY xor eax,eax jmp exit0 wmINITDIALOG:mov hWnd,rcx ; Найти окно получателя mov edx,offset szWin invoke FindWindow,NULL or eax,eax jnz @f;wmBYE mov edx,offset Error1 mov r8d,offset szAppName invoke MessageBox,hWnddlg,,,MB_ICONEXCLAMATION or MB_YESNO cmp eax,IDNO jz @f mov ecx,offset szService invoke WinExec,,SW_SHOW @@: invoke CreateEvent,NULL, TRUE, FALSE, NULL mov eventStop,rax mov r9d,offset EVENT_BEGIN_TRANSMISSION invoke CreateEventW,NULL, TRUE, FALSE mov eventBegin,rax mov r9d,offset EVENT_END_TRANSMISSION invoke CreateEventW,NULL, TRUE, TRUE mov eventEnd,rax mov r9d,offset EVENT_RECEIVER_READY invoke CreateEventW,NULL, TRUE, FALSE mov eventReceiverReady,rax movr qword ptr[rsp+28h],threadId xor ecx,ecx mov [rsp+20h],rcx mov r8d,offset ReceiverThread invoke CreateThread,,0,,0 mov threadHandle,rax jmp wmBYE wmDESTROY:invoke SetEvent,eventStop cmp threadHandle,0 jz @f ; dlg->eventStop invoke WaitForSingleObject,threadHandle,INFINITE invoke CloseHandle,threadHandle @@: invoke CloseHandle,eventStop invoke CloseHandle,eventBegin invoke CloseHandle,eventEnd invoke CloseHandle,eventReceiverReady jmp wmBYE wmCOMMAND:cmp r8d,BN_CLICKED shl 16 + IDCANCEL jne wmBYE wmCLOSE:invoke EndDialog,,0 wmBYE: mov eax,TRUE exit0: leave retn DialogProc endp ReceiverThread proc local threadHandle:qword local threadId:qword local handles[2]:qword local index:dword mov rax,eventBegin mov handles[0*8],rax mov rax,eventStop mov handles[1*8],rax @@: lea edx,handles invoke WaitForMultipleObjects,2,,FALSE,INFINITE mov index,eax or eax,eax jnz exit0 lea eax,threadId mov [rsp+28h],rax xor ecx,ecx mov [rsp+20h],rcx mov r8d,offset ReceiveMessageThread invoke CreateThread,,0,,0 mov threadHandle,rax or eax,eax jz @0;if (threadHandle) invoke WaitForSingleObject,eax, INFINITE invoke CloseHandle,threadHandle @0: lea edx,handles invoke WaitForMultipleObjects,2,,FALSE,INFINITE or eax,eax jz @b exit0: leave xor eax,eax ret ReceiverThread endp ReceiveMessageThread proc local handles[18]:qword local MutexName[256]:word local index:byte local value:byte local buffer[256]:byte local count:dword lea edi,buffer xor eax,eax mov ecx,256/8 rep stosq xor edi,edi @@: lea ecx,MutexName mov edx,offset fmt invoke wsprintfW,,,edi lea r8d,MutexName invoke OpenMutexW,MUTEX_ALL_ACCESS,FALSE mov handles[rdi*8],rax or eax,eax jz @3 inc edi cmp edi,MUTEX_DATA_COUNT jb @b mov rax,eventEnd mov handles[16*8],rax mov rax,eventStop mov handles[17*8],rax and count,0 and value,0 xor esi,esi invoke SetEvent,eventReceiverReady @2: lea edx,handles invoke WaitForMultipleObjects,18,,FALSE,INFINITE mov index,al cmp al,WAIT_OBJECT_0 + 18 jb @f sub index,WAIT_ABANDONED_0 cmp index, 17 ja break @@: cmp index, 15 ja break movzx eax,index invoke ReleaseMutex,handles[rax*8] mov al,value shl al,4 or al,index mov value,al;value = (value << 4) | index mov ecx,esi inc esi test ecx,ecx jz @2 mov ecx,count ;---------------------------------------------- mov edx,-17 sub edx,eax cmp edx,1 jb @f mov eax,count mov dl,value mov buffer[rax],dl inc count @@: and value,0 xor esi,esi jmp @2 ;-------------------------------------------------- @3: or esi,esi jz exit0;if (!handles[i]) @@: dec esi;while (i--) invoke CloseHandle,handles[rsi*8] or esi,esi jnz @b jmp exit0 break: invoke ResetEvent,eventReceiverReady xor edi,edi @@: invoke CloseHandle,handles[rdi*8] inc edi cmp edi,MUTEX_DATA_COUNT jb @b lea esi,buffer cmp byte ptr[rsi],0 jz exit0 lea r8d,buffer invoke SetDlgItemText,hWnd,ID_TXT exit0: xor eax,eax leave ret ReceiveMessageThread endp ;----------------------------------------------------------------------- .data szWin db "Transmitter",0 Error1 db "The server is not running.",10,"Run?",0 szAppName db "Mutex Reciever",0 szService db "09s",0 eventBegin dq ? eventEnd dq ? eventReceiverReady dq ? eventStop dq ? threadHandle dq ? threadId dd ? hWnd dq ? align 10h EVENT_BEGIN_TRANSMISSION dw "E","v","B","e","g","i","n","T","r","a","n","s","m","i","s","s","i","o","n",0 EVENT_END_TRANSMISSION dw "E","v","E","n","d","T","r","a","n","s","m","i","s","s","i","o","n",0 EVENT_RECEIVER_READY dw "E","v","R","e","c","e","i","v","e","r","R","e","a","d","y",0 fmt dw "M","t","x","D","a","t","a",":","%","u",0 end ресурсы клиента (09r.rc) Code (C): #include "resource.h" #define IDC_DIALOG 200 #define ID_TXT 100 #define ID_ICON 102 IDC_DIALOG DIALOG 0,0,212,160 STYLE 0x0004 | DS_CENTER | WS_CAPTION | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPED | DS_MODALFRAME | DS_3DLOOK CAPTION "Mutex Reciever" BEGIN CONTROL "",-1,"BUTTON",BS_GROUPBOX, 2, -1, 207, 24 CONTROL "",ID_TXT,"STATIC",WS_BORDER | WS_TABSTOP | ES_AUTOHSCROLL,5,7,199,13 DEFPUSHBUTTON "Exit",IDCANCEL,149,27,60,15 CONTROL "",ID_ICON,"STATIC",WS_CHILDWINDOW | SS_ICON,5,48,128,128 END В аттаче asm/rc/exe-файлы P.P.S. Буду очень рад если мне подскажут ЧТО я сделал не так Продолжение здесь
Зачем выдумывать какие-то отмеченные/неотмеченные состояния? У всех объектов синхронизации есть одно состояние - сигнальное; функции ожидания возвращают управление, когда объект переходит в сигнальное состояние. Мьютекс находится в сигнальном состоянии, когда он не захвачен ни одним потоком. Важные не указанные моменты: - если при вызове CreateMutex именованный мьютекс уже существует и захвачен другим потоком, функция возвращает управление сразу, не дожидаясь его освобождения, даже если параметр bInitialOwner равен TRUE. Так что если по логике кода создающий мьютекс поток должен стать его владельцем, необходимо явно вызвать WaitForSingleObject, если GetLastError вернула ERROR_ALREADY_EXISTS. - если владеющий мьютексом поток завершится до освобождения мьютекса, мьютекс освобождается принудительно, при этом функции ожидания вернут WAIT_ABANDONED + N вместо WAIT_OBJECT + N
Справедливости ради, следует ещё упомянуть, что в Windows есть механизм отложенного получения данных Clipboard (Delayed Rendering), связанный с обработкой сообщений WM_RENDERFORMAT, WM_RENDERALLFORMATS, WM_DESTROYCLIPBOARD.
12. Sockets Сокеты, датаграммы и каналы связиВ локальных и глобальных сетях существует два принципиально разных способа передачи данных. посылка пакетов данных от одного узла другому (или сразу нескольким узлам) без получения подтверждения о доставке и без гарантии того, что передаваемые пакеты будут получены в правильной последовательности. Примером такого протокола ― протокол UDP (User Datagram Protocol), используемый в сетях TCP/IP, или протокол IPX, который является базовым в сетях Novell NetWare. Основные преимущества датаграмных протоколов высокое быстродействии и возможность широковещательной передачи данных, когда одновременно один узел отправляет сообщения, а другие их получают. Создание канала передачи данных между двумя различными узлами сети. Канал создается средствами датаграммных протоколов, доставка пакетов в канале гарантированна. Пакеты доходят в целостности и сохранности, в правильном порядке, хотя быстродействие в среднем ниже за счет посылки подтверждений. Пример протоколов, использующих каналы связи ― протоколы TCP и SPX протокол NETBIOS допускает передачу данных с использованием как датаграмм, так и каналов связи. Для передачи данных с использованием любого из перечисленных выше способов каждое приложение должно создать объект, который называется сокетом. По своему назначению сокет больше всего похож на дескриптор файла (file handle), который нужен для выполнения над файлом операций чтения или записи. Прежде чем приложение, запущенное на узле сети сможет выполнять передачу или прием данных, оно должно создать сокет и проинициализировать его, указав параметры. Для сокета необходимо указать три параметра: IP-адрес, связанный с сокетом, номер порта, для которого будут выполняться операции передачи данных, тип сокета. Существуют сокеты следующих типов Предназначен для передачи данных в виде датаграмм, с использованием каналов связи Сокеты смешанного типа Инициализация приложения и завершение его работыВ процессе инициализации приложение должно зарегистрировать себя в библиотеке WSOCK32.DLL, которая предоставляет приложениям интерфейс Windows Sockets в среде операционных систем Windows. Для инициализации необходимо вызвать функцию WSAStartup, определенную как Code (C): int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData); В параметре wVersionRequested указывают версию интерфейса Windows Sockets. Старший байт ― младший номер версии (minor version), младший байт ― старший номер версии (major version). Перед вызовом функции WSAStartup параметр lpWSAData содержит указатель на структуру типа WSADATA, в которую записаны сведения о конкретной реализации интерфейса Windows Sockets. В случае успеха функция WSAStartup возвращает нулевое значение. При ошибке возвращается одно из следующих значений: Код ошибкиhexОписаниеWSASYSNOTREADY276BСетевое программное обеспечение не готово для работыWSAVERNOTSUPPORTED276CФункция не поддерживается данной реализацией интерфейса Windows SocketsWSAEINVAL2726Библиотека DLL, обеспечивающая интерфейсe Windows Sockets, не соответствует версии, указанной приложением указанной в параметре wVersionRequestedфрагмент исходного текста приложения, выполняющий инициализацию интерфейса Windows Sockets: Code (C): rc = WSAStartup (MAKEWORD(1, 1), &WSAData); if(rc != 0) { MessageBox(NULL, "WSAStartup Error", "Error", MB_OK); return FALSE; } // Отображаем описание и версию системы Windows Sockets // в окне органа управления Statusbar wsprintf(szTemp, "Server use %s %s", WSAData.szDescription,WSAData.szSystemStatus); hwndSb = CreateStatusWindow(WS_CHILD | WS_VISIBLE | WS_BORDER | SBARS_SIZEGRIP, szTemp, hWnd, IDS_STATUSBAR); Содержимое двух полей структуры типа WSADATA отображается в Statusbar'е Определение структуры WSADATA и указателя на нее: Code (C): typedef struct WSAData { WORD wVersion; WORD wHighVersion; char szDescription[WSADESCRIPTION_LEN+1]; char szSystemStatus[WSASYS_STATUS_LEN+1]; unsigned short iMaxSockets; unsigned short iMaxUdpDg; char FAR * lpVendorInfo; } WSADATA ; typedef WSADATA FAR *LPWSADATA; Поля szDescription и szSystemStatus после вызова WSAStartup содержат описание конкретной реализации интерфейса Windows Socket и текущее состояние этого интерфейса в виде текстовых строк. В полях wVersion и wHighVersion записаны, версия спецификации Windows Socket, которую будет использовать приложение, и версия спецификации, которой соответствует конкретная реализация интерфейса Windows Socket. Приложение может одновременно создавать несколько сокетов, для использования в разных подзадачах одного процесса. В поле iMaxSockets хранится максимальное количество сокетов, которое можно получить для одного процесса. В поле iMaxUdpDg записан максимальный размер пакета данных, который можно переслать с использованием датаграммного протокола UDP. В поле lpVendorInfo указатель на дополнительную информацию, формат которой зависит от фирмы-изготовителя конкретной реализации системы Windows Sockets. Перед завершением работы, приложение должно освободить ресурсы, полученные у операционной системы для работы с Windows Sockets. Для этого приложение должно вызвать функцию WSACleanup. Она возвращает нулевое значение при успехе или значение SOCKET_ERROR = -1. Для получения кода ошибки используют функцию WSAGetLastError. Эта функция позволяет определить код ошибки при неудачном завершении функций интерфейса Windows Sockets. Ее нужно вызывать сразу вслед за неудачно завершившейся функцией. Если ошибка возникла при выполнении функции WSACleanup, функция WSAGetLastError может вернуть следующее значение: Код ошибкиhexОписаниеWSANOTINITIALISED276DИнтерфейс Windows Sockets не был проинициализирован функцией WSAStartupWSAENETDOWN2742Сбой сетевого программного обеспеченияWSAEINPROGRESS2734Во время вызова функции WSACleanup выполнялась одна из блокирующих функций интерфейса Windows SocketsНекоторые функции интерфейса Windows Sockets способны блокировать работу приложения, так как они не возвращают управление до своего завершения. Windows использует вытесняющую многозадачность, это не приводит к блокировке всей системы. Можно избежать использования блокирующих функций, так как для них в интерфейсе Windows Sockets существует замена.Создание и инициализация сокета После инициализации интерфейса Windows Sockets приложение должно создать один или несколько сокетов, которые будут использованы для передачи данных. Создание сокетаСокет создается с помощью функции socket: Code (C): SOCKET socket (int af, int type, int protocol); Параметр af определяет формат адреса. Для этого параметра нужно указывать значение AF_INET = 2 или PF_INET с тем же значением, что соответствует формату адреса, принятому в Internet. КонстантаhexКонстантаhexAF_UNSPEC, PF_UNSPEC0AF_DATAKIT, PF_DATAKIT9AF_UNIX, PF_UNIX1AF_CCITT, PF_CCITTAAF_INET, PF_INET2AF_SNA, PF_SNABAF_IMPLINK, PF_IMPLINK3AF_DECnet, PF_DECnetCAF_PUP, PF_PUP4AF_DLI, PF_DLIDAF_CHAOS, PF_CHAOS5AF_LAT, PF_LATEAF_NS, PF_NS6AF_HYLINK, PF_HYLINKFAF_IPX, PF_IPX6AF_APPLETALK, PF_APPLETALK10AF_ISO, PF_ISO7AF_NETBIOS11AF_OSI, PF_OSI7AF_MAX, PF_MAX12AF_ECMA, PF_ECMA8 Параметр type определяет тип сокета, который будет использован для данного сокета typehexОписаниеSOCK_STREAM1Сокет будет использован для передачи данных через канал связи с использованием протокола TCPSOCK_DGRAM2Передача данных будет выполняться без создания каналов связи через датаграммный протокол UDPSOCK_RAW3SOCK_RDM4SOCK_SEQPACKET5 Параметр protocol определяет протокол, который будет использован для данного сокета. Обычно указывают нулевое значение. Наиболее часто используются константы: protocolhexprotocolhexprotocolhexIPPROTO_IP0IPPROTO_GGP3IPPROTO_ST7IPPROTO_ICMP1IPPROTO_IPV44IPPROTO_UDP11IPPOROTO_IGMP2IPPROTO_TCP6IPPROTO_RAWFFПри ошибке возвращается одно из следующих значений: Код ошибкиhexОписаниеWSANOTINITIALISED276DИнтерфейс Windows Sockets не был проинициализирован функцией WSAStartupWSAENETDOWN2742Сбой сетевого программного обеспеченияWSAEAFNOSUPPORT273FУказан неправильный тип адресаWSAEINPROGRESS2734Выполняется блокирующая функция интерфейса Windows SocketsWSAEMFILE2728Израсходован весь запас свободных дескрипторовWSAENOBUFS2747Нет памяти для создания буфераWSAEPROTONOSUPPORT273BУказан неправильный протоколWSAEPROTOTYPE2739Указанный протокол несовместим с данным типом сокетаWSAESOCKTNOSUPPORT273CУказанный тип сокета несовместим с данным типом адреса В случае успеха функция socket возвращает дескриптор, который нужно использовать для выполнения всех операций над данным сокетом. Если произошла ошибка ― функция возвращает значение INVALID_SOCKET = -1. Для анализа причины ошибки вызывают функцию WSAGetLastError, которая вернет код ошибки: фрагмент кода, в котором создается сокет для передачи данных с использованием протокола TCP: Code (C): srv_socket = socket(AF_INET, SOCK_STREAM, 0); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "socket Error", "Error", MB_OK); return; } Удаление сокетаДля освобождения ресурсов приложение должно закрывать сокеты, которые ему больше не нужны, вызывая функцию closesocket: Code (C): int closesocket (SOCKET sock); коды ошибок для этой функции: Код ошибкиhexОписаниеWSANOTINITIALISED276DПеред использованием функции closesocket необходимо вызвать функцию WSAStartupWSAENETDOWN2742Сбой в сетиWSAENOTSOCK2736Указанный в параметре дескриптор не является сокетомWSAEINPROGRESS2734Выполняется блокирующая функция интерфейса Windows SocketsWSAEINTR2714Работа функции была отменена при помощи функции WSACancelBlockingCallПараметры сокетаПеред использованием нужно задать параметры сокета. Для этого готовят структуру типа sockaddr: Code (C): struct sockaddr { u_short sa_family; char sa_data[14]; }; typedef struct sockaddr SOCKADDR ; typedef struct sockaddr *PSOCKADDR ; typedef struct sockaddr FAR *LPSOCKADDR ; Для работы с адресами в формате Internet используется другой вариант этой структуры, в котором детализируется формат поля sa_data: Code (C): struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; }; typedef struct sockaddr_in SOCKADDR _IN; typedef struct sockaddr_in *PSOCKADDR _IN; typedef struct sockaddr_in FAR *LPSOCKADDR _IN; Поле sin_family определяет тип адреса. Нужно записать в это поле значение AF_INET, которое соответствует типу адреса, принятому в Internet: Code (C): srv_address.sin_family = AF_INET ; Поле sin_port определяет номер порта, который будет использоваться для передачи данных. Порт ― это дескриптор программы, выполняющей обмен на сети. На одном узле может одновременно работать несколько программ, использующих разные порты. Особенностью поля sin_port является использование так называемого сетевого формата данных. Формат отличается от того, что принят в процессорах с архитектурой Intel ― младшие байты данных хранятся по старшим адресам памяти. Архитектура процессоров Intel подразумевает хранение старших байтов данных по младшим адресам. Универсальный сетевой формат данных используют при организации глобальных сетей, так как в узлах такой сети могут использоваться компьютеры с различной архитектурой. Для выполнения преобразований из обычного формат в сетевой и обратно в интерфейсе Windows Sockets предусмотрен специальный набор функций. В частности, для заполнения поля sin_port нужно использовать функцию htons, выполняющую преобразование 16-разрядных данных из формата Intel в сетевой формат. Инициализация поля sin_port: Code (C): #define SERV_PORT 5000 srv_address.sin_port = htons(SERV_PORT); Поле sin_addr структуры sockaddr_in представляет собой структуру in_addr: Code (C): struct in_addr { union { struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { u_short s_w1,s_w2; } S_un_w; u_long S_addr; } S_un; }; #define s_addr S_un.S_addr #define s_host S_un.S_un_b.s_b2 #define s_net S_un.S_un_b.s_b1 #define s_imp S_un.S_un_w.s_w2 #define s_impno S_un.S_un_b.s_b4 #define s_lh S_un.S_un_b.s_b3 При инициализации сокета в этой структуре нужно указать IP-адрес, с которым будет работать данный сокет. Если сокет будет работать с любым адресом (например, создается сервер, который будет доступен из узлов с любым адресом), адрес для сокета можно указать следующим образом: Code (C): srv_address.sin_addr.s_addr = INADDR_ANY ; Если сокет будет работать с определенным IP-адресом (например, создается приложение-клиент, которое будет обращаться к серверу с конкретным IP-адресом), в указанную структуру необходимо записать реальный адрес. Датаграммный протокол UDP позволяет посылать пакеты данных одновременно всем рабочим станциям в широковещательном режиме. Для этого нужно указать адрес как INADDR_BROADCAST. Если известен адрес в виде четырех десятичных чисел, разделенных точкой (как его вводит пользователь), тогда поле адреса заполняется при помощи функции inet_addr: Code (C): dest_sin.sin_addr.s_addr = inet_addr ("200.200.200.201"); В случае ошибки функция вернет значение INADDR_NONE. Обратное преобразование IP-адреса в текстовую строку выполняется с помощью функции inet_ntoa, Code (C): char FAR * inet_ntoa (struct in_addr in); При ошибке функция вернет значение NULL. Пользователь работает с доменными именами, используя сервер DNS или файл HOSTS. Вначале используется функция gethostbyname, возвращающая IP-адрес, затем полученный адрес записывается в структуру sin_addr Code (C): PHOSTENT phe; phe = gethostbyname ("ftp.microsoft.com"); if(phe == NULL) { closesocket (srv_socket); MessageBox(NULL, "gethostbyname Error", "Error", MB_OK); return; } memcpy((char FAR *)&(dest_sin.sin_addr ), phe->h_addr, phe->h_length); При ошибке gethostbyname возвращает NULL. Причину ошибки выясняют, при проверке кода возврата функции WSAGetLastError. Если указанный узел найден в базе DNS или в файле HOSTS, функция gethostbyname возвращает указатель на структуру hostent Code (C): struct hostent { char FAR * h_name; // имя узла char FAR * FAR * h_aliases; // список альтернативных имен short h_addr type; // тип адреса узла short h_length; // длина адреса char FAR * FAR * h_addr _list; // список адресов #define h_addr h_addr_list[0] // адрес }; typedef struct hostent *PHOSTENT ; typedef struct hostent FAR *LPHOSTENT ; Искомый адрес находится в первом элемента списка h_addr _list[0], на который можно ссылаться при помощи h_addr. Длина поля адреса находится в поле h_length.Привязка адреса к сокету После подготовки структуры SOCKADDR, в нее записывают параметры сокета (в частности, адрес), выполняется привязка адреса к сокету при помощи функции bind: Code (C): int bind (SOCKET sock, const struct sockaddr FAR * addr, int namelen); Параметр sock содержит дескриптор сокета, созданного функцией socket. В поле addr записывается указатель на подготовленную структуру SOCKADDR, а в поле namelen ― размер этой структуры. В случае ошибки функция bind возвращает значение SOCKET_ERROR. Дальнейший анализ причин ошибки следует выполнять при помощи функции WSAGetLastError. Возможные коды ошибок перечислены ниже: Код ошибкиhexОписаниеWSANOTINITIALISED276DПеред использованием функции необходимо вызвать функцию WSAStartupWSAENETDOWN2742Сбой в сетиWSAEADDRINUSE2740Указанный адрес уже используетсяWSAEFAULT271EЗначение параметра namelen меньше размера структуры sockaddrWSAEINPROGRESS2734Выполняется блокирующая функция интерфейса Windows SocketsWSAEAFNOSUPPORT273FЭтот протокол не может работать с указанным семейством адресовWSAEINVAL2726Сокет уже привязан к адресуWSAENOBUFS2747Установлено слишком много соединенийWSAENOTSOCK2736Указанный в параметре дескриптор не является сокетомПример вызова функции bind Code (C): if(bind (srv_socket, (LPSOCKADDR )&srv_address, sizeof(srv_address)) == SOCKET_ERROR ) { closesocket (srv_socket); MessageBox(NULL, "bind Error", "Error", MB_OK); return; } Создание канала связи Если требуется передача датаграммных сообщений при помощи протокола негарантированной доставки UDP, канал связи не нужен. Сразу после создания сокетов и их инициализации можно приступать к передаче данных. Для передачи данных с использованием протокола TCP необходимо создать канал связи. Сторона сервера Рассмотрим процедуру создания канала связи со стороны сервера. Нужно переключить сокет в режим приема для выполнения ожидания соединения с клиентом при помощи функции listen: Code (C): int listen(SOCKET sock, int backlog); Через параметр sock функции необходимо передать дескриптор сокета, который будет использован для создания канала. Параметр backlog задает максимальный размер очереди для ожидания соединения (указывается значение от 1 до 5). Очередь содержит запросы на установку соединений для каждой пары значений (IP-адрес, порт). Список возможных кодов ошибок для функции listen. Код ошибкиhexОписаниеWSANOTINITIALISED276DПеред использованием функции необходимо вызвать функцию WSAStartupWSAENETDOWN2742Сбой в сетиWSAEADDRINUSE2740Указанный адрес уже используетсяWSAEINPROGRESS2734Выполняется блокирующая функция интерфейса Windows SocketsWSAEINVAL2726Сокет еще не был привязан к адресу или уже находится в подключенном состоянииWSAEISCONN2748Сокет уже находится в подключенном состоянииWSAEMFILE2728Недостаточно дескрипторов файловWSAENOBUFS2747Нет места для размещения буфераWSAENOTSOCK2736Указанный в параметре дескриптор не является сокетомWSAEOPNOTSUPP273DФункция listen не работает с сокетом указанного типаПример вызов функции listen: Code (C): if(listen(srv_socket, 1) == SOCKET_ERROR ) { closesocket (srv_socket); MessageBox(NULL, "listen Error", "Error", MB_OK); return; } Необходимо выполнить ожидание соединения. Это выполняется двумя различными способами. Циклически вызывается функция accept до тех пор, пока не будет установлено соединение. Затем приступают к обмену данными. Функция accept имеет следующий прототип: Code (C): SOCKET accept (SOCKET sock, struct sockaddr FAR * addr, int FAR * addrlen); Через параметр sock необходимо указать дескриптор сокета, который находится в режиме приема для выполнения ожидания. Параметр addr должен содержать адрес буфера, в который будет записан адрес узла, подключившегося к серверу. Размер этого буфера необходимо указать в переменной типа int, адрес которой передается через параметр addrlen. Если ожидание соединения в цикле не вызывает особого энтузиазма, используют способ, основанный на использовании расширения программного интерфейса Windows Socket, предназначенного для выполнения асинхронных операций. Список возможных кодов ошибок для функции accept. Код ошибкиhexОписаниеWSANOTINITIALISED276DПеред использованием функции необходимо вызвать функцию WSAStartupWSAENETDOWN2742Сбой в сетиWSAEFAULT271EЗначение параметра addrlen меньше размера структуры адресаWSAEINTR2714Работа функции была отменена при помощи функции WSACancelBlockingCallWSAEINPROGRESS2734Выполняется блокирующая функция интерфейса Windows SocketsWSAEINVAL2726Перед вызовом функции accept не была вызвана функция listenWSAEMFILE2728Нет доступных дескрипторовWSAENOBUFS2747Установлено слишком много соединенийWSAENOTSOCK2736Указанный в параметре дескриптор не является сокетомWSAEOPNOTSUPP273DДанный тип сокета нельзя использовать при вызове функций, ориентированных на работу с каналом связиWSAEWOULDBLOCK2733Сокет отмечен как неблокирующий и в настоящее время нет каналов связи, которые нужно устанавливатьВместо того чтобы ожидать соединение, вызывая в цикле функцию accept, ваше приложение может вызвать один раз функцию WSAAsyncSelect, указав ей, что при получении запроса на установку соединения функция окна вашего приложения должна получить сообщение: Code (C): #define WSA_ACCEPT (WM_USER + 1) // При попытке установки соединения главное окно приложения // получит сообщение WSA_ACCEPT rc = WSAAsyncSelect (srv_socket, hWnd, WSA_ACCEPT, FD_ACCEPT ); if(rc > 0) { closesocket (srv_socket); MessageBox(NULL, "WSAAsyncSelect Error", "Error", MB_OK); return; } В данном случае ожидание соединения выполняется для сокета srv_socket. Последний параметр функции равен FD_ACCEPT. Это означает, что при попытке создания канала связи функция окна с дескриптором hWnd получит сообщение WSA_ACCEPT, определенное в вашем приложении. Обработчик этого сообщения может выглядит следующим образом: Code (C): void WndProc_OnWSAAccept(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { int rc; // При ошибке отменяем поступление извещений в главное окно приложения if(WSAGETSELECTERROR(lParam) != 0) { MessageBox(NULL, "accept Error", "Error", MB_OK); WSAAsyncSelect (srv_socket, hWnd, 0, 0); return; } // Определяем размер адреса сокета acc_sin_len = sizeof(acc_sin); // Разрешаем установку соединения srv_socket = accept (srv_socket, (LPSOCKADDR )&acc_sin, (int FAR *)&acc_sin_len); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "accept Error, invalid socket ", "Error", MB_OK); return; } // Если на данном сокете начнется передача данных от клиента, в главное окно // приложения поступит сообщение WSA_NETEVENT. Это же сообщение поступит при // разрыве соединения rc = WSAAsyncSelect (srv_socket, hWnd, WSA_NETEVENT, FD_READ | FD_CLOSE ); if(rc > 0) { closesocket (srv_socket); MessageBox(NULL, "WSAAsyncSelect Error", "Error", MB_OK); return; } } Обработчик сообщения вначале вызывает функцию accept, выполняющую создание канала передачи данных. После этого функция WSAAsyncSelect вызывается еще один раз для того чтобы установить асинхронную обработку приема данных от удаленного клиента, а также обработку ситуации разрыва канала связи.Сторона клиента Для установки соединения в приложении используется функция SetConnection: Code (C): SOCKADDR _IN dest_sin; void SetConnection(HWND hWnd) { PHOSTENT phe; // Создаем сокет srv_socket = socket(AF_INET, SOCK_STREAM, 0); if(srv_socket == INVALID_SOCKET) { MessageBox(NULL, "socket Error", "Error", MB_OK); return; } // Устанавливаем адрес IP и номер порта dest_sin.sin_family = AF_INET ; // Определяем адрес узла phe = gethostbyname ("localhost "); if(phe == NULL) { closesocket (srv_socket); MessageBox(NULL, "gethostbyname Error", "Error", MB_OK); return; } // Копируем адрес узла memcpy((char FAR *)&(dest_sin.sin_addr ), phe->h_addr, phe->h_length); // Копируем номер порта dest_sin.sin_port = htons(SERV_PORT); // Устанавливаем соединение if(connect(srv_socket, (PSOCKADDR )&dest_sin, sizeof(dest_sin)) < 0) { closesocket (srv_socket); MessageBox(NULL, "connect Error", "Error", MB_OK); return; } } Вначале с помощью функции socket создается сокет. Затем выполняется заполнение адресной информацией структуры dest_sin. Для получения IP-адреса используют функцию gethostbyname, указав имя узла localhost. Это имя отображается в файле HOSTS на адрес 127.0.0.1: Code (C): localhost Адрес 127.0.0.1 является локальным. Его можно использовать для тестирования приложений, выполняющих обмен данными при помощи протокола TCP/IP, запуская сервер и клиент на одном и том же компьютере. После заполнения структуры с адресной информацией функция connect создает канал связи с сервером. Передача и прием данныхПосле того как канал создан, начинают передачу данных. Для передачи данных при помощи протокола гарантированной доставки TCP используют функции send и recv. У функции передачи данных send четыре параметра дескриптор сокета sock, на котором выполняется передача, адрес буфера buf, содержащего передаваемое сообщение, размер этого буфера bufsize и флаги flags (Продолжение здесь)
Не хватает некоторых форматов клипборды. Привожу обновлённый и дополненный список. КонстантаhexbinОписаниеCF_TEXT100000001Текстовые данные в кодировке ANSI, представляющие собой массив символов. Каждая строка завершается комбинацией символов "\r\n", весь массив закрыт двоичным нулемCF_BITMAP200000010Хендл битового изображения (HBITMAP) в формате, который зависит от устройства отображения (Device Dependent Bitmap)CF_META-FILEPICT300000011Хендл метафайла, описываемый структурой METAFILEPICT CF_SYLK400000100Формат Microsoft Symbolic Link. Этот формат предназначен для передачи текстовых данных, причем каждая строка заканчивается комбинацией символов "\r\n". Используется в некоторых программных продуктах MicrosoftCF_DIF500000101Формат Data Interchange Format. Аналогично формату CF_SYLK, формат CF_DIF предназначен для передачи текстовых данныхCF_TIFF600000110Графическое изображение в формате Tag Image File Format, который был разработан Microsoft, Aldus Corporation и Hewlett-PackardCF_OEMTEXT700000111Аналогично предыдущему, однако буфер содержит символы в кодировке OEMCF_DIB800001000Битовое изображение в формате BITMAPINFO, который не зависит от устройства отображения (Device Independent Bitmap)CF_PALETTE900001001Палитра цветов. Используется при записи в Clipboard битовых изображений DIBCF_PENDATAA00001010Этот формат данных используется в расширении операционной системы Windows, предназначенном для работы с перьевым вводомCF_RIFFB00001011Формат Resource Interchange File Format, более сложный, чем CF_WAVECF_WAVEC00001100Звуковые данные. Подмножество формата Resource Interchange File FormatCF_UNICODE-TEXTD00001101Текст в формате UTF-16, аналогично CF_TEXT и CF_OEMTEXTCF_ENH-METAFILEE00001110Хендл (HANDLE) к EMF (Enhanced Metafile) ― HENHMETAFILECF_HDROPF00001111Хендл к структуре HDROP, которая содержит список путей к файлам, используемая при Drag&Drop. Приложение может запросить информацию о файлах, вызвав функцию DragQueryFileCF_LOCALE1000010000Объект HGLOBAL, содержащий информацию о локали текста в буфере обменаCF_DIBV51100010001Объект в памяти, содержащий структуру BITMAPV5HEADER, за которой следует информация о цветном пространстве и данные картинкиCF_OWNER-DISPLAY8010001110Формат данных определяется приложением, записавшим такие данные в Clipboard. Это же приложение отвечает за отображение данныхCF_DSPTEXT8110000001Текстовое представление данных, формат которых определен приложениемCF_DSPBITMAP8210000010Представление данных, формат которых определен приложением, в виде битового изображенияCF_DSPMETA-FILEPICT8310000011Представление данных, формат которых определен приложением, в виде метафайлаCF_DSPENH-METAFILE8E10001110Формат EMF (Enhanced Metafile)