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):
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:
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:
Update 4
I had to add into my MFC application InitInstance
:
::Coinitialize(true);
And in ExitInstance
:
::CoUnitialize();
Then the exceptions went away.
Try this modification, generating a new guid with VS, because i do not remember if it is auto written in another file.
EDIT
Ensure your Assembly.cs has a line like this:
[assembly: Guid("xxx-xxx-xxx-xxx-xxx")]