Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Interop / January 2005

Tip: Looking for answers? Try searching our database.

RegisterAssembly does not add typelib entry in registry

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Boz - 11 Jan 2005 17:49 GMT
I have an assembly which I want to use from COM.  My project's build
has a step to register for COM interop, and if I use COM/OLE Object
Viewer I can see my type library.

But, I want windows installer to do the registering.  So I have put an
Installer class in my project and a customaction in my install kit to
use this class.  I have overidden the Install and Uninstall methods:

   public override void Install(System.Collections.IDictionary
stateSaver)
   {
     base.Install(stateSaver);

     RegistrationServices regsrv = new RegistrationServices();
     if (!regsrv.RegisterAssembly(this.GetType().Assembly,
AssemblyRegistrationFlags.SetCodeBase))
     {
       throw new InstallException("Failed To Register for COM");
     }
   }

   public override void Uninstall(System.Collections.IDictionary
savedState)
   {
     base.Uninstall(savedState);

     RegistrationServices regsrv = new RegistrationServices();
     if (!regsrv.UnregisterAssembly(this.GetType().Assembly))
     {
       throw new InstallException("Failed To Unregister for COM");
     }
   }

However, when I install using my install kit (or test the Install
class using "installutil my.dll"), the type library is not visible in
the OLE/COM Object Viewer.

The help for RegisterAssembly indicates that the TYPELIB registry key
is only added if the /TLB parameter is specified, but there is no way
of specifying that parameter with RegisterAssembly.

So is this a bug, and what should I do?

Do I need the type library information?

James
Charles Oppermann - 12 Jan 2005 02:54 GMT
Not a bug, but a limitation of the RegistrationServices::RegisterAssembly
method.  To create a type library from an Assembly, use the
ConvertAssemblyToTypeLib method of the TypeLibConverter class.  Then add
keys to the typelib portion of the registry to point to the generated file.

>I have an assembly which I want to use from COM.  My project's build
> has a step to register for COM interop, and if I use COM/OLE Object
[quoted text clipped - 42 lines]
>
> James
Boz - 12 Jan 2005 13:41 GMT
Before I embark on writing the code to create the TLB and adding the
appropriate registry keys (which sounds a bit messy actually), do I actually
need the TLB information?

I appreciate this is perhaps a difficult question to answer, but what
questions should I ask myself to decide whether the TYPELIB information is
actually required.

James

> Not a bug, but a limitation of the RegistrationServices::RegisterAssembly
> method.  To create a type library from an Assembly, use the
[quoted text clipped - 47 lines]
> >
> > James
Phil Wilson - 14 Jan 2005 17:28 GMT
It occurs to me that you should be able to a tlbexp on your assembly to
create a typelibrary. You can add this to your setup project, and I think
you'll find it will have a Register property that will cause it to be
registered at install time.
Signature

Phil Wilson
[Microsoft MVP-Windows Installer]
Definitive Guide to Windows Installer
http://apress.com/book/bookDisplay.html?bID=280

> Before I embark on writing the code to create the TLB and adding the
> appropriate registry keys (which sounds a bit messy actually), do I
[quoted text clipped - 59 lines]
>> >
>> > James
Boz - 12 Jan 2005 16:34 GMT
Ok, I've found other newsgroup posts that have used the
ConvertAssemblyToTypeLib, and I've tried to merge that code into my
Installer class (see code below).

When I run the installer (or do installutil.exe my.dll) I get:
 An exception occurred during the Install phase.
 System.UnauthorizedAccessException: Access is denied.

This happens at the point I do the:
 typeLib.SaveAllChanges();

I assume this is because the assembly is currently in use (I am running the
Install method in it after all !!)

So how do I apply the changes from within the Installer class?  This seems
like an awful lot of effort to simply get the installer to reliably do
REGASM on a packaged DLL.

James

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;

