Adventures of a Setup Developer

my musings about setups and other things in my life

  Home  |   Contact  |   Syndication    |   Login
  30 Posts | 1 Stories | 11 Comments | 73 Trackbacks

News

Article Categories

Archives

Post Categories

General

Regular Reads

Tools

With the new .NET framework API providing such wonderful functionality, everybody is programming in C# or VB.NET except setup developers. There was quite some commotion in the WIX users list on writing managed custom actions. It was finally decided that it is generally harmful to write managed code custom actions as they would depend on .NET Framework and having dependencies for setup is plain bad design. But IMHO, we can use managed code custom actions if we are 100% sure that it would be present on the target platform or if it is a prerequisite and a part of your LaunchConditions.
 
Writing managed code custom actions can be tricky. The easiest way is to implement the System.Configuration.Install.Installer class. This Installer class has four methods which can be executed during the MSI thread. The Installer class was only meant for developers to do some dirty stuff. But it is far from perfect to be used as a part of the MSI thread. Some of my strong reasons for not using the Installer class are as below.
  • InstallUtilLib.DLL is not completely silent. It still pops up an ugly message box with it fails.
  • The methods do not have access to the MSI thread. So we would not be able to use properties or write to the MSI log file.
We are then left with only one choice. Writing DLLs with C++ which export functions which use managed code. One of the several places that this could be used is handling XML configuration data. Windows Installer supports writing properties to both INI and Registry files but we do not have such facility for XML files. I guess this should be on the TODO list for the next version of Windows Installer <smile/>. But until then we would have to do it ourselves. There is a neat task in NAnt named <xmlpoke>. This custom action is programmed in pretty much the same way without namespace support. Given the path to the XML file and the XPath expression, we would be able to replace the value with our own using the installer. And it should also support properties.
 
I start off by creating a class library project in VS.NET 2003 and start editing the ProjectName.CPP file. Note that you would have to create a Module Definition file to support exporting the DLL functions that you write as symbols. Now for the fun stuff.
 
Lets first write functions that can write to the log file.
 

UINT WriteToLog(MSIHANDLE hMSI, CHAR *strMessage)
{

MSIHANDLE hrec=MsiCreateRecord(1);
MsiRecordSetString(hrec,0, strMessage);
MsiProcessMessage(hMSI,INSTALLMESSAGE_INFO,hrec);
MsiCloseHandle(hrec);
return ERROR_SUCCESS;

}

The above function is a pretty simple function that just writes any string of characters to the log file. While this function is good enough, we will write another function that accepts the managed String type.

void WriteToLog1(MSIHANDLE hMSI, String* strMessage)
{

CHAR* strMessageArr={0};
strMessageArr=(char*)(void
*)Marshal::StringToHGlobalAnsi(strMessage);
WriteToLog(hMSI,strMessageArr);

}

This functions accepts the managed String* and Marshals it to a character array and then writes it to the log file. Now that we have the logging functions ready, lets concentrate on the actual juice. The idea here is that we use a deferred custom action to poke values into the XML file. The custom action expects data in a '|' delimited list in the following format.

[XMLFILEPATH]|[XPATHEXPRESSION]|[NEWVALUE]

A immediate type 51 custom action sets the CustomActionData property in the above format.

extern "C" __declspec(dllexport) UINT XmlPoke(MSIHANDLE hMSI)
{

TCHAR propval[261]={0};
DWORD len=261;
MsiGetProperty(hMSI,TEXT("CustomActionData"),propval,&len);
WriteToLog(hMSI,propval);
String *str=new
String(propval);
String *delims="|";
Char delim[]=delims->ToCharArray();
String* split[]=str->Split(delim);
FileInfo* fi=new
FileInfo(split[0]);
if(fi->Exists){

try{

XmlDocument* xdoc=new XmlDocument();
xdoc->Load(split[0]);
XmlNodeList * nl=xdoc->SelectNodes(split[1]);
IEnumerator * nodesenum=nl->GetEnumerator();
WriteToLog1(hMSI,split[1]);
while
(nodesenum->MoveNext())
{

XmlNode* xNode=__try_cast<XmlNode*>(nodesenum->Current);
String* oldValue1=xNode->InnerXml;
WriteToLog1(hMSI,String::Concat(oldValue1, new
String(" was the old value")));
xNode->InnerXml=split[2];
String* NewValue="New Value is ";
WriteToLog1(hMSI,String::Concat(NewValue,split[2]));

}

xdoc->Save(split[0]);

}

catch(Exception* exc){

WriteToLog1(hMSI,exc->Message);

}

}

else

{

WriteToLog(hMSI,"Error: The file does not exist");

}

return ERROR_SUCCESS;

}

