Connecting Tech Pros Worldwide Help | Site Map

Calling MFC DLL from C#

alexgm23's Avatar
Newbie
 
Join Date: Mar 2009
Posts: 6
#1: Mar 11 '09
Hi,

I'm trying to call a MFC DLL function from C# code, but I just don't know how to do it.

I've got the DLL's function names using "Dependency Walker" but they look like this:

?AddApl@CDocsHeadings@@QAEHPAVCDocsApl@@@Z
?AddItem@CDocsHeadings@@QAEHPAVCDocsItems@@H@Z
?AddNew@CDocsHeadings@@UAEXXZ
?AddRef@CDocsHeadings@@QAEXDVCString@@@Z
.....

sgsql.dll.jpg

I've got the C++ header files used to make the DLL, so I know how many parameters and what parameter types the DLL's functions expect.

Any idea on how can I accomplish this?


Alejandro Gutierrez
Newbie
 
Join Date: Mar 2009
Posts: 5
#2: Mar 17 '09

re: Calling MFC DLL from C#


Those look like debug strings. Build -> Clean Solution. Ensure the Configuration is set to Release. Then Build -> Build Solution

Craig
alexgm23's Avatar
Newbie
 
Join Date: Mar 2009
Posts: 6
#3: Mar 17 '09

re: Calling MFC DLL from C#


Thanks Craig.

I already found out what those strings mean and how to deal with them, but I still need (too much) help on import a function from the DLL that has a return value of type 'CString &'.

Can you help me on this?

Thanks in advance.
Newbie
 
Join Date: Mar 2009
Posts: 5
#4: Mar 17 '09

re: Calling MFC DLL from C#


Well this can be attacked in many different ways, but heres how I would do it;

Have the function return 'const char*' instead of 'CString'

CString theString( "This is a test" );
char* mychar = new char[theString.GetLength()+1];
_tcscpy(mychar, theString);
return mychar;

then in C# use DLLImport to import the function which it will now return a normal C# string that is recognised and terminated by a null value.

If you still have any problems, let me know

Craig
alexgm23's Avatar
Newbie
 
Join Date: Mar 2009
Posts: 6
#5: Mar 17 '09

re: Calling MFC DLL from C#


Thanks again!

After doing a research I got the same answer, and I think that's the best. But I haven't been explicit enough. The fact is that I can't add such wrapper function returning something more suitable like char* because I don't have the DLL code.

There's a way to instantiate a C++ class from a DLL. You can call a function that returns a pointer such as a class constructor (returns this). Then in C# you have to declare a struct that properly holds the this pointer:

Expand|Select|Wrap|Line Numbers
  1. [StructLayout(LayoutKind.Sequential, Pack = 4)]
  2. public unsafe struct __CString
  3. {
  4.     public IntPtr* _vtable;
  5.     public int m_Value;
  6. }
  7.  
Then you can make a C# wrapper class for the C++ class CString:

