Don’t use vbscript/jscript to write your custom actions!

Rob Mensching wrote a blog entry a while back that explains some reasons why you should not use script-based custom actions in your setup.  I encourage you all to read it if you haven’t yet, and I also strongly encourage you to heed his recommendations if at all possible.

I can personally relate to one of his explanations as well.  Reason #3 on his list talks about anti-virus programs.  When we shipped Visual Studio .NET 2002 we started getting reports of seemingly random failures during setup from our product support team.  After some detailed analysis we found that many of these failures were being caused by overly aggressive anti-virus programs that were blocking scripts from running even as part of custom actions during VS setup.  We scrubbed our custom actions before shipping Visual Studio .NET 2003 to re-write or eliminate the script-based custom actions to avoid these failures, and we were able to eliminate a fairly high support call generator.


Comments (16)

  1. Steven Bone says:

    Hi Aaron, I actually published a rebuttal to Rob’s post a while ago (link below). Statements saying "don’t" and "never" regarding the use of a language or platform capability are almost always a bit overreaching.

    This sort of reminds me of the C/C++ "goto" debates…

    In any case, my rebuttal can be found here:

  2. Thanks for the link! Your blog entry has a lot of good points. I wasn’t necessarily saying to "never" use script-based custom actions. The main reason I would strongly discourage them is that the times I have seen them used, script was chosen because it was faster and easier to implement. There definitely wasn’t much (if any) thought put into the overall robustness or design of the CA or the setup itself.

    The most important point you make is that you can create a robust, well-designed, debuggable custom action in script like you can for any other language just like you can create a fragile, poorly designed custom action in C/C++.

    Also, that is a good point about script custom actions being easily reverse engineered. This is a double-edged sword. CA’s are generally a black box and being able to view the script helps with that. But if there is some kind of proprietary action being taken then you cannot obfuscate it. The script code even ends up written to a verbose MSI log if you have logging enabled…

  3. Robert Fu says:

    I am changing the topic a bit, but I’d like to extend on a point Steve made in his blog regarding using "MSI Standards". I would go further and say: "Always try to stay with MSI Standard actions and avoid CAs when possible."
    <br>What I’ve noticed with my interactions with countless setup developers is that CA’s are way over used. CA’s are often used because the setup developers are not familiar with the Windows Installer technology and the perception among developers are that it’s easy to just write some code to solve setup issues. As any experience setup developer will tell you, most setup failures are due to poorly written CAs and not standard Windows Installer actions. Using native Windows Installer functionalities generally will take care of issues such as ref-count, uninstall, rollback, and many other pitfalls that can create major setup issues. Just remember that a setup failure is mostly likely worst than any bug you can come up with in your software, so put some effort into making a robust setup and stick with standard actions.

  4. Peter Evans says:

    If Script Actions are a "Never Use" feature then why was support for Scripting host added to the MSI installer in the first place.

    Are you really stating that Script Custom Actions a feature that should be deprecated in MSI?

  5. My personal opinion is that script-based custom actions should not be used, but I am just one person. If I was writing my own setup I would choose to use DLL-based custom actions if I had to use custom actions at all.

    As Steven points out in his blog there are ways to make script-based custom actions reasonably robust, which eliminates some of my concern. I simply haven’t seen enough of that discipline in place on the groups I’ve worked with who use script-based custom actions. Instead they tend to choose script because it is quicker and easier to get something going. They always intend to rewrite their custom actions as DLLs in the future when they have more time but in reality that always ends up at the bottom of the to-do list and the work gets cut.

  6. Steven Bone says:

    Hi Aaron! Since there is a rule at Microsoft regarding DLL based CA’s, and I’m sure you use COM in some of them, and given Larry Osterman’s post at

    How does the MSI engine handle CA DLL’s in this regard? Are the DLL’s run in their own thread (thus making it safe to CoInitialize in it)? Should we spin off a worker thread in the DLL if we need to use COM as Larry suggests? What do your CA’s look like in this regard? The documentation is a bit fuzzy in this area, and a trial and error approach may rely on an assumption that is subject to change, ala the Pragmatic Programmer’s "Programming by Coincidence" topic.

    Perhaps this could be the subject of another blog topic rather than a simple (but probably not so simple) reply here…

  7. Steven,

    I don’t know about the MSI engine, but I can speak to my comment a bit. There is a key caveat in the comment: It depends on the threading model of the caller – if the caller’s already called CoInitialize, then you’re ok calling CoCreateInstance in your DLL. My comment was for the generic case – if you’re writing a DLL that’s going to be used by different clients, you can make no assumptions about the client.

    But a CA isn’t a general purpose DLL, instead it runs under a single application, msiexec. So the restrictions that apply to generic DLLs don’t necessarily apply to CAs

  8. Steven Bone says:

    Thanks for the reply, Larry. Your recent (excellent) concurrency articles and a link back to the old article had me wondering on how the (black box) MSI engine handled things when calling into our CA DLL’s. Typically, I use script actions to hit the "safer" COM API’s (that aren’t ScriptBlocked by AntiVirus apps), and have not had to worry about using COM in a Custom Action DLL.

    I will be writing a MSI DLL that must call CoCreateInstance, thus I need to be sure I can count on the caller of my DLL (MSI engine) to have called CoInitialize on that thread for me. The safest case is to spin off a worker thread as you suggest for generic DLL’s – and I have done that before in the past for generic DLLs.

  9. Hey Steve, sorry for the delay on this. This question rang a bell so I dug into some of the emails that have gone across our internal Windows Installer question-n-answer alias and here is the official answer from the Windows Installer team:

    "One of the first things that a custom action server does when it is created is to call CoInitializeEx(0, COINIT_MULTITHREADED).

    However, the thread on which a DLL custom action is run is different from the main thread (on which COM is initialized) and COM will not be initialized on it. It is up to the custom action to initialize COM to its liking."

  10. Steven Bone says:

    That is fantastic, Aaron – I take it that _every_ Custom Action DLL call is done on a different thread than the main (and a different thread from the "Custom Action Server". From a terminology standpoint, I am not sure what a "Custom Action Server" actually is (other than seeing the PID of it in the logs when it is spun up – one seems to be created impersonated and one elevated based on install context of immediate vs. deferred/rollback). My guess is that the "Custom Action Server" is the plumbing that contains the code that glues calls made using HINSTALL to the actual MSI engine. I wish some of these interesting tidbits made it into MSDN, though.

  11. Hey Steve – these are really good questions. I went ahead and posted a new entry on my main blog page with some more info about custom actions and how they are managed by Windows Installer (I wanted to put it on my main page to make it more discoverable to others as well).

    You can find this new post at, please let me know if you find it useful or not and/or if you have any follow up questions….

  12. Ok, so there will be exceptions, but it seems like more then 90% of the time I see someone doing a CA they could have really done it with standard actions.   For example today I saw this blog entry:

    Here is some DotNet "junkie" who probably doesn’t know a thing about Windows Installer other then he just found the InstallClass and Visuall Studio deployment projects.   Perhaps someone should tell him about WriteEnvironmentStrings ( ).

    Seriously I have a point here somewhere… We need to get better use case support out of the WindowsInstaller so we can write fewer and fewer CA’s instead of arguing over what language they should be written in.

  13. aogilmor says:

    I agree with Christopher.  I’ve seen Custom Actions with regasm.exe in them when MSI provides for registering assemblies, no need for a CA to do that.  

  14. Hi Aogilmor – I’m with you on this.  There is no reason to need to use a custom action to accomplish something that can be done with Windows Installer standard actions.  You can use tools like Tallow (in WiX v2.0) or Heat (in WiX v3.0) to harvest registration information from DLLs in order to eliminate the need for self-registration or regasm.exe custom actions in an MSI.

Skip to main content