有时候需要在 Web 页面上检测用户计算机上是否安装了某个软件的特定版本,接下来就可以做以下处理:如果用户安装的软件版本过低或未安装该软件,就提示用户安装该软件的最新版本。本文提供一个实现示例,其实现的功能是:使用VC编写一个 ocx 控件,该控件通过读取注册表,获取计算机上安装的 Apple 公司 iTunes 客户端软件的版本号。
如果用户计算机上安装了 Apple 公司的 iTunes 客户端软件(本文中计算机上安装的 iTunes 的版本号为 12.5.3.17),那么在注册表中,可以看到有关于 iTunes 版本号的项存在,如下图:
本文使用的 IDE 是 Visual Studio 2010,先写一个 Win32 控制台应用程序,读取一下 iTunes 在注册表中的版本,该程序如下:
/************************************************** * File name: ReadReg.cpp * Author: HAN Wei * Author's blog: http://blog.csdn.net/henter/ * Date: Mar 5th, 2018 * Description: demonstrate how to get iTunes client version by means of reading Windows registry **************************************************/ #include <Windows.h> #include <comutil.h> #include <iostream> #include <tchar.h> using namespace std; #pragma comment(lib, "comsuppwd.lib") BSTR GetClientVersion(void) { const TCHAR SoftwareRegPath[] = { _TEXT("SOFTWARE\\Apple Computer, Inc.\\iTunes") }; HKEY hKey; TCHAR SoftwareVersion[128]; DWORD type = 0; DWORD DataLen = 1024; long ErrorCode; BSTR ClientVerBstr; memset(SoftwareVersion, 0, sizeof(SoftwareVersion)); if ( (ErrorCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SoftwareRegPath, 0, KEY_READ, &hKey)) != ERROR_SUCCESS ) { return SysAllocString(_TEXT("RegOpenKeyEx function failed.")); } if ( RegQueryValueEx(hKey, _TEXT("Version"), 0, &type, (LPBYTE)(SoftwareVersion), &DataLen) != ERROR_SUCCESS ) { RegCloseKey(hKey); return SysAllocString(_TEXT("RegQueryValueEx function failed.")); } if ( RegCloseKey(hKey) != ERROR_SUCCESS ) { return SysAllocString(_TEXT("RegCloseKey function failed.")); } ClientVerBstr = SysAllocString(SoftwareVersion); return ClientVerBstr; } int main(void) { BSTR bstring; char *version_string; if ( bstring = GetClientVersion() ) { version_string = _com_util::ConvertBSTRToString(bstring); cout<<"iTunes version: "<<version_string<<endl; delete[] version_string; SysFreeString(bstring); return 0; } else { cout<<"Get iTunes version from Windows registry failed!"<<endl; SysFreeString(bstring); return (-1); } }
函数 GetClientVersion( ) 的返回值类型为 BSTR,是为了方便以后将该函数移植到 ocx 控件程序中。运行后报告RegOpenKeyEx( ) 函数执行失败,原因在于当前计算机上安装的是 64 位的 Windows 7 操作系统,安装的 iTunes 是一个 64 位的应用程序,当前在编译命令行程序时,“解决方案”平台被设置为“Win32”,32 位应用程序在 64 位 的 Windows 操作系统上,读取不到 64 位程序对应的注册表项。要解决这个问题,只需要将 RegOpenKeyEx( ) 函数的第 4 个输入参数由 KEY_READ 改为 KEY_READ | KEY_WOW64_64KEY ,这样就能让 32 位应用程序读取到 64 位程序在注册表中的相关项了。在 32 位 Windows 操作系统上,将会自动忽略 KEY_WOW64_64KEY 参数。修改以后的程序如下:
/************************************************** * File name: ReadReg.cpp * Author: HAN Wei * Author's blog: http://blog.csdn.net/henter/ * Date: Mar 5th, 2018 * Description: demonstrate how to get iTunes client version by means of reading Windows registry **************************************************/ #include <Windows.h> #include <comutil.h> #include <iostream> #include <tchar.h> using namespace std; #pragma comment(lib, "comsuppwd.lib") BSTR GetClientVersion(void) { const TCHAR SoftwareRegPath[] = { _TEXT("SOFTWARE\\Apple Computer, Inc.\\iTunes") }; HKEY hKey; TCHAR SoftwareVersion[128]; DWORD type = 0; DWORD DataLen = 1024; long ErrorCode; BSTR ClientVerBstr; memset(SoftwareVersion, 0, sizeof(SoftwareVersion)); if ( (ErrorCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SoftwareRegPath, 0, KEY_READ | KEY_WOW64_64KEY, &hKey)) != ERROR_SUCCESS ) { return SysAllocString(_TEXT("RegOpenKeyEx function failed.")); } if ( RegQueryValueEx(hKey, _TEXT("Version"), 0, &type, (LPBYTE)(SoftwareVersion), &DataLen) != ERROR_SUCCESS ) { RegCloseKey(hKey); return SysAllocString(_TEXT("RegQueryValueEx function failed.")); } if ( RegCloseKey(hKey) != ERROR_SUCCESS ) { return SysAllocString(_TEXT("RegCloseKey function failed.")); } ClientVerBstr = SysAllocString(SoftwareVersion); return ClientVerBstr; } int main(void) { BSTR bstring; char *version_string; if ( bstring = GetClientVersion() ) { version_string = _com_util::ConvertBSTRToString(bstring); cout<<"iTunes version: "<<version_string<<endl; delete[] version_string; SysFreeString(bstring); return 0; } else { cout<<"Get iTunes version from Windows registry failed!"<<endl; SysFreeString(bstring); return (-1); } }
注意在编译设置中,“字符集”要设为“使用Unicode字符集”。程序执行结果如下:
接下来开始编写 ocx 控件。以管理员身份运行 Visual Studio, 这样做的好处是将来在编译 ocx 控件时,Visual Studio 能自动注册控件。在“新建项目”中选择“MFC ActiveX控件”,假定将要创建的ocx控件名为 ReadRegistry,一步一步操作如下:
点击“完成”按钮,Visual Studio 自动创建了相关的文件。接下来添加一个方法,如下图:
假定方法名称为 GetClientVersion,添加方法的过程如下:
点击“下一步”按钮,显示如下:
点击“完成”按钮。在 ReadRegistryCtrl.cpp 文件中,找到函数 BSTR CReadRegistryCtrl::GetClientVersion(void) ,如下图:
删去图中选定部分的代码,将前面实现的 Win32 控制台应用程序中 GetClientVersion( ) 函数的实现代码粘贴过来,如下图:
为 ocx 控件实现安全接口,具体过程请参考博文:http://blog.csdn.net/henter/article/details/79424020,这里不再详述。完成后重新生成 ReadRegistry.ocx 等文件,如果有条件的话,最好对 ReadRegistry.ocx 做数字签名。在 ReadRegistry.idl 文件中找到以下字样:
// CReadRegistryCtrl 的类信息
[
uuid(8976F52E-4096-4C8D-B845-11350BA3E2AD)
]
在不同计算机上产生的 uudi 值是不同的,将 uuid 后面括号中的值记录下来,
接下来编写以下名为 index.html 的网页文件:
<!DOCTYPE HTML> <html> <BODY> <OBJECT id="ReadRegistry" classid="clsid:8976F52E-4096-4C8D-B845-11350BA3E2AD" width="0" height="0"> </OBJECT> <SCRIPT Language="JavaScript" type="text/JavaScript"> function ReadReg() { var myOcx=document.getElementById("ReadRegistry"); if (myOcx) { alert("ocx 控件存在。"); var version = myOcx.GetClientVersion(); alert("iTunes version: "+ version); } else { alert("ocx 控件不存在!"); } } </SCRIPT> <button type="button" onclick="JavaScript:ReadReg()">读取注册表中iTunes的版本号</button> </BODY> </html>
注意在 html 文件中用到了刚才记录下来的 uuid 值。将文件 index.html 和 ReadRegistry.ocx 放到 Web 服务器上,这里使用一个轻量级的 Web 服务器软件 Abyss Web Server X1。将文件 index.html 和 ReadRegistry.ocx 拷贝到 Abyss Web Server X1 服务器软件的 \Abyss Web Server\htdocs 目录下,启动 Abyss Web Server X1 服务器软件,启动 IE 浏览器,输入网址:http://localhost/index.html ,页面显示如下:
点击“读取注册表中iTunes的版本号”按钮,页面依次显示如下:
可以看到成功地获取到了 iTunes 软件的版本号。为了保证在用户计算机上执行 ocx 控件时能够找到必要的库文件,建议在编译控件程序时,在设置中将“MFC的使用”设为“在静态库中使用 MFC”,如下图: