Programming Microsoft Windows Ce Net 3Rd [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Microsoft Windows Ce Net 3Rd [Electronic resources] - نسخه متنی

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






Chapter 8, I mentioned that the ReadFile and WriteFile functions don't support asynchronous I/O. This limitation means that the Windows CE implementation of the file system API doesn't support having the operating system provide data back to the application after the function returns. For file reads and writes, an application can get around this problem simply by spawning a separate thread to perform the read or write and then signaling the primary thread when the data transfer is complete. At times, however, it might be better to have a device driver perform the asynchronous data transfer and notify the calling application or driver when the transfer is complete. This tactic simplifies the application and allows the driver to tune the secondary thread to provide the best performance when reading or writing the data. The question is, how can a device driver perform asynchronous I/O if the operating system doesn't? The answer is simple: just because Windows CE doesn't support a feature doesn't mean you can't implement it yourself. Before I go into more detail about asynchronous drivers, I need to provide some background information.

One question you might have asked when I was talking about mapping pointers was how a device driver can write to a buffer that's in another application's slot. And if a driver can access another slot, can any application write into any slot? The answers to both questions lie in how Windows CE memory protection works.

As I mentioned earlier, each application is assigned a slot when it launches. While a thread in the application is running, its slot is cloned into slot 0. While the application is running, it can access slot 0 and its own slot. Attempting to read or write data in the other slots will result in a memory protection exception[1]. This way, applications are protected from one another.

When an application calls an operating system function, the part of the operating system that processes the function, NK, FileSys, Device, or GWES, is granted access to the calling process's slot for the duration of the function. This is also true for calls to device drivers. While the device driver is processing the call—whether it's Read, Write, or IOControl—the driver can write to the buffers located in the calling application's slot. As soon as the function is complete, the driver loses access to the calling application's slot.

If we apply this knowledge to asynchronous I/O, we see that the driver has a problem. Although it can map a pointer back to the calling application's slot, it doesn't have access rights to that slot after the call to the driver completes.

However, one Windows CE–specific function allows an application to modify the slot protection scheme. This function is SetProcPermissions and is prototyped as

DWORD SetProcPermissions (DWORD newperms);

The single parameter is a bitmask, one bit for each slot. When a bit is set to 1, the application will have access to the corresponding slot. For example, to enable access to slot 1, set the least significant bit to 1. A function prototype for SetProcPermissions isn't defined in the SDK include files, only in the Platform Builder. The description of SetProcPermissions just might make some programmers sit up in their chairs. Yes, this function is essentially the keys to the kingdom. A quick call to SetProcPermissions with the newperms parameter set to 0xFFFFFFFF enables an application to write to every slot in the system. One caveat: just because you can doesn't mean you should.

Memory protection exists for the benefit of programmers. By throwing exceptions when an errant memory access is made, the operating system catches the mistake the programmer made. So although applications can disable the Windows CE slot protection scheme, there is no reason they should, and plenty of reasons they shouldn't. Instead, applications should query the permissions they are currently granted and, if necessary, modify them for the situation. To query an application's permissions, use the function

DWORD GetCurrentPermissions (void);

The function returns the slot permission bitmap for the current application. If this function is called from within a driver, the permission mask will include the slot containing the Device Manager and the calling process's slot. Remember, during the life of the call, the driver has access to the caller's slot.

At this point, we have all the tools necessary for asynchronous I/O. We can create a secondary thread; we learned how to do that in Chapter 10. We can map pointers back to the calling process's slot. Finally, we can query the current permissions and set them when necessary. However, you should consider a few more items when implementing asynchronous I/O.

First, the rights to access other slots that can be changed with SetProcPermissions are thread-specific, not process-specific, which means that setting the permission mask of one thread in a process doesn't affect the other thread's permissions. So the secondary thread must call SetProcPermissions, not the thread processing the call to the driver.

Second, any mapping of pointers must take place in the call to the driver, not in the secondary thread because the function GetCallerProcess, which is used in conjunction with MapPtrToProcess, needs a calling process. The secondary thread wasn't called; it was started—so calling GetCallerProcess in the secondary thread will fail.

Finally, the secondary thread will need some way to signal the calling process that the I/O is complete. You can achieve this by means as simple as the driver posting a message to a window owned by the calling process or by signaling an event. The following code implements an IOCTL command that uses asynchronous I/O to fill a buffer:

// Structure passed by application to driver
typedef struct {
PBYTE pBuff; // Pointer to destination buffer
int nSize; // Size of buffer
HWND hWnd; // Window handle to send message when done
UINT wMsg; // Message to send to app when done
} ASYNCSTRUCT, *PASYNCSTRUCT;
// Structure passed from primary driver thread to secondary thread
typedef struct {
ASYNCSTRUCT asy; // Copy of caller's data
DWORD dwCurrPermissions; // Calling thread's permissions
} THREADASYNCSTRUCT, *PTHREADASYNCSTRUCT;
// SetProcPermissions is defined only in the Platform Builder include files.
#ifndef SetProcPermissions
DWORD SetProcPermissions (DWORD);
#endif //SetProcPermissions
//======================================================================
// AsyncThread - Secondary thread that performs async I/O
//
int AsyncThread (PVOID pArg) {
DWORD dwOldPerms;
PTHREADASYNCSTRUCT ptArgs;
int i, rc = ERROR_SUCCESS;
if (!pArg) return -1;
ptArgs = (PTHREADASYNCSTRUCT)pArg;
// Set thread permissions.
dwOldPerms = SetProcPermissions (ptArgs->dwCurrPermissions);
// Write the "data."
__try {
for (i = 0; (i < 10) && (i < ptArgs->asy.nSize); i++) {
*ptArgs->asy.pBuff++ = i;
Sleep (1000); // This call makes this loop take a while.
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_BUFFER_OVERFLOW;
}
// We're done; notify calling application.
if (IsWindow (ptArgs->asy.hWnd))
PostMessage (ptArgs->asy.hWnd, ptArgs->asy.wMsg, rc, 0);
// We don't really need to do this since we're terminating, but
// it's better to set a good example.
SetProcPermissions (dwOldPerms);
// Clean up.
LocalFree ((PVOID)ptArgs);
return 0; // Terminate thread by returning.
}
//======================================================================
// IOControl – Driver IOControl entry point
//
DWORD xxx_IOControl (DWORD dwOpen, DWORD dwCode, PBYTE pIn, DWORD dwIn,
PBYTE pOut, DWORD dwOut, DWORD *pdwBytesWritten) {
PDRVCONTEXT pState;
pState = (PDRVCONTEXT) dwOpen;
switch (dwCode) {
case IOCTL_ASYNC:
{
PTHREADASYNCSTRUCT ptArgs;
PASYNCSTRUCT pAppAsyncIn;
HANDLE hThread;
// Validate input parameters.
if (!pIn || (dwIn < sizeof (ASYNCSTRUCT))) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// Cast input buff ptr to struct pointer we can understand.
pAppAsyncIn = (PASYNCSTRUCT)pIn;
// Allocate a buffer to pass data to secondary thread.
ptArgs = (PTHREADASYNCSTRUCT)LocalAlloc (LPTR,
sizeof (THREADASYNCSTRUCT));
// Copy input structure from application since some applications
// forget and put this kind of stuff on the stack.
ptArgs->asy = *pAppAsyncIn;
ptArgs->dwCurrPermissions = GetCurrentPermissions();
// Map pointer to app buffer.
ptArgs->asy.pBuff = MapPtrToProcess (pAppAsyncIn->pBuff,
GetCallerProcess());
// Create async thread.
hThread = CreateThread (NULL, 0, AsyncThread,(PVOID)ptArgs,
0, 0);
if (!hThread) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY); // Catchall error
LocalFree ((PVOID)ptArgs);
return FALSE;
}
// Always close handles.
CloseHandle (hThread);
}
return TRUE;
default:
DEBUGMSG (ZONE_ERROR,
(DTAG TEXT("GEN_IOControl: unknown code\r\n")));
return FALSE;
}
return TRUE;
}

