概述
背景
项目背景为非UVC标准USB工业相机,连接Windows PC,需要安装特定USB驱动。我负责开发PC端相机软件部分(已完成),所以也需要把驱动安装这部分囊括进来。
USB芯片厂商已经提供了winxp/winvista/win7/win8/win8.1/win10、32bits/64bits不同平台的不同驱动文件,包括.inf、.sys、.cat、.dll文件。因此插上相机设备后,可以找到对应平台驱动,通过Windows设备管理器或者右键.inf文件进行驱动的安装。但是这些事情对于开发人员只是鼠标点点的操作,对于用户却会成为很麻烦的事情。因此开发PC端软件的我需要简化驱动安装的一切操作,尽量做到对用户透明。
捣鼓了不少时间,写出目前的解决方案。
驱动安装
为了让用户接触不到驱动安装的繁琐操作,只能用代码解决了,解决过程中在《竹林蹊径——深入浅出Windows驱动开发》这本书上受益匪浅,大概阅读,也算是加深了硬件设备与Windows系统间连接过程的理解。
开发环境win8.1 vs2013 c++ 控制台应用程序(有预编译头)
spdlog为日志输出(github开源项目)
使用的ANSI字符串
- 驱动预安装部分,上代码,参考竹林蹊径第12章
API参考链接
该函数成功运行需要程序具有管理员权限,具体可在项目属性->链接器->清单文件->UAC执行级别中更改为requireAdministrator。
BOOL InstallDriver(TCHAR* inf_path, TCHAR* inf_name_out)
{
/*
* inf_path: .inf文件所在路径,例如C:\driver.inf
* inf_name_out: inf文件预安装成功或系统已存在,则返回相应文件名,如oemxx.inf
*/
TCHAR path1[MAX_PATH] = {0};
TCHAR *path2;
if (FALSE == SetupCopyOEMInf(inf_path, NULL, SPOST_PATH, SP_COPY_NOOVERWRITE, path1, MAX_PATH, NULL, &path2)) //需要管理员权限
{
DWORD error = GetLastError();
if (error == ERROR_FILE_EXISTS)
{
spdlog::get("driver_install_info")->info("the driver file has existed, so succeeded");
//cout << "the driver file has existed, so succeeded" << endl;
//_tprintf(_T("the oem name is: %sn"), path2);
spdlog::get("driver_install_info")->info("the oem name is: n" + string(path2));
//cout << path2;
//printf("the file name is: %s", path2);
_tcscpy_s(inf_name_out, MAX_PATH, path2);
return TRUE;
}
else
{
spdlog::get("driver_install_info")->info("install failed, ");
spdlog::get("driver_install_info")->info("the error code is: " + to_string(error));
//cout << "install failed, ";
//cout << "the error code is: " << error << endl;
return FALSE;
}
}
else
{
spdlog::get("driver_install_info")->info("install succeededn");
spdlog::get("driver_install_info")->info("the oem name is: " + string(path2) + "n");
//_tprintf(_T("the oem name is: %sn"), path2);
_tcscpy_s(inf_name_out, MAX_PATH, path2);
return TRUE;
}
}
- 不同平台系统对应不同驱动文件,因此程序中还需要获取系统信息,上代码
API参考链接
/*
* 该API准确判断系统版本,还需要对程序的manifest文件进行修改,
* 也需要有win10 SDK的VersionHelper.h以及sdkddkver.h等头文件适配,具体可以百度
*/
enum WINDOWS_VERSION
{
WINDOWS_XP,
WINDOWS_VISTA,
WINDOWS_7,
WINDOWS_8,
WINDOWS_8_1,
WINDOWS_10,
OTHER_VERSION
};
WINDOWS_VERSION GetSystemVersion()
{
if (IsWindows10OrGreater())
{
return WINDOWS_10;
}
else if (IsWindows8Point1OrGreater())
{
return WINDOWS_8_1;
}
else if (IsWindows8OrGreater())
{
return WINDOWS_8;
}
else if (IsWindows7OrGreater())
{
return WINDOWS_7;
}
else if (IsWindowsVistaOrGreater())
{
return WINDOWS_VISTA;
}
else if (IsWindowsXPOrGreater())
{
return WINDOWS_XP;
}
else
{
return OTHER_VERSION;
}
}
- 判断操作系统位数,代码参考于网络,如下
enum WINDOWS_BITS_WIDTH
{
WINDOWS_32BITS,
WINDOWS_64BITS
};
// 安全的取得真实系统信息
VOID SafeGetNativeSystemInfo(__out LPSYSTEM_INFO lpSystemInfo)
{
if (NULL == lpSystemInfo) return;
typedef VOID(WINAPI *LPFN_GetNativeSystemInfo)(LPSYSTEM_INFO lpSystemInfo);
LPFN_GetNativeSystemInfo fnGetNativeSystemInfo = (LPFN_GetNativeSystemInfo)GetProcAddress(GetModuleHandle(_T("kernel32")), "GetNativeSystemInfo");
if (NULL != fnGetNativeSystemInfo)
{
fnGetNativeSystemInfo(lpSystemInfo);
}
else
{
GetSystemInfo(lpSystemInfo);
}
}
// 获取操作系统位数
WINDOWS_BITS_WIDTH GetSystemBits()
{
SYSTEM_INFO si;
SafeGetNativeSystemInfo(&si);
if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ||
si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64)
{
return WINDOWS_64BITS;
}
return WINDOWS_32BITS;
}
- 驱动预安装最终代码
int InstallDriver()
{
//获取.exe执行文件所在路径
TCHAR path[MAX_PATH] = {0};
GetModuleFileName(NULL, path, MAX_PATH);
*(_tcsrchr(path, _T('\'))) = 0;
string msg(path);
_tcscat_s(path, MAX_PATH, _T("\Drivers\"));
//日志初始化
//spdlog::set_async_mode(4096);
auto logger = spdlog::basic_logger_st("driver_install_info", msg + "\driverlog.txt");
spdlog::get("driver_install_info")->info("the driver files' directory is " + msg);
//_tcscat_s(path, _T("winxp"));
switch (GetSystemVersion())
{
case OTHER_VERSION:
_tcscat_s(path, MAX_PATH, _T("wxp\"));
msg = "before winxp or windows server n";
//cout << "before winxp or windows server " << endl;
break;
case WINDOWS_XP:
_tcscat_s(path, MAX_PATH, _T("wxp\"));
msg = "winxp ";
//cout << "winxp ";
break;
case WINDOWS_VISTA:
_tcscat_s(path, MAX_PATH, _T("vista\"));
msg = "vista ";
//cout << "vista ";
break;
case WINDOWS_7:
_tcscat_s(path, MAX_PATH, _T("Win7\"));
//cout << "win7 ";
msg = "win7 ";
break;
case WINDOWS_8:
_tcscat_s(path, MAX_PATH, _T("Win8\"));
msg = "win8 ";
//cout << "win8 ";
break;
case WINDOWS_8_1:
_tcscat_s(path, MAX_PATH, _T("Win81\"));
msg = "win8.1 ";
//cout << "win8.1 ";
break;
case WINDOWS_10:
_tcscat_s(path, MAX_PATH, _T("Win10\"));
msg = "win10 ";
//cout << "win10 ";
break;
default:
msg = "can't find related windows versionn";
//cout << "can't find related windows version" << endl;
break;
}
switch (GetSystemBits())
{
case WINDOWS_32BITS:
_tcscat_s(path, MAX_PATH, _T("x86\cyusb3.inf"));
msg += "32bits systemn";
//cout << "32bits system" << endl;
break;
case WINDOWS_64BITS:
_tcscat_s(path, MAX_PATH, _T("x64\cyusb3.inf"));
msg += "64bits systemn";
//cout << "64bits system" << endl;
break;
default:
msg += "can't find related machine's drivers of other bits widthn";
//cout << "can't find related machine's drivers of other bits width" << endl;
break;
}
spdlog::get("driver_install_info")->info(msg);
msg = "";
TCHAR cur_dir[MAX_PATH] = { 0 };
GetCurrentDirectory(MAX_PATH, cur_dir);
spdlog::get("driver_install_info")->info("the current dir is " + string(cur_dir));
//_tprintf(_T("the current dir is: %sn"), cur_dir);
//_tprintf(_T("the .exe dir is: %sn"), path);
BOOL flag = FALSE;
TCHAR inf_name[MAX_PATH] = { 0 };
flag = InstallDriver(path, inf_name);
//关闭日志
spdlog::drop("driver_install_info");
return 0;
}
相机程序安装包制作
利用InstallShield Limited制作,目标点击setup.exe之后,驱动程序、相机程序全部安装。主要在于程序安装之后,驱动程序的自动运行。InstallShield提供了Custom Actions去进行相应操作。如下,打开Custom Actions窗口,添加DriverInstall,相应设置根据图中显示。
日志输出
为了使驱动安装对用户透明,同时输出一定运行调试信息,便于程序出错时查错,因此控制台输出需要替代为日志输出。
这里使用github开源项目spdlog输出日志,用法简单。
遇到的问题与收获
- ansi与unicode字符串的不同
win8.1 sdk没有IsWindows10OrGreater()函数,需要额外加入,还需要添加相应的宏
该问题可以将需要的头文件从win10 sdk复制到项目中,在项目中修改这些文件为需要的版本,如果相应头文件没有复制到项目文件夹根目录下,项目属性需要设置好头文件附加包含目录
所需头文件具体包括sdkddver.h、VersionHelpers.h 、winapifamily.h兼容xp,需要设置平台工具集,由于使用了spdlog,还需要在targetver.h中定义宏
#define _WIN32_WINNT 0x0501
,以使用xp具有的API,该发现源于spdlogdetailsos.h代码段#ifdef _WIN32 #if _WIN32_WINNT < _WIN32_WINNT_WS08 TIME_ZONE_INFORMATION tzinfo; auto rv = GetTimeZoneInformation(&tzinfo); #else DYNAMIC_TIME_ZONE_INFORMATION tzinfo; auto rv = GetDynamicTimeZoneInformation(&tzinfo); #endif
- 程序运行不显示控制台窗口,可以在main函数前添加
#pragma comment( linker, "/subsystem:"windows" /entry:"mainCRTStartup"" )
最后
以上就是端庄皮皮虾为你收集整理的驱动安装+打包程序的全部内容,希望文章能够帮你解决驱动安装+打包程序所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复