namespace Collins.Datalink.Hermes.CommonInterfaces
{
 [ComImport,
 GuidAttribute( "00020406-0000-0000-C000-000000000046" ),
 InterfaceTypeAttribute( ComInterfaceType.InterfaceIsIUnknown ),
 ComVisible( false )]
 public interface UCOMICreateITypeLib
 {
   void CreateTypeInfo();
   void SetName();
   void SetVersion();
   void SetGuid();
   void SetDocString();
   void SetHelpFileName();
   void SetHelpContext();
   void SetLcid();
   void SetLibFlags();
   void SaveAllChanges();
 }

 public class ConversionEventHandler : ITypeLibExporterNotifySink
 {
   public void ReportEvent( ExporterEventKind eventKind, int eventCode,
string eventMsg )
   {
     // Handle the warning event here.
   }

   public Object ResolveRef( Assembly asm )
   {
     // Resolve the reference here and return a correct type library.
     return null;
   }
 }

 /// <summary>
 /// Summary description for Installer.
 /// </summary>
 [RunInstaller(true)]
 public class ClientInterfaceInstaller :
System.Configuration.Install.Installer
 {
   #region Fields and Properties
   /// <summary>
   /// Required designer variable.
   /// </summary>
   private System.ComponentModel.Container components = null;
   #endregion

   [DllImport("oleaut32.dll")]
   private static extern int LoadTypeLib([MarshalAs(UnmanagedType.LPWStr)]
     string libPath, ref IntPtr ITypLib);

   [DllImport("oleaut32.dll")]
   private static extern int RegisterTypeLib(IntPtr ITypLib,
     [MarshalAs(UnmanagedType.LPWStr)] string libPath,
     [MarshalAs(UnmanagedType.LPWStr)] string helpDir);

   [DllImport("oleaut32.dll")]
   private static extern int UnRegisterTypeLib(ref Guid libID, Int16
     wVerMajor, Int16 wVerMinor, int LCID, SYSKIND syskind);

   /// <summary>
   /// Default constructor
   /// </summary>
   public ClientInterfaceInstaller()
   {
     // This call is required by the Designer.
     InitializeComponent();
   }

   /// <summary>
   /// Install method, overridden to allow this assembly to be registered
for COM
   /// </summary>
   /// <param name="stateSaver"></param>
   public override void Install(System.Collections.IDictionary stateSaver)
   {
     base.Install(stateSaver);

     RegistrationServices regsrv = new RegistrationServices();
     if (!regsrv.RegisterAssembly(this.GetType().Assembly,
AssemblyRegistrationFlags.SetCodeBase))
     {
       throw new InstallException("Failed To Register for COM");
     }
     RegisterTypeLibrary();
   }

   /// <summary>
   /// Uninstall method, overridden to allow this assembly to be registered
for COM
   /// </summary>
   /// <param name="savedState"></param>
   public override void Uninstall(System.Collections.IDictionary
savedState)
   {
     base.Uninstall(savedState);

     RegistrationServices regsrv = new RegistrationServices();
     if (!regsrv.UnregisterAssembly(this.GetType().Assembly))
     {
       throw new InstallException("Failed To Unregister for COM");
     }
     UnRegisterTypeLibrary();
   }

   public void RegisterTypeLibrary()
   {
     string assemblyLocation = GetType().Assembly.Location;
     string typeLibLocation = Path.ChangeExtension(assemblyLocation,
".tlb");

     //Convert assembly to type library file
     Assembly asm = Assembly.LoadFrom(assemblyLocation);
     TypeLibConverter converter = new TypeLibConverter();
     ConversionEventHandler eventHandler = new ConversionEventHandler();

     UCOMICreateITypeLib typeLib =
(UCOMICreateITypeLib)converter.ConvertAssemblyToTypeLib(asm,
assemblyLocation, 0, eventHandler);
     typeLib.SaveAllChanges(); // EXCEPTION THROWN HERE

     //Now register the type library
     IntPtr obj = IntPtr.Zero;
     LoadTypeLib(typeLibLocation, ref obj);
     RegisterTypeLib(obj, typeLibLocation, "");
   }

   public void UnRegisterTypeLibrary()
   {
     string assemblyLocation = GetType().Assembly.Location;
     string typeLibLocation = Path.ChangeExtension(assemblyLocation,
".tlb");

     // Call LoadTypeLib function to get ITypeLib interface pointer
     IntPtr obj = IntPtr.Zero;
     LoadTypeLib(typeLibLocation, ref obj);
     System.Runtime.InteropServices.TYPELIBATTR typeLibAttr = new
TYPELIBATTR();

     // Call GetLibAttr method of ITypeLib interface to get pointer to Type
Library attributes
     System.Runtime.InteropServices.UCOMITypeLib myLib =

(System.Runtime.InteropServices.UCOMITypeLib)Marshal.GetTypedObjectForIUnkno
wn(obj, typeof(System.Runtime.InteropServices.UCOMITypeLib));
     myLib.GetLibAttr(out obj);
     typeLibAttr = (TYPELIBATTR)Marshal.PtrToStructure(obj,
typeof(TYPELIBATTR));

     // Call UnRegisterTypeLib to unregister type library
     UnRegisterTypeLib(ref typeLibAttr.guid, typeLibAttr.wMajorVerNum,
typeLibAttr.wMinorVerNum, typeLibAttr.lcid, SYSKIND.SYS_WIN32);
   }

   /// <summary>
   /// Clean up any resources being used.
   /// </summary>
   protected override void Dispose( bool disposing )
   {
     if( disposing )
     {
       if(components != null)
       {
         components.Dispose();
       }
     }
     base.Dispose( disposing );
   }

   #region Component Designer generated code
   /// <summary>
   /// Required method for Designer support - do not modify
   /// the contents of this method with the code editor.
   /// </summary>
   private void InitializeComponent()
   {
     components = new System.ComponentModel.Container();
   }
   #endregion
 }
}
Phil Wilson - 12 Jan 2005 02:59 GMT
Not a bug - if you need a type library, that's what the TypeLibConverter
class is for (I believe that's what regasm does too).

By the way, Visual Studio and Windows Installer will do the equivalent of
RegisterAssembly if you set the Register property on the assembly to a
vsr<something>Register value in the properties for the assembly in the VS
IDE.
Signature

Phil Wilson
[Microsoft MVP-Windows Installer]
Definitive Guide to Windows Installer
http://apress.com/book/bookDisplay.html?bID=280

>I have an assembly which I want to use from COM.  My project's build
> has a step to register for COM interop, and if I use COM/OLE Object
[quoted text clipped - 42 lines]
>
> James
Boz - 12 Jan 2005 12:31 GMT
Thanks for the response.  I'll try the converter.

The reason I chose to use an Installer class is because of the problems
getting the Windows Installer to COM register .NET assembly Project Outputs
in Merge Modules.  It simply didn't work at all for me.  The alternative was
to add a file link to my assembly and COM register that, but then it makes
Merge Modules seem pointless.

I have found many other reports of the same problem in newsgroups and on the
web.  Do a google search for "vsdrpCOM" or "vsdraCOM" through for reference.
Not sure if it is a bug or feature, but it is very frustrating.  The problem
becomes even more difficult when you want the .NET assembly to be put in the
GAC, and be copied to a folder for COM registration.

But for now, the Installer class direction seems a nice tidy solution.
Making each DLL handle its own registration in code seems better than
external registration.

James

> Not a bug - if you need a type library, that's what the TypeLibConverter
> class is for (I believe that's what regasm does too).
[quoted text clipped - 49 lines]
> >
> > James
Phil Wilson - 13 Jan 2005 00:12 GMT
When you say:
> But for now, the Installer class direction seems a nice tidy solution.
> Making each DLL handle its own registration in code seems better than
> external registration.

This is the approach that has been used for years and that MS would prefer
you abandon and use things like vsrdaCOM. There are perhaps some issues with
it, mainly that it doesn't work using project output, but it is the right
thing to. See this:
http://blogs.msdn.com/robmen/archive/2004/04/28/122491.aspx

ans some of the associated comments. It's a shame that Visual Studio setup
projects are pushing people away from the right approach, which is to get
the registration and typelib data in the MSI file so that you don't need to
be running code at install time.
Signature

Phil Wilson
[Microsoft MVP-Windows Installer]

> Thanks for the response.  I'll try the converter.
>
[quoted text clipped - 75 lines]
>> >
>> > James
Boz - 13 Jan 2005 10:30 GMT
Ah, I see.  Well there are more than "some issues".  It simply doesn't work
reliably when you need to COM register a group of .NET assemblies that have
a dependency hierarchy.

So what is the preferred solution when vsrdpCOM doesn't work (i.e. when
trying to COM register a .NET assembly in a project output).

In my scenario (assembly dependency hierarchy), the order of COM
registration is important, but the installer options in Visual Studio don't
let you specify an order.

James

> When you say:
> > But for now, the Installer class direction seems a nice tidy solution.
[quoted text clipped - 90 lines]
> >> >
> >> > James
Phil Wilson - 13 Jan 2005 16:57 GMT
Some inline comments:
Signature

Phil Wilson
[Microsoft MVP-Windows Installer]
Definitive Guide to Windows Installer
http://apress.com/book/bookDisplay.html?bID=280

> Ah, I see.  Well there are more than "some issues".  It simply doesn't
> work
> reliably when you need to COM register a group of .NET assemblies that
> have
> a dependency hierarchy.

> So what is the preferred solution when vsrdpCOM doesn't work (i.e. when
> trying to COM register a .NET assembly in a project output).