The preceding code contains a driver IOControl entry point and a routine AsyncThread that executes the secondary thread. When the IOCTL_ASYNC command is received, the driver allocates a structure for the data and copies the data passed from the application. The driver then maps the pointer contained in the structure and saves its current permissions mask. The secondary thread is then created by means of a call to CreateThread. The AsyncThread routine then starts. The routine sets its permissions mask to match the mask that was passed from the driver's primary thread. The data is then written with a Sleep statement to kill some time and thereby simulate the time it might take to read data from real hardware. Once the data is written, a message is sent to the window handle passed in the original call. AsyncThread then frees the buffer containing the information passed from the primary thread and terminates.

Windows CE provides an IOCTL command to assist drivers that have commands pending within a driver from an application that is being terminated. If an application is a multithreaded application that has an open handle to a driver, Windows CE sends an IOCTL_PSL_NOTIFY command to the driver to inform the driver that the application is terminating. This command allows a driver to clean up any pending operations for the terminating application that might be in progress.

Although most application programmers will never need to know how to implement asynchronous I/O in a driver, understanding the fundamental concepts of this technique is a good foundation for understanding how Windows CE works under the covers. Now that the foundation has been laid with the discussion of device drivers, it's time to look at Windows CE services, which are derived from the basic stream driver architecture.

[1] This is not technically true because the operating system might enable a thread to access slots containing operating system processes as needed.

/ 169