Reputation: 43
I've got a C++ software (not managed C++), the goal of which is to control certain actions of other applications on client's machine and send the alert to server. Once this action happens, software get application exe's path and sends. My modification task is to get application's DisplayName (like it is in the "Programs and Features" folder) and send it.
In the end, client machine's OS version is unknown, all I know is that it is Windows.
By reading Windows registry I could get display names of all those applications that can be seen in the "Programs and Features" folder. I used these keys:
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
But there are few applications that have their installation location written in the registry. In my case, it is about 1 application of 6 has its installation location written. Using Vista's functions SHGetKnownFolderItem and SHGetKnownFolderPath is not helpful because "Programs and Features" is the virtual folder.
Is there any way to learn "Programs and Features" implementation? Or perhaps another available option?
Upvotes: 0
Views: 1334
Reputation: 5525
See this article on The Old New Thing: How can I get the list of programs the same way that Programs and Features gets it?
An example:
#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);
}
}
}
}
}
Additional Hints:
Upvotes: 1
Reputation: 67148
You won't get all the installed applications (user may even simply unzip an xcopy deployed application into a folder and run it) but you can do exactly what Programs and Features does using its own API: enumerate all installed applications using Windows Installer API.
Function you need is MsiEnumProducts()
, it's pretty easy to use, in short:
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;
}
Information about a specific product (through its product code) can be obtained using MsiGetProductInfo()
function. In your specific case you first need to check if it's the one you're looking for (comparing its installation directory obtained with INSTALLPROPERTY_INSTALLLOCATION
). If it's the one you want then you can use INSTALLPROPERTY_INSTALLEDPRODUCTNAME
to obtain its name. Something like this (pretty raw, not production code!):
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
}
Note that if you have executable path you may also do a simpler check StringFileInfo structures in the .exe version resources. First of all retrieve version information resources size with GetFileVersionInfoSize
then read version information with GetFileVersionInfo. Now what you have to do is simply search for the VS_VERSIONINFO
structure you need (but this may even be more complicate because such structure is localized).
As last resort you may also check another registry key (I'm not sure if this is portable): HKEY_CLASSES_ROOT\Installer\Products
.
Upvotes: 0