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

August 16, 2006

More Fun with CoCreateObjectDotNet

Emetrue recently asked on InstallShield Community how to pass managed datatypes rather than primitive strings to managed code custom actions using CoCreateObjectDotNet. The trick is to exploit Interop to create a COM representation of the .Net object then pass it back to the managed code environment.

Try this:

InstallScript:

function MyFunction(hMSI)

OBJECT colors;
OBJECT customaction;

begin

set colors = CoCreateObject( "System.Collections.ArrayList");

colors.Add( "RED" );
colors.Add( "WHITE" );
colors.Add( "BLUE" );

set myObject = CoCreateObjectDotNet("C:\\customaction.dll", "customaction.MyClass" );
customaction.MyMethod( colors);

end;

C#

using System;
using System.Runtime.InteropServices;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Forms;

namespace customaction
{
public class MyClass
{
public void MyMethod( ArrayList colors )
{
foreach( string color in colors )
{
MessageBox.Show( color );
}
}
}
}

7 comments:

John Reynolds said...

should the last line of the InstallScript read myObject.MyMethod(colors)? (not customaction.MyMethod)

Not trying to nitpick -- really have no idea what I'm doing and fabulously grateful to have found some code examples that might help me figure out how to call into my C# dlls. :)

John Reynolds said...

or rather it should read "customaction =" since that's the name given to the object.

I was able to get this working with the hardcoded location. (THANKS SO MUCH!)

Now I'm trying to figure out why this fails:
set myObject = CoCreateObjectDotNet(INSTALLDIR ^ "InstallerPlayground.dll", "InstallerPlayground.Class1" );

when this works:
set myObject = CoCreateObjectDotNet("c:\\InstallerPlayground.dll", "InstallerPlayground.Class1" );

It's set to deferred synchronous execution and runs after a different exe custom action that is also a file that is in the INSTALLDIR. Is this not the appropriate way to reference the INSTALLDIR from InstallScript?

Christopher Painter said...

The macros ( SUPPORTDIR, INSTALLDIR ) are no longer supported in IS12 due to the proper isolation of the installer handle. Instead use MsiGetProperty to pull the MSI Directory Property INSTALLDIR into a local variable and use it to build the first string argument to CoCreateObjectDotnet().

5to9 - Admin said...

Thanks for this article.

Just for information, with Visual Studio 2005 and InstallShield 2008, I need to add the attribute : " [ComVisible(true)]" to the class "MyClass". Without this attribute, it doesn't work.

[ComVisible(true)]
public class MyClass
{
....

Christopher Painter said...

That is correct. In my other blog posting I talk about the class has to have a ComVisible attribute. In VisualStudio 2003 projects this is automatically done in the AssemblyInfo.cs but in Visual Studio 2005 it is not. I typically add that attribute to my AssemblyInfo.cs file so that all of my classes are exposed.

Unknown said...

Sorry for the novice question, but does the dll have to be one of the installed files? If not, how do I include it in the setup project? I want to call the custom action from a button click during the setup to get some feature class names from an ArcGIS SDE server to populate a dropdown. I've spent a couple of days looking for some decent instructions on this sort of custom action and I've come up empty. Thanks.

Christopher Painter said...

I no longer suggest using CoCreateObject or CoCreateObjectDotNet because of CLR sticky issues.

Read Jason Ginchereau's Blog and more recent blog posts here for a description of DTF.

In DTF, you can use link additional assemblies into your DLL and they will be available at runtime. Alternatively, you could also use reflection to call them from just about anywhere you could want including [SUPPORTDIR] ( Setup Files ).

In short, DTF Rocks.