How to get "INSTALLED" property in Custom Action DLL (MSI/Wix)? - c++

In my custom DLL I need to check if product is being installed or uninstalled, and hence need to get value of "INSTALLED" property (just like in WiX script). Here is what I am doing in C++ DLL:
WCHAR propValue[MAX_PATH];
DWORD propValLen = MAX_PATH;
// MSIHANDLE msiHandle;
MsiGetProperty(msiHandle, L"INSTALLED", propValue, &propValLen);
propValue[propValLen] = 0;
But the outcome is always an empty string (for both installation and uninstallation)! How check if product is being installed or uninstalled?

Property name is case-sensitive, it is "Installed": https://msdn.microsoft.com/en-us/library/windows/desktop/aa369297(v=vs.85).aspx

Related

Windows registry key AppliesTo does not work with Users directory

What I am trying to do is to enable a customized context menu item to appear when users right-click folders in a specific directory.
So in HKEY_CLASSES_ROOT\Directory\shell I created my key (say with the name: MyProgram), and I created the subkey command that has the path to my program to run (say, "C:\Users\myuser\myApp\MyProgram.exe").
Until now it is ok and works correctly. But when I add the entry AppliesTo under HKEY_CLASSES_ROOT\Directory\shell\MyProgram and set it to C:\Users it does not work and the context menu item does not appear anymore!
Important Note: My windows language is German and the display name of Users folder in my windows explorer is Benutzer. Whenever I set AppliesTo to C:\Benutzer instead it works correctly, despite the fact that the command works with Users path properly! Alsowhen I echo %USERPROFILE% in the cmd it is printed in English as C:\Users\myuser and not Benutzer.
Is there a way to programmatically get the display path of Users or any folder in the system?
Please note: I cannot simply write Benutzer instead of Users in the path because the path can be dynamic. I am doing it programmatically and want my code to have consistent behavior in different machines with different languages. I am using C++ winreg API to set the registry values (for example: RegOpenKeyEx() and RegSetValueEx()).
Below is an export of the working version of the key:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\shell\MyProgram]
"AppliesTo"="C:\\Benutzer\\myuser"
[HKEY_CLASSES_ROOT\Directory\shell\MyProgram\command]
#="\"C:\\Users\\myuser\\path\\to\\MyProgram.exe\" \"%1\""
I found the solution. The localized name of folders can be obtained using the function SHGetLocalizedName, however it requires some additional work to get the string representation of the localized name. The following code snippet shows an example of how to do it.
PWSTR name = new WCHAR[100];;
PCWSTR folder = TEXT("C:\\Users");
UINT len=100;
int id=0;
HRESULT hr = SHGetLocalizedName(folder, name, len, &id);
if (SUCCEEDED(hr)) {
wprintf(L"%ls\n", name);
ExpandEnvironmentStrings(name, name, len);
HMODULE shell_handle = LoadLibraryEx(name, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
if (shell_handle) {
if (LoadString(shell_handle, id, name, len) != 0) {
wprintf(L"%ls\n", name);
}
FreeLibrary(shell_handle);
}
}
The result of the previous code will be Benutzer in case of German locale.
You will need to include the following headers as well
#include <wchar.h>
#include <Shellapi.h>

Qt Installer Framework - Reading downloaded package version

Using QtIFW-1.5.0, so far I'm able to generate an online installer for my Qt application on Windows. The installer downloads the appropriate package from my web server and performs some operations defined in the control script installscript.qs, e.g. writing some keys into registry and creating a desktop shortcut with an icon:
installscript.qs:
Component.prototype.createOperations = function()
{
try
{
// call the base create operations function
component.createOperations();
// Add some keys to registry;
var userProfile = installer.environmentVariable("USERPROFILE");
installer.setValue("UserProfile", userProfile);
var reg = installer.environmentVariable("SystemRoot") + "\\System32\\reg.exe";
var key= "HKCU\\Software\\Company\\Product";
component.addOperation("Execute", reg, "ADD", key, "/f");
component.addOperation("Execute", reg, "ADD", key, "/v", "productId", "/t", "REG_BINARY");
// Add a desktop shortcut with icon:
component.addOperation("CreateShortcut",
"#TargetDir#\\MyExecutable.exe",
"#UserProfile#\\Desktop\\MyExecutable.lnk",
"workingDirectory=#TargetDir#",
"iconPath=#TargetDir#\\MyIcon.ico");
}
catch (e)
{
print(e);
}
}
All right, but another key I need to write into registry is the package VERSION NUMBER, defined in the installer configuration file config.xml in tag
<Version></Version>
How can I get this value from installscript.qs ? I read --I'd said more: studied-- the docs component QML Type and installer QML Type and I have not found any reference to version, except:
installer QML type:
boolean versionMatches(string version, string requirement)
which is useless for me, because you have to know the version, which is precisely what I find.
So any help would be appreciated.
You can call
var version = installer.value("ProductVersion");
to get the version specified in the config.xml file.

CPFT_SMALL_TEXT value cant modified after initialize in Windows 7 Credential Provider DLL?

i am modifying credential provider dll in windows 7.
In that dll, i try to add a Small text field and set a value.
it works fine using the below code.
hr = SHStrDupA("Bala", &_rgFieldStrings[SFI_I_WORK_IN_STATIC]);
after that i want to change the text value in a particular condition,like below.
hr = SHStrDupA("Goto other Logon", &_rgFieldStrings[SFI_I_WORK_IN_STATIC]);
But it not works.How to resolve that?

How to properly use %USERPROFILE% inside code?

Is my code correct? It seems can compile but does not work properly..
CString testing = _T(" --url=") + cstring + _T(" --out=%USERPROFILE%\\snapshot.png");
I want to point it to user's folder..but still cannot work.
The answer is that you don't use environment variables at all. Rather, you use the shell functions specifically designed to retrieve the path of special folders.
On Windows Vista and later, that function is SHGetKnownFolderPath. It takes KNOWNFOLDERID values to identify the folder whose path you wish to retrieve. In your case, that would be FOLDERID_Profile.
If you need to target earlier versions of Windows (such as XP), you will need to use the SHGetSpecialFolderPath function, instead. It takes a CSIDL value identifying the folder whose path you wish to retrieve. Again, in your case, that would be CSIDL_PROFILE.
Of course, you should never store data directly in the user's profile folder. So hopefully the bit of code that you've shown is for demonstration purposes only. Applications should only create files in the specific locations under the user profile folder, designed for application data storage.
These locations are CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. If you are creating data that the user should be able to modify and should treat as his/her own, then it would be appropriate to store that data in the user's documents folder (CSIDL_MYDOCUMENTS).
More usage information is available in my answer here.
Sample code:
TCHAR szFolderPath[MAX_PATH];
if (!SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_APPDATA, FALSE))
{
// Uh-oh! An error occurred; handle it.
}
Or, using MFC's CString class:
CString buffer;
BOOL bRet = SHGetSpecialFolderPath(NULL, buffer.GetBuffer(MAX_PATH), CSIDL_APPDATA, FALSE);
buffer.ReleaseBuffer();
if (!bRet)
{
// Uh-oh! An error occurred; handle it.
}
As Cody suggested, it's better to use the SHGetSpecialFolderPath function. However, you could use the GetEnvironmentVariable function to get that and other variables set in the system.
TCHAR szBuf[MAX_PATH] = {0};
::GetEnvironmentVariable(_T( "USERPROFILE" ), szBuf, MAX_PATH);

