View Full Version : Deferred custom Installscript actions not working

07-19-2006, 06:57 PM
I have two custom actions that call installscript functions to edit some XML. They are deferred and I am sure I have them in between install intialize and finalize. In fact, this worked perfectly in InstallShield X but when I converted to 12, all the sudden it does not execute. If I change them to Immediate Execution, they work, so the script is ok. But I need them deferred because I edit files that I install. From the log, it looks like they are being called, but none of the changes are made. If I use the debugger, it never even steps into the functions. Any help will be very appreciated. This is driving me crazy.

LOG (well the part that is supposedly calling the custom action):

SI (s) (F0:DC) [15:36:03:398]: Executing op: ActionStart(Name=EditAdminConsoleWizardConfig,,)
MSI (s) (F0:DC) [15:36:03:398]: Executing op: CustomActionSchedule(Action=EditAdminConsoleWizardConfig,ActionType=1025,Source=BinaryData,Target=f2,)
MSI (s) (F0:58) [15:36:03:570]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI1369.tmp, Entrypoint: f2
MSI (s) (F0:10) [15:36:03:570]: Generating random cookie.
MSI (s) (F0:10) [15:36:03:586]: Created Custom Action Server with PID 3472 (0xD90).
MSI (s) (F0:FC) [15:36:03:633]: Running as a service.
MSI (s) (F0:3C) [15:36:03:633]: Hello, I'm your 32bit Impersonated custom action server.
InstallShield: Running InstallScript function f2
InstallShield: IsBE.dll location: C:\Documents and Settings\All Users\Application Data\InstallShield\ISEngine12.0\IsBE.dll
InstallShield: Using temp folder C:\DOCUME~1\richmond\LOCALS~1\Temp\{36AA67C3-0ED7-4524-90AD-9A5DE4179590}
InstallShield: Installing engine...
InstallShield: Using product language 1033
InstallShield: Extracting support file setup.inx to C:\DOCUME~1\richmond\LOCALS~1\Temp\{36AA67C3-0ED7-4524-90AD-9A5DE4179590}\setup.inx
InstallShield: Opening stream of file C:\WINDOWS\Installer\MSI1369.tmp
InstallShield: Extracting support file ISRT.dll to C:\DOCUME~1\richmond\LOCALS~1\Temp\{36AA67C3-0ED7-4524-90AD-9A5DE4179590}\ISRT.dll
InstallShield: Extracting support file IsConfig.ini to C:\DOCUME~1\richmond\LOCALS~1\Temp\{36AA67C3-0ED7-4524-90AD-9A5DE4179590}\IsConfig.ini
InstallShield: Extracting support file _isres1033.dll to C:\DOCUME~1\richmond\LOCALS~1\Temp\{36AA67C3-0ED7-4524-90AD-9A5DE4179590}\_isres.dll
InstallShield: Extracting support file String1033.txt to C:\DOCUME~1\richmond\LOCALS~1\Temp\{36AA67C3-0ED7-4524-90AD-9A5DE4179590}\String1033.txt
InstallShield: Skipping optional support file _isuser1033.dll
InstallShield: Setting script cmdline...
InstallShield: ProductCode is {2A55E5FA-57A6-41FB-AA91-06467C9A2C43}
InstallShield: Initializing Engine
InstallShield: Done Initializing...
InstallShield: Registering Msi Server...
InstallShield: Invoking script function EditAdminConsoleWebConfig
InstallShield: CallScriptFunctionFromMsiCA() ends
MSI (s) (F0:DC) [15:36:05:242]: Executing op: ActionStart(Name=EditSetupAndRepairWizardConfig,,)
MSI (s) (F0:DC) [15:36:05:258]: Executing op: CustomActionSchedule(Action=EditSetupAndRepairWizardConfig,ActionType=1025,Source=BinaryData,Target=f1,)
MSI (s) (F0:6C) [15:36:05:430]: Invoking remote custom action. DLL: C:\WINDOWS\Installer\MSI136A.tmp, Entrypoint: f1
InstallShield: Running InstallScript function f1
InstallShield: IsBE.dll location: C:\Documents and Settings\All Users\Application Data\InstallShield\ISEngine12.0\IsBE.dll
InstallShield: Using temp folder C:\DOCUME~1\richmond\LOCALS~1\Temp\{7E4EB064-5F2D-4E6B-8EE6-89D7C03D25B4}
InstallShield: Installing engine...
InstallShield: Using product language 1033
InstallShield: Extracting support file setup.inx to C:\DOCUME~1\richmond\LOCALS~1\Temp\{7E4EB064-5F2D-4E6B-8EE6-89D7C03D25B4}\setup.inx
InstallShield: Opening stream of file C:\WINDOWS\Installer\MSI136A.tmp
InstallShield: Extracting support file ISRT.dll to C:\DOCUME~1\richmond\LOCALS~1\Temp\{7E4EB064-5F2D-4E6B-8EE6-89D7C03D25B4}\ISRT.dll
InstallShield: Extracting support file IsConfig.ini to C:\DOCUME~1\richmond\LOCALS~1\Temp\{7E4EB064-5F2D-4E6B-8EE6-89D7C03D25B4}\IsConfig.ini
InstallShield: Extracting support file _isres1033.dll to C:\DOCUME~1\richmond\LOCALS~1\Temp\{7E4EB064-5F2D-4E6B-8EE6-89D7C03D25B4}\_isres.dll
InstallShield: Extracting support file String1033.txt to C:\DOCUME~1\richmond\LOCALS~1\Temp\{7E4EB064-5F2D-4E6B-8EE6-89D7C03D25B4}\String1033.txt
InstallShield: Skipping optional support file _isuser1033.dll
InstallShield: Setting script cmdline...
InstallShield: ProductCode is {2A55E5FA-57A6-41FB-AA91-06467C9A2C43}
InstallShield: Initializing Engine
InstallShield: Done Initializing...
InstallShield: Registering Msi Server...
InstallShield: Invoking script function EditSetupAndRepairWebConfig
InstallShield: CallScriptFunctionFromMsiCA() ends
MSI (s) (F0:DC) [15:36:06:945]: Executing op: End(Checksum=0,ProgressTotalHDWord=0,ProgressTotalLDWord=110772080)
MSI (s) (F0:DC) [15:36:07:367]: User policy value 'DisableRollback' is 0
MSI (s) (F0:DC) [15:36:07:367]: Machine policy value 'DisableRollback' is 0
MSI (s) (F0:DC) [15:36:07:398]: Calling SRSetRestorePoint API. dwRestorePtType: 0, dwEventType: 103, llSequenceNumber: 768, szDescription: "".
MSI (s) (F0:DC) [15:36:07:398]: The call to SRSetRestorePoint API succeeded. Returned status: 0.
MSI (s) (F0:DC) [15:36:07:398]: Unlocking Server
MSI (s) (F0:DC) [15:36:07:398]: PROPERTY CHANGE: Deleting UpdateStarted property. Its current value is '1'.
Action ended 15:36:07: InstallFinalize. Return value 1.
MSI (s) (F0:DC) [15:36:07:398]: Skipping action: ScheduleReboot (condition is false)
Action ended 15:36:07: INSTALL. Return value 1.
MSI (s) (F0:DC) [15:36:07:414]: MainEngineThread is returning 0
MSI (s) (F0:64) [15:36:07:414]: Destroying RemoteAPI object.
MSI (s) (F0:10) [15:36:07:414]: Custom Action Manager thread ending.
MSI (c) (10:88) [15:36:07:414]: Back from server. Return value: 0
MSI (c) (10:88) [15:36:07:414]: Decrementing counter to disable shutdown. If counter >= 0, shutdown will be denied. Counter after decrement: -1
MSI (c) (10:88) [15:36:07:414]: PROPERTY CHANGE: Deleting SECONDSEQUENCE property. Its current value is '1'.
Action ended 15:36:07: ExecuteAction. Return value 1.
MSI (c) (10:88) [15:36:07:414]: Doing action: SetupCompleteSuccess
Action start 15:36:07: SetupCompleteSuccess.

