Win32 API Programming with Visual Basic

Page 150
  10. Objects and Their Handles  
   
  Windows is full of objects. A kernel object is a data structure whose members are accessible only by the Windows kernel. Examples of kernel objects are:  
 
  Process object

Represents a process

 
 
  Thread object

Represents a thread

 
 
  File object

Represents an open file

 
 
  File-mapping object

Represents a memory-mapped file, that is, a file whose contents are directly mapped to virtual memory addresses and used like physical memory

 
 
  Pipe object

Used to send data between processes

 
 
  Event object

A thread synchronization object used to signal when an operation has completed

 
 
  Mutex object

A thread synchronization object that can be used across multiple processes

 
 
  Semaphore object

Used for resource counting, to signal a thread when a resource is available

 
   
  In addition to kernel objects, there are also user objects and GDI objects, such as menus, windows, fonts, brushes, and mouse cursors.  
Page 151
 
  Handles  
   
  One of the characteristics of an object is that it has a handle that is used to identify it.  
   
  Although kernel objects are not directly accessible from user mode, the Windows API provides user-mode functions for manipulating these objects. This is a form of encapsulation that protects the objects from unwarranted tampering. When a kernel object is created through a call to the appropriate API function (such as CreateProcess, CreateThread, CreateFile, and CreateFileMapping), the function will return the newly created object's handle, which can then be passed to other API functions in order to manipulate the object.  
   
  Generally speaking, an object handle is process-specific, which means that it is only valid within a given process. On the other hand, some identifiers, such as process IDs, are system-wide, which means they are valid throughout all processes. We will have occasion to use both process handles and process IDs from time to time.  
   
  Usage Counts  
   
  Kernel objects are owned by the Windows kernel, not by the process that created the object (or any other process). As we will see, objects can be shared by multiple processes in a variety of ways. Each process that uses an object has its own process-specific handle to that object.  
   
  In view of this, the kernel must maintain a usage count for each object. When that count reaches 0, the kernel will destroy the object, but not before. In this way, the process that created an object can close its handle (by calling the CloseHandle API function), but the object will not be destroyed if some other process currently has a handle to the object.  
   
  We note also that kernel objects have security attributes that can be used to restrict access to the object. In fact, this is one of the main features that distinguishes kernel objects from user and GDI objects.  
   
  Object Sharing Across Process Boundaries  
   
  There are several ways in which an object can be shared among processes:  
   
  Inheritance  
   
  When a process (that is, a thread within the process) creates a kernel object, it can specify that the object's handle be inheritable to child processes that the parent  
Page 152
   
  process may subsequently create. In this case, the value of the child's handle is the same as the value of the parent's handle.  
   
  Handle duplication  
   
  The DuplicateHandle function is defined as:  