Visual Studio Addin - Get Name\Path of binary of a c++ project?

I want to get the binary name of a C++ Project with a Visual Studio C# Addin.
I googled and found, that the the EnvDTE.Configuration.properties should have a element called "AssemblyName" but C++ projects do not seem to have this element.
Did somebody know where could I get this information inside a visual studio addin?
For VC++ projects you need to get access to the VCConfiguration object which you should be able to get at from the EnvDTE.Project's Object property like:
EnvDTE.Project project = ...
VCProject vcProj = (VCProject)project.Object;
IVCCollection configs = (IVCCollection)vcProj.Configurations;
VCConfiguration config = (VCConfiguration)configs.Item(configName); // like "Debug"
At that point with the VCConfiguration how exactly to get at the correct properties depends on your set up. You can access the the VCLinkerTool from the Tools property and get at the OutputFile and other properties. Or, if you use the newer inherited property sheets you may access those through the Rules property.
IVCCollection tools = (IVCCollection)config.Tools;
VCLinkerTool linkTool = (VCLinkerTool)tools.Item("Linker Tool");
string outputFile = linkTool.OutputFile;
// -------
IVCRulePropertyStorage ruleStorage = config.Rules.Item(ruleName);
string outputFile = ruleStorage.GetEvaluatedPropertyValue("TargetName");
In order to get the complete path of the binary, follow the steps as #Chadwick said to get the VCConfiguration object. And then, just use the following line of code:
//returns the complete binary name including path as a string
var primaryOutput = config.PrimaryOutput;