Christopher Painter
07-19-2006, 08:19 PM
Checkout the sticky in this forum:


I also talk about this on my blog:




07-20-2006, 03:49 PM
As usual, you are the man! :D I wish I would have read your blog sooner!!! I created a .NET exe application about 6 months ago to call after my install because I was so frustrated with Installscripts limitations (linking to COM specifically).

Thanks for pointing me to this article about the debugger not working:

Thanks for your example on Installsite also. I have about 13 properties I need for my deferred Installscript function. The example worked perfect. One thing, in your header for the example function, you might want to make note that beside making the property custom action and naming it correctly, you also need to schedule it in the the Install Execute sequence. I know this is probably implied, but it had me stuck for a few minutes.

Thanks again.

Christopher Painter
07-20-2006, 04:41 PM
Glad to help.

Sorry for the scheduling to be implied. Only public properties get passed from the UI to the Execute sequence so yes, it would have to be set in the execute sequence.

Thanks for the feedback. I'll ask Stefan to update the comments.

Christopher Painter
07-20-2006, 04:57 PM
And if InstallShield is reading this.... I wish a function like this was built into InstallScript and that the CA Editor had an attribute for setting properties to be serialized into CustomActionData. This was InstallShield would completly handle creating the Type 51 CA for you.

08-11-2006, 02:09 PM
Amen Christopher!

