Tom’s Blog

November 1, 2008

Calling Managed Code from a DLL Created in Visual C++ 2008

Filed under: General .NET — Tom Shelton @ 9:57 pm

I got involved in a discussion the other day over on the MSDN Visual C# General forum, in which a poster had a situation where he needed to create a dll in C++ to extend a 3rd party application – but, he wanted to reuse some of his C# code from that dll.  So, I am putting this post together to discuss the steps I took to get this working in Visual C++ 2008.  The goal of this article is to provide a step-by-step walk through.  If you follow all the steps (and I am communicating coherently) – then you should have a working code sample at the end of this article.

Since in real life, I’m primarily a C# developer, the order I do things in might be a little different then say someone who is primarily a VB.NET developer or a C++ developer.  Don’t let that throw you,  if things are in a different order,  just search around until you find the option.

 

1.

The first step is to create a new Visual C++ solution.  Click on “File -> New -> Project…”.  This brings up the “New Project” dialog.  In the “Project Types” pane, select “Other Languages -> Visual C++ -> Win32”.  Then from the “Templates” pane on the right, select “Win32 Console Application”.  Give the project a name (I called mine CallCSharpCodeSample) and select the “OK” button.

 

step1

 

When the “Application Settings Wizard” dialog appears, select the “Finish” button. The Console project just created will become the host application to test out the dll.

2.

Add a C# Class Library project.  This library will represent the existing .NET assembly that needs to exposed via the C++ dll.  Go to Solution Explorer, and right click on the solution and select “Add -> New Project…”.  In the “Project types” pane of the “Add New Project” dialog,  select “Visual C# -> Windows”.  In the “Templates” pane select “Class Library”.  Give the library a name, and then select the “OK” button.

 

step4

3.

Rename the default Class1.cs to AddClass.cs and allow the IDE to rename your class.  Insert a method to add two integers.  Your code should look something like this:

 
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5:  
   6: namespace CustomCSharpLib
   7: {
   8:     public class AddClass
   9:     {
  10:         public int AddTwoNumbers ( int x, int y )
  11:         {
  12:             return x + y;
  13:         }
  14:     }
  15: }

4.

Setup is now complete and you’re ready to create the dll.  Right click on the solution in Solution Explorer, select “Add -> New Project…”.  Add a new C++ Win32 project from the “Add New Project” dialog.  And give it a name and select the “OK” button.

  step7

5.

You will again be presented with the “Application Settings Wizard”.  Select the “Next” button and set the project options as shown below.  Select the “Finish” button.

  step8b

6.

When the MyDll project is created, Visual Studio adds some unneeded code to “MyDll.h” and “MyDll.cpp”.  Edit these files to look like the code below.

MyDll.h:

 
   1: // The following ifdef block is the standard way of creating macros which make exporting 
   2: // from a DLL simpler. All files within this DLL are compiled with the MYDLL_EXPORTS
   3: // symbol defined on the command line. this symbol should not be defined on any project
   4: // that uses this DLL. This way any other project whose source files include this file see 
   5: // MYDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
   6: // defined with this macro as being exported.
   7: #ifdef MYDLL_EXPORTS
   8: #define MYDLL_API __declspec(dllexport)
   9: #else
  10: #define MYDLL_API __declspec(dllimport)
  11: #endif
  12:  
  13: MYDLL_API int AddTwoNumbers(const int, const int);
 

MyDll.cpp:

 
   1: // MyDll.cpp : Defines the exported functions for the DLL application.
   2: //
   3:  
   4: #include "stdafx.h"
   5: #include "MyDll.h"
   6:  
   7: using namespace CustomCSharpLib;
   8:  
   9: MYDLL_API int AddTwoNumbers(const int x, const int y)
  10: {
  11:     return x + y;
  12: }
 

At this point, everything should compile just fine.  I have purposely not changed any project settings yet to enable managed code – but don’t worry, we’ll get to that!  Let’s make sure we can actually use and call this dll from the test program first.

7.

Change the default calling convention for the MyDll project to __stdcall.  You do this by right clicking on the project in the Solution Explorer and selecting “Properties…”.  Change the “Configuration” option to “All Configurations” and then select “C/C++ -> Advanced”.  Change the “Calling Convention” entry in the property page to “__stdcall (/Gz)” and select OK.

  step10
 

