Link to home
Start Free TrialLog in
Avatar of AviationAce
AviationAceFlag for United States of America

asked on

C# DLL exposing managed code to COM

The code below compiles with out error, but I must be missing something simple here.
The idea is to expose .NET managed code to COM so it can be called by MS Access using VBA.

Here is the C# code:
// cd C:\Users\DaUser\Documents\Visual Studio 2010\Projects\Programming\Projects\Google Ops\GoogleOpsCOM\bin\Debug
// RegAssembly.bat
// TableExp.bat

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using RepBusGoogleCalendarV3;

    //[ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("6662492C-F7D9-46E5-8718-837895B4219C")]
    public interface ICalandarMainCOMv30
    {
        string MakeCalandarEventCOMv3(string Title, string Description, string Location,
                                      DateTime dtStartDate, DateTime dtStartTime,
                                      DateTime dtEndDate, DateTime dtEndTime,
                                      bool bLogIt);  //returns EventID string
    }

    namespace GoogleOps
    {
        //[ComVisible(true)]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [Guid("71B8D02D-4A6A-4293-835B-9992978367D1")]
        [ProgId("CalandarMainCOM.iface")]

        public class clsCalandarMainCOMv3 : ICalandarMainCOMv30
        {
            //public string m_fs;
            public CalandarMainV3 m_calMainV3;
            
            //public string fs { get { return m_fs; } }
            
            clsCalandarMainCOMv3()
            {
                m_calMainV3 = new CalandarMainV3();
            }

            public string MakeCalandarEventCOMv3(string Title, string Description, string Location,
                                                 DateTime dtStartDate, DateTime dtStartTime,
                                                 DateTime dtEndDate, DateTime dtEndTime,
                                                 bool bLogIt)  //returns EventID string
            {
                return m_calMainV3.MakeCalandarEvent(Title, Description, Location,
                                                     dtStartDate, dtStartTime, dtEndDate, dtEndTime,
                                                     bLogIt);
            }
        }
    }

Open in new window


User generated image
User generated image
VBA Code:
Dim o as ICalandarMainCOMv30
This will compile in VBA, but there is nothing from the DLL that will work with the NEW keyword

Set o = New clsCalandarMainCOMv3  <--- INVALID USE OF NEW KEYWORD

I hope there is just something simple I am missing when it comes to exporting managed code to COM.
Avatar of louisfr
louisfr

Your constructor is private. Make it public:
public clsCalandarMainCOMv3()
{
        m_calMainV3 = new CalandarMainV3();
}

Open in new window

Avatar of AviationAce

ASKER

That didn't help.  It had no effect on what is seen by MS Access in the object browser.

Also, when I tried to add the constructor to the interface, I got the following compiler error:
'GoogleOps.clsCalandarMainCOMv3' does not implement interface member 'ICalandarMainCOMv30.clsCalandarMainCOMv3()'
Line 33
Column 22

// cd C:\Users\DaUser\Documents\Visual Studio 2010\Projects\Programming\Projects\Google Ops\GoogleOpsCOM\bin\Debug
// RegAssembly.bat
// TableExp.bat

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using RepBusGoogleCalendarV3;

    //[ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("6662492C-F7D9-46E5-8718-837895B4219C")]
    public interface ICalandarMainCOMv30
    {
        //void clsCalandarMainCOMv3();  <---- Causes error: 'GoogleOps.clsCalandarMainCOMv3' does not implement interface member 'ICalandarMainCOMv30.clsCalandarMainCOMv3()'
        
        string MakeCalandarEventCOMv3(string Title, string Description, string Location,
                                      DateTime dtStartDate, DateTime dtStartTime,
                                      DateTime dtEndDate, DateTime dtEndTime,
                                      bool bLogIt);  //returns EventID string
    }

    namespace GoogleOps
    {
        //[ComVisible(true)]
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [Guid("71B8D02D-4A6A-4293-835B-9992978367D1")]
        [ProgId("CalandarMainCOM.iface")]

        public class clsCalandarMainCOMv3 : ICalandarMainCOMv30  // This is line 33
        {
            //public string m_fs;
            public CalandarMainV3 m_calMainV3;
            
            //public string fs { get { return m_fs; } }
            
            public clsCalandarMainCOMv3()
            {
                m_calMainV3 = new CalandarMainV3();
            }

            public string MakeCalandarEventCOMv3(string Title, string Description, string Location,
                                                 DateTime dtStartDate, DateTime dtStartTime,
                                                 DateTime dtEndDate, DateTime dtEndTime,
                                                 bool bLogIt)  //returns EventID string
            {
                return m_calMainV3.MakeCalandarEvent(Title, Description, Location,
                                                     dtStartDate, dtStartTime, dtEndDate, dtEndTime,
                                                     bLogIt);
            }
        }
    }

