我有一个C ++软件(不是托管C ++),其目标是控制客户机上其他应用程序的某些操作并将警报发送给服务器。一旦这个动作发生,软件就会获得应用程序exe的路径并发送。我的修改任务是获取应用程序的DisplayName(就像它在“程序和功能”文件夹中)并发送它。
最后,客户端机器的操作系统版本未知,我所知道的是它是Windows。
通过阅读Windows注册表,我可以获得可在“程序和功能”文件夹中看到的所有应用程序的显示名称。我用了这些钥匙:
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
HKEY_USERS\<User_sid>\Software\Microsoft\Windows\CurrentVersion\Uninstall
但是很少有应用程序将其安装位置写在注册表中。在我的情况下,它是大约1个应用程序的6写了它的安装位置。 使用Vista的功能SHGetKnownFolderItem和SHGetKnownFolderPath没有用,因为“程序和功能”是虚拟文件夹。
有没有办法学习“程序和功能”实现?或者也许是另一种可选方案?
答案 0 :(得分:1)
请参阅旧文章上的这篇文章:How can I get the list of programs the same way that Programs and Features gets it?
示例:
#include <Shobjidl.h>
#include <ShlGuid.h>
#include <atlbase.h>
CComPtr<IShellItem> programs;
if(SUCCEEDED(::SHCreateItemFromParsingName(
L"::{26EE0668-A00A-44D7-9371-BEB064C98683}\\8\\"
L"::{7B81BE6A-CE2B-4676-A29E-EB907A5126C5}",
nullptr,
IID_PPV_ARGS(&programs))))
{
//
// Another super secret property, this time for the Version.
// See the link above and http://msdn.microsoft.com/en-us/library/cc251929(v=prot.10).aspx
//
const PROPERTYKEY PROP_KEY_VERSION = {
{
0x0cef7d53,
0xfa64,
0x11d1,
0xa2, 0x03, 0x00, 0x00, 0xf8, 0x1f, 0xed, 0xee
},
8
};
CComPtr<IEnumShellItems> shellEnum;
programs->BindToHandler(nullptr, BHID_EnumItems, IID_PPV_ARGS(&shellEnum));
for(CComPtr<IShellItem> prog;
S_OK == shellEnum->Next(1, &prog, nullptr);
prog.Release())
{
CComHeapPtr<wchar_t> name;
if(SUCCEEDED(prog->GetDisplayName(SIGDN_NORMALDISPLAY, &name))) {
// Do something with 'name'
CComQIPtr<IShellItem2> shellItem2(prog);
if(shellItem2) {
LPWSTR ver;
if(SUCCEEDED(shellItem2->GetString(PROP_KEY_VERSION, &ver))) {
// Do something with 'ver'
::CoTaskMemFree(ver);
}
}
}
}
}
其他提示:
答案 1 :(得分:0)
您不会所有已安装的应用程序(用户甚至可以简单地将 xcopy部署的应用程序解压缩到文件夹中并运行它)但您可以完全执行以下操作< em>程序和功能使用自己的API:使用Windows Installer API枚举所有已安装的应用程序。
您需要的功能是MsiEnumProducts()
,简单易用:
char productCode[39];
int productIndex = 0;
while (true) {
UINT result = MsiEnumProducts(productIndex++, productCode);
if (result == ERROR_SUCCESS) {
// Search information about this product
} else if (result == ERROR_NO_MORE_ITEMS)
break; // Finished
cerr << "Error: " << result;
}
可以使用MsiGetProductInfo()
函数获取有关特定产品的信息(通过其产品代码)。在您的具体情况下,您首先需要检查它是否是您正在寻找的那个(比较它与INSTALLPROPERTY_INSTALLLOCATION
获得的安装目录)。如果它是您想要的那个,那么您可以使用INSTALLPROPERTY_INSTALLEDPRODUCTNAME
来获取其名称。像这样的东西(非常原始,不是生产代码!):
char buffer[255];
DWORD bufferSize = sizeof(buffer);
result = MsiGetProductInfo(
productCode, // Product we want to query a property
INSTALLPROPERTY_INSTALLLOCATION, // Property we want to read
buffer, // Buffer where property value will be stored
&bufferSize); // Size of that buffer
if (result != ERROR_SUCCESS) {
cerr << "Error: " << result;
} else {
// Compare with path you're looking for and
// if it matches then call MsiGetProductInfo
// to obtain INSTALLPROPERTY_INSTALLEDPRODUCTNAME
}
请注意,如果您有可执行文件路径,则还可以在.exe版本资源中更简单地检查StringFileInfo结构。首先使用GetFileVersionInfoSize
检索版本信息资源大小,然后使用GetFileVersionInfo读取版本信息。现在你需要做的是简单地搜索你需要的VS_VERSIONINFO
结构(但这可能更复杂,因为这样的结构是本地化的。)
作为最后的手段,您还可以检查另一个注册表项(我不确定这是否可移植):HKEY_CLASSES_ROOT\Installer\Products
。