现在的位置: 首页 > 综合 > 正文

WinCE驱动的动态加载

2013年01月21日 ⁄ 综合 ⁄ 共 12271字 ⁄ 字号 评论关闭

 

//=====================================================================
//TITLE:
//    WinCE驱动的动态加载
//AUTHOR:
//    norains
//DATE:
//    Monday 22- February-2010
//Environment:
//     WINDOWS CE 5.0
//=====================================================================

    

     WinCE驱动的调试,很多人的第一感觉就是:编写好DLL文件,接着在PB中添加相关注册表信息,然后将DLL文件包含进系统,最后生成系统,下载,调试。如果有误,那么依次按步骤重来。
 
  其实这种繁琐的操作完全可以不必要,因为在WinCE下驱动是可以动态加载和卸载的。
  
  驱动的加载和卸载非常简单,我们只需要如下两个函数:
  

  1.   HANDLE ActivateDeviceEx(  
  2.     LPCWSTR lpszDevKey,   
  3.     LPCVOID lpRegEnts,   
  4.     DWORD cRegEnts,   
  5.     LPVOID lpvParam  
  6.   );  
  7.     
  8.   BOOL DeactivateDevice(  
  9.     HANDLE hDevice  
  10.   );  


  
  前者是加载,后者是卸载。
  
  我们首先来看一下加载函数。这函数很简单,lpszDevKey指向的是驱动信息在注册表的位置。比如,我之前文章所提到的虚拟串口的驱动的注册表信息如下:
  
  [HKEY_LOCAL_MACHINE/Drivers/Builtin/VirtualSerial]
      "Prefix"="VSP"
      "Dll"="VirtualSerial.dll"
      "Order"=dword:0
      "Index"=dword:1
      "Map_Port"="COM1:"
  
  那么对于这个信息而言,lpszDevkey的取值为TEXT("Drivers//Builtin//VirtualSerial")。对于系统而言,驱动的Root Key为HKEY_LOCAL_MACHINE,故这里并不需要特别指出。换而言之,驱动的信息只能放置于HKEY_LOCAL_MACHINE,因为我们无法另外指定Root Key。
  
  接下来再看看别的参数。lpRegEnts和cRegEnts是和BUS有关的,但我们接下来的例子并没有用上,所以这里直接可以忽略,直接赋值NULL即可。其实,如果不使用这两个形参的话,我们还可以选择ActivateDevice。
  
  lpvParam指向的是传给驱动XXX_Init函数的形参,如果有特别需求,我们可以通过该指针进行传递。
  
  函数功能很简单。我们写一个功能简单的驱动,来测试该函数是否有效。

  驱动代码如下:  
  

  1. DWORD g_dwParam = 0;  
  2. std::wstring g_strContext;  
  3.   
  4. BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved)  
  5. {  
  6.     switch ( dwReason )   
  7.     {  
  8.         case DLL_PROCESS_ATTACH:  
  9.         break;  
  10.     }  
  11.     return TRUE;  
  12. }  
  13.   
  14.   
  15.   
  16. DWORD FKE_Init(  
  17.   LPCTSTR pContext,  
  18.   LPCVOID lpvBusContext  
  19. )  
  20. {  
  21.  printf("FKE_Init !/r/n");  
  22.   
  23.  if(pContext != NULL)  
  24.  {  
  25.   g_strContext = pContext;  
  26.  }  
  27.   
  28.  if(lpvBusContext != NULL)  
  29.  {  
  30.   g_dwParam = *(reinterpret_cast<const DWORD *>(lpvBusContext));  
  31.  }  
  32.  return TRUE;  
  33. }  
  34.   
  35.   
  36. BOOL FKE_Deinit(  
  37.     DWORD dwContext       
  38.     )  
  39. {  
  40.     
  41.  printf("FKE_Deinit !/r/n");  
  42.  return TRUE;  
  43. }  
  44.   
  45. DWORD FKE_Open(  
  46.     DWORD dwData,  
  47.     DWORD dwAccess,  
  48.     DWORD dwShareMode  
  49.     )  
  50. {  
  51.  return TRUE;  
  52. }  
  53.   
  54. BOOL FKE_Close(DWORD dwHandle)  
  55. {  
  56.  return TRUE;  
  57. }  
  58.   
  59.   
  60. BOOL FKE_IOControl(  
  61.     DWORD dwHandle,  
  62.     DWORD dwIoControlCode,  
  63.     PBYTE pBufIn,  
  64.     DWORD dwBufInSize,  
  65.     PBYTE pBufOut,  
  66.     DWORD dwBufOutSize,  
  67.     PDWORD pBytesReturned  
  68.     )  
  69. {   
  70.  return FALSE;  
  71. }  
  72.   
  73. DWORD FKE_Read(DWORD dwHandle, LPVOID pBuffer, DWORD dwNumBytes)  
  74. {  
  75.  std::vector<TCHAR> vtVal(MAX_PATH,0);  
  76.  _stprintf(&vtVal[0],TEXT("Context:%s/r/n Parameter:%d/r/n"),g_strContext.c_str(),g_dwParam);  
  77.   
  78.  int iLen = _tcslen(&vtVal[0]);  
  79.  memcpy(pBuffer,&vtVal[0],iLen * 2);  
  80.    
  81.  return iLen;  
  82. }  
  83.   
  84. DWORD FKE_Write(DWORD dwHandle, LPCVOID pBuffer, DWORD dwNumBytes)  
  85. {  
  86.  return 0;  
  87. }  
  88.   
  89.   
  90. DWORD FKE_Seek(DWORD dwHandle, long lDistance, DWORD dwMoveMethod)  
  91. {  
  92.  return FALSE;  
  93. }  
  94.   
  95. void FKE_PowerUp(void)  
  96. {  
  97.  return;  
  98. }  
  99.   
  100. void FKE_PowerDown(void)  
  101. {  
  102.  return;  
  103. }  
  104.   
  105.     

  代码意思很简单,就是在加载和卸载的时候,分别打印信息。然后还有两个全局变量,一个是g_strContext,用来保存成功加载时的注册表位置;另一个是g_dwParam,用来保存通过ActivateDeviceEx函数传递的第4个形参。而这两个全局变量的数值,之后我们可以通过ReadFile函数获得。只不过FKE_Read函数健壮性不高,没有判断缓冲区是否为空。但作为测试,还是够了。
  
  驱动方面大致如此,我们再来实例看看如何加载驱动。因为驱动的加载涉及到注册表的写入,所以我这里直接采用了CReg类。关于该类的代码,详情可参见:(http://blog.csdn.net/norains/archive/2007/06/20/1659925.aspx
  
  我们先写注册表信息:  

  1.     #define DEV_KEY TEXT("Drivers//Builtin//FakeDriver")
      
  2.   
  3. CReg reg;  
  4. reg.Create(HKEY_LOCAL_MACHINE,DEV_KEY);  
  5. reg.SetDW(TEXT("Order"),0);  
  6. reg.SetDW(TEXT("Index"),1);  
  7. reg.SetSZ(TEXT("Prefix"),TEXT("FKE"));  
  8. reg.SetSZ(TEXT("Dll"),TEXT("//Windows//Driver.dll"));  

  
  
  稍微说一下注册表写入数值的意思。Order是加载的顺序,其实手工加载的话可以无视该字段。Index是打开时的序号,与此相关的还有Prefix,为驱动名。Dll则简单了,则是我们编译好的驱动的存放路径。
  
  接下来就简单多了,我们加载驱动,然后传递一个DWORD的数值作为形参:  

  1. DWORD dwParam = 89;  
  2. HANDLE hd = ActivateDeviceEx(DEV_KEY,NULL,0,&dwParam);  
  3.   
  4. HANDLE hDriver = CreateFile(TEXT("FKE1:"),  
  5.    GENERIC_READ | GENERIC_READ,  
  6.    0,  
  7.    NULL,  
  8.    OPEN_EXISTING,  
  9.    FILE_ATTRIBUTE_NORMAL,  
  10.    NULL);  
  11.   
  12. std::vector<TCHAR> vtBuf(MAX_PATH);  
  13. DWORD dwRead = 0;  
  14. ReadFile(hDriver,&vtBuf[0],vtBuf.size(),&dwRead,NULL);  
  15.   
  16.     CloseHandle(hDriver);  
  17.    

  如果运行这段代码,你则会看到ReadFile之后,vtBuf则会存储到相应的数值。在我的平台上,某次运行时的数值如下:
  
  Context:Driver/Active/37 Parameter:89
  
  最后,就是卸载驱动。这个最简单,直接传递ActivateDeviceEx执行成功后返回的数值即可:
  

 

//=====================================================================
//TITLE:
//    WinCE驱动的动态加载
//AUTHOR:
//    norains
//DATE:
//    Monday 22- February-2010
//Environment:
//     WINDOWS CE 5.0
//=====================================================================

    

     WinCE驱动的调试,很多人的第一感觉就是:编写好DLL文件,接着在PB中添加相关注册表信息,然后将DLL文件包含进系统,最后生成系统,下载,调试。如果有误,那么依次按步骤重来。
 
  其实这种繁琐的操作完全可以不必要,因为在WinCE下驱动是可以动态加载和卸载的。
  
  驱动的加载和卸载非常简单,我们只需要如下两个函数:
  

  1.   HANDLE ActivateDeviceEx(  
  2.     LPCWSTR lpszDevKey,   
  3.     LPCVOID lpRegEnts,   
  4.     DWORD cRegEnts,   
  5.     LPVOID lpvParam  
  6.   );  
  7.     
  8.   BOOL DeactivateDevice(  
  9.     HANDLE hDevice  
  10.   );  


  
  前者是加载,后者是卸载。
  
  我们首先来看一下加载函数。这函数很简单,lpszDevKey指向的是驱动信息在注册表的位置。比如,我之前文章所提到的虚拟串口的驱动的注册表信息如下:
  
  [HKEY_LOCAL_MACHINE/Drivers/Builtin/VirtualSerial]
      "Prefix"="VSP"
      "Dll"="VirtualSerial.dll"
      "Order"=dword:0
      "Index"=dword:1
      "Map_Port"="COM1:"
  
  那么对于这个信息而言,lpszDevkey的取值为TEXT("Drivers//Builtin//VirtualSerial")。对于系统而言,驱动的Root Key为HKEY_LOCAL_MACHINE,故这里并不需要特别指出。换而言之,驱动的信息只能放置于HKEY_LOCAL_MACHINE,因为我们无法另外指定Root Key。
  
  接下来再看看别的参数。lpRegEnts和cRegEnts是和BUS有关的,但我们接下来的例子并没有用上,所以这里直接可以忽略,直接赋值NULL即可。其实,如果不使用这两个形参的话,我们还可以选择ActivateDevice。
  
  lpvParam指向的是传给驱动XXX_Init函数的形参,如果有特别需求,我们可以通过该指针进行传递。
  
  函数功能很简单。我们写一个功能简单的驱动,来测试该函数是否有效。

  驱动代码如下:  
  

  1. DWORD g_dwParam = 0;  
  2. std::wstring g_strContext;  
  3.   
  4. BOOL WINAPI DllEntry(HANDLE hInstDll, DWORD dwReason, LPVOID lpvReserved)  
  5. {  
  6.     switch ( dwReason )   
  7.     {  
  8.         case DLL_PROCESS_ATTACH:  
  9.         break;  
  10.     }  
  11.     return TRUE;  
  12. }  
  13.   
  14.   
  15.   
  16. DWORD FKE_Init(  
  17.   LPCTSTR pContext,  
  18.   LPCVOID lpvBusContext  
  19. )  
  20. {  
  21.  printf("FKE_Init !/r/n");  
  22.   
  23.  if(pContext != NULL)  
  24.  {  
  25.   g_strContext = pContext;  
  26.  }  
  27.   
  28.  if(lpvBusContext != NULL)  
  29.  {  
  30.   g_dwParam = *(reinterpret_cast<const DWORD *>(lpvBusContext));  
  31.  }  
  32.  return TRUE;  
  33. }  
  34.   
  35.   
  36. BOOL FKE_Deinit(  
  37.     DWORD dwContext       
  38.     )  
  39. {  
  40.     
  41.  printf("FKE_Deinit !/r/n");  
  42.  return TRUE;  
  43. }  
  44.   
  45. DWORD FKE_Open(  
  46.     DWORD dwData,  
  47.     DWORD dwAccess,  
  48.     DWORD dwShareMode  
  49.     )  
  50. {  
  51.  return TRUE;  
  52. }  
  53.   
  54. BOOL FKE_Close(DWORD dwHandle)  
  55. {  
  56.  return TRUE;  
  57. }  
  58.   
  59.   
  60. BOOL FKE_IOControl(  
  61.     DWORD dwHandle,  
  62.     DWORD dwIoControlCode,  
  63.     PBYTE pBufIn,  
  64.     DWORD dwBufInSize,  
  65.     PBYTE pBufOut,  
  66.     DWORD dwBufOutSize,  
  67.     PDWORD pBytesReturned  
  68.     )  
  69. {   
  70.  return FALSE;  
  71. }  
  72.   
  73. DWORD FKE_Read(DWORD dwHandle, LPVOID pBuffer, DWORD dwNumBytes)  
  74. {  
  75.  std::vector<TCHAR> vtVal(MAX_PATH,0);  
  76.  _stprintf(&vtVal[0],TEXT("Context:%s/r/n Parameter:%d/r/n"),g_strContext.c_str(),g_dwParam);  
  77.   
  78.  int iLen = _tcslen(&vtVal[0]);  
  79.  memcpy(pBuffer,&vtVal[0],iLen * 2);  
  80.    
  81.  return iLen;  
  82. }  
  83.   
  84. DWORD FKE_Write(DWORD dwHandle, LPCVOID pBuffer, DWORD dwNumBytes)  
  85. {  
  86.  return 0;  
  87. }  
  88.   
  89.   
  90. DWORD FKE_Seek(DWORD dwHandle, long lDistance, DWORD dwMoveMethod)  
  91. {  
  92.  return FALSE;  
  93. }  
  94.   
  95. void FKE_PowerUp(void)  
  96. {  
  97.  return;  
  98. }  
  99.   
  100. void FKE_PowerDown(void)  
  101. {  
  102.  return;  
  103. }  
  104.   
  105.     

  代码意思很简单,就是在加载和卸载的时候,分别打印信息。然后还有两个全局变量,一个是g_strContext,用来保存成功加载时的注册表位置;另一个是g_dwParam,用来保存通过ActivateDeviceEx函数传递的第4个形参。而这两个全局变量的数值,之后我们可以通过ReadFile函数获得。只不过FKE_Read函数健壮性不高,没有判断缓冲区是否为空。但作为测试,还是够了。
  
  驱动方面大致如此,我们再来实例看看如何加载驱动。因为驱动的加载涉及到注册表的写入,所以我这里直接采用了CReg类。关于该类的代码,详情可参见:(http://blog.csdn.net/norains/archive/2007/06/20/1659925.aspx
  
  我们先写注册表信息:  

  1.     #define DEV_KEY TEXT("Drivers//Builtin//FakeDriver")
      
  2.   
  3. CReg reg;  
  4. reg.Create(HKEY_LOCAL_MACHINE,DEV_KEY);  
  5. reg.SetDW(TEXT("Order"),0);  
  6. reg.SetDW(TEXT("Index"),1);  
  7. reg.SetSZ(TEXT("Prefix"),TEXT("FKE"));  
  8. reg.SetSZ(TEXT("Dll"),TEXT("//Windows//Driver.dll"));  

  
  
  稍微说一下注册表写入数值的意思。Order是加载的顺序,其实手工加载的话可以无视该字段。Index是打开时的序号,与此相关的还有Prefix,为驱动名。Dll则简单了,则是我们编译好的驱动的存放路径。
  
  接下来就简单多了,我们加载驱动,然后传递一个DWORD的数值作为形参:  

  1. DWORD dwParam = 89;  
  2. HANDLE hd = ActivateDeviceEx(DEV_KEY,NULL,0,&dwParam);  
  3.   
  4. HANDLE hDriver = CreateFile(TEXT("FKE1:"),  
  5.    GENERIC_READ | GENERIC_READ,  
  6.    0,  
  7.    NULL,  
  8.    OPEN_EXISTING,  
  9.    FILE_ATTRIBUTE_NORMAL,  
  10.    NULL);  
  11.   
  12. std::vector<TCHAR> vtBuf(MAX_PATH);  
  13. DWORD dwRead = 0;  
  14. ReadFile(hDriver,&vtBuf[0],vtBuf.size(),&dwRead,NULL);  
  15.   
  16.     CloseHandle(hDriver);  
  17.    

  如果运行这段代码,你则会看到ReadFile之后,vtBuf则会存储到相应的数值。在我的平台上,某次运行时的数值如下:
  
  Context:Driver/Active/37 Parameter:89
  
  最后,就是卸载驱动。这个最简单,直接传递ActivateDeviceEx执行成功后返回的数值即可:
  

 

抱歉!评论已关闭.