Open in new window

Without a public constructor, you shouldn't be able to compile a COM DLL. I tried to be sure, and I get an MSB3214 warning ""ClassLibrary1.dll" does not contain any types that can be registered for COM Interop"
After making the constructor public, it compiles and I can access and call it from VB6.

I see you have commented out the ComVisible attributes. Did you set it to true in AssemblyInfo.cs?
The ComVisible attributes are indeed set to true in AssemblyInfo.cs.

Note the command prompt I used is the Visual Studio Command Prompt (2010) with admin privileges.
tlbexp GoogleOpsCOMv3.dll /verbose
The command above had the following output:
Microsoft (R) .NET Framework Assembly to Type Library Converter 4.0.30319.1
Copyright (C) Microsoft Corporation.  All rights reserved.

Resolved referenced file 'GoogleOpsCOMv3.dll' to file 'C:\Users\DaUser\Documents\Visual Studio 2010\Projects\Programming\Projects\Google Ops\GoogleOpsCOM\bin\Debug\GoogleOpsCOMv3.dll'.
Assembly reference 'RepBusGoogleCalendarV3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' has been resolved to assembly 'RepBusGoogleCalendarV3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' with the following codebase: file:///C:/Users/DaUser/Documents/Visual Studio 2010/Projects/Programming/Projects/Google Ops/GoogleOpsCOM/bin/Debug/RepBusGoogleCalendarV3.DLL
Type 'I' exported.
Type 'G' exported.
Assembly exported to 'C:\Users\DaUser\Documents\Visual Studio 2010\Projects\Programming\Projects\Google Ops\GoogleOpsCOM\bin\Debug\GoogleOpsCOMv3.tlb'

Open in new window

I can write the NEW keyword to set new instance of the class.  This compiles now so this is an improvement.
Now the program stops at the NEW keyword.  Run-Time Error 429
User generated image
Is the CalandarMainV3 class defined in the same assembly?
The CalandarMainV3 class is defined in another project that is referenced in this project.
Error	1
Cannot register assembly "C:\Users\DaUser\Documents\Visual Studio 2010\Projects\Programming\Projects\Google Ops\GoogleOpsCOM\bin\Debug\GoogleOpsCOMv3.dll" - access denied. Please make sure you're running the application as administrator.
Access to the registry key 'HKEY_CLASSES_ROOT\CalandarMainCOM.iface' is denied.
GoogleOpsCOM

Open in new window


I would get the error above the first time I compiled this project.  So, I would just hit F6 again and the error would not be there.
Out of curiosity I decided to look at the registry.
HKEY_CLASSES_ROOT\CalandarMainCOM.iface
The key listed above did not exist,so I created it manually.
The next time I compiled it added the following REG-SZ:
GoogleOps.clsCalandarMainCOMv3
With the changes above it is now getting run-time error 801131500 at the NEW keyword.

I get the feeling that I am so close to figuring this out, thanks for the help!  What do you think now?
User generated image
Visual Studio couldn't register the type library. When you hit F6 again, VS sees that you didn't change anything, so it doesn't try to compile anything and doesn't try to register the type library again. The result is it doesn't show the registration error again.

I guess your account doesn't have admin privileges, and you're not running Visual Studio with admin privileges either. Hence the error.