The above function reads the CustomActionData property and splits it into an array. I assume that setup developers are good and they always give good data. So I have only gone for rudimentary error handling. The custom action never fails unless the error is catastrophic and is not handled by the try-catch block. It tries find if the XML file exists and if it does, it loads the XML file. I then use the SelectNodes() function to select the list of nodes as per the XPath expression. And finally I set the InnerXml value of the function to the new value. I also log these values as and when required. Thats it... as simple as it seems. 

Dont forget to have the following header files and namespaces on top of the file. You would also have to include reference to System.Xml.Dll.

#include "stdafx.h"
#include "windows.h" //Required by MSI.h
#include "XmlTasksManaged.h"
//The MSI Stuff
#include "Msi.h"

#include "MsiQuery.h"

//.NET Stuff goes here
using namespace System;
using namespace System::IO;
//For the FileInfo Object
using namespace System::Collections; //For the IEnumerator
using namespace System::Xml; //The XML Stuff
using namespace System::Runtime::InteropServices; //For the Marshal object

As mentioned earlier the Namespace support is still not included. It is not a very difficult functionality to add. We can pass it to the CustomActionData property. We can then cut and slice the CustomActionData property as we please. <smile/>

posted on Monday, March 28, 2005 8:55 PM

Feedback

# re: Writing managed code custom actions 4/3/2005 9:28 AM Luke Stevens
Cool. I was trying to do exactly this in C++ with MSXML, driven by tables, but when you already require .NET anyway this idea seems quite a bit better. It would be great if we could make something like this a SCA for WiX.

# re: Writing managed code custom actions 4/6/2005 9:29 AM Balaji
Cool.. a very good idea, will deftly try this out

# re: Writing managed code custom actions 4/18/2005 10:35 PM Jake
Very cool. However, because I am by no means a C++ guru, I could not get this to compile. I created a class library and placed the code you specified in my C++ file but it doesn't seem to work for me.

I get the following errors below. I tried on both VS.NET 2003 and VS.NET 2005 with the same results. Could you provide this sample code as a download?

Thanks

c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\ServProv.h(48): warning C4935: assembly access specifier modified from 'public'

c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\ServProv.h(93): error C2872: 'IServiceProvider' : ambiguous symbol
could be 'c:\Program Files\Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\ServProv.h(48) : System::IServiceProvider IServiceProvider'
or 'Stdafx.cpp(0) : System::IServiceProvider'


# re: Writing managed code custom actions 4/19/2005 12:10 AM Owen Corpening
Could you compare your way with this way for me?

http://weblogs.asp.net/cibrax/archive/2005/04/11/399839.aspx

# re: Writing managed code custom actions 4/19/2005 9:31 AM Vagmi Mudumbai
Jake,

I would not be able to upload the entire project on the site. However, If you send me an email using the 'Contact' link above, I would be more than happy to assist you.


# re: Writing managed code custom actions 4/20/2005 3:51 AM Jake
Thanks, Vagmi.

My email address is jake@codefreaks.net



# re: Writing managed code custom actions 5/17/2005 7:13 AM dan.g
g'day (yes we really do say it in australia!)

the early portion of your posting suggests that this might be related to an issue i'm investigating. i'm trying to figure out how a .NET managed dll can be made to export a pure virtual interface and allow the exe to pass back another pure virtual interface for the dll to manipulate.

the dll loading is all done at runtime by the exe using LoadLibrary and GetProcAddress. i've been googling on and off for a couple of weeks now without finding anything clear enough for my VC6 oriented brain to comprehend. is this worthy of a blog entry?

rgds

dan

