Interop problem between DLL C++ MFC and CLI [TypeLoadException and more]

  • Thread starter Thread starter nbrau
  • Start date Start date
N

nbrau

Hi all,

After 3 days of researches in the depths of Google and all dev forums, I'am asking for your help.

I'm doing dev / c ++ linux for more than 10 years, but today I have a new job and they ask me to develop on Windows environment with Visual Studio. I'm in the middle of the authorized period of self-training and despite hundreds of websites on about this subject I am really not comfortable with the notions of managed object, COM, and finally... all the .NET environment ^^ So I am totally lost and no colleague to help me.

Today I have to write a DLL that contains a class with its pretty methods, and which creates a custom dialog box in which programs must send text messages.

I specify: programs that use this DLL are written in C ++ MFC, C #, C ++ / CLI, delphi and Python ...

So after several liters of sweat I wrote it in C ++ MFC, to have a dialog and a simple use with the outside. That I thought...

I managed to use C#, using the IUnknown interface and a small method to create an instance of my class.

In C ++ MFC I use it easily.

Where I'm blocked for 3 whole days this is for the C ++ / CLI. And that's where I need you!

Clearly I can call a basic method, but I can not create my graphic object and not call the constructor of my mfc class.


DLL header :

#ifdef CPPDLL_EXPORTS
#define CPPDLL_API __declspec(dllexport)
#else
#define CPPDLL_API __declspec(dllimport)
#endif


#include <Windows.h>

// Generate from Visual Studio, Menu "Tools/Create GUID"
// {0D825F4E-7028-4628-8EBA-BBF77610EC7E}
static const GUID IID_IMyInterface =
{ 0xd825f4e, 0x7028, 0x4628,{ 0x8e, 0xba, 0xbb, 0xf7, 0x76, 0x10, 0xec, 0x7e } };
struct IMyInterface : public IUnknown // COM Interface. Enables clients to get pointers to other interfaces on a given object through the QueryInterface method.
{
STDMETHOD_(double, GetValue)() = 0;
STDMETHOD(CustomMsgBox)(const char*) = 0;
};

// This class is exported from the MyClass.dll
class CPPDLL_API CMyClass: public IMyInterface
{
volatile long refcount_;
public:
CMyClass() : refcount_(1) {};

STDMETHODIMP QueryInterface(REFIID guid, void **pObj)
{
if (NULL == pObj) {
return E_POINTER;
}
else if (guid == IID_IUnknown) {
*pObj = this;
AddRef();
return S_OK;
}
else if (guid == IID_IMyInterface) {
*pObj = this;
AddRef();
return S_OK;
}
else {
// Always set 'out' parameter
*pObj = NULL;
return E_NOINTERFACE;
}
}

STDMETHODIMP_(ULONG) AddRef() {
return InterlockedIncrement(&refcount_);
}

STDMETHODIMP_(ULONG) Release() {
ULONG result = InterlockedIncrement(&refcount_);
if (result == 0) delete this;
return result;
}

STDMETHODIMP_(DOUBLE) GetValue() {
return 3.14;
}

STDMETHODIMP ThrowError() {
return E_FAIL;
}

CMyClass(int p_flagDebug, CWnd* p_Parent);
CMyClass(int p_flagDebug, int left, int top, int right, int bottom);
~CMyClass();
// TODO: add your methods here.
void WriteMessage(CString p_sMsg);
void WriteMessage(const wchar_t* p_sMsg);
void InitDlg();
STDMETHODIMP CustomMsgBox(const char* p_sStr = nullptr);

private:
void AppendText(HWND hEditWnd, LPCTSTR Text);
void DoIt(const char *p_sStr, long p_lNumber);
void *m_gui; // Handle on the child dialog.
CWnd *m_parent; // Handle on parent used to get the window position
int m_Flag;
};


extern "C" CPPDLL_API LPUNKNOWN WINAPI CreateInstance(int FlagDebug, void* cwnd);

