.NET Forum / .NET Framework / Interop / January 2005
RegisterAssembly does not add typelib entry in registry
|
|
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 MagazinesGet 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 ...
|
|
|