因为公司要做一个驱动程序的安装程序,因为那个视频卡驱动在某个时候会丢失,这个时候要求重新安装,所以也就出现了这样的一个要求 ,后来在网上找答案,发现这个问题在很多网络上都有提问,有点是好几年前提的,我不知道他们有没有解决这个问题,至少我没有从他们中找到一个很完整的答案。像驱动开发网上,提问的人也很多,回答的不少。
在网上我还是收集到了对我写这个程序有益处的东西,一个是MgrSrc[Written by Chuan-Liang Teng 2006],一个设备管理器,并带有安装,卸载功能,但我试过了,对于安装我的驱动不管用,与设备管理器不同点还有就是不能枚举到求知设备[计算机无法找到驱动相关INF的设备,或者驱动不正常的设备]。这样,因为我的视频卡无法枚举,所以也无法正确安装。
另外一个有用的程序就是一个网友在贴子上发表的,原文是安装一个猫驱动的程序,我也试过了,也无法实现安装,我想那段代码应该是可以安装一些驱动的。
…………
在网上找到好长的时间,也没有得到我想要得到的答案,只好面对着DDK开发文档,WDM驱动开发模型看,我不知道我看懂了多少,但最后我知道计算机是如何处理PNP设备的,又是如何找到驱动,并开始设备驱动的。下面我就简单的讲一下这个流程,希望对写同样程序的人有点帮助:
(1)当一个新设备接入到系统,这个时候系统总线将会得到有新设备加入,这个时候内核PNP管理器知道了,然后就开始向该设备获取相关设备信息[硬件ID,兼容ID之类的信息];
(2)内核PNP管理器通知用户模式PNP管理器,用户模式PNP管理器开始执行一个安装动作,如果在系统中找到一个与设备硬件ID或者兼容ID相同的INF文件(文件时间最新)时,用户PNP管理器得到该设备的驱动文件;如果不能打到INF文件,那么将会弹出新硬件安装向导,在发生这个事情的时候,PNP管理器会将一些设备信息写入注册表,包括硬件ID之类的信息(HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Enum/USB [USB枚举键]),在其下有很多USB设备信息,有的是曾经拉入到系统的设备,如果是没有正确安装的话,信息是不完整的;
(3)在硬件得到驱动文件的地方之后,内核PNP管理器将向驱动发送一些DIF码(这个时候就正式开始与设备驱动进行沟通了,也就是驱动程序开始提供设备的服务了),如果一切正常,设备可以正常开始。其它情况参见MSDN。
[参考 MSDN -- Example Pnp Device Installation]
有了上面的基础之后,我们知道驱动系统在接入一个新设备的时候一定会在注册表中加入一些新值,我们就可以得到该设备的一些信息,方便我们建立一个新的设备信息结构,对于正确的驱动安装是很重要的。
下面讲述一下我使用的方法:
首先从INF文件中得到下列数据:
GUID:设备类的GUID
ClassName:设备类的类名
硬件IDs:INF提供所有硬件ID列表,表示该INF支持的设备范围
然后从注册表中得到你需要安装设备的信息:
如何获得呢?你一定知道你设备产商的一个编码,因为这个编码是唯一分配的,如果系统中存在多种这个产商的设备,并要区分一下了,/Vid_eb1a&Pid_2821&MI_00/6&3ad105ea&2b&0000 [设备实例ID], 这是一个实例ID字串,从中我们知道Vid_eb1a为产商编码,这样我就可以从注册表中得到该产商提供的设备实例的一些信息,找到之后,从子键中得到该设备的信息,包括硬件ID,以及兼容ID。有了这些信息之后,我们就可以去与INF文件中的硬件ID进行比较,看提供的INF文件是否支持该设备,如果支持,那么恭喜你了,我可正式实现安装了。
如何实现安装:
建立一个空的设备信息集,然后依赖上面的信息建立一个相关的设备信息数据,然后发送安装DIF码,之后使用更新驱动函数完成安装。
//建立一个空设备信息集
HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(NULL , NULL);
SetupDiCreateDeviceInfo(
hDevInfo, //上面建立空的设备信息集
ClassName, //从INF文件中得到的类名
didcVideo.ClassGuid, //从INF文件中得到的GUID
didcVideo.DeviceDescription, //从注册表中得到的设备描述,没有应该也没有关系
NULL,
DICD_GENERATE_ID , //使用类名,生成一个实例ID
&spDevInfoData //返回的设备信息数据
)
//使用设备信息集与数据,发送安装设备DIF码
lRet = SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
hDevInfo, &spDevInfoData);
//更新设备驱动
UpdateDriverForPlugAndPlayDevices(0L,
(LPCSTR)szMatchID, //得到的相匹配的硬件ID
argv[2],//INF文件
INSTALLFLAG_FORCE, //强制安装
&bRebootRequired)//是否需要要求重启
这个时候如果没有提供.CAT文件[windows徽标测试文件],系统将会提示没有通过windows徽标测试,是否继续,如果是制作傻瓜安装程序,应该创建一个线程,在找到该窗口的时候,让它继续下去。[听说XP通过改注册表没有什么用]
这样一个驱动程序应该是安装好了,这也是我用到的方法,有很我想法,都是来源于看了设备安装的流程来的,想不到在找到匹配的硬件ID,还是自己手动去找,系统应该也提供了这种功能,但我也不知道如道去做,这种方法是实现我视频卡驱动程序完全自动安装功能了。
在上面讲述中也有太多的不仔细,有时间的话再去完善,如果那位网友有更好的方法,请多多指教哦。
为了完整性,特在此由上代码:
... {
if( 3 != argc)...{
_tprintf("Error command! %s Install <INF>", argv[0]);
return -1;
}
if(0 != stricmp(TEXT("Install"), argv[1]))...{
_tprintf("Error command! %s Install <INF>", argv[0]);
return -1;
}
//_tprintf("%s, %s, %s ", argv[0], argv[1], argv[2]);
//获得与平台相关的窗口名
TCHAR szBuffer[512] = ...{0};
TCHAR szLang[512] = ...{0};
HMODULE hModule = GetModuleHandle(NULL);
DWORD lRet = LoadString((HINSTANCE)hModule, IDS_STRING_LANG, szLang, 512);
LANGID lid = GetSystemDefaultLangID();
lRet = VerLanguageName(lid, szBuffer, 512);
if(strcmp(szLang,szBuffer) == 0)...{//China
LoadString((HINSTANCE)hModule, IDS_STRING_FOUND_WIZARD_CH, _szFoundWizardTitle, MAX_PATH);
LoadString((HINSTANCE)hModule, IDS_STRING_HARDWARE_INSTALL_CH, _szHardInstallTitle, MAX_PATH);
}else...{//Other country
LoadString((HINSTANCE)hModule, IDS_STRING_FOUND_WINZARD_EN, _szFoundWizardTitle, MAX_PATH);
LoadString((HINSTANCE)hModule, IDS_STRING_HARDWARE_INSTALL_EN, _szHardInstallTitle, MAX_PATH);
}
//在建立设备信息数据所需信息
DeviceInfoDataCreate didcVideo;
GUID ClassGUID; //获得该类的GUID
TCHAR ClassName[MAX_CLASS_NAME_LEN];//在建立设备信息集时需要
// Use the INF File to extract the Class GUID.
SetupDiGetINFClass(argv[2],&ClassGUID,ClassName,sizeof(ClassName),0);
didcVideo.ClassGuid = &ClassGUID;
//建立一个与UNKNOWN类相关的空设备信息集
HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList(NULL , NULL);
SP_DEVINFO_DATA spDevInfoData;
if (hDevInfo == INVALID_HANDLE_VALUE)...{
_tprintf("Error on SetupDiCreateDeviceInfoList. ");
return -1;
}
//DeviceInfoDataCreate didcVideo;
//GUID ClassGuid;
//通过注册表枚举USB下的所以与UNKNOWN相符的设备元素
//HKEY_LOCAL_MACHINESYSTEMCurrentControlSetEnumUSB [USB枚举键]
//Vid_eb1a&Pid_2821&MI_00&3ad105ea&2b&0000 [设备实例ID]
TCHAR sVid[] = TEXT("Vid_eb1a&"); //为我们公司使用的视频卡的产商编号
LPSTR szHardware = NULL; //硬件ID
LPSTR szCompID = NULL; //兼容硬件ID
LPSTR szMatchID = NULL; //获得的相匹配的硬件ID,用于安装
HKEY hkUsb; //[USB枚举键]句柄
HKEY hkVid; //与产商相关的USB设备标识
HKEY hkInst;//设备的实例句柄
//打开USB枚举键
lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
TEXT("SYSTEM/CurrentControlSet/Enum/USB"), 0,
KEY_QUERY_VALUE | KEY_READ, &hkUsb);
DWORD wIdx = 0; //USB根枚举索引
DWORD wInstIDIdx = 0;//实例枚举索引
PTSTR sKeyName = (PTSTR)new BYTE[128]; //保存USB键名
PTSTR sInstName = (PTSTR)new BYTE[128];//保存设备实例名
PTSTR sClassName = (PTSTR)new BYTE[128];//保存设备实例类名
LONG lEnumRet, lEnumInstanceIDRet;//枚举函数返回值,用于检查
DWORD dwRequireSize = 0;//用于传递并获得分配空间
FILETIME ftLastWriteTime;//枚举注册表时要用到的。
DWORD dwValueType;
const cMaxHarewareIDs = 10;
LPSTR aszHardware[cMaxHarewareIDs] = ...{0};
BOOL bInstallSucceed = FALSE;
int cntHardwareIDs = GetINFHardwareIDs(argv[2],aszHardware , 10);
_tprintf("Scan device on computer...... ");
//开始进行枚举USB根键
while(TRUE)
...{
dwRequireSize = 127;
lEnumRet = RegEnumKeyEx(hkUsb, wIdx++, sKeyName, &dwRequireSize, 0, NULL, NULL, &ftLastWriteTime);
if (ERROR_NO_MORE_ITEMS == lEnumRet)...{
break; //Exit While
}
if (ERROR_SUCCESS != lEnumRet)...{
break; //Some error occur
}
//该产商设备是否曾经接入到该计算机
if (strncmp(sKeyName, sVid, strlen(sVid)) == 0)...{
//打开该键,以查看该设备是否为Unknown类
lRet = RegOpenKeyEx(hkUsb, sKeyName, 0, KEY_QUERY_VALUE | KEY_READ, &hkVid);
if (ERROR_SUCCESS != lRet)...{
SDKError err;
_tprintf("RegOpenKeyEx -%s, %s ", sKeyName, err.GetErrMsg());
continue;
}
//在产商键下为实例ID
wInstIDIdx = 0; //一定要重设为0
while(TRUE)...{
dwRequireSize = 127;
lEnumInstanceIDRet = RegEnumKeyEx(hkVid, wInstIDIdx++, sInstName, &dwRequireSize, 0, NULL, NULL, &ftLastWriteTime);
if (ERROR_NO_MORE_ITEMS == lEnumInstanceIDRet)...{
break; //Exit While
}
lRet = RegOpenKeyEx(hkVid, sInstName, 0, KEY_QUERY_VALUE | KEY_READ, &hkInst);
if (ERROR_SUCCESS != lRet)...{
SDKError err;
_tprintf("RegOpenKeyEx -%s, %s ", sInstName, err.GetErrMsg());
continue;
}
//查看注册表中的值,看驱动是否已安装(存在Driver项,并且,存在数据)
dwValueType = REG_SZ;
dwRequireSize = 0;
RegQueryValueEx(hkInst, TEXT("Driver"), 0, &dwValueType, NULL, &dwRequireSize);
if (dwRequireSize)...{
//该驱动已安装,程序将不作处理
_tprintf("Driver already exist. -- %s ", ClassName);
bInstallSucceed = TRUE;
lRet = 2;
continue;
}
//在注册表中,有些节点是没有类与GUID项的,也就是说,
//没有办法得到该设备的类,唯有从INF中得到了,不过应该是可以的。
//DeviceDesciption
dwRequireSize = 0;
RegQueryValueEx(hkInst, TEXT("DeviceDesc"), 0, &dwValueType, NULL, &dwRequireSize);
PBYTE pData = new BYTE[dwRequireSize];
RegQueryValueEx(hkInst, TEXT("DeviceDesc"), 0, &dwValueType, pData, &dwRequireSize);
didcVideo.DeviceDescription = (LPTSTR)pData; //之后,我们需要释放设备字符串空间
//HardwareID
dwValueType = REG_MULTI_SZ;
RegQueryValueEx(hkInst, TEXT("HardwareID"), 0, &dwValueType, NULL, &dwRequireSize);
szHardware = new CHAR[dwRequireSize];
RegQueryValueEx(hkInst, TEXT("HardwareID"), 0, &dwValueType, (LPBYTE)szHardware, &dwRequireSize);
//compatible ID
dwRequireSize = 0;
RegQueryValueEx(hkInst, TEXT("CompatibleIDs"), 0, &dwValueType, NULL, &dwRequireSize);
szCompID = new CHAR[dwRequireSize];
RegQueryValueEx(hkInst, TEXT("CompatibleIDs"), 0, &dwValueType,(LPBYTE)szCompID, &dwRequireSize);
BOOL bFoundHardwareID = GetMatchHardwareID(szHardware,
(LPCSTR*)aszHardware, cntHardwareIDs, &szMatchID); //Need to release szMatchID.
if (!bFoundHardwareID)
...{
//从兼容ID中找
bFoundHardwareID = GetMatchHardwareID(szCompID,
(LPCSTR*)aszHardware, cntHardwareIDs, &szMatchID); //Need to release szMatchID.
}
if (!bFoundHardwareID) break;
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiCreateDeviceInfo(
hDevInfo, ClassName,
didcVideo.ClassGuid, didcVideo.DeviceDescription,
NULL, DICD_GENERATE_ID , &spDevInfoData))
...{
DWORD nErrCode = GetLastError();
switch(nErrCode)...{
case ERROR_CLASS_MISMATCH:
_tprintf("%s", "ERROR_CLASS_MISMATCH");
break;
case ERROR_DEVINST_ALREADY_EXISTS:
_tprintf("%s", "ERROR_DEVINST_ALREADY_EXISTS");
break;
case ERROR_INVALID_USER_BUFFER:
_tprintf("%s", "ERROR_INVALID_USER_BUFFER");
break;
default :
SetLastError(nErrCode);
SDKError err;
_tprintf("%s ",err.GetErrMsg());
break;
}
lRet = -2;
}
else
...{
BOOL bRebootRequired = 0;
//获得了设备信息数据,可以开始安装了
//
_tprintf("Device found! Start installing...... ");
//打开一个线程,用于监视硬件安装出现的提示,以让安装在无人操作下继续
BOOL bOverInstallWizard = FALSE;
_beginthread(InstallContinueAnyway, 0, &bOverInstallWizard);
lRet = SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
hDevInfo, &spDevInfoData);
if(lRet != NO_ERROR)...{
SDKError err;
_tprintf("SetupDiCallClassInstaller -- %s", err.GetErrMsg());
break;
}
if(UpdateDriverForPlugAndPlayDevices(0L, (LPCSTR)szMatchID,
argv[2],
0,//INSTALLFLAG_FORCE,
&bRebootRequired))
...{
_tprintf("Install OK!!!!! ");
bInstallSucceed = TRUE;
lRet = 1;
}
else
...{
SDKError err;
_tprintf("UpdateDriverForPlugAndPlayDevices -- %s ",err.GetErrMsg());
}
//如果没有出现该对话框,线程不会自动结束,强制结束该进程
bOverInstallWizard = TRUE;
}
if (bFoundHardwareID) delete[] szMatchID;
delete[] szCompID;
delete[] szHardware;
delete[] pData;
RegCloseKey(hkInst);
}//while [inner]
RegCloseKey(hkVid);
}//if sVid = "Vid_eb1a"
}
RegCloseKey(hkUsb);
delete[] sKeyName;
delete[] sInstName;
delete[] sClassName;
for(int i = 0; i < cntHardwareIDs; ++i)
...{
delete[] aszHardware[i];
}
//取消该设备信息集
SetupDiDestroyDeviceInfoList(hDevInfo);
if(!bInstallSucceed)
...{
_tprintf("Can't found specify device --%s. ", ClassName);
}
else
...{
//获得对话框句柄,并处理后续操作
HWND hDlgFra = FindWindow(NULL, _szFoundWizardTitle);
if (hDlgFra != NULL)
...{
//获得按钮ID
//HWND hPrevBtn = GetDlgItem(hDlgFra, PREVID); //上一步
//HWND hNextBtn = GetDlgItem(hDlgFra, NEXTID);//下一步按钮句柄
HWND hCancelBtn = GetDlgItem(hDlgFra, CANCELID);//;取消
//HWND hFinishedBtn = GetDlgItem(hDlgFra, FINISHEDID); //完成按钮句柄
SingleClick(hCancelBtn);
}
}
return lRet; // = 1 : Successed.
}
// 从INF文件中得到所以可用的硬件ID列表
// 参数:
// sInf: 为INF文件名
// aszHardwareIDs: 为一个硬件ID数据
// num:为数组数目
// 返回值为找到的硬件ID数目
int GetINFHardwareIDs(LPCSTR sInf, LPSTR aszHardwareIDs[], int num)
... {
BOOL retVal = false;
int iIdx = 0;
UINT ErrorLine;
HINF hInf = SetupOpenInfFile(sInf, NULL, INF_STYLE_WIN4, &ErrorLine);
if(INVALID_HANDLE_VALUE == hInf) return retVal;
INFCONTEXT hInfContext = ...{0};
retVal = SetupFindFirstLine(hInf, TEXT("Manufacturer"),NULL, &hInfContext);
if (!retVal)...{
SDKError err;
_tprintf(err.GetErrMsg());
SetupCloseInfFile(hInf);
return 0;
}
LPSTR pReturnBuffer = NULL;
DWORD RequiredSize = 0;
SetupGetLineText(&hInfContext, NULL, NULL, NULL, NULL, RequiredSize, &RequiredSize);
if (RequiredSize > 0 )...{
pReturnBuffer = new CHAR[RequiredSize];
SetupGetLineText(&hInfContext, NULL, NULL, NULL, pReturnBuffer, RequiredSize, &RequiredSize);
retVal = SetupFindFirstLine(hInf, pReturnBuffer,NULL, &hInfContext);
delete[] pReturnBuffer;
if (!retVal)...{
SDKError err;
_tprintf(err.GetErrMsg());
}else...{
do...{
DWORD RequiredSize = 0;
SetupGetLineText(&hInfContext, NULL, NULL, NULL, NULL, RequiredSize, &RequiredSize);
LPTSTR pReturnBuffer = new TCHAR[RequiredSize];
aszHardwareIDs[iIdx] = new TCHAR[RequiredSize]; //Need delete after used.
SetupGetLineText(&hInfContext, NULL, NULL, NULL, pReturnBuffer, RequiredSize, &RequiredSize);
char *ptr = strrchr(pReturnBuffer,',');
if (ptr == 0) continue;
strcpy(aszHardwareIDs[iIdx++], ptr+1);
delete[] pReturnBuffer;
}while(SetupFindNextLine(&hInfContext, &hInfContext));
}
}
SetupCloseInfFile(hInf);
return iIdx; //不需要减1,是需要从1开始的计数的。
}
// 从硬件ID数组中找到可以与硬件ID或者兼容ID匹配的硬件ID,并返回,该调用者需要释放匹配硬件ID
// sHardware, sCompID 为REG_MULTI_SZ字符
BOOL GetMatchHardwareID( LPCSTR sHardware, LPCSTR * aHardwareIDs, int num, LPSTR * sMatchID)
... {
BOOL retVal = false;
BOOL bFound = false;
int i = 0;
//从硬件ID中查找
LPCSTR ptr = sHardware;
while(TRUE)...{
for(i = 0; i < num; ++i)...{
if(0 == stricmp(ptr, aHardwareIDs[i]))...{//Found!!
*sMatchID = new CHAR[strlen(aHardwareIDs[i])+1];
strcpy(*sMatchID, aHardwareIDs[i]);
bFound = TRUE;
break;
}
}
if (bFound) break;
ptr+=strlen(ptr);
if (*ptr == '