Solved

C# DLL exposing managed code to COM

Posted on 2014-10-26
18
207 Views
Last Modified: 2014-11-09
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


What the Object Browser in MS Access sees
What the Object Browser in MS Access sees
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.
0
Comment
Question by:AviationAce
  • 12
  • 6
18 Comments
 
LVL 11

Expert Comment

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

Open in new window

0
 

Author Comment

by:AviationAce
ID: 40406168
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

0
 
LVL 11

Expert Comment

by:louisfr
ID: 40406254
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?
0
 

Author Comment

by:AviationAce
ID: 40407203
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

0
 

Author Comment

by:AviationAce
ID: 40407218
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
Where the code stops in MS Access
0
 
LVL 11

Expert Comment

by:louisfr
ID: 40407240
Is the CalandarMainV3 class defined in the same assembly?
0
 

Author Comment

by:AviationAce
ID: 40407265
The CalandarMainV3 class is defined in another project that is referenced in this project.
0
 

Author Comment

by:AviationAce
ID: 40407277
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
0
 

Author Comment

by:AviationAce
ID: 40407296
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?
Run time error at the NEW keyword
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 11

Expert Comment

by:louisfr
ID: 40407866
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.
0
 

Author Comment

by:AviationAce
ID: 40413180
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.
0
 
LVL 11

Expert Comment

by:louisfr
ID: 40414041
Do you get an exception?
0
 

Author Comment

by:AviationAce
ID: 40414124
VS-Error-01.png
SystemAggregateException
HRESULT: -2146233088

I am thinking that I need to add a reference to the COM interface project?
0
 
LVL 11

Accepted Solution

by:
louisfr earned 500 total points
ID: 40414135
0
 

Author Comment

by:AviationAce
ID: 40416646
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?
0
 

Author Comment

by:AviationAce
ID: 40418189
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.
0
 

Assisted Solution

by:AviationAce
AviationAce earned 0 total points
ID: 40423006
It appears that the real culprit here is NuGet.  I'll simply have to wait for them to fix the bug in how they handle inheritance.

In the mean time I'll either have to import all the libraries manually or use a different approach.  I think I'll try to use VB to create the COM class library interface.
0
 

Author Closing Comment

by:AviationAce
ID: 40431051
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.
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Convert between Excel file formats (.XLS, .XLSX, .XLSM) with/without macro option David Miller (dlmille) Intro Over this past Fall, I've had the opportunity to see several similar requests and have developed a couple related solutions associate…
This tutorial explains how to create a series of drop-down lists that are dependent upon prior selections to guide (“force”) the user to make the correct selection and reduce data errors within Microsoft Excel. Excel 2010 was used for this tutorial;…
The view will learn how to download and install SIMTOOLS and FORMLIST into Excel, how to use SIMTOOLS to generate a Monte Carlo simulation of 30 sales calls, and how to calculate the conditional probability based on the results of the Monte Carlo …
The viewer will learn how to use the =DISCRINV command to create a discrete random variable, use this command to model a set of probabilities and outcomes in a Monte Carlo simulation, and learn how to find the standard deviation of a set of probabil…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now