Windows installer self-healing using ProvideComponent - delphi

I have an application(app1) that uses another application(app2)'s DLL to do some things. The problem is that I need to have some current user registries set for the DLL to work. If I call app2 from the advertised shortcut it will invoke windows installer self-healing and populate its registries. However if I've just installed app2 from another user,logged in for the first time, and used app1 it will fail as the DLL will not have it's registries populated.
With this in mind I tried to use the Self-Invoked Resiliency method to trigger MSI self-healing.
The "HKCU" feature is the top feature containing a single component "HCKURegistry" which only contains registry keys. The keypath for the "HCKURegistry" component is a key in the HKCU hive.
I'm using Delphi XE3 and here is the line of code:
szProductCode :='{293A0959-6ECF-4026-929B-ECC777934525}';
szComponent:= '{45282475-634F-4222-81BA-030FA63703BD}' ;
MsiProvideComponent(pwidechar(szProductCode),pwidechar('HKCU'),pwidechar(szComponent),INSTALLMODE_DEFAULT, lpPathBuf,#pcchPathBuf);
When, however, my app1 executes this line the Windows Installer displays a windows installer "Preparing to install..." window and stays like this forever. If I click "Cancel" it says "Canceling..." and stays like this until I kill the app from the task manager.
The Event log shows two items:
Detection of product '{293A0959-6ECF-4026-929B-ECC777934525}', feature 'HKCU', component '{45282475-634F-4222-81BA-030FA63703BD}' failed.
Detection of product '{293A0959-6ECF-4026-929B-ECC777934525}', feature 'HKCU' failed during request for component '{45282475-634F-4222-81BA-030FA63703BD}'
A possibly relevant information. When I trigger the self healing the normal way I get the same two lines but the second one has additional sentence (the keypath registry key for the component):
Detection of product '{293A0959-6ECF-4026-929B-ECC777934525}', feature 'HKCU', component '{45282475-634F-4222-81BA-030FA63703BD}' failed. The resource 'HKEY_CURRENT_USER\Software\MyData\SomePath' does not exist.
I moments of desperation I tried using:
res:=MsiReinstallFeature(pwidechar(szProductCode),pwidechar('HKCU'),REINSTALLMODE_USERDATA);
And swapping INSTALLMODE_DEFAULT for REINSTALLMODE_USERDATA in the MsiProvideComponent call. Both behaved differenty in that they did not put anything into the Event log. Otherwise they both stuck in "Preparing to install..." just as the original call...
I'm stuck on this for a while now. Can someone spot what I'm doing wrong?

Related

How to make container installation behave like host machine installation

I'm working with the following:
Docker for Windows v20.10.11
Docker running in Windows container mode
mcr.microsoft.com/windows:1903 base image
Proprietary application installed on top of this base image
Each year we create a Docker image with the latest version of our company's software. However this year's version behaves differently. Host machine installation runs fine. Containerized installation fails to run in certain situations. I can start the application as a simple EXE, for example using the Docker run command. The app will start and show up in "tasklist". However I can't start the app via the COM API, which is a critical requirement. The problem appears to be COM related. Normally we can create COM objects for our software just like for any other application. For example, IE returns a COM object just fine:
Creating these objects for our application works outside containers. However inside the container, our latest installation gives this error:
Access permissions appear to be ok. I tried a couple tests to prove this. First I can install other software like MS Word into a container and create COM objects for that:
Second I tried retrieving + modifying the application's DACL in PowerShell.
Changing access masks or trustees can cause an Access Denied error:
This also appears to confirm the access permissions were Ok by default.
Next I made sure COM is aware of the application. This appears to be fine. I get the same result on host machine and container when running this PS script:
gci HKLM:\Software\Classes -ea 0| ? {$.PSChildName -match '^\w+.\w+$' -and
(gp "$($.PSPath)\CLSID" -ea 0)} | ft PSChildName
The application shows up just like any other. The details show up fine when querying by AppID. LocalServer32 points to the correct EXE:
Some other things I tried:
Querying registry keys. There are 7 keys created when installing our software. These appear identical on host machine install and container install.
Even though permissions appear fine, I still tried logging into the container as alternate users. For example "nt authority\system" is another virtual admin user. I also changed the password of the "builtin\administrator" user to enable logging in with that one. Lastly tried creating new users entirely and adding them to the Administrators user group. All these attempts had the same errors as "builtin\containeradministrator" (default user).
A minor check was ensuring CMD.exe / Powershell is running as x64:
Re-registering the DLLs associated with the installation using regsvr32.
Starting from different base images. https://learn.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-base-images. The full Win Server base image behaves exactly the same way regarding errors. The smaller Win Server Core base image is even more problematic, as I can't even start the app's EXE manually using that base. Lastly I tried other tags of the full Windows base image such as 20H2 and 2004. Same result from those. Multiarch or x64 makes no difference.
Included the "Ogawa hack" which was historically needed to make MS Office apps function correctly with COM: https://stackoverflow.com/a/1680214/7991646. It could be necessary for other COM apps too, but didn't help with my specific installation.
Is there anything else I can do to diagnose or solve this COM issue?
There are several things to consider:
The Considerations for server-side Automation of Office article states the following:
Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment.
If you are building a solution that runs in a server-side context, you should try to use components that have been made safe for unattended execution. Or, you should try to find alternatives that allow at least part of the code to run client-side. If you use an Office application from a server-side solution, the application will lack many of the necessary capabilities to run successfully. Additionally, you will be taking risks with the stability of your overall solution.
The When CoCreateInstance returns 0x80080005 (CO_E_SERVER_EXEC_FAILURE) page describes possible reasons.
If many COM+ applications run under different user accounts that are specified in the This User property, the computer cannot allocate memory to create a new desktop heap for the new user. Therefore, the process cannot start. See Error when you start many COM+ applications: Error code 80080005 -- server execution failed for more information.
Finally, you may find a similar thread here helpful, see Server execution failed (Exception from HRESULT: 0x80080005 (CO_E_SERVER_EXEC_FAILURE)).

Detecting Quick Access command state

We have an application that is using Windows Ribbon Framework for an UI. The app itself is written in Delphi and uses Windows Ribbon Framework for Delphi to interface with the ribbon API.
Our ribbon XML places few commands into the Quick Access toolbar. Use can then remove/add commands either by using the built-in ribbon mechanism (selecting the drop/down button and clicking on a command name) or by selecting More commands command which opens the configuration dialog.
The problem I've encountered is that I cannot find a way to get the current state of commands in the QA collection (whether they are visible or not).
In the example above (picture) I would like to detect that first five commands are checked and that the last is not so I can prepare the configuration dialog accordingly.
I have no problems enumerating the IUICollection and accessing the items stored inside. I can also get the UI_PKEY_CommandId for each item. I cannot, however, find a way to read the checked/unchecked state. I tried reading UI_PKEY_BooleanValue and UI_PKEY_Enabled for all items in the collection, but they do not return that state.
I have also tried to monitor IUICommandHandler.UpdateProperty but it doesn't get called when such item is checked/unchecked (except that it is called with the UI_PKEY_Label key).
Does ribbon API even support this functionality?

Installshield 2014 Basic MSI Hide features dynamically not installing features

I have created a Basic MSI Installer using InstallShield 2014 for a server/client program and have to hide features dynamically based on the License Key of a database that is installed prior to our Server app being installed. I have created conditions for the features that need to be hidden, setting the InstallLevel to 0 if they are not licensed and 1 if they are licensed. I am getting the license key after the SQL Login dialog (because the installer wouldn't know what database to look in otherwise) but conditions are evaluated during the CostFinalize action, which runs before the dialogs are created. So after I get the license key and run some other custom actions to determine the availability of each feature, I call the CostFinalize action before the CustomSetup dialog is shown.
I am getting the correct behavior for the features that need to be shown, and you can select or deselect said features in the dialog, however, when the installation executes, the selected feature is not installed....and the log file says that the feature is not selected for install, even though the user clearly selects it. Why would this be happening? Is there another approach to hiding features dynamically (I have tried the FeatureSetData function in an InstallScript action, but to no avail)?
Also, after I added the conditions to the features, whenever I try to uninstall the program from the Programs and Features app, I get an Error 1606 Could not access network location. It's like the registry key gets messed up when there are conditions on the features...Any help would be greatly appreciated.
I found the problem...If you set the features InstallLevel to 0 to start with and have a condition that sets it to something greater than zero, then it will not install the feature, regardless of whether it's selected. If you invert this logic and start with the features InstallLevel set to 1 and have a condition that changes the installlevel to 0, it will hide or show the feature AND it will be installed properly. This also caused the error 1606 I was getting on the uninstall...
Also, if anyone ever has components that get installed that aren't supposed to be installed, then you might try switching the Dependency Checking to none. For some reason, the .NET dependency check that InstallShield does causes certain components to install all the time, even if their assigned feature is turned off. Hope this helps someone in the future.
The CostFinalize can also be run by a dialog to refresh the feature list. Here are the steps:
In the Next PushButton of the SetupType Dialog, create a new item at the top.
Event: DoAction
Argument: CostFinalize
Condition: 1=1
In my case, I was hiding a feature based on a previous dialog and needed it to reevaluate conditions in the Program Feature.
Condition: Level:0 GLOBAL_VAR=0
Condition: Level:1 GLOBAL_VAR=1

Forcing a Windows service to fail

Whenever a specific Windows service fails I want to run a program I've created myself. However, I simply can't find a way to make it fail on purpose, so that I can actually test that everything works correctly.
Note that the service in question is not something I've written myself, so I can't make it fail programmatically from inside the code. I wouldn't, however, mind writing a program that can make a service fail.
Of course I would prefer just having a "Make service fail" button somewhere in services.msc ... ;)
The server I'm doing this on is running Windows Server 2012.
If you don't want to use command line :
As an admin open the Windows Task Manager, in the Services tab find the service you want to test. Right click the service and click on Go to process. The selected process (if any) is the one corresponding to your service. Kill this process to simulate a service failure.
Be aware that killing a process this way can lead to problems.
Define "fail". If you want the process to end, just use pskill or a similar tool that can terminate a process elevated (as an admin).

How do I do an http request upon successful Inno Setup install, for tracking purposes?

When my program successfully installs via Inno Setup, how do I run a URL in the background? I want to just load a tracking pixel (or a postback) which says that my program was installed.
I'm aware than in the [Run] section, I can run a .url file with the flag "runhidden", but as far as I know that won't do what I need it to. Am I wrong about that, or are there better option?
Thank you.
There is no TCP/IP related Support Functions, defined by default.
You could build this functionality into a DLL and call it from you Install Script.
This is done installation's where Activation is required to make the program work.
However, I would just launch a browser window using the [Run] section that contains "Getting Started" information. You can then track hits to that page.

Resources