extern CPPDLL_API int nMyClass;

CPPDLL_API int fnMyClass(void);





DLL Code :

// MyClass.cpp : Defines the exported functions for the DLL application.
// @TODO : Manage close event of our Gui !!! (Actually nothing is done)

#include "stdafx.h"
#include "MyClass.h"
#include "Gui.h"
#include <Dbghelp.h>
#include <windowsx.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#pragma comment(lib, "dbghelp.lib")

// The one and only application object
CWinApp theApp;

HMODULE hModule; // Handle on this module
WNDPROC PrevPresLVWndProc; // Pointer to parent callback function
struct s_dialog {
CGui* g_gui;
CWnd* g_parent;
int g_parentTop;
int g_parentLeft;
int g_parentRight;
int g_parentBottom;
};
s_dialog g_dlg;

void SetGuiPosition(void);
LRESULT CALLBACK ParentCallback_OnMove(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);


/// <summary>
/// Constructor
/// </summary>
/// <param name="p_Parent">Parent CWND</param>
CMyClass::CMyClass(int p_flag, CWnd* p_Parent)
{
if (NULL == p_Parent->m_hWnd)
{
AfxMessageBox(_T("CMyClass object creation may be before main dialog initialization"), MB_OK | MB_ICONERROR);
return;
}

m_Flag = p_flag;

if (m_Flag == 1)
{
// Initilization of the global struct g_dlg
g_dlg.g_parent = m_parent = p_Parent;
g_dlg.g_gui = NULL;
m_gui = NULL;

// Here we save pointer of the parent process
PrevPresLVWndProc = (WNDPROC)GetWindowLongPtr(m_parent->m_hWnd, GWL_WNDPROC);
// Now we associate the new callback method for event
SetWindowLongPtr(m_parent->m_hWnd, GWL_WNDPROC, (LONG_PTR)&ParentCallback_OnMove);

// Creation of our gui dialog
CGui* mGuiLoc = new CGui;
AFX_MANAGE_STATE(AfxGetStaticModuleState());
m_gui = (void*)(new CGui());
BOOL ret = ((CGui*)m_gui)->Create(MAKEINTRESOURCE(IDD_DIALOG1));
((CGui*)m_gui)->ShowWindow(SW_SHOW);
// Setting the dialog text
((CGui*)m_gui)->m_EditBox.SetWindowText(_T("Text to display\r\n"));
g_dlg.g_gui = ((CGui*)m_gui);
InitDlg();
}
}
CMyClass::CMyClass(int p_flag, int left, int top, int right, int bottom)
{
m_Flag = p_flag;
// Empty for the moment
}

/// <summary>
/// Destructor
/// </summary>
CMyClass::~CMyClass()
{
SetWindowLongPtr(m_parent->m_hWnd, GWL_WNDPROC, (LONG_PTR)&PrevPresLVWndProc);
}

/// <summary>
/// Non member function - It is the new callback used to catch parent event
/// </summary>
/// <param name="all">that's default param used for this kind of callback method and content depends of message type (uMsg)</param>
LRESULT CALLBACK ParentCallback_OnMove(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_MOVE)
{
CRect rectGui;
g_dlg.g_gui->GetWindowRect(&rectGui);
CString s;
s.Format(_T("gui top"));
OutputDebugString((LPCTSTR)s);
}
return CallWindowProc(PrevPresLVWndProc, hwnd, uMsg, wParam, lParam);
}

LPUNKNOWN WINAPI CreateInstance(int p_flag, void* cwnd) {
return new CMyClass(p_flag, CWnd::FromHandle((HWND)cwnd));
}


/// <summary>
/// Initialize Gui
/// </summary>
void CMyClass::InitDlg()
{
if (m_Flag == 0) return;

if (NULL == m_gui)
{
//SetGuiPosition();
}
else
{
if (((CGui*)m_gui)->IsWindowVisible() != TRUE)
{
((CGui*)m_gui)->ShowWindow(SW_SHOW);
}
}
}

