.Net API for Windows Installer

308 views
Skip to first unread message

Rick Foster

unread,
Aug 22, 2002, 10:31:58 AM8/22/02
to
Has anyone heard word about a WIS assembly? I'm hankering to write the next
batch of tools and would like to do so in C#.

I tried to port my own version. Very humbling... :)


Phil Wilson

unread,
Aug 22, 2002, 2:29:17 PM8/22/02
to
I've used PInvoke fairly successfully to call the Msi functions from a C# client program, but I've
not heard anythiung about .Net runtime classes to supply the same fuctionazlity.

This is the C# class I used:

public class CallMsi
{
[DllImport("msi")]
public static extern int MsiOpenDatabase (string dbpath, string persist, ref IntPtr msihandle);
[DllImport("msi")]
public static extern int MsiDatabaseOpenView(IntPtr handle, string query, ref IntPtr viewhandle);
[DllImport("msi")]
public static extern int MsiViewExecute (IntPtr viewhandle, IntPtr recordhandle);
[DllImport("msi")]
public static extern int MsiViewFetch (IntPtr viewhandle, ref IntPtr recordhandle);
[DllImport("msi", CharSet=CharSet.Auto)]
public static extern int MsiRecordGetString (IntPtr recordhandle, int recno,
string szbuff,
ref int len);
[DllImport("msi")]
public static extern int MsiCloseHandle (IntPtr handle);
[DllImport("msi")]
public static extern int MsiViewClose (IntPtr viewhandle);
}

and this is class I'm working with to perform queries on a database, no error coding.

public class GetMsiData : IDisposable
{
string thepath = null;
IntPtr dbhandle = IntPtr.Zero;
public GetMsiData (string package)
{
thepath = package;
int nres = CallMsi.MsiOpenDatabase (thepath, null, ref dbhandle);
}
~GetMsiData ()
{
InternalDispose (false);
}
public string DoQuery(string query)
{
IntPtr viewhandle = IntPtr.Zero;
IntPtr nothing = IntPtr.Zero;
int nres = CallMsi.MsiDatabaseOpenView (dbhandle, query, ref viewhandle);
nres = CallMsi.MsiViewExecute (viewhandle, nothing);
IntPtr rechandle = IntPtr.Zero;
nres = CallMsi.MsiViewFetch (viewhandle, ref rechandle);
if (0!=nres) // No data
return null;
string outbuff = new String (' ', 255);
int outlen = 255;
nres = CallMsi.MsiRecordGetString (rechandle, 1, outbuff, ref outlen);
nres = CallMsi.MsiViewClose (viewhandle);
if (0!=nres) // No data
return null;
return outbuff.Substring(0, outlen); // trim to actual length
}
public void InternalDispose(bool disposing)
{
CallMsi.MsiCloseHandle (dbhandle);
dbhandle = IntPtr.Zero;
}
public void Dispose()
{
InternalDispose (true);
GC.SuppressFinalize(this);
}

}
"Rick Foster" <_Rick....@Autodesk.com> wrote in message news:u8UGLieSCHA.2792@tkmsftngp09...

Rick Foster

unread,
Aug 27, 2002, 11:35:22 AM8/27/02
to
Thanks for the source, Phil, I like your results better than mine.

Looks like we'll have to roll our own for a while!
Rick


"Phil Wilson" <phil....@unisys.spamcom> wrote in message
news:#gWmxmgSCHA.1976@tkmsftngp11...

sith

unread,
Aug 28, 2002, 4:52:05 AM8/28/02
to
is there another way of using msi.dll ? This is a com library, isn't it ? I
used it from c++ and it was pretty straight forward.
in c# though, it is not so easy, i don't even know how to instantiate an
installer object. Actually threse is no object class to instantiate, there
are only a bunch of interfaces and a couple of enums.

can any one help ?

sith


"Rick Foster" <_Rick....@Autodesk.com> wrote in message
news:u8UGLieSCHA.2792@tkmsftngp09...

Andreas Magnusson

unread,
Aug 28, 2002, 7:39:59 AM8/28/02
to
It is documented in the Platform SDK, this is the link on MSDN Library:
http://msdn.microsoft.com/library/en-us/msi/setup/using_the_automation_inter
face.asp

"sith" <ako...@poczta.wp.pl> wrote in message
news:#MvBOBnTCHA.3924@tkmsftngp12...

