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

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

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

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

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

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

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






Building a Device Driver

Building a device driver is as simple as building a DLL. Although you can use the Platform Builder and its more extensive set of tools, you can easily build stream drivers by using eMbedded Visual C++. All you need to do is create a Windows CE DLL project, export the proper entry points, and write the code. The most frequently made mistake I see is in not declaring the entry points as extern C so that the C++ compiler doesn't mangle the exported function names.


Debug Zones


Debug zones allow a programmer or tester to manipulate debug messages from any module, EXE or DLL, in a Windows CE system. Debug zones are typically used by developers who use Platform Builder because debug zones allow developers to access the debug shell that allows them to interactively enable and disable specific groups, or zones, of debug messages. Another feature of debug zone messages is that the macros that are used to declare the messages insert the messages only when compiling a debug build of the module. When a release build is made, the macros resolve to 0 and don't insert any space-hogging Unicode strings. The value of debug zones isn't just that developers can use them; it's that all the modules that make up Windows CE have debug builds that are packed full of debug messages that can be enabled.

Using debug zones in applications or DLLs is a fairly straightforward process. First, up to 16 zones can be assigned to group all the debug messages in the module. The zones are declared using the DEBUGZONE macro, as in

#define ZONE_ERROR        DEBUGZONE(0)
#define ZONE_WARNING DEBUGZONE(1)
#define ZONE_INIT DEBUGZONE(2)

Then debug messages are inserted in the code. Instead of directly calling OutputDebugString, which was the old way of sending strings to a debug port, the messages should be enclosed in a DEBUGZONE macro, defined as

DEBUGMSG (zone, (printf expression));

The zone parameter is one of the 16 zones declared. The printf expression can be any printf style string plus the parameters. Note the additional parentheses around the printf expression. These are needed because DEBUGMSG is a macro and requires a fixed number of parameters. The following is an example of using DEBUGMSG:

DEBUGMSG (ZONE_ERROR, (TEXT("Read failed. rc=%d\r\n"), GetLastError()));

In addition to inserting the debug messages, a module must declare a structure named dpCurSettings of type DBGPARAM, defined as

typedef struct _DBGPARAM {
WCHAR lpszName[32];
WCHAR rglpszZones[16][32];
ULONG ulZoneMask;
} DBGPARAM, *LPDBGPARAM;

The first field is the debug name of the module. Typically, but not always, this is the name of the file. The second field is an array of strings. Each string identifies a particular zone. These names can be queried by the system to tell the programmer what zones are in a module. The final field, ulZoneMask, is a bitmask that sets the zones that are enabled by default. Although this field is a 32bit value, only the first 16 bits are used.

The only action a module must take at run time to enable debug zones is to initialize the zones with the following macro:

DEBUGREGISTER(HANDLE hInstance);

The only parameter is the instance handle of the EXE or DLL. Typically this call is made early in WinMain for applications and in the process attach call to LibMain for DLLs. The GenDriver example shown in Listing 22-1 demonstrates the use of debug zones.

Listing 22-1: The GenDriver example








GenDriver.h
//======================================================================
// Header file
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
//
// Declare the external entry points here. Use declspec so we don't
// need a .def file. Bracketed with extern C to avoid mangling in C++.
//
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
__declspec(dllexport) DWORD GEN_Init (DWORD dwContext);
__declspec(dllexport) BOOL GEN_Deinit (DWORD dwContext);
__declspec(dllexport) DWORD GEN_Open (DWORD dwContext, DWORD dwAccess,
DWORD dwShare);
__declspec(dllexport) BOOL GEN_Close (DWORD dwOpen);
__declspec(dllexport) DWORD GEN_Read (DWORD dwOpen, LPVOID pBuffer,
DWORD dwCount);
__declspec(dllexport) DWORD GEN_Write (DWORD dwOpen, LPVOID pBuffer,
DWORD dwCount);
__declspec(dllexport) DWORD GEN_Seek (DWORD dwOpen, long lDelta,
WORD wType);
__declspec(dllexport) DWORD GEN_IOControl (DWORD dwOpen, DWORD dwCode,
PBYTE pIn, DWORD dwIn,
PBYTE pOut, DWORD dwOut,
DWORD *pdwBytesWritten);
__declspec(dllexport) void GEN_PowerDown (DWORD dwContext);
__declspec(dllexport) void GEN_PowerUp (DWORD dwContext);
#ifdef __cplusplus
} // extern "C"
#endif //__cplusplus
// Suppress warnings by declaring the undeclared.
#ifndef GetCurrentPermissions
DWORD GetCurrentPermissions(void);
DWORD SetProcPermissions (DWORD);
DWORD GetCallerProcess(void);
PVOID MapPtrToProcess (PVOID, DWORD);
#endif //GetCurrentPermissions
DWORD GetConfigData (DWORD);
//
// Driver instance structure
//
typedef struct {
DWORD dwSize;
INT nNumOpens;
} DRVCONTEXT, *PDRVCONTEXT;