[[ The detour seems to be to add the files individually - I think that was
mentioned somewhere in the blog or the link you posted. There also are other
tools for building MSI files. ]]

> In my scenario (assembly dependency hierarchy), the order of COM
> registration is important, but the installer options in Visual Studio
> don't
> let you specify an order.

[[ That's exactly one of the reasons why registration at run time is a "bad
thing". When all the registration is internally in the MSI file,
installation consists of copying the file and adding the registration
entries to the registry - no code runs therefore there is no dependency
order. ]]

> James
>
[quoted text clipped - 109 lines]
>> >> >
>> >> > James
Boz - 13 Jan 2005 17:08 GMT
Thanks for looking into this for me.  In line responses...

> > So what is the preferred solution when vsrdpCOM doesn't work (i.e. when
> > trying to COM register a .NET assembly in a project output).

> [[ The detour seems to be to add the files individually - I think that was
> mentioned somewhere in the blog or the link you posted. There also are other
> tools for building MSI files. ]]

Yes, I didn't appreciate that the tools in Visual Studio .NET 2003 were not
up to the job when we embarked on the install kits.

> > In my scenario (assembly dependency hierarchy), the order of COM
> > registration is important, but the installer options in Visual Studio
> > don't let you specify an order.

> [[ That's exactly one of the reasons why registration at run time is a "bad
> thing". When all the registration is internally in the MSI file,
> installation consists of copying the file and adding the registration
> entries to the registry - no code runs therefore there is no dependency
> order. ]]

Understood.  Will look into this aspect to see if we are suffering from it.

James
Phil Wilson - 14 Jan 2005 17:34 GMT
You need typelibrary information for custom interfaces (all your
interfaces). You don't need one if you use late-binding (like scriptable
interfaces, using Type.InvokeMember) because they're based on the standard
IDispatch interface. Hopefully I've remembered my COM correctly....
Signature

Phil Wilson
[Microsoft MVP-Windows Installer]
Definitive Guide to Windows Installer
http://apress.com/book/bookDisplay.html?bID=280

>I have an assembly which I want to use from COM.  My project's build
> has a step to register for COM interop, and if I use COM/OLE Object
[quoted text clipped - 42 lines]
>
> James

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.