(引用此文需注明来源 http://www.armce.cn WinCE家园)
这个文章讲述的是如何利用BusEnum可以Active自己的特性,把一部分不是启动的时候必须的流驱动延迟到任何时间加载(依然可以用order来控制顺序,只是可以滞后到explorer跑起来后开始)。
原理是这样的,默认的device.exe只加载一个流驱动,它就是busenum.dll,然后其他的流驱动是靠busenum.dll去遍历和Active(加载)的。因为busenum.dll也是流驱动接口的,所以它自己也可以被自己Active,上文就是利用了这个特点,对busenum.dl做了一个微妙的改动,实现了我们可以对流驱动的分批加载,让最关键的dll在出现桌面前加载,其他的都滞后,让用户可以在较短时间见到桌面。
下面来解析实现的具体原理:
默认的,你的注册表肯定有下面的项目:
[HKEY_LOCAL_MACHINE\Drivers]
"RootKey"="Drivers\\BuiltIn"
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn]
"Dll"="BusEnum.dll"
"BusName"="BuiltIn"
"Flags"=dword:8
"BusIoctl"=dword:2a0048
"InterfaceType"=dword:0
"IClass"=multi_sz:"{B3CC6EBA-5507-4196-8E41-2BF42E4A47C9}=%b","{6F40791D-300E-44E4-BC38-E0E63CA8375C}=%b"
设备管理器Device.exe在初始化的时候会去读上面的rootkey,然后自己加载rootkey指向的那个驱动,这里就是[HKEY_LOCAL_MACHINE\Drivers\BuiltIn],所以在这里busenum.dll就跑起来了。详细的代码自己可以去private下面check,懒的可以看我借用的一篇文章 http://www.armce.cn/bbs/thread-35-1-1.html ,它分析了device.exe的初始化过程,最下一行很有意思。
接下来busenum.dll会开始遍历它键值下面的流驱动并一一加载。比如:
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\SDMEMORY]
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\NAND]
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PowerButton]
所以类似的,如果你把BusEnum.dll放到[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\hStartEvent]下面并且把她Active起来,它就会去加载所有[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\hStartEvent\*****]键值下的流驱动
分时加载已经分组的流驱动
这来讲如何加载两个BusEnum.dll的问题,和如何从时间上把他们分开。
刚才有讲,BusEnum.dll(后启动的)自己是可以被当做普通的流驱动被BusEnum.dll(先启动的,device加载)加载的,比如下面的键值。
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus]
"Dll"="BusEnum.dll"
"BusName"="AsyncBus"
"Order"=dword:10;
"Flags"=dword:0
"BusIoctl"=dword:2a0048
"InterfaceType"=dword:0
"IClass"=multi_sz:"{B3CC6EBA-5507-4196-8E41-2BF42E4A47C9}=%b","{6F40791D-300E-44E4-BC38-E0E63CA8375C}=%b"
因为处于BuiltIn下面,所以肯定会被轮询到被加载。但是就简单这么写还不行,虽然它可以实现加载[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\****]的所有流驱动,但是时间上面它无法控制,它会立刻开始加载操作,无法实现我们从时间上的分批次加载驱动的目的,所以一楼的文章就介绍了一种方法,在上面的键值里面加一个flag,如下:
"Flags"=dword:408 ; 0x400+0x8 (0x400 is the asyncbus flag)
通过修改busenum.dll的源码,让这个flag会在第二个busenum.dll被加载的时候被第一个busenum读到,我们的例子里面加入的0x400就是代码里面宏定义DEVFLAGS_LOAD_ASYNC的值,第一个busenum就可以判断这个标志不立刻走正常的遍历和加载它,延迟第二个busenum的启动。
下面是一段修改devbus.cpp的一个sample,不去ActivateDeviceEx,而是去调用线程等待启动信号。
if(GetLoadFlag() & DEVFLAGS_LOAD_ASYNC)
{
HANDLE hAsyncLoadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) AsyncLoadThread, this, 0, NULL);
}
else
{
m_hDevice = ActivateDeviceEx(m_lpTemplateRegPath, m_dwInitRegArray, (m_dwInitRegArray!=NULL?m_dwInitRegCount:0) , NULL);
}
在AsyncLoadThread的线程里面去做类似下面的事情,等待一个hStartEvent事件,以便开始加载第二个busenum.dll. hStartEvent事件要你自己手动去发,比如你可以在explorer.exe起来之后调用一个AP去发这个自定义事件。
WaitForSingleObject(hStartEvent, INFINITE);
NKDbgPrintfW(L"[busenum]Start load %s\r\n",m_lpTemplateRegPath);
m_hDevice = ActivateDeviceEx(m_lpTemplateRegPath, m_dwInitRegArray, (m_dwInitRegArray!=NULL?m_dwInitRegCount:0) , NULL);
m_fDriverLoaded = (m_hDevice!=NULL);
当你的AP调用SetEvent(hStartEvent)后,第二个busenum.dll就开始加载路径位于[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\****]下的所有驱动,order依然有效。
打印消息: DeviceFolder:oadDevice(Drivers\BuiltIn\SDHC_ZYLONITE2) last 163 Ticks ... ... ActivateDeviceEx busenum.dll successful.m_lpTemplateRegPath=Drivers\BuiltIn\AsyncBus. DeviceFolder:oadDevice(Drivers\BuiltIn\PCC_ZYLONITE0) last 1 Ticks DeviceFolder:oadDevice(Drivers\BuiltIn\PCI) last 0 Ticks ... ... 下面是我的一些代码: 注册表里: [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus] "Dll"="BusEnum.dll" "Order"=dword:10 "Flags"=dword:1000008 "BusName"="AsyncBus" "LoadAsyncEvent"="Start" ;wait for the event to load the childkey of AsyncBus "LoadAsyncDelay"=dword:2710 ;setup the timeout is 10s [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\SWC] "refix"="SWC" "Dll"="$(_TGTPLAT_PFX)_SWC.dll" "Order"=dword:1 ; Must loading after MFP driver loading "Flags"=dword:0 [HKEY_LOCAL_MACHINE\Drivers\BuiltIn\AsyncBus\RTC] "refix"="RTC" "Dll"="$(_TGTPLAT_PFX)_RTC.dll" "Order"=dword:2 "Flags"=dword:0 BOOL DeviceFolder:oadDeviceAsync() { WCHAR szEventName[MAX_PATH] = {}; DWORD dwEventTimeout = 0; BOOL aa = GetRegValue(DEVLOAD_LOAD_ASYNC_EVENT_TIMEOUT_VALNAME,(LPBYTE)&dwEventTimeout,sizeof(dwEventTimeout)); if (!aa) { // no timeout/delay option set. dwEventTimeout = INFINITE; } BOOL bb = GetRegValue(DEVLOAD_LOAD_ASYNC_EVENT_VALNAME,(PUCHAR)szEventName,sizeof(szEventName)); if (bb) { m_hAsyncLoadStartEvent = CreateEvent(NULL, TRUE, FALSE, szEventName); if(m_hAsyncLoadStartEvent && dwEventTimeout) { // Wait for event or timeout if(WAIT_FAILED == WaitForSingleObject(m_hAsyncLoadStartEvent, dwEventTimeout))//等待加载信号或超时 { return FALSE; } } } else { // No event option set. Will start loading after delay if(dwEventTimeout && (dwEventTimeout != INFINITE)) { Sleep(dwEventTimeout); } } // Time to load the driver. m_hDevice = ActivateDeviceEx(m_lpTemplateRegPath, m_dwInitRegArray, (m_dwInitRegArray!=NULL?m_dwInitRegCount:0) , NULL); if (m_hDevice) { //正确加载了第2个BusEnum.dll RETAILMSG(1,(TEXT("ActivateDeviceEx busenum.dll successful.m_lpTemplateRegPath=%s.\r\n"), m_lpTemplateRegPath)); } else { RETAILMSG(1,(TEXT("ActivateDeviceEx faild.\r\n"))); } Lock(); m_PendingLoadAsync = FALSE; m_fDriverLoaded = (m_hDevice!=NULL); Unlock(); return m_fDriverLoaded; } #endif BOOL DeviceFolder:oadDevice() { ... ... m_hAsyncLoadThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) AsyncLoadThread, this, CREATE_SUSPENDED, NULL); if(m_hAsyncLoadThread) { RETAILMSG(1,(TEXT("m_hAsyncLoadThread succeed.\r\n"))); Lock(); m_PendingLoadAsync = TRUE; Unlock(); ResumeThread(m_hAsyncLoadThread); return TRUE; } // Failed creating the async loading thread for this driver. return FALSE; } m_hDevice = ActivateDeviceEx(m_lpTemplateRegPath, m_dwInitRegArray, (m_dwInitRegArray!=NULL?m_dwInitRegCount:0) , NULL); m_fDriverLoaded = (m_hDevice!=NULL); //加载任何BuiltIn下的驱动都会打印 RETAILMSG(1,(TEXT("DeviceFolder:oadDevice(%s) last %d Ticks\r\n"),m_lpTemplateRegPath,GetTickCount()-dwStartTicks)); ... ... } |