GenDriver.cpp
//======================================================================
// GenDriver - Generic stream device driver for Windows CE
//
// Written for the book Programming Windows CE
// Copyright (C) 2003 Douglas Boling
//======================================================================
#include <windows.h> // For all that Windows stuff
#include "GenDriver.h" // Local program includes
//
// Globals
//
HINSTANCE hInst; // DLL instance handle
//
// Debug zone support
//
#ifdef DEBUG
// Used as a prefix string for all debug zone messages.
#define DTAG TEXT ("GENDrv: ")
// Debug zone constants
#define ZONE_ERROR DEBUGZONE(0)
#define ZONE_WARNING DEBUGZONE(1)
#define ZONE_FUNC DEBUGZONE(2)
#define ZONE_INIT DEBUGZONE(3)
#define ZONE_DRVCALLS DEBUGZONE(4)
#define ZONE_EXENTRY (ZONE_FUNC | ZONE_DRVCALLS)
// Debug zone structure
DBGPARAM dpCurSettings = {
TEXT("GenDriver"), {
TEXT("Errors"),TEXT("Warnings"),TEXT("Functions"),
TEXT("Init"),TEXT("Driver Calls"),TEXT("Undefined"),
TEXT("Undefined"),TEXT("Undefined"), TEXT("Undefined"),
TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),
TEXT("Undefined"),TEXT("Undefined"),TEXT("Undefined"),
TEXT("Undefined") },
0x0003
};
#endif //DEBUG
//======================================================================
// DllMain - DLL initialization entry point
//
BOOL WINAPI DllMain (HANDLE hinstDLL, DWORD dwReason,
LPVOID lpvReserved) {
hInst = (HINSTANCE)hinstDLL;
switch (dwReason) {
case DLL_PROCESS_ATTACH:
DEBUGREGISTER(hInst);
// Improve performance by passing on thread attach calls
DisableThreadLibraryCalls (hInst);
break;
case DLL_PROCESS_DETACH:
DEBUGMSG(ZONE_INIT, (DTAG TEXT("DLL_PROCESS_DETACH\r\n")));
break;
}
return TRUE;
}
//======================================================================
// GEN_Init - Driver initialization function
//
#if (_WIN32_WCE_ > 300)
DWORD GEN_Init (DWORD dwContext, LPCVOID lpvBusContext) {
#else
DWORD GEN_Init (DWORD dwContext) {
#endif
PDRVCONTEXT pDrv;
DEBUGMSG (ZONE_INIT | ZONE_EXENTRY,
(DTAG TEXT("GEN_Init++ dwContex:%x\r\n"), dwContext));
// Allocate a device instance structure.
pDrv = (PDRVCONTEXT)LocalAlloc (LPTR, sizeof (DRVCONTEXT));
if (pDrv) {
// Initialize structure.
memset ((PBYTE) pDrv, 0, sizeof (DRVCONTEXT));
pDrv->dwSize = sizeof (DRVCONTEXT);
// Read registry to determine the size of the disk.
GetConfigData (dwContext);
} else
DEBUGMSG (ZONE_INIT | ZONE_ERROR,
(DTAG TEXT("GEN_Init failure. Out of memory\r\n")));
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_Init-- pDrv: %x\r\n"), pDrv));
return (DWORD)pDrv;
}
//======================================================================
// GEN_Deinit - Driver de-initialization function
//
BOOL GEN_Deinit (DWORD dwContext) {
PDRVCONTEXT pDrv = (PDRVCONTEXT) dwContext;
DEBUGMSG (ZONE_EXENTRY,
(DTAG TEXT("GEN_Deinit++ dwContex:%x\r\n"), dwContext));
if (pDrv && (pDrv->dwSize == sizeof (DRVCONTEXT))) {
// Free the driver state buffer.
LocalFree ((PBYTE)pDrv);
}
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_Deinit--\r\n")));
return TRUE;
}
//======================================================================
// GEN_Open - Called when driver opened
//
DWORD GEN_Open (DWORD dwContext, DWORD dwAccess, DWORD dwShare) {
PDRVCONTEXT pDrv = (PDRVCONTEXT) dwContext;
DEBUGMSG (ZONE_EXENTRY,
(DTAG TEXT("GEN_Open++ dwContext: %x\r\n"), dwContext));
// Verify that the context handle is valid.
if (pDrv && (pDrv->dwSize != sizeof (DRVCONTEXT))) {
DEBUGMSG (ZONE_ERROR, (DTAG TEXT("GEN_Open failed\r\n")));
return 0;
}
// Count the number of opens.
InterlockedIncrement ((long *)&pDrv->nNumOpens);
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_Open--\r\n")));
return (DWORD)pDrv;
}
//======================================================================
// GEN_Close - Called when driver closed
//
BOOL GEN_Close (DWORD dwOpen) {
PDRVCONTEXT pDrv = (PDRVCONTEXT) dwOpen;
DEBUGMSG (ZONE_EXENTRY,
(DTAG TEXT("GEN_Close++ dwOpen: %x\r\n"), dwOpen));
if (pDrv && (pDrv->dwSize != sizeof (DRVCONTEXT))) {
DEBUGMSG (ZONE_FUNC | ZONE_ERROR,
(DTAG TEXT("GEN_Close failed\r\n")));
return 0;
}
if (pDrv->nNumOpens)
pDrv->nNumOpens--;
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_Close--\r\n")));
return TRUE;
}
//======================================================================
// GEN_Read - Called when driver read
//
DWORD GEN_Read (DWORD dwOpen, LPVOID pBuffer, DWORD dwCount) {
DWORD dwBytesRead = 0;
DEBUGMSG (ZONE_EXENTRY,
(DTAG TEXT("GEN_Read++ dwOpen: %x\r\n"), dwOpen));
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_Read--\r\n")));
return dwBytesRead;
}
//======================================================================
// GEN_Write - Called when driver written
//
DWORD GEN_Write (DWORD dwOpen, LPVOID pBuffer, DWORD dwCount) {
DWORD dwBytesWritten = 0;
DEBUGMSG (ZONE_EXENTRY,
(DTAG TEXT("GEN_Write++ dwOpen: %x\r\n"), dwOpen));
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_Write--\r\n")));
return dwBytesWritten;
}
//======================================================================
// GEN_Seek - Called when SetFilePtr called
//
DWORD GEN_Seek (DWORD dwOpen, long lDelta, WORD wType) {
DEBUGMSG (ZONE_EXENTRY,(DTAG TEXT("GEN_Seek++ dwOpen:%x %d %d\r\n"),
dwOpen, lDelta, wType));
DEBUGMSG (ZONE_EXENTRY, (DTAG TEXT("GEN_Seek--\r\n")));
return 0;
}
//======================================================================
// GEN_IOControl - Called when DeviceIOControl called
//
DWORD GEN_IOControl (DWORD dwOpen, DWORD dwCode, PBYTE pIn, DWORD dwIn,
PBYTE pOut, DWORD dwOut, DWORD *pdwBytesWritten) {
PDRVCONTEXT pState;
DWORD err = ERROR_INVALID_PARAMETER;
DEBUGMSG (ZONE_EXENTRY,
(DTAG TEXT("GEN_IOControl++ dwOpen: %x dwCode: %x\r\n"),
dwOpen, dwCode));
pState = (PDRVCONTEXT) dwOpen;
switch (dwCode) {
// Insert IOCTL codes here.
default:
DEBUGMSG (ZONE_ERROR,
(DTAG TEXT("GEN_IOControl: unknown code %x\r\n"), dwCode));
return FALSE;
}
SetLastError (err);
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GEN_IOControl--\r\n")));
return TRUE;
}
//======================================================================
// GEN_PowerDown - Called when system suspends
//
// NOTE: No kernel calls, including debug messages, can be made from
// this call.
//
void GEN_PowerDown (DWORD dwContext) {
return;
}
//======================================================================
// GEN_PowerUp - Called when resumes
//
// NOTE: No kernel calls, including debug messages, can be made from
// this call.
//
void GEN_PowerUp (DWORD dwContext) {
return;
}
//----------------------------------------------------------------------
// GetConfigData - Get the configuration data from the registry.
//
DWORD GetConfigData (DWORD dwContext) {
int nLen, rc;
DWORD dwLen, dwType, dwSize = 0;
HKEY hKey;
TCHAR szKeyName[256], szPrefix[8];
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GetConfigData++\r\n")));
nLen = 0;
// If ptr < 65K, it's a value, not a pointer.
if (dwContext < 0x10000) {
return -1;
} else {
__try {
nLen = lstrlen ((LPTSTR)dwContext);
}
__except (EXCEPTION_EXECUTE_HANDLER) {
nLen = 0;
}
}
if (!nLen) {
DEBUGMSG (ZONE_ERROR, (DTAG TEXT("dwContext not a ptr\r\n")));
return -2;
}
// Open the Active key for the driver.
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPTSTR)dwContext,0, 0, &hKey);
if (rc == ERROR_SUCCESS) {
// Read the key value.
dwLen = sizeof(szKeyName);
rc = RegQueryValueEx (hKey, TEXT("Key"), NULL, &dwType,
(PBYTE)szKeyName, &dwLen);
RegCloseKey(hKey);
if (rc == ERROR_SUCCESS)
rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, (LPTSTR)
dwContext, 0, 0, &hKey);
if (rc == ERROR_SUCCESS) {
// This driver doesn't need any data from the key, so as
// an example, it just reads the Prefix value, which
// identifies the three-char prefix (GEN) of this driver.
dwLen = sizeof (szPrefix);
rc = RegQueryValueEx (hKey, TEXT("Prefix"), NULL,
&dwType, (PBYTE)szPrefix, &dwLen);
RegCloseKey(hKey);
} else
DEBUGMSG (ZONE_ERROR, (TEXT("Error opening key\r\n")));
} else
DEBUGMSG (ZONE_ERROR, (TEXT("Error opening Active key\r\n")));
DEBUGMSG (ZONE_FUNC, (DTAG TEXT("GetConfigData--\r\n")));
return 0;
}











Unfortunately for application developers, the debug messages produced by debug zones are sent to the debug port, which is generally not available on shipping systems. Some systems, however, do allow the primary serial port on the system to be redirected so that it's used as a debug port, instead of as COM1. Because each OEM will have a different method of enabling this redirection, you will need to contact the specific OEM for information on how to redirect the serial port. Nonetheless, debug zones are a powerful tool for debugging Windows CE systems.


The Generic Driver Example


The following example, GenDriver, is a simple stream driver. Although it doesn't talk to any hardware, it exports the proper 10 entry points and can be loaded by any Windows CE system. To have a system load GenDriver, you can add an entry under [HKEY_LOCAL_MACHINE]\Drivers\Builtin to have the driver loaded when the system boots, or you can write an application that creates the proper driver keys elsewhere and calls ActivateDevice.

The majority of the lines of code in GenDriver are DEBUGZONE macros. The messages are handy for learning exactly when and how the different entry points of the driver are called. The GetConfigData routine at the end of the code shows how to test the Context value to determine whether the value passed to the Init function was a pointer to a string or merely a number.

The preceding driver template is a good starting point for any stream driver you want to write. Simply change the three-character name GEN to whatever your driver is named and go from there.

/ 169