InstallShield is in a position to offer added value over the functionality contained in MSI. For example, MSI has no mechanism by which a user can identify a large group of files that need to be included in an install. InstallShield provides the Dynamic File Linking feature to address this deficency and it is one of the places where InstallShield clearly has greater value than MSI. (It is unfortunate that the current design of Dynamic File Linking feature does not support patching, other than quick patch, but that is very fixable... the feature grows in value as more and more products contain thousands of files over directory trees of hundreds of directories, far more than one can manually manage).

Providing a clean and easy mechanism for providing context information to non-Immediate custom actions would be a fabulous feature. Without it, I'm finding myself now having to implement serialization/de-serialization code in my setups, though Christopher's contribution helps a great deal!

Christopher Painter
08-11-2006, 02:26 PM
There is also the Component Wizard that will look at a directory structure and compentize it. Once thats done it's the same as creating the components yourself and it'll support patching.

I think non-immeadiate custom actions should have a CustomActionData attribute that allows you to type the values to be serialized. It should then automatically create a hidden CA that is sequenced correctly and a built in InstallScript function similar to the Installer Class Context for deserializing the data.

But I don't work for Macrovision so until then I'll just use the little function I whipped up.

08-16-2006, 05:57 PM
I have found good success using the technique described by Christopher, though with a couple of small changes:

1 - I check both the preceeding " /" AND trailing "=" to find a given attribute. This avoids problems of name subsetting.

2 - I found the start index was one too small and thus capturing the "=" in the value returned for the requested attribute.

I am appending my small InstallScript function for reference.

By the way, while I was able to get our main installer working with InstallShield 12 using this technique, I had to drop our migration to IS 12 until a future release because a couple of secondary installers that I have not previously overhauled (I did totally rewrite our main installer less than a year ago) need to be rewritten for IS 12.

Finally, what is the limit on the length of CustomActionData? I fear the day when the amount of deferred context grows too large for this technique. This is very necessary in our installer as our need to support both Initial installations and Modify installations, along with patching via Quick Patch, forces us into using Custom Actions for most "configuration" tasks... Registry Settings, Services Creation/Deletion Stop/Start, IIS Virtual Directory Add/Delete. While the functionality for these areas inherent in InstallShield may work for initial installations, I found they are totally inadequate for Modify installs and patching.


// Function: GetDeferredContext (DEFERRED EXECUTION)
// Purpose: Gets value from the deferred context packed into CustomActionData
function string GetDeferredContext(hMSI, attributePrefix)
STRING attributeData;
STRING buffer;
NUMBER length;
NUMBER lengthAttributePrefix;
NUMBER result;
NUMBER size;
NUMBER start;
NUMBER theEnd;
attributePrefix = " /" + attributePrefix + "=";
lengthAttributePrefix = StrLength(attributePrefix);
result = MsiGetProperty(hMSI, "CustomActionData", buffer, size);
start = StrFindEx(buffer, attributePrefix, 0) + lengthAttributePrefix;

if start >= 0 then
theEnd = StrFindEx(buffer, " /", start);
if theEnd < 0 then
length = StrLength(buffer) - start;
length = theEnd - start;

StrSub(attributeData, buffer, start, length);
return attributeData;

Christopher Painter
08-16-2006, 06:13 PM
Ah, yes, subsetting would be a problem.

Sorry, I wrote that in about 5 minutes and posted it.

BTW, you don't like hungarian notation? :)

AFAIK I don't think I've ever seen a document that specifies the limit of an MSI Property. Worse case you'll have to split your CA's up.

08-16-2006, 08:35 PM
The only hard and fast limit I would expect would be the maximum size of a DWORD.

Other than that, the only other limitation I could imagine may be the size of MAX_PATH; however, I can't imagine that being the case.

Christopher Painter
08-16-2006, 08:54 PM
I have seen some wierd bugs where I was reading multivalued registry data into a public property as part of an AppSearch implementation and when it passed the property to the Execute sequence everything went to hell in a hand basket.

I never established the exact data conditions that caused that problem though.

08-31-2006, 08:06 AM
I assume this CustomActionData mechanism for accessing parameters through installscript is the only way?

I'm seeing future problems with this. With old style installscript (11.5 and previous) we had IDriver. Yes, it was buggy. Yes, it made nasty installation errors appear. Ok, so it wasn't perfect.

I know the reliability <-> Performance sacrifice is a balance, and on the whole I would opt for reliability in an installer over performance. However, I don't think our customers would appreciate nasty slow installers.

For installscript heavy installations, there are going to be a lot of custom actions. The only way I see is to conform to a standard installer, and not do anything whizzy. This isn't going to happen, as installers always need to be extended.