/// <summary>
/// Add a message to Edit Box
/// </summary>
/// <param name="p_sMsg">The message to print</param>
void CMyClass::WriteMessage(CString p_sMsg)
{
if (m_Flag == 0) return;

InitDlg();
if (NULL != m_gui)
{
AppendText(((CGui*)m_gui)->m_EditBox, p_sMsg);
}
}

/// <summary>
/// Add a message to Edit Box - Compatibility for others languages
/// </summary>
/// <param name="p_sMsg">The message to print</param>
void CMyClass::WriteMessage(const wchar_t* p_sMsg)
{
if (m_Flag == 0) return;

CString csMsg(p_sMsg);
WriteMessage(csMsg);
}

/// <summary>
/// Append a message to Edit Box
/// </summary>
/// <param name="p_hEditWnd">Handle on Edit Box</param>
/// <param name="p_text">The message to print</param>
void CMyClass::AppendText(HWND p_hEditWnd, LPCTSTR p_text)
{
if (m_Flag == 0) return;

int idx = GetWindowTextLength(p_hEditWnd);
SendMessage(p_hEditWnd, EM_SETSEL, (WPARAM)idx, (LPARAM)idx);
SendMessage(p_hEditWnd, EM_REPLACESEL, 0, (LPARAM)p_text);
}

/// <summary>
/// Display one message
/// <param name="p_sStr">Name</param>
/// </summary>
HRESULT CMyClass::CustomMsgBox(const char* p_sStr)
{
if (m_Flag == 0) return S_OK; // Useless

InitDlg();

size_t outSize;
wchar_t *wsStr = new wchar_t[256];
// Conversion char* to wchar_t*
mbstowcs_s(&outSize, wsStr, strlen(p_sStr) + 1, p_sStr, strlen(p_sStr));

CString sTime(CTime::GetCurrentTime().Format("%H:%M:%S"));
CString sMessage;
sMessage.Format(_T("%s;%s;-\r\n"), sTime, wsStr);
WriteMessage(sMessage);
return S_OK;
}


void CMyClass::DoIt(const char *p_sStr, long p_lNumber)
{
size_t outSize;
wchar_t *wsStr = new wchar_t[256];
// Conversion char* to wchar_t*
mbstowcs_s(&outSize, wsStr, strlen(p_sStr) + 1, p_sStr, strlen(p_sStr));

CString sTime(CTime::GetCurrentTime().Format("%H:%M:%S"));
CString sMessage;
sMessage.Format(_T("%s;%s;%d\n"), sTime, wsStr, p_lAllParamSum);

WriteMessage(sMessage);
}





C# code used to call MFC DLL class methods :

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

/***
USAGE :
1 : Create a pointer on this object and initialize it ONLY after main dialog init !
2 : Create ONLY ONE object
Example to create object :
CMyClass myCClassInstance = new CMyClass();
IMyInterface myObjectInterface = myCClassInstance.LocCreateInstance();
Console.WriteLine(myObjectInterface.GetValue());
***/