Now for the command prompt.
TlbExp does NOT register the type library it generates. Adding the registry key by hand is not a good idea. What did you put under it? Did you add the other needed registry keys? I doubt it. Use RegAsm to do that.
OK.
I started launching Visual Studio with a right click and Run as Administrator.  That cleared up the type lib being registered correctly.

As far as the run time error goes, I found out something I thought fascinating.  I tweaked the code a bit:

// cd C:\Users\DaUser\Documents\Visual Studio 2010\Projects\Programming\Projects\Google Ops\GoogleOpsCOM\bin\Debug
// RegAssembly.bat
// TableExp.bat

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

using RepBusGoogleCalendarV3;

    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    [Guid("6662492C-F7D9-46E5-8718-837895B4219C")]
    public interface ICalandarMainCOMv30
    {
        //void clsCalandarMainCOMv3();
        
        string MakeCalandarEventCOMv3(string Title, string Description, string Location,
                                      DateTime dtStartDate, DateTime dtStartTime,
                                      DateTime dtEndDate, DateTime dtEndTime,
                                      bool bLogIt);  //returns EventID string
    }

    namespace GoogleOps
    {
        [ClassInterface(ClassInterfaceType.AutoDual)]
        [Guid("71B8D02D-4A6A-4293-835B-9992978367D1")]
        [ProgId("CalandarMainCOM.iface")]

        public class clsCalandarMainCOMv3 : ICalandarMainCOMv30
        {
            public CalandarMainV3 m_calMainV3;

            private bool b_init = false;
            
            //public clsCalandarMainCOMv3()
            //{
            //    m_calMainV3 = new CalandarMainV3();
            //}

            public string MakeCalandarEventCOMv3(string Title, string Description, string Location,
                                                 DateTime dtStartDate, DateTime dtStartTime,
                                                 DateTime dtEndDate, DateTime dtEndTime,
                                                    bool bLogIt)  //returns EventID string
            {
                if (b_init == false)
                {
                    m_calMainV3 = new CalandarMainV3();
                    b_init = true; 
                }

                return m_calMainV3.MakeCalandarEvent(Title, Description, Location,
                                                     dtStartDate, dtStartTime, dtEndDate, dtEndTime,
                                                     bLogIt);
            }
        }
    }

Open in new window


Notice I commented out the constructor and now have the member variable (public CalandarMainV3 m_calMainV3;) being initialized in the function itself.  When I ran the code in the debugger it now showed exactly what line of code is causing the error and it is NOT in this module!

It is coming from ClalandarMainV3.cs.  (using RepBusGoogleCalendarV3;)  The RepBusGoogleCalendarV3 project is the main code used to access the Google calendar and is referenced in this project.

                using (var stream = new FileStream(ClientSecretsFile, FileMode.Open, FileAccess.Read))
                {
                    credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                        GoogleClientSecrets.Load(stream).Secrets,
                        new[] { CalendarService.Scope.Calendar },
                        "user", CancellationToken.None,
                        new FileDataStore("SDER.cal")).Result;
                }

Open in new window


The line above is where the error occurs.

I have written a small console only C# application to test the managed code and it runs just fine from there.  However, when I call it from the COM interface I am getting this error.
Do you get an exception?
User generated image
SystemAggregateException
HRESULT: -2146233088

I am thinking that I need to add a reference to the COM interface project?
ASKER CERTIFIED SOLUTION
Avatar of louisfr
louisfr

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I tried all the suggestions mentioned in that thread.  No effect.

Thought: I am using VS 2010 so the highest .NET framework I can target is 4.0.3.
Question: Does any of the Google code require a higher framework?
After going over the Google APIs at NuGet.org, it appears that they are all targeted at the .NET framework 4.0.

What really bugs me the most about this is; I have a simple .NET console app that I can call the Google Calendar class (DLL) I have written and it works just fine.  This only happens when I call the same code using the COM interface.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The issue was not fixed.  I had to do a lot of reading to determine that it was not "fixable".  However, the expert's comment lead me in the right direction.