| ||
The CreateFile function is universal. This is because of the concept of a device adopted in the Windows operating system. In one of examples provided in Chapter 11 (see Listing 11.4), I used the CreateFile function for console output. Now, consider another example that demonstrates the principles of working with mailslots.
This device allows information to be exchanged between processes within the framework of a local area network, not only within a single computer. The only problem is that the mailslot volume doesn't exceed 64 KB. Nevertheless, after considering the mechanism of transmitting data using mailslots, you'll understand that the mailslot size isn't important because under the condition of bidirectional transmission, it is possible to transfer information in sequential blocks. The main idea of the mailslot mechanism is as follows :
Every process can create a mailslot using the CreateMailslot function. If this function completes successfully, the process gets the mailslot descriptor and the possibility of reading data from it using the ReadFile function (as if it were a file). The process that has created the mailslot is called the server.
After the mailslot is created, any process can connect to it ( open the mailslot) using the CreateFile function and write a portion of data there using the WriteFile function.
The mailslot is closed when the process that generated it terminates or when all duplicates of the mailslot handle are closed using the CloseHandle function.
When working with the mailslot, the following conventions about the mailslot name should be observed .
In general, when creating a mailslot, the \\.\mailsiot\[ path ] name name is used. Here, name is the mailslot name, and path is the path that can consist of the names of several directories separated by a slash. Note that these directories have nothing in common with the directories existing on the disk.
When a mailslot is opened for reading, the name used when creating it must be used. For example, if the texts mailslot was created that is, the \\. \mailsiot\texts full name was used then the same string must be used when opening the mailslot for reading.
If it is necessary to open the mailslot for writing and this mailslot is located on another computer of the local area network, then the following string must be used: \\ ComputerName \mailslot\[ path ] name . Here, ComputerName is the network name of the required computer.
If processes that create mailslots operate within the same domain, then it is possible to create a shared mailslot for several processes. For this purpose, all processes must create a mailslot with the same name. If you now open the mailslot for writing using the CreateFile function and specify the name in the following format \\ DomainName \mailslot\[ path ] name , where DomainName is the name of the domain, then all processes that have created this mailslot will receive messages. In addition, it is also possible to use the \\*\mailslot\[ path ]name for the primary domain.
Thus, after learning the main theory behind mailslots, it is possible to implement practical examples. Listings 13.1 and 13.2 provide the source code of the server software that creates a mailslot and reads messages from there, and the client that sends messages to that mailslot. Note that I have used the results of previous chapters and have written programs that can be translated using both MASM32 and TASM32. Note that starting from this chapter, most programs will be presented in this universal form.
; Server that creates a mailslot and reads from there .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 MAILSLOT_WAIT_FOREVER equ -1 ; Prototypes of external procedures IFDEF MASM EXTERN ReadFile@20:NEAR EXTERN CloseHandle@4:NEAR EXTERN lstrlenA@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN CreateMailslotA@16:NEAR EXTERN ExitProcess@4:NEAR EXTERN GetStdHandle@4:NEAR ELSE EXTERN ReadFile:NEAR EXTERN CloseHandlerNEAR EXTERN lstrlenA:NEAR EXTERN WriteConsoleA:NEAR EXTERN CreateMailslotA:NEAR EXTERN GetStdHandle:NEAR EXTERN ExitProcess:NEAR WriteConsoleA@20 = WriteConsoleA ReadFile@20 = ReadFile lstrlenA@4 = lstrlenA CreateMailslotA@16 = CreateMailslotA ExitProcess@4 = ExitProcess GetStdHandle@4 = GetStdHandle CloseHandle@4 = CloseHandle ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;----------------------------------------------- ; Data segment _DATA SEGMENT LENS DD ? ; String length will be placed here PATHM DB "\.\mailslot\maill", 0 BUFER DB 1000 DUP.(0) H DD ? N DD ? HANDL DD ? ERRS DB 'Error!', 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ; Create a mailslot PUSH 0 PUSH MAILSLOT_WAIT_FOREVER PUSH 0 PUSH OFFSET PATHM CALL CreateMailslotA@16 CMP EAX, -1 JNZ CON LEA EAX, ERRS MOV EDI, 1 CALL WRITE JMP EXI CON: MOV H, EAX ; Read from the mailslot PUSH 0 PUSH OFFSET N PUSH 1000 PUSH OFFSET BUFER PUSH H CALL ReadFile@20 ; Output the contents LEA EAX, BUFER MOV EDI, 1 CALL WRITE ; Close the mailslot PUSH H CALL CloseHandle@4 ; Exit the program EXI: PUSH 0 CALL ExitProcess@4 ; Display the string (terminated with line feed) ; EAX --- To the start of the string ; EDI --- With or without the line feed WRITE PROC ; Get the parameter length PUSH EAX PUSH EAX CALL lstrlenA@4 MOV ESI, EAX POP EBX CMP EDI, 1 JNE NO_ENT ; Trailing line feed MOV BYTE PTR [EBX+ESI], 13 MOV BYTE PTR [EBX+ESI+1], 10 MOV BYTE PTR [EBX+ESI+2], 0 ADD EAX, 2 NO_ENT: ; String output PUSH 0 PUSH OFFSET LENS PUSH EAX PUSH EBX PUSH HANDL CALL WriteConsoleA@20 RET WRITE ENDP _TEXT ENDS END START
The SERVER.ASM program creates a mailslot and then calls the ReadFile function. Note when creating the mailslot, the MAILSLOT_WAIT_FOREVER Parameter was set. This means that the ReadFile function will wait infinitely for the data to arrive at the mailslot. The function returns control only after the arrival of the data. The mailslot contents are then displayed on the console.
To translate this program using MASM32, issue the following:
ML /c /coff /DMASM server.ASM LINK /SUBSYSTEM:CONSOLE server.OBJ
To translate the same program using TASM32, issue the following:
TASM32 /ml server.ASM TLINK32 -ap server.OBJ
;Client that opens the mailslot and writes information into it .586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 GENERIC_WRITE equ 40000000h FILE_SHARE_READ equ 1h OPEN_EXISTING equ 3 ; Prototypes of external procedures IFDEF MASM EXTERN WriteFile@20:NEAR EXTERN CreateFileA@28:NEAR EXTERN ExitProcess@4: NEAR EXTERN CloseHandle@4:NEAR ELSE EXTERN WriteFile:NEAR EXTERN CreateFileA:NEAR EXTERN ExitProcess:NEAR EXTERN CloseHandle:NEAR WriteFile@20 = WriteFile CreateFileA@28 = CreateFileA ExitProcess@4 = ExitProcess CloseHandle@4 = CloseHandle ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ; Data segment _DATA SEGMENT PATHM DB "\.\mailslot\mail1", 0 H DD ? MES DB 'Hello! Server!', 0 N DD ? _DATA ENDS ; Code segment _TEXT SEGMENT START: ; Open the mailslot PUSH 0 ; Must be zero PUSH 0 ; File attribute doesn't matter PUSH OPEN_EXISTING ; How to open PUSH 0 ; Pointer to the security attribute PUSH FILE SHARE_READ ; Shared access mode PUSH GENERIC_WRITE ; Access type PUSH OFFSET PATHM ; Mailslot name CALL CreateFileA@28 MOV H, EAX ; Write data to the mailslot PUSH 0 PUSH OFFSET N PUSH 16 ; Message length PUSH OFFSET MES PUSH H CALL WriteFile@20 ; Close the mailslot PUSH H CALL CloseHandle@4 ; Exit EXI: PUSH 0 CALL ExitProcess@4 _TEXT ENDS END START
The client program opens the mailslot using the CreateFile function. Having received the descriptor, it can send the data to the mailslot. The program sends a zero-terminated string.
To translate this program using MASM32, issue the following commands:
ML /c /coff /DMASM client.ASM LINK /SUBSYSTEM:CONSOLE client.OBJ
To translate it using TASM32, use the following:
TASM32 /ml client.ASM TLINK32 -ap client.OBJ
The programs in Listings 13.1 and 13.2 are simple. The server program carries out a single data-read operation from the mailslot. There are no difficulties in using the mailslot for permanent data exchange. For this purpose, it is necessary to remember the following:
The mailslot is released in the course of data read.
In the course of the write operation, the contents of the mailslot are overwritten.
Principally, the client also can create a mailslot, in which there will be bidirectional data exchange between the client and the server.
Pipes provide an efficient method of bidirectional data exchange between processes. There are two types of pipes: anonymous and named. Anonymous pipes will be covered in Chapter 18 (see Listing 18.3). This type of pipes is convenient for use within the framework of a single application for data exchange between two processes. Named pipes are more powerful. They allow data to be exchanged with applications located on different computers within a local area network. In particular, the Microsoft SQL Server uses named pipes as one of the possible mechanisms of information exchange with clients .
Naturally, before using a named pipe, it is necessary to create it. For this purpose, the CreateNamePipe function is used. The process that creates the named pipe is called the server. When creating a named pipe, it is necessary to set several parameters that influence the way, in which this object will operate. The list of these parameters includes the parameter that defines the mode of operation through the pipe. Three modes of operation can be set:
Bidirectional (duplex)This method assumes the possibility of bidirectional data exchange: from server to client and from client to server.
Two unidirectional (simplex) pipes They assume unidirectional data transmission only.
Generally, the pipe name is represented by the following string: \\.\pipe\pipename . Here, pipename is the name of the pipe. As with mailslots, for named pipes it is possible
to set the mode for infinite waiting. In this case, the ReadFile and writeFile functions will complete execution only when data transmission is completed. Having created the pipe, use the ConnectNamedPipe function to allow client processes to connect to the pipein other words, to switch the server process to the waiting state.
The client process, as with mailslots, can connect to the pipe using the all-embracing CreateFile function. To open the pipe, it must use the same structure of the pipe name: \\.\pipe \ pipename . Thus, the client process must know the name of the pipe, to which it connects. If this attempt has failed, use the GetLastError function to discover the cause of the error. If the function returns the ERROR_PIPE_BUSY = 231 error code, [i] this means that the pipe is busy serving another process and it is necessary to wait until that process releases the pipe. To set the waiting mode, use the WaitNamedPipe function.
The CreateFile function can also open disk devices. To open the first disk of your computer, it is necessary to use the \\.\PhysicalDrive0 name; to open the C: partition, use the \\. \C : name. The most interesting fact here is that after opening the device, you can use the ReadFile and WriteFile functions. [ii] Additionally, you have the DeviceIoControl function at your disposal. The latter function can carry out various operations, including getting statistics about the disk and even formatting the disk. I won't concentrate attention on this function but, instead, will provide an example illustrating the reading of the Master Boot Record (MBR) of the disk. When the device is opened, the MBR will be located at the starting position of the hypothetical file (Listing 13.3).
.586P ; Flat memory model .MODEL FLAT, stdcall ; Constants STD_OUTPUT_HANDLE equ -11 GENERIC_READ equ 80000000h FILE_SHARE_WRITE equ 2h OPEN_EXISTING equ 3 ; Prototypes of external procedures IFDEF MASM EXTERN GetLastError@0:NEAR EXTERN wsprint fA:NEAR EXTERN lstrlenA@4:NEAR EXTERN ReadFile@20:NEAR EXTERN CreateFileA@28:NEAR EXTERN ExitProcess@4:NEAR EXTERN CloseHandle@4:NEAR EXTERN WriteConsoleA@20:NEAR EXTERN GetStdHandle@4:NEAR ELSE EXTERN GetLastError:NEAR EXTERN _wsprintfA:NEAR EXTERN GetStdHandle:NEAR EXTERN lstrlenA:NEAR EXTERN ReadFile:NEAR EXTERN CreateFileA:NEAR EXTERN ExitProcess:NEAR EXTERN CloseHandle:NEAR EXTERN WriteConsoleA:NEAR GetLastError@0 = GetLastError wsprintfA = 0 wsprintfA GetStdHandle@4'= GetStdHandle WriteConsoleA@20 = WriteConsoleA lstrlenA@4 = lstrlenA ReadFile@20 = ReadFile CreateFileA@28 = CreateFileA ExitProcess@4 = ExitProcess CloseHandle@4 = CloseHandle ENDIF ; INCLUDELIB directives for the linker IFDEF MASM includelib c:\masm32\lib\user32.lib includelib c:\masm32\lib\kernel32.lib ELSE includelib c:\tasm32\lib\import32.lib ENDIF ;------------------------------------------------ ; Data segment _DATA SEGMENT H DD 0 NN DD 0 ; Device name (first hard disk) PATHM DB "\.\PhysicalDrive0", 0 ALIGN 4 ; Alignment by the double-word boundary BUF DB 512 DUP(0) BUF1 DB 24 DUP(0) HANDL DD 0 LENS DD 0 ERRS DB "Error %u", 0 SINGL DB "Signature %x", 0 _DATA ENDS ; Code segment _TEXT SEGMENT START: PUSH STD_OUTPUT_HANDLE CALL GetStdHandle@4 MOV HANDL, EAX ; Open the physical disk PUSH 0 ; Must be zero PUSH 0 ; File attribute doesn't matter PUSH OPEN_EXISTING ; How to open PUSH 0 ; Pointer to the security attribute = NULL PUSH FILE_SHARE_WRITE ; Shared access mode PUSH GENERIC_READ ; Access type - Read PUSH OFFSET PATHM ; Device name CALL CreateFileA@28 CMP EAX, -1 JNZ NO_ERR ER: ; Get error number CALL GetLastError@0 ; MOVZX EAX, AX PUSH EAX PUSH OFFSET ERRS PUSH OFFSET BUF1 CALL wsprintfA ; Output error number LEA EAX, BUF1 MOV EDI, 1 CALL WRITE JMP EXI NO_ERR: MOV H, EAX ; Reading the partition table PUSH 0 PUSH OFFSET NN PUSH 512 PUSH OFFSET BUF PUSH H CALL ReadFile@20 CMP EAX, 0 JZ ER ; Display the partition table signature ; Must be aa55 PUSH DWORD PTR BUF+510 PUSH OFFSET SINGL PUSH OFFSET BUF1 CALL wsprintfA LEA EAX, BUF1 MOV EDI, 1 CALL WRITE ; Close the device PUSH H CALL CloseHandle@4 ; Exit EXI: PUSH 0 CALL ExitProcess@4 ; Display the string terminated by line feed ; EAX --- To the start of the string ; EDI --- With or without the line feed WRITE PROC ; Get the parameter length PUSH EAX PUSH EAX CALL lstrlenA@4 MOV ESI, EAX POP EBX CMP EDI, 1 JNE NO_ENT ; The string is terminated with the line feed MOV BYTE PTR [EBX+ESI], 13 MOV BYTE PTR [EBX+ESI+1], 10 MOV BYTE PTR [EBX+ESI+2], 0 ADD EAX, 2 NO_ENT: ; String output PUSH 0 PUSH OFFSET LENS PUSH EAX PUSH EBX PUSH HANDL CALL WriteConsoleA@20 RET WRITE ENDP _TEXT ENDS END START
Now, it is necessary to provide some comments about Listing 13.3.
Note that the starting point of the buffer, into which the sector will be read, must be aligned by the 4-byte boundary. To ensure this, the ALIGN directive is used. Such alignment is not needed for reading from a normal file except when you disable data caching (see the description of the CreateFile function in the opening section of this chapter).
In this program, I have used the GetLastError function for the first time. If an error occurs when you open or read from the device, the program will output the error code to the console. To determine the cause of the error, consult the documentation. You can also try to change the parameters of the CreateFile and ReadFile functions and view the error codes that will result. In fact, such experimenting is instructive.
To translate this program, issue the following commands using MASM32:
ML /c /coff /DMASM prog.ASM LINK /SUBSYSTEM:CONSOLE prog.OBJ
Issue the following commands using TASM32:
TASM32 /ml prog.ASM TLINK32 -ap prog.OBJ
[i] Remember that to discover the cause of the error by its number, it is convenient to use the ERRLOOK.EXE program supplied as part of Microsoft Visual Studio.NET.
[ii] Do not write anything in such a way without carefully studying the disk structure before proceeding.
| ||