I’m not sure this step is actually necessary, but __stdcall is the same calling convention used by the Windows API and will make your dll easily callable from languages such as Visual Basic 6 that can’t handle the default __cdecl calling convention.

8.

Add a module definition file to the MyDll project – this will allow us to override the default name mangling done by the compiler, and make the dll more easily callable.  Right click on the MyDll project in the Solution Explorer and select “Add -> New Item…”.

9.

In the “Templates” pane of the “Add New Item” dialog, select “Module-Definition File (.def)”.  Give it a name.  Select the “Add” button.

  step12

10.

Edit your module definition file so that it looks like this (it will have a .def extension):

 
   1: LIBRARY    "MyDll"
   2:  
   3: EXPORTS
   4:     AddTwoNumbers @1

11.

Edit your CallCSharpCodeSample.cpp file to look like this:

 
   1: // CallCSharpCodeSample.cpp : Defines the entry point for the console application.
   2: //
   3:  
   4: #include "stdafx.h"
   5: #include <iostream>
   6: #include <windows.h>
   7:  
   8: using namespace std;
   9:  
  10: // our function pointer definition
  11: typedef int (WINAPI* LPFN_ADDFUNC)(const int, const int);
  12:  
  13: int _tmain(int argc, _TCHAR* argv[])
  14: {
  15:     HMODULE hMod = LoadLibrary(_T("MyDll.dll"));
  16:  
  17:     if (hMod != NULL)
  18:     {
  19:         LPFN_ADDFUNC addFunc = (LPFN_ADDFUNC)GetProcAddress(hMod, "AddTwoNumbers");
  20:  
  21:         if (addFunc != NULL)
  22:         {
  23:             cout << addFunc(1, 2) << endl;
  24:             cout << addFunc(5, 6) << endl;
  25:             cout << addFunc(100, 10024) << endl;
  26:         }
  27:         else
  28:         {
  29:             cout << "Failed to load function AddTwoNumbers" << endl;
  30:         }
  31:     }
  32:     else
  33:     {
  34:         cout << "Failed to load module MyDll.dll" << endl;
  35:     }
  36:  
  37:     return 0;
  38: }
  39:  
 

At this point, assuming you’ve done everything correctly, you should be able to compile and run the application:

  step14

12.

Now that the dll and it’s test harness are in place, let’s return our attention to the MyDll project.  At this step, we are going to enable CLR support.  Right click on the MyDll project in Solution Explorer and select “Properties…”.  Set the “Configuration” option to “All Configurations”.  Select “Configuration Properties -> General” in the menu tree, and then change the “Common Language Runtime support” option in the properties pane to “Common Language Runtime Support (/clr)” and select the “OK” button.

  step15

13.

Right click on your MyDll project in the Solution Explorer and select “References…”.  When the “Property Pages” dialog appears, select the “Add New Reference…” button.

  step18a
 

In the “Add Reference” dialog, select the “Projects” tab.  Select the CustomCSharpLib project from the list.  Select OK.

  step18b
 

This will add the reference to the C# class library to your MyDll project.  Select OK on the “Property Pages” dialog.

  step18c

14.

Edit MyDll.cpp to look like this:

 
   1: // MyDll.cpp : Defines the exported functions for the DLL application.
   2: //
   3:  
   4: #include "stdafx.h"
   5: #include "MyDll.h"
   6:  
   7: using namespace CustomCSharpLib;
   8:  
   9: MYDLL_API int AddTwoNumbers(const int x, const int y)
  10: {
  11:     AddClass addClass;
  12:     return addClass.AddTwoNumbers(x, y);
  13: }
 

Then compile and run your project.  If all goes well, this is what you should see:

  step14

One thing I did learn from this, is if you are going to include any .NET related header files (such as vcclr.h) – don’t do it in “stdafx.h”.  Otherwise, this file will be included in dllmain.cpp, and you will suddenly start getting an error about needing to add /clr switch to the command line.  The reason is that DllMain can’t be a managed function – so the IDE helpfully turns off /clr for dllmain.cpp.  There is a work around for this, you can turn on the /crl option for this file in it’s properties, but then you need to surround DllMain with some preprocessor directives to prevent it from being compiled as managed code.  It seems easier to just include any of the .NET related headers in your MyDll.h and be done with it.

Well, I hope this helps someone.  If not, it was definitely a good learning experience for me.

Powered by WordPress