Expand|Select|Wrap|Line Numbers
  1. public unsafe class CString : IDisposable
  2. {
  3.     private __CString* _cpp;
  4.  
  5.     [DllImport("mfc42.dll", EntryPoint = "#535", CallingConvention = CallingConvention.ThisCall)]
  6.     private static extern int _CString_Constructor(__CString* ths, CString str);
  7.  
  8.     [DllImport("mfc42.dll", EntryPoint = "#537", CallingConvention = CallingConvention.ThisCall)]
  9.     private static extern int _CString_Constructor(__CString* ths, [MarshalAs(UnmanagedType.LPStr)] String str);
  10.  
  11.     [DllImport("mfc42.dll", EntryPoint = "#540", CallingConvention = CallingConvention.ThisCall)]
  12.     private static extern int _CString_Constructor(__CString* ths);
  13.  
  14.     [DllImport("mfc42.dll", EntryPoint = "#800", CallingConvention = CallingConvention.ThisCall)]
  15.     private static extern void _CString_Destructor(__CString* ths);
  16.  
  17.     [DllImport("mfc42.dll", EntryPoint = "#2915", CallingConvention = CallingConvention.ThisCall)]
  18.     [return: MarshalAs(UnmanagedType.LPStr)]
  19.     private static extern String _CString_GetBuffer(__CString* ths, int Length);
  20.  
  21.     public CString(CString str)
  22.     {
  23.         _cpp = (__CString*)Memory.Alloc(sizeof(__CString));
  24.         _CString_Constructor(_cpp, str);
  25.     }
  26.  
  27.     public CString(String str)
  28.     {
  29.         _cpp = (__CString*)Memory.Alloc(sizeof(__CString));
  30.         _CString_Constructor(_cpp, str);
  31.     }
  32.  
  33.     public CString()
  34.     {
  35.         _cpp = (__CString*)Memory.Alloc(sizeof(__CString));
  36.         _CString_Constructor(_cpp);
  37.     }
  38.  
  39.     public void Dispose()
  40.     {
  41.         _CString_Destructor(_cpp);
  42.         Memory.Free(_cpp);
  43.     }
  44.  
  45.     public String GetBuffer()
  46.     {
  47.         return _CString_GetBuffer(_cpp, 255);
  48.     }
  49. }
  50.  
Actually, I can instatiate a CString in this way without any problems:

Expand|Select|Wrap|Line Numbers
  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         CString s = new CString("Hello, World!");
  6.         Console.WriteLine(s.GetBuffer());
  7.     }
  8. }
  9.  
But when I try to do this:

Expand|Select|Wrap|Line Numbers
  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         Sample s = new Sample(1000);
  6.         Console.WriteLine(s.AsString().GetBuffer());
  7.     }
  8. }
  9.  
  10. /*
  11.   CString code...
  12. */
  13.  
  14. /*
  15.   Sample Class Wrapper
  16. */
  17.  
  18. [StructLayout(LayoutKind.Sequential, Pack = 4)]
  19. public unsafe struct __Sample
  20. {
  21.     public IntPtr* _vtable;
  22.     public int m_Value;
  23. }
  24.  
  25. public unsafe class Sample : IDisposable
  26. {
  27.     private __Sample* _cpp;
  28.  
  29.     [DllImport("OldMFC.dll", EntryPoint = "??0Sample@@QAE@H@Z", CallingConvention = CallingConvention.ThisCall)]
  30.     private static extern int _Sample_Constructor(__Sample* ths, int Value);
  31.  
  32.     [DllImport("OldMFC.dll", EntryPoint = "??1Sample@@UAE@XZ", CallingConvention = CallingConvention.ThisCall)]
  33.     private static extern void _Sample_Destructor(__Sample* ths);
  34.  
  35.     [DllImport("OldMFC.dll", EntryPoint = "?AsString@Sample@@QAEAAVCString@@XZ", CallingConvention = CallingConvention.ThisCall)]
  36.     private static extern CString _Sample_AsString(__Sample* ths);
  37.  
  38.     [DllImport("OldMFC.dll", EntryPoint = "?GetValue@Sample@@QAEHXZ", CallingConvention = CallingConvention.ThisCall)]
  39.     private static extern int _Sample_GetValue(__Sample* ths);
  40.  
  41.     public Sample(int Value)
  42.     {
  43.         _cpp = (__Sample*)Memory.Alloc(sizeof(__Sample));
  44.         _Sample_Constructor(_cpp, Value);
  45.     }
  46.  
  47.     public void Dispose()
  48.     {
  49.         _Sample_Destructor(_cpp);
  50.         Memory.Free(_cpp);
  51.     }
  52.  
  53.     public CString AsString()
  54.     {
  55.         return _Sample_AsString(_cpp);
  56.     }
  57. }
  58.  
  59. /*
  60.   Memory Class code...
  61. */
  62.  
I get this error:

Quote:
Unspecied error
Exception from HRESULT: 0x80004005 (E_FAIL)
The DLL function declaration is:

Quote:
CString & Sample::AsString();
Any idea about it?

Alex G.
Reply