namespace MyNamespace
{
[ComImport]
[Guid("0D825F4E-7028-4628-8EBA-BBF77610EC7E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyInterface
{
[PreserveSig]
double GetValue();
IntPtr CustomMsgBox([MarshalAs(UnmanagedType.LPStr, SizeParamIndex = 3)] string str);
void ThrowError();
}
////////////////////////////////////////////////////////////
// Inclusion of other needed stuff
////////////////////////////////////////////////////////////

public class CMyClass
{
[DllImport("MyClass.dll")]
extern static IMyInterface CreateInstance(int Flag, IntPtr o2);

public IMyInterface LocCreateInstance(IntPtr o1)
{
return CreateInstance(1, o1);
}
}
}





This is the C++/CLI code I'm trying to do, I have and beautiful exception System.TypeLoadException :

header :

using namespace System;
using namespace System::Text;
using namespace System::Runtime::InteropServices;
using namespace System::Windows::Forms;

namespace company
{
namespace project
{
namespace module
{
public interface class IMyInterface
{
double GetValue();
};

public ref class CMyClass : IMyInterface //ref class CMyClass
{
public:
CMyClass() {};

[DllImport("MyClass.dll", EntryPoint = "_CreateInstance@8Z", CallingConvention = CallingConvention::StdCall)]
IMyInterface^ CreateInstance(int Flag, IntPtr cwnd);

[DllImport("MyClass.dll", EntryPoint = "?GetValue@CMyClass@@UAGNXZ", CallingConvention = CallingConvention::StdCall)]
virtual double GetValue();
};
}
}
}



Code :

CMyClass^ m_cMyClass = gcnew CMyClass();
IMyInterface ^m_i_MyInterface = m_cMyClass->CreateInstance(1, this->Handle);



Bellow the exception I catch :

- e {"Méthode implémentée PInvoke virtuelle.":"company.project.module.CMyClass"} System::Exception^ {System::TypeLoadException^}
AssemblyName "PCExample, Version=1.0.6198.29074, Culture=neutral, PublicKeyToken=null" System::String^
ClassName "company.project.module.CMyClass" System::String^
+ Data {System::Collections::ListDictionaryInternal^} System::Collections::IDictionary^ {System::Collections::ListDictionaryInternal^}
HResult -2146233054 int
HelpLink nullptr System::String^
+ IPForWatsonBuckets {28553626} System::UIntPtr
+ InnerException nullptr System::Exception^
IsTransient false bool
Message "Méthode implémentée PInvoke virtuelle." System::String^
MessageArg nullptr System::String^
RemoteStackTrace nullptr System::String^
ResourceId 8209 int
Source "PCExample" System::String^
StackTrace " à PCExample.FormMain..ctor()\r\n à main(String[] args) dans d:\\proje\\PCExample\\checkit\\c++_clr\\PCExample\\PCExample.cpp:ligne 19" System::String^
+ TargetSite {Void .ctor()} System::Reflection::MethodBase^ {System::Reflection::RuntimeConstructorInfo^}
TypeName "company.project.module.CMyClass" System::String^
WatsonBuckets nullptr System::Object^
_HResult -2146233054 int
_className nullptr System::String^
+ _data {System::Collections::ListDictionaryInternal^} System::Collections::IDictionary^ {System::Collections::ListDictionaryInternal^}
_dynamicMethods nullptr System::Object^
+ _exceptionMethod {Void .ctor()} System::Reflection::MethodBase^ {System::Reflection::RuntimeConstructorInfo^}
_exceptionMethodString nullptr System::String^
_helpURL nullptr System::String^
+ _innerException nullptr System::Exception^
+ _ipForWatsonBuckets {28553626} System::UIntPtr
_message "Méthode implémentée PInvoke virtuelle." System::String^
_remoteStackIndex 0 int
_remoteStackTraceString nullptr System::String^
+ _safeSerializationManager {System::Runtime::Serialization::SafeSerializationManager^} System::Runtime::Serialization::SafeSerializationManager^
_source "PCExample" System::String^
+ _stackTrace array<char>(48) System::Object^ {array<char>^}
_stackTraceString nullptr System::String^
_watsonBuckets nullptr System::Object^
_xcode -532462766 int
+ _xptrs {0} System::IntPtr
+ [Static Members]


It is really important for me to understand and succed...

I hope you will understand my problem and help me. And I apologize about my english !

For information : I use the same target platform and same toolset. DLL is working really fine with C# and C++/MFC.

Thanks by advance,

nb












Continue reading...
 

Similar threads

B
Replies
0
Views
53
BharatShah5
B
Z
Replies
0
Views
262
ZubinSethna
Z
Back
Top