天天看點

WinCE6.0中應用程式如何直接通路實體空間

      在實際開發過程中,經常希望能在應用程式中直接讀寫裝置的實體空間。以前在做WinCE6.0下的MEMMgr時通過秘密加載一個核心态驅動實作了這個需求。但這種方式有一個明顯的缺陷,每次讀寫都必須經由它才能完成。如果隻是讀取GPIO,那問題不算大。如果想通過這種方式實作視訊播放的加速就比較困難了。估計非但不能加速,反而會變得更慢。

      早先曾與ZL仔細的讨論過這個問題,他當時在WinCE6.0上移植TCPMP,發現播放視訊不太流暢,于是想通過直接寫顯存進行加速。目的很明确,在應用中申請一段虛拟空間,通過某種方法将其映射到顯存上,視訊解碼過程中直接往映射過的虛拟空間上寫。這種方法與使用GAPI有一點類似。

      實作這個需求,需要用到函數VirtualCopyEx()。看看幫助中關于它的說明,This function dynamically maps a virtual address to a physical address by creating a new page-table entry.This function is callable in kernel mode and in user mode, when the source and destination process handles are the active process.This function is similar to VirtualCopy, except VirtualCopyEx requires handles to the source and destination process.

      據此基本可以确定,我們的确可以在應用中申請一段虛拟空間,然後通過這個函數将其映射到某段實體空間上。其中目标程序是我們的應用,而源程序是NK.exe。為了實作在NK.exe中執行VirtualCopyEx(),可以加載一個核心态的驅動。更為友善的方法是移植一個OALIOCTL,并在IOControl()中添加一個case。這樣,應用程式在做記憶體映射時就無需打開某個流驅動,直接調用KernelIoControl()即可。

      OALIOCTL中添加的關鍵代碼如下。 

 1 typedef struct {

 2     void*    pvDestMem;

 3     DWORD    dwPhysAddr;

 4     DWORD    dwSize;

 5 } VIRTUAL_COPY_EX_DATA;

 6 

 7 #define IOCTL_VIRTUAL_COPY_EX CTL_CODE (FILE_DEVICE_UNKNOWN,3333,METHOD_BUFFERED,FILE_ANY_ACCESS)

 8 

 9 

10 case IOCTL_VIRTUAL_COPY_EX:

11 {

12     VIRTUAL_COPY_EX_DATA *p = (VIRTUAL_COPY_EX_DATA*)pInBuf;

13     HANDLE hDst = (HANDLE)GetDirectCallerProcessId();

14     HANDLE hSrc = (HANDLE)GetCurrentProcessId();

15     fRet = VirtualCopyEx(hDst,p->pvDestMem,hSrc,(LPVOID)p->dwPhysAddr,p->dwSize,

16      PAGE_READWRITE|PAGE_PHYSICAL|PAGE_NOCACHE);

17 }break;

       應用程式中進行記憶體映射的關鍵代碼如下。

 1 volatile LPVOID GetVirtual(DWORD dwPhyBaseAddress, DWORD dwSize)

 2 {

 3     volatile LPVOID pVirtual;

 4     VIRTUAL_COPY_EX_DATA vced;

 5     

 6     if(dwPhyBaseAddress&0xFFF)

 7     {

 8         return NULL;

 9     }

10     vced.dwPhysAddr = dwPhyBaseAddress>>8;

11     pVirtual = VirtualAlloc(0,dwSize,MEM_RESERVE,PAGE_NOACCESS);

12     vced.pvDestMem = pVirtual;

13     vced.dwSize = dwSize;

14     KernelIoControl(IOCTL_VIRTUAL_COPY_EX,&vced, sizeof(vced), NULL, NULL, NULL);

15     return pVirtual;

16 }

17 

18 // WinCE6.0模拟器中應用程式直接寫屏

19 PBYTE pLCDBuf = (PBYTE)GetVirtual(0x33f00000,0x100000);

20 memset(pLCDBuf,0,0x100000);

21 

       這種方法在WinCE6.0的模拟器中測試了一下,能達到預期的效果。