BOOL DuplicateHandle( HANDLE hSourceProcessHandle, // handle to the source process HANDLE hSourceHandle, // handle to duplicate HANDLE hTargetProcessHandle, // handle to process to duplicate to LPHANDLE lpTargetHandle, // pointer to duplicate handle DWORD dwDesiredAccess, // access for duplicate handle BOOL bInheritHandle, // handle inheritance flag DWORD dwOptions // optional actions );

   
  This function allows a handle in one process to be duplicated into another process. The new process-relative handle in the target process may have a different value than the source handle, but this is of no concern since the handles are process-relative.  
   
  Named objects  
   
  Many kernel objects can be given a name when they are created. Names are valid system-wise, which means that any other process can access the object by using its name (assuming that it knows the name, of course). For instance, the last parameter in the CreateFileMapping function:  
 
  HANDLE CreateFileMapping(

   HANDLE hFile,                        // handle to file to map

   LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // optional security attributes

   DWORD flProtect,                     // protection for mapping object

   DWORD dwMaximumSizeHigh,             // high-order 32 bits of object size

   DWORD dwMaximumSizeLow,              // low-order 32 bits of object size

   LPCTSTR lpName                       // name of file-mapping object

);

 
   
  can be used to specify a name for the file mapping.  
   
  Assume, for instance, that we have created a file-mapping object named MyFMO. Another process can call OpenFileMapping with this name as its last argument. The function will return a process-relative handle to this object for use by the second process. Alternatively, the second process can call CreateFileMapping, using the object's name as its last argument. The system will see that a file-mapping object by this name already exists and simply return a handle to this object. (This does create a potential problem, because a process may think it is creating a new object when, in fact, it is getting a handle to an existing object. The programmer must check the return value of CreateFileMapping immediately to determine which is the case.)  
Page 153
 
  Example: File Mapping  
   
  Let us conclude this chapter with an example of creating, sharing, and destroying a kernel object. For this example, we will use the file-mapping object.  
   
  To put it simply, Windows is capable of treating a disk file as though it were part of memory. To do this, a portion of the file is mapped to a block of virtual memory addresses. Memory-related API functions such as CopyMemory can then be used to view and alter the disk file contents. When a file is mapped to memory in this way, the file is called a memory-mapped file.  
   
  Our application will do the following:  
   
  1. Create a file object, based on an existing file, using the CreateFile API function. This function will return a handle to the file object.  
   
  2. Use the file handle and the CreateFileMapping API function to create a filemapping object. The function returns a file-mapping object handle.  
   
  3. Use the file-mapping object handle and the MapViewOfFile API function to map a portion of the file to memory. This function assigns a block of virtual memory addressed to the file. The base address of this block is the handle for the file-mapping view.  
   
  4. Use the base address and the CopyMemory function to read the file and then write to the file. The program just toggles the case of the text of the sample file Mapped.txt.  
   
  5. Finally, close all handles.  
   
  Since the code from this example will be used in two VB applications (to demonstrate object sharing) and since you might want to use it in your own applications, the best place for it is in a class module. In fact, it is not difficult to create a class named CFileMapping with all of the necessary properties and methods for creating file objects, file-mapping objects, and memory-mapped views. Example 10-1 shows the entire code for the CFileMapping class.  
   
  Example 10-1. The CFileMapping Class  
   
  Option Explicit

Private mFileMappingName As String

Private mFileViewBase As Long

Private mFileMappingHandle As Long

Private mFileHandle As Long

Private mFileName As String

' ------------

' File mapping

' ------------

' Create a handle to console output

Private Declare Function CreateFile Lib  kernel32  _

 
Page 154
   
  Example 10-1. The CFileMapping Class (continued)  
   
     Alias  CreateFileA  ( _

   ByVal lpFileName As String, _

   ByVal dwDesiredAccess As Long, _

   ByVal dwShareMode As Long, _

   ByVal lpSecurityAttributes As Long, _

   ByVal dwCreationDisposition As Long, _

   ByVal dwFlagsAndAttributes As Long, _

   ByVal hTemplateFile As Long _

) As Long

Const GENERIC_READ = &H80000000

Const GENERIC_WRITE = &H40000000

Const FILE_SHARE_READ = &H1

Const FILE_SHARE_WRITE = &H2

Const OPEN_EXISTING = 3

Private Declare Function CreateFileMapping Lib  kernel32  _

   Alias  CreateFileMappingA  ( _

   ByVal hFile As Long, _

   ByVal lpSecurityAttributes As Long, _

   ByVal flProtect As Long, _

   ByVal dwMaximumSizeHigh As Long, _

   ByVal dwMaximumSizeLow As Long, _

   ByVal lpName As String _

) As Long

Const PAGE_NOACCESS = &H1

Const PAGE_READONLY = &H2

Const PAGE_READWRITE = &H4

Const PAGE_WRITECOPY = &H8

Const PAGE_EXECUTE = &H10

Const PAGE_EXECUTE_READ = &H20

Const PAGE_EXECUTE_READWRITE = &H40

Const PAGE_EXECUTE_WRITECOPY = &H80

Const PAGE_GUARD = &H100

Const PAGE_NOCACHE = &H200

Private Declare Function MapViewOfFile Lib  kernel32  ( _

   ByVal hFileMappingObject As Long, _

   ByVal dwDesiredAccess As Long, _

   ByVal dwFileOffsetHigh As Long, _

   ByVal dwFileOffsetLow As Long, _

   ByVal dwNumberOfBytesToMap As Long _

) As Long

Const SECTION_EXTEND_SIZE = &H10

Const SECTION_MAP_EXECUTE = &H8

Const SECTION_MAP_READ = &H4

Const SECTION_MAP_WRITE = &H2

Const SECTION_QUERY = &H1

Const FILE_MAP_COPY = SECTION_QUERY

Const FILE_MAP_READ = SECTION_MAP_READ

Const FILE_MAP_WRITE = SECTION_MAP_WRITE

 
Page 155
   
  Example 10-1. The CFileMapping Class (continued)  
   
  Private Declare Function CloseHandle Lib  kernel32  ( _

   ByVal hObject As Long) As Long

Private Declare Function UnMapViewOfFile Lib  kernel32  _

   Alias  UnmapViewOfFile  (ByVal lpBaseAddress As Long) As Long

Public Property Get FileMappingName() As String

   FileMappingName = mFileMappingName

End Property

Public Property Let FileMappingName(pFileMappingName As String)

   mFileMappingName = pFileMappingName

End Property

Public Property Get FileName() As String

   FileName = mFileName

End Property

Public Property Let FileName(pFileName As String)

   If Dir$ (pFileName, vbNormal) <>   Then

      mFileName = pFileName

   Else

      mFileName = 

   End If

End Property

Public Property Get FileHandle() As Long

   FileHandle = mFileHandle

End Property

Public Property Let FileHandle(pFileHandle As Long)

   mFileHandle = pFileHandle

End Property

Public Property Get FileMappingHandle() As Long

   FileMappingHandle = mFileMappingHandle

End Property

Public Property Let FileMappingHandle(pFileMappingHandle As Long)

   mFileMappingHandle = pFileMappingHandle

End Property

Public Property Get FileViewBase() As Long

   FileViewBase = mFileViewBase

End Property

Public Property Let FileViewBase(pFileViewBase As Long)

   mFileViewBase = pFileViewBase

End Property

Public Function OpenFile() As Long

' Opens file and gets file handle

mFileHandle = CreateFile(mFileName, _

 
Page 156
   
  Example 10-1. The CFileMapping Class (continued)  
   
     GENERIC_READ Or GENERIC_WRITE, _

   FILE_SHARE_READ Or FILE_SHARE_WRITE, _

   0&, _

   OPEN_EXISTING, 0&, 0&)

OpenFile = mFileHandle

End Function

Public Function OpenFileMapping() As Long

' Create file mapping

mFileMappingHandle = CreateFileMapping( _

   mFileHandle, 0&, PAGE_READWRITE, 0&, _

   FileLen(mFileName), mFileMappingName)

OpenFileMapping = mFileMappingHandle

End Function

Public Function MapFileView() As Long

mFileViewBase = MapViewOfFile( _

   mFileMappingHandle, FILE_MAP_WRITE, 0&, 0&, 0&)

MapFileView = mFileViewBase

End Function

Public Function ReadFromFile(cBytes As Long) As String

' Read from file mapping

ReDim bFile(1 To cBytes) As Byte

CopyMemory ByVal VarPtr(bFile(1)), ByVal mFileViewBase, cBytes

ReadFromFile = StrConv(bFile, vbUnicode)

End Function

Public Function WriteToFile(sWrite As String) As Long

' Return count of bytes written

Dim b() As Byte

Dim lpsz As Long

BSTRtoLPSTR sWrite, b, lpsz

CopyMemory ByVal mFileViewBase, ByVal lpsz, Len(sWrite)

WriteToFile = Len(sWrite)

End Function

Public Function UnMapFileView() As Long

UnMapFileView = UnMapViewOfFile(mFileViewBase)

End Function

Public Function CloseFileMapping() As Long

CloseFileMapping = CloseHandle(mFileMappingHandle)

End Function

Public Function CloseFile() As Long

CloseFile = CloseHandle(mFileHandle)

End Function

 
Page 157
   
  Note that several of the methods are just wrappers for the API function.  
   
  If you open the FileMapping project in the Code_Mapping subdirectory of the accompanying CD-ROM and press the command button, the following procedure will be executed. It assumes that the file Mapped.txt is in the application directory.  
 
  Sub DoFileMapping()

Dim sFileName As String

Dim s As String

' Create file

Dim oFile As New CFileMapping

' Set properties

oFile.FileName = App.Path & "\Mapped.txt"

If oFile.FileName = "" Then

   MsgBox App.Path & "\Mapped.txt does not exist", vbExclamation

   Exit Sub

End If

oFile.FileMappingName = "TestFileMapping"

List1.AddItem "Open file: " & oFile.OpenFile

List1.AddItem "Open file mapping: " & oFile.OpenFileMapping

List1.AddItem "Map file: " & oFile.MapFileView

' Read entire file

s = oFile.ReadFromFile(FileLen(oFile.FileName))

List1.AddItem "Read file: " & s

' Reverse case

If s = UCase$ (s) Then

   s = LCase$ (s)

Else

   s = UCase$ (s)

End If

List1.AddItem "Write file: " & oFile.WriteToFile(s) & " bytes"

List1.AddItem "Read file: " & oFile.ReadFromFile(FileLen(oFile.FileName))

List1.AddItem "UnMap file: " & oFile.UnMapFileView

List1.AddItem "Close file mapping: " & oFile.CloseFileMapping

List1.AddItem "Close file: " & oFile.CloseFile

List1.AddItem "*****"

Set oFile = Nothing

End Sub

 
   
  The output is shown in Figure 10-1.  
   
  To see how two applications can share the same object in this case, a file object just place the CFileMapping class into two VB projects. Add the DoFileMapping subroutine to the projects and run them both.  
Page 158
   
   
   
  Figure 10-1.

The file-mapping application

 
   
  Coherence  
   
  Since we are on the subject of memory-mapped files, we should say a few more words about it. There is nothing to prevent the following from occurring to a single memory-mapped file:  
   
  Have multiple active views based on a single file-mapping object  
   
  Have several file-mapping objects based on the file, perhaps in different processes, each with its own views of the file  
   
  Have one process map a view of the file for writing while another process uses traditional file I/O functions on the file  
   
  Needless to say, these possibilities raise questions of what happens when one view changes the data in the file. Here are the facts:  
   
  If only a single file-mapping object is involved, even if that object is shared among different processes, the system will insure that all views based on that file-mapping object are coherent, that is, all views see the current state of the data, including any changes made through any of the views. This follows from the fact that all of these views see the same data, since it is stored in a single location in physical memory.  
   
  However, views of the file that are based on different file-mapping objects are not guaranteed to be coherent.  
   
  There is no guarantee that changes to a file made with traditional file I/O operations (ReadFile and WriteFile, for instance) will be reflected in file views.  
Page 159
   
  After all, these methods use memory buffers that are different from the buffers used by a file mapping. For this reason, we should not mix file-mapping techniques and memory-mapped techniques in the same process.  

Категории