sith

unread,
Aug 28, 2002, 9:03:34 AM8/28/02
to
thanks for advice, but _I know_ how to use it from VB, c++. I want to use it
from c# which I am kinda new to.
I suppose it should be easy though it ain't.


"Andreas Magnusson" <andreas_c...@hotmail.com> wrote in message
news:uSBICeoTCHA.2780@tkmsftngp09...

Phil Wilson

unread,
Aug 29, 2002, 4:27:02 PM8/29/02
to
I don't what the point would be of using C# to call the Msi COM automation interface. The automation
interface was designed for scripters or VB coders who have difficulty calling the Msixxx Win32 APIs.
It isn't particularly difficult to use interop from C# to call the Msi APIs. Using C# to call the
COM automation interface wouldn't necessarily be difficult, except that navigating through
GetTypeFromProgID and the Activator class with C# is far more painful than simply using interop to
call the Msi functions.

"sith" <ako...@poczta.wp.pl> wrote in message news:euCnwNpTCHA.3588@tkmsftngp08...

sith

unread,
Aug 30, 2002, 4:37:06 AM8/30/02
to
What you trying to say is that going thru a list of at least couple of
hundreds functions every single one starting with Msixxx is more prospective
than using com automation interface ? You gotta be a hardcore
win32-api-programer than. I myself prefer using object/com libraries and
foundation classes. Well, I think that we live in the house of com, it is
everywhere; so why don't use it ? methods grouped in interfaces (objects)
are far more appealing than api fuctions.

The issue I raised was not how to use msi or how to use msi api but _how to
use msi automation interface from c#_. Loading library and calling its
functions is a trivial task even for a begginer. Is was com automation that
i wanted to use from c# (actually i am porting some stuff from c++)

GetTypeFromProgID doesn't work as it needs a CoClass which is not defined in
msi.dll; as a consequence you can not call Activator.CreateInstance.

I finally figured out how it's done. I needed to create a wrapper class for
WindowsInstaller.Installer interface and from then on it was as simple as it
was from c++.

"Phil Wilson" <phil....@unisys.spamcom> wrote in message

news:uuBuMp5TCHA.3740@tkmsftngp08...

Phil Wilson

unread,
Aug 30, 2002, 1:06:22 PM8/30/02
to
Type.GetTypeFromProgID takes (as the name implies) a ProgID, as in:

Type comType=Type.GetTypeFromProgID(ProgID);
object comObject=Activator.CreateInstance(comType);

then:

comType.InvokeMember (string methodname, bindingflags etc..........)

so I assume you're doing something similar with the Windows Installer ProgID.

It's not that I'm a hardcore Win32 programmer, but I do prefer type safety, and you don't get it
with late-binding automation interfaces. You make a parameter mistake or spell a method name wrong
you find out at run time, not compile time.

COM might be everywhere, but it's pretty much finished as a tool for new development as you can see
from .Net.


"sith" <ako...@poczta.wp.pl> wrote in message news:eLsQOCAUCHA.3964@tkmsftngp12...

sith

unread,
Sep 2, 2002, 4:11:10 AM9/2/02
to
Hi !

I have tried the approach you show below, with no effect whatsoever.
The problem was that msi.dll defined interfaces only. There was no coclass
from which you could create an object.
WindowsInstaller.Installer progID ( which i passed to
Type.GetTypeFromProgID ) was in fact an Installer interface ID so executin
the sequence below resulted in an error message stating that you could not
create an instance of an interface directly. The reason for this is probably
that Type returne by GetTypeFromProgID was an interface type.

After having writen a wrapper class which was pretty much as that:

[ComImport, Guid("000c1090-0000-0000-c000-000000000046")]

class WindowsInstallerClass

{

}

the usage was even more simpler

WindowsInstaller.Installer msi = null;
msi = new WindowsInstallerClass() as WindowsInstaller.Installer;


WindowsInstaller.Database db =
OpenDatabase"PATH_TO_DB",WindowsInstaller.MsiOpenDatabaseMode.msiOpenDatabas
eModeReadOnly);

You made the point, though, .net is not com oriented; does that make ME a
hard-core COM progremmer ? ;-)

cheers,

sith

"Phil Wilson" <phil....@unisys.spamcom> wrote in message

news:#xmXudEUCHA.3072@tkmsftngp13...

Reply all
Reply to author
Forward
0 new messages