To call a custom action, requiring just one extra parameter I have to:

1) Set this property using a type 51 - Set Property, setting a property with the same name as my internal Installscript method.
2) I have to create a wrapper function that has the export prototype function, passing only the single HWND parameter (which must be followed, or the function cannot be called).
3) In this wrapper function, I have to call my internal installscript function. This wrapper doen't actually do anything, and is only like an interface to my private function.
4) My Private function has to use MSIGetProperty to get the CustomAction property data that holds my single parameter.
5) Finally..! I can use my single parameter.

Is anyone noticing a hell of a lot of stuff just to do something simple - i.e. pass a single parameter??

Now, this isn't the only thing. Now for every custom action we have this performance hit of ~2 seconds (according to Chris Painter). As installers inevitably bloat over time (as bugs are fixed, new features added etc) - then the number of these custom actions is gonna bloat too.

In the very near future, there's gonna be a nasty hacked installer, that runs like a lame 3 legged-dog.

What are the ways to avoid these problems/this way of creating the installers?

Chris mentioned ways of mitigating the performance issues on his blog. Can anyone add to this/educate me?

Cheers :)

08-31-2006, 09:38 AM
The two biggest things you can do are to be careful about where you call your functions, and to be careful about how many functions you call as custom actions.

These are both really the same thing. The overhead is in starting the engine instances for each custom action invocation. By calling functions from either events (no HWND parameter), or from other functions, they use the engine that the calling code was using. So, when possible, try to collapse your entry points to only a few custom actions. If you have five functions you want to call during the execute sequence, and you always want to call all of them, you can make one custom action function (HWND hMSI), and have it in turn call all of those other functions.

As for what you described, where you have to create an extra wrapper function, I don't think that's correct. If you were calling your function as a custom action before, while its initialization time was shorter with IDriver, the single parameter interface was the same. If instead you're calling it from another function, then the interface can be whatever you want, and you just have to make sure its caller has access to all the information it needs to pass in.

However, if you had a function called as a custom action which used to simply do MsiGetProperty on an arbitrary property, chances are you need to replace that call with the CustomActionData workaround. But you can do that in the existing function; you don't need to split it up.

09-01-2006, 02:15 AM
Thanks Michael for your prompt reply :)

For clarification:

My previous installations were Installscript-MSI projects. These relied heavily on Installscript (events and functions) for overridden behaviour at various parts of the installation.

As I did not previously use any custom actions, all calls were via Installscript, and hence, did not have any restrictions on the amount of parameters (several calls had 4 or 5 parameters).

The 'wrapper' function to which I was referring meant the interface/entry point function (export prototype DoSomething(HWND)) that the custom action calls.

So to translate my pre-12 projects to version 12, I will need to create an
entry point function for every one of my private functions that I need.

Thanks for the suggestion - If I have a bunch of functions that need to run one after the other, I could use a single entry point, which in turn calls all my other functions one after the other. I obviously need to set properties for each and every parameter I wish to use in these private functions because of the global data restriction introduced with 12.

Is my understanding correct?

09-01-2006, 10:23 AM
Yes, sounds correct to me. The part that isn't clear to me is you seem to have had these called from other functions before, so they shouldn't need to become entry points themselves. What might be giving you trouble is if they use MsiGetProperty in various places throughout their code, that's what will fail in deferred actions in 12.

Chris Painter has offered a couple helper functions on his blog to serialize and deserialize several properties across a single CustomActionData transfer point, so you can pass all the parameters you need into the entry point function. Then it's just a matter of getting the data to the functions that currently call MsiGetProperty. If you can either turn those into parameters, or global variables (which aren't shared across separate custom action invocations, but should be shared within one), it should work. Or does MSI let you set properties within a deferred action for later use in that same deferred action?

09-04-2006, 02:24 AM
My functions used to be Installscript events (such as OnBegin, OnMaintFirstUI etc). AFAIK, some of these installscript event functions have been removed in version 12, due to the architecture changes. So, I will need to create the new entry points for them, and get the data there for them.

>Or does MSI let you set properties within a deferred action for later use in >that same deferred action?

I've tried it using a type 51 (property set) so that my data is set in a property with the same name as my entrypoint function. I can then call my entry point function (which in turn, calls my private function that requires the data). The entry point function was set as a deferred Installscript function (type 65536). Inside the entry point I used MsiGetProperty to get the CustomActionData, which I then used to pass to my private function. This seemed to work ok.

09-05-2006, 10:11 AM
That all sounds correct - both the changes, and the use of CustomActionData. Don't worry about the rest of my comment, as I think I created an overly complex scenario in my head: I tried to address multiple layers of functions with inner ones using MsiGetProperty. I'll set that aside.