ISWIX, LLC View Christopher Painter's profile on LinkedIn profile for Christopher Painter at Stack Overflow, Q&A for professional and enthusiast programmers

March 31, 2005

Custom Actions

I've been giving thoughts on Custom Actions lately and Nates feedback has prompted me to start publishing them. I'll probably keep updating this entry as my thoughts change.

First of all, I'm sure we all try to avoid custom actions in the first place! It is often best to handle a problem using native MSI standard actions for several reasons which I won't name here. But we all probably also know by now sometimes CA's are needed. So what is my list of requirements when choosing a solution for a CA?

1) Capable: The CA must be able to get the job done. It needs to be able to call Win32 API and COM. Possibly it needs to be able to call .NET services also, although at this point I havn't needed to. Some on the web have argued that alot of the .NET services are just wrappers of existing COM and Win32 interfaces, but I really don't know yet. It also must be able to access the MSI handle for doing things like getting/setting properties and posting messages back to MSI logs and subscribers.

2) Reliable: The implementation needs to have as few points of failure as possible. The less moving parts and dependencies the better.

3) Debugable: When things aren't working you MUST be able to step through line by line and get meaningful data to aid in troubleshooting. You'll pull your hair out if you can't.

4) Ease of Development: This is a subjective requirement. The language used by the CA should be easy to program in. We shouldn't be loads of time developing CAs. This is one reason I love InstallScript over C++. Unfortunatly InstallScript is failing requirement #2 and hence C++ is beggining to trump InstallScript in my mind.

5) Supportable: You won't always be working at Acme Software House. What languages are well understood by your fellow developers who will be stuck with your project when you leave?

6) Compiled: What goes in a CA, stays in a CA! This is important in terms of protecting source. It also aids in requirement #2 since a truely compiled language doesn't require a runtime intepreter.

7) Integrated: Ideally the CA's source would be integrated into the MSI development IDE. This is one thing I really like about InstallScript. It's very easy to author, compile, build and debug the CA from one IDE. Unfortunatly refer to rule #2.


Obviously runtime requirements (gets the job done reliably ) are more important then design time requirements ( easy for the developer) . So where does this leave me? Seriously considering dumping InstallScript and going to a language that can compile to a Native Win32 DLL ( not COM ). Unfortunatly where I work there aren't many C++ programmers and a whole lot of VB programmers. So I'm also considering using the DLL as a wrapper to register and invoke a VB COM object where the real work gets done. But this creates more moving parts and starts to violate rule #2. There are some programs that claim to be able to create true DLL's out of VB6 projects. This also looks very tempting to me. VBAdvance, VBExport and VisualDLL seem promising, but in truth all of this is a hack to get around doing C++ CA's.

6 comments:

Christopher Painter said...

I've started playing with vbAdvance, and it does work. I compile my DLL with exported functions and then build by MSI to pick up the DLL and load it into the binary table.

But I see two problems so far...

1) When I start the debugger in VB and call my function from MSI the debugger doesn't start stepping through the code. I read a thread over on the vbAdvance forum site and they suggested exporting the DllRegisterServer functions and making a wrapper function that calls an ActiveX method within your DLL if you want to use the debugger. I suppose this would be acceptable, I havn't thought it through yet.

2) I can get something like this to work:

Sub Test()
MsgBox "Hello"
End Sub

but if I try to pass a string argument and use it the VB code blows up. I read another thread that suggests this is due to differences in CSTR vs BSTR. Ugh, this kind of stuff is the exact reason why I wanted to get away from C++ in the first place!!

Still in practice I'll only be passing an MSI Handle to my exported function then calling my COM passing the handle. Then I should be in a pure VB environment with debugging capabilities.

I see promise here... :)

Christopher Painter said...

Nate,

I'm not the sole source of opinion of knowledge around here, your insites are always welcome.

You did lose me on the argue against #1 though. I think compiling / obfuscating was covered in #6.

I can see how C++ CA's are a good choice, it just irritates me that MSI isn't more flexible. I wish you could import an ActiveX DLL into the binary table and call its methods directly as CAs without going through VB/J script CAs.

Unknown said...

Whew... ActiveX DLLs as custom actions are definitely not a good idea. We should actually register the ActiveX DLLs before using them. So they at best be as deferred custom actions. And since they cannot log the actions they perform or have access to MSI properties, they are at best 3rd class citizens as far as MSI is concerned. I would suggest that we come up with a list of common tasks that are used for custom actions and we could have C++ DLLs made so that others MSI developers could use them. There is such an initiative under the WIX Toolset. Others could just hook in and use the custom actions even if you are yet to appreciate the WIX Toolset or if you are not able to adopt it for some reason. And C++ custom actions are not that difficult. I am not C++ programmer but I was able to come up with a custom action for manipulating XML files in less than 2 hours.

http://www.geekswithblogs.net/vagmi.mudumbai/archive/2005/03/28/27473.aspx

-Vagmi

Christopher Painter said...

When I was talking about ActiveX, I was saying I wish there was a way of invoking an ActiveX DLL right out of the binary table ( or SUPPORTDIR ) without having a registration footprint. Or to have the registration footprint be taken care of by MSI outside of the InstallInitialize...InstallFinalize so that the code can be called in Immediate Execute CA's.

Until this occurs I have to use a bootstrapper to do it the way everyone else does it: Bootstrap a setup prereq. MSI's rollback capabilities are all fine, but ignore the fact that you sometimes must install things before you can install things.

Eitherway that's the reason I was looking into VBAdvance. To come up with a way of calling VBCode without having to go through COM.

Anonymous said...

We are calling .Net code that is ComVisible and using ISScript to bootstrap it and it is working very well for us. It is allowing us to tackle issues that we left to documentation before. Stuff like creating MSMQ queues, smartly adding new probing paths to config files, etc.

Anonymous said...

We are calling .Net code that is ComVisible and using ISScript to bootstrap it and it is working very well for us. It is allowing us to tackle issues that we left to documentation before. Stuff like creating MSMQ queues, smartly adding new probing paths to config files, etc.