Search code examples
c#mfctlb

Why are the methods from my C# DLL missing?


I am trying to learn how to write a C# DLL and call it from a MFC application.

Here is a bit of the DLL code:

using System;
using System.IO;
using System.Xml.Serialization;

namespace MSATools
{
    public class MSAToolsLibrary
    {
        public MSAToolsLibrary()
        {
            _PublisherData = new PublisherData();
        }

        [XmlIgnore]
        private string _strPathXML;

        public void SetPathXML(String strPathXML)
        {
            _strPathXML = strPathXML;
        }

        [XmlIgnore]
        private PublisherData _PublisherData;

        public void SavePublisherData()
        {
            XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
            using (StreamWriter writer = new StreamWriter(_strPathXML))
            {
                x.Serialize(writer, _PublisherData);
            }
        }

        public void ReadPublisherData()
        {
            _PublisherData.Publishers.Clear(); // Reset

            XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
            using (StreamReader reader = new StreamReader(_strPathXML))
            {
                _PublisherData = (PublisherData)x.Deserialize(reader);
            }
        }

        public void AddPublisherData()
        {
            _PublisherData.AddStudent("Andrew", "Andrew notes");
            _PublisherData.AddStudent("Rachel", "Rachel notes");
            _PublisherData.AddStudent("Joshua", "Joshua notes");
            _PublisherData.AddStudent("Finlay", "Finlay notes");
        }
    }
}

I ticked the options to register it as a interop and I have a TLB file.

I then went into a new MFC project and followed a tutorial to create a class from a TLB file. But all it created was:

// Machine generated IDispatch wrapper class(es) created with Add Class from Typelib Wizard

#import "D:\\My Programs\\2017\\MSATools\\MSATools\\bin\\Release\\MSATools.tlb" no_namespace
// CMSAToolsLibrary wrapper class

class CMSAToolsLibrary : public COleDispatchDriver
{
public:
    CMSAToolsLibrary() {} // Calls COleDispatchDriver default constructor
    CMSAToolsLibrary(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
    CMSAToolsLibrary(const CMSAToolsLibrary& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

    // Attributes
public:

    // Operations
public:


    // _MSAToolsLibrary methods
public:

    // _MSAToolsLibrary properties
public:

};

I am clearly missing something here. I was anticipating seeing my public methods:

SetPathXML
ReadPublisherData
SavePublisherData
AddPublisherData

What have I missed?

This is how the classes look in the IDE (the MSATools DLL project):

Classes

Update

I tried to add an interface to my C# library and I must have done something wrong:

using System;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.InteropServices;

namespace MSATools
{
    [Guid("xxxxxx")]
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface iInterface
    {
        void SetPathXML(String strPathXML);
        void SavePublisherData();
        void ReadPublisherData();
        void AddPublisherData();
    }

    [Guid("xxxx")]
    [ClassInterface(ClassInterfaceType.None)]
    [ComVisible(true)]
    public class MSAToolsLibrary
    {
#region iInterface Members
        public MSAToolsLibrary()
        {
            _PublisherData = new PublisherData();
        }

        [XmlIgnore]
        private string _strPathXML;

        public void SetPathXML(String strPathXML)
        {
            _strPathXML = strPathXML;
        }

        [XmlIgnore]
        private PublisherData _PublisherData;

        public void SavePublisherData()
        {
            XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
            using (StreamWriter writer = new StreamWriter(_strPathXML))
            {
                x.Serialize(writer, _PublisherData);
            }
        }

        public void ReadPublisherData()
        {
            _PublisherData.Publishers.Clear(); // Reset

            XmlSerializer x = new XmlSerializer(_PublisherData.GetType());
            using (StreamReader reader = new StreamReader(_strPathXML))
            {
                _PublisherData = (PublisherData)x.Deserialize(reader);
            }
        }

        public void AddPublisherData()
        {
            _PublisherData.AddStudent("Andrew", "Andrew notes");
            _PublisherData.AddStudent("Rachel", "Rachel notes");
            _PublisherData.AddStudent("Joshua", "Joshua notes");
            _PublisherData.AddStudent("Finlay", "Finlay notes");
        }
#endregion
    }
}

It compiles OK. But when I go to Visual C++ MFC and go to add a class from TypeLib and choose this TLB file nothing shows in the list as available.

Confused.

Update 2

I decided to try and create my own class as a wrapper:

#pragma once

#import "D:\\My Programs\\2017\\MSATools\\MSATools\\bin\\Release\\MSATools.tlb" raw_interfaces_only named_guids

class CMSATools
{
public:
    CMSATools();
    ~CMSATools();
    void SetPathXML(CString strPath);
    void Test();
};

And:

#include "stdafx.h"
#include "MSATools.h"


CMSATools::CMSATools()
{
}


CMSATools::~CMSATools()
{
}


void CMSATools::SetPathXML(CString strPath)
{
    MSATools::iInterfacePtr     pInterface = NULL;

    HRESULT         hr;
    hr = pInterface.CreateInstance(__uuidof(MSATools::MSAToolsLibrary));

    if (SUCCEEDED(hr))
    {
        CComBSTR    bstrText = strPath.AllocSysString();
        pInterface->SetPathXML(bstrText);
    }
}


void CMSATools::Test()
{
    MSATools::iInterfacePtr     pInterface = NULL;

    HRESULT         hr;
    hr = pInterface.CreateInstance(__uuidof(MSATools::MSAToolsLibrary));

    if (SUCCEEDED(hr))
    {
        CComBSTR    bstrText(_T("d:\\TestStudents.XML"));

        pInterface->SetPathXML(bstrText);
        pInterface->AddPublisherData();
        pInterface->SavePublisherData();
    }
}

It compiles and I can see the methods with VisualAssist:

Syntax

But for some reason, hr does not pass the SUCCEEDED call.

Update 3

If I rebuild the DLL as x64 and rebuild the MFC EXE as x64 then I get an exception:

Exception

Update 4

I had to add into my MFC application InitInstance:

::Coinitialize(true);

And in ExitInstance:

::CoUnitialize();

Then the exceptions went away.


Solution

  • Try this modification, generating a new guid with VS, because i do not remember if it is auto written in another file.

    using System.Runtime.InteropServices;
    ...
    [ComVisibleAttribute(true)]
    [Guid("xxx-xxx-xxx-xxx-xxx")]
    [ProgId("MSAToolsLibraryClass")]    
    public class MSAToolsLibrary
    {
    ...
    

    EDIT

    Ensure your Assembly.cs has a line like this:

    [assembly: Guid("xxx-xxx-xxx-xxx-xxx")]