# re: Writing managed code custom actions 6/1/2005 9:07 PM Ian Thomas
I'm no purist!
Apart from dependencies on .NET, what stopping a Custom Action DLL being written entirely in a .NET language (C#)? Can that be done with the aid of InstallUtilLib.DLL ?
For some of my applications that need to be delivered for .NET only, then my installer MUST fail if .NET v1.1 isn't there (on fail, then I would run the recently-released “Microsoft Component Installer Software Development Kit [Spring 2005]” found at www.microsoft.com/downloads/details.aspx?FamilyID=cd11be5a-c735-48d9-9cdd-a6d211c2e1c1&displaylang=en), which is a "common-dependencies" thing. Once .NET was in place, other dependencies and more elaborate conditions that are not currently checked by the Windows Installer SDK or WiX might be handled*.

A simple case is the requirement that an NTFS partition is available on the target system (simply knowing that the OS is post-Win2K is not sufficient). That's easy to check using VBScript, and also in .NET.

* - the "right" way is to let the MSI installer know about the situation, and I assume this should be done via an entry in the installer tables (since a "copy" of the MSI is always available, for reference). I'm still trying to understand all this, but - for the NTFS filesystem case - uninstalling is a "don't care" situation wrto NTFS, and there's nothing to restore or clear.

[What's so special about NTFS is that it can make use of Alternate Data Streams for each file, useful for ancillary information that is "tied" to the "main" file and so cannot easily be lost. Think of ADS as large buckets for custom properties.]


# re: Writing managed code custom actions 9/17/2005 3:37 AM Omar Vera
I really find this article amazing and that is what I am trying to do. Could you send me the complete code? I will make a WiX sample and I will post it after it run flawlessly. My mail is omar.vera@jalasoft.com

# re: Writing managed code custom actions 3/9/2006 6:45 AM Christopher Painter
Chris get's inspired by Vagmi and comes up with a simple pattern for managed code CA's.


# re: Writing managed code custom actions 4/29/2006 8:07 AM Christopher Painter
Chris discusses the completly refactored InstallScript implementation in InstallShield 12. Managed Code can now be invoked simply and reliably.

# re: Writing managed code custom actions 7/18/2006 1:46 PM Steve
I was getting the same error as Jake above in ServProv.h, adding the #include <windows.h> after the #include "stdafx.h" fixed the problem.

# re: Writing managed code custom actions 8/17/2006 6:10 PM Maria Koryakina
I keep having the same problem in ServProv.h and adding #include <windows.h> didn't help either... any more ideas?!
Thanks, M

# re: Writing managed code custom actions 10/8/2007 6:07 PM asa
porca troia grassa
pamela trombata
segretaria scopate
vecchia troia puttana
bel culo gratis
figa rossa troia gratis video
video troia mondo
foto bella troietta gratis
scopate di vecchia troia
chat ragazza americane convivenza
sesso tetta grossa mezzo
mature porno star
casalinghe com
piede sborra
foto negra si fanno inculare
sesso viedo gallerie gratis
roma escort transex
ciccione scopano gratis
ingoio cavallo
tettona bionda pompino
migliore trailer web pornografico
nera nuda
gang bang coppia
pompino ingoio porno
sesso pagamento senigallia
foto culo grossi
cartone porno gratis
roma annuncio transex laverr
contatto amico grasso cinquantenne
erotismo dei piedi femminili

# Deployment Tools Foundation - Managed CA's are Greenlighted 5/20/2008 8:43 PM Christopher Painter
First made available to the public in WiX weekly release 3.0.4116.0 (http://wix.sourceforge.net/releases/3.0.4116.0/ ), DTF provides a framework for easily and reliably writing managed code custom actions for the Windows Installer. In a nutshell, it provides a robust set of interop classes to simplify communicating with MSI and a hosting model to abstract the CLR code from the MSI process. At runtime, MSI thinks it’s calling a Win32 DLL in it’s own sandbox but in reality the CLR is being fired up out of process and communicated with through a named pipe. From your managed codes perspective, you are simply communicating with MSI through the interop classes with no need (generally) to be concerned with all of the nightmares of unmanaged code.


Post Feedback

Title:
Name:
Email: (never displayed)
Url:
Comments: 
Please add 8 and 8 and type the answer here: