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

This article has moved.

15 Comments

  1. Thanks, this code works,bit small change i have made but it may be the setting problem, it gives me the error and fixed those error and here is the code in MyDll.cpp that is difference between the your code and my code

     AddClass addClass;
     return addClass.AddTwoNumbers(x,y);
    

    Well your code may be work correct but this worked for me Thanks again

    Thanks
    Vm

    Comment by VijayMajagaonkar — December 2, 2008 @ 3:53 am

  2. Vm – the code should work either way, but actually, your code is probably preferable 🙂

    Anyway, glad that it helped.

    Comment by Tom Shelton — December 2, 2008 @ 9:12 am

  3. Code modified to reflect the more correct version VM showed.

    Comment by Tom Shelton — December 3, 2008 @ 4:08 pm

  4. Hi, I converted the project to VB.NET but when declaring the class (see VM’s snippet) I get a AddClass: undeclared identifier error. Any help would be appreciated.

    Comment by DA — December 29, 2008 @ 5:30 am

  5. DA – well, I just tested this out with a VB.NET library and it worked just fine. I created a VBAddClass in a project called VBCodeLib. I added the reference to my C++ project and then added:

    using namespace VBCodeLib;

    to the top of the MyDll.cpp file. I then changed the code in the AddTwoNumbers method to look like this:

    VBAddClass addClass;
    return addClass.AddTwoNumbers(x,y);

    Not sure if that helps or not, but without a little more information, this is the best help I can provide.

    Comment by Tom Shelton — December 29, 2008 @ 11:19 am

  6. Thanks for the prompt reply, here’s my code. Basically the function returns the current year instead of adding two numbers.

    // MyDll.cpp : Defines the exported functions for the DLL application.
    //

    #include “stdafx.h”
    #include “MyDll.h”

    / MyDll.h
    //

    #ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)
    #else
    #define MYDLL_API __declspec(dllimport)
    #endif

    MYDLL_API int GetYear(const int);

    //DateClass.vb
    //

    Imports System

    Namespace VBNETLib

    Public Class DateClass

    Public Function GetYear(ByVal x As Integer) As Integer

    Return DateTime.Today.Year

    End Function

    End Class

    End Namespace

    using namespace VBNETLib;
    //using namespace CSharpLib;

    MYDLL_API int GetYear(const int x)
    {
    DateClass dateClass;
    return dateClass.GetYear(x);
    }

    Here’s the error when compiling MyDll.cpp.

    Error 1 error C2065: ‘DateClass’ : undeclared identifier w:\BUS_1_16_1_0\MyDll\MyDll.cpp 12 MyDll

    Comment by DA — December 30, 2008 @ 7:50 am

  7. This got cut off, don’t know why.

    // MyDll.cpp : Defines the exported functions for the DLL application.
    //

    #include “stdafx.h”
    #include “MyDll.h”

    using namespace VBNETLib;
    //using namespace CSharpLib;

    MYDLL_API int GetYear(const int x)
    {
    DateClass dateClass;
    return dateClass.GetYear(x);
    }

    Comment by DA — December 30, 2008 @ 7:51 am

  8. DA,

    I can’t tell for sure from your comment, but you aren’t trying to define the VB class in the C++ project correct? What, I mean is that you have created a new VB.NET class library project – called VBNETLib – and are adding a reference to that project in your C++ project(MyDll -> Properties -> Common Properties -> Framework and References)?

    Assuming that the above is correct, then you will want to make sure that you are importing the correct namespace. You can check by right clicking on the vb.net project and selecting properties. On the application tab, you should see an entry called Root Namespace. That is what you will use in your using in the C++ code.

    Anyway, here is what I did to get this working…

    1) Added a vb.net class library project to the demo solution. I called mine VBCodeLib.

    2) In VBCodeLib I defined the following class:

    ‘ DateClass.vb
    Option Strict On
    Option Explicit On

    Public Class DateClass
    Public Function GetYear() As Integer
    Return DateTime.Now.Year
    End Function
    End Class

    3) I added a reference to the VBCodeLib project in the MyDll project. Right click on MyDll. Select Properties. Then in the navigation treeview, I selected Common Properties -> Framework and References. Clicked the Add New Reference button, and from the projects tab, I referenced the VBCodeLib project.

    4) I modified MyDll.h:

    // The following ifdef block is the standard way of creating macros which make exporting
    // from a DLL simpler. All files within this DLL are compiled with the MYDLL_EXPORTS
    // symbol defined on the command line. this symbol should not be defined on any project
    // that uses this DLL. This way any other project whose source files include this file see
    // MYDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
    // defined with this macro as being exported.
    #ifdef MYDLL_EXPORTS
    #define MYDLL_API __declspec(dllexport)
    #else
    #define MYDLL_API __declspec(dllimport)
    #endif

    MYDLL_API int AddTwoNumbers(const int, const int);
    MYDLL_API int GetYear();

    5) I modified MyDll.def:
    LIBRARY “MyDll”

    EXPORTS
    AddTwoNumbers @1
    GetYear @2

    6) I modified MyDll.cpp:

    // MyDll.cpp : Defines the exported functions for the DLL application.
    //

    #include “stdafx.h”
    #include “MyDll.h”

    using namespace CustomCSharpLib;
    using namespace VBCodeLib;

    MYDLL_API int AddTwoNumbers(const int x, const int y)
    {
    AddClass addClass;
    return addClass.AddTwoNumbers(x,y);
    }

    MYDLL_API int GetYear()
    {
    DateClass dateClass;
    return dateClass.GetYear();
    }

    7) I then modified the C++ console application – CallCSharpCodeSample.cpp:

    // CallCSharpCodeSample.cpp : Defines the entry point for the console application.
    //

    #include “stdafx.h”
    #include
    #include

    using namespace std;

    // our function pointer definition
    typedef int (WINAPI* LPFN_ADDFUNC)(const int, const int);
    typedef int (WINAPI* LPFN_YEARFUNC)(void);

    int _tmain(int argc, _TCHAR* argv[])
    {
    HMODULE hMod = LoadLibrary(_T(“MyDll.dll”));

    if (hMod != NULL)
    {
    LPFN_ADDFUNC addFunc = (LPFN_ADDFUNC)GetProcAddress(hMod, “AddTwoNumbers”);

    if (addFunc != NULL)
    {
    cout << addFunc(1, 2) << endl;
    cout << addFunc(5, 6) << endl;
    cout << addFunc(100, 10024) << endl;
    }
    else
    {
    cout << “Failed to load function AddTwoNumbers” << endl;
    }

    LPFN_YEARFUNC getYear = (LPFN_YEARFUNC)GetProcAddress(hMod, “GetYear”);
    if (getYear != NULL)
    {
    cout << “The current year is ” << getYear() << endl;
    }
    else
    {
    cout << “Failed to load function GetYear” << endl;
    }
    }
    else
    {
    cout << “Failed to load module MyDll.dll” << endl;
    }

    return 0;
    }

    Everything compiles and works as expected.

    Comment by Tom Shelton — December 30, 2008 @ 8:34 am

  9. Tom,
    after checking out your code, I started to feel a bit discouraged… I had the exact same code. Then I figured the problem must be in the VB project cause it compiled OK with the C# one. I tried removing the “Namespace VBNETLib” declaration and bingo, the dll compiled without errors. When I checked your code, you had in fact not put that line like in C#. I tried removing the ns declaration from the C# code which resulted in an error. In the end, VB and C# are different like the US and Europe… Different but alike. On an other note, I tried the code in the debugger and it works great but when I tried running the exe, it gave me a File Not Found error. I put the MyDll.dll file with the exe. Anything else I might have missed?

    Comment by DA — December 31, 2008 @ 1:02 am

  10. Changed
    HMODULE hMod = LoadLibrary(_T”MyDll.dll”));
    to
    HMODULE hMod = LoadLibrary(“MyDll.dll”);

    Fixed the problem. Thanks a lot for your help!

    Comment by DA — December 31, 2008 @ 4:14 am

  11. Turns out I was wrong about the last problem. I can LoadLibrary() AND GetProcAddress() but the call fails. Any ideas?

    Comment by DA — December 31, 2008 @ 8:27 am

  12. DA,

    The VB dll needs to be either in the GAC, or in the same directory as MyDll.dll. So, you will need to have at minumum:

    TheApplication.exe
    MyDll.dll
    VBCodeLib.dll

    All in the same directory.
    HTH

    Comment by Tom Shelton — December 31, 2008 @ 3:29 pm

  13. […] I understand what your asking, check out this little walk through I created for another discussion: https://tomshelton.wordpress.com/2008…visual-c-2008/ I don’t claim to be an expert at this, but I have done this a couple of times, and it seemed to […]

    Pingback by Call .Net Method from VC++ | keyongtech — January 18, 2009 @ 10:37 am

  14. Hi, Tom:

    This is an excellent article for calling the managed C# dll from C application.

    I have a question regarding the passing C# string back to C function caller.
    In C, I’d like to get a string stored in Managed C# dll to display in C, how do I do that?

    For example, in your MyDll.dll,
    Add one function,

    void GetmyMessage(char* myMsg)
    {
    AddClass addClass;
    addClass.GetMyMessage(myMsg);

    }

    and in CustomCSharpLib.AddClass,
    add one method,
    public void GetMyMessage(ref byte[] strMsg)
    {
    string MyMsg = “Hello.”;
    strMsg = Encoding.ASCII.GetBytes(MyMsg);

    }

    Any help?

    Thanks

    Wayne

    Comment by Wayne — January 28, 2009 @ 8:18 pm

  15. Wayne,

    Here are the steps I used to return a string:

    1) Added the following method to the C# AddClass:

    public string GetMsg ()
    {
    return "Hello, World!";
    }

    2) Opend MyDll.h and added:

    MYDLL_API char* GetMsg();

    3) Opened MyDll.cpp and added the following usings:

    using namespace System;
    using namespace System::Runtime::InteropServices;

    4) Added this method to MyDll.cpp:

    MYDLL_API char* GetMsg()
    {
    AddClass addClass;
    return (char*) Marshal::StringToHGlobalAnsi(addClass.GetMsg()).ToPointer();
    }

    5) Opened MyDll.def and added

    GetMsg @2

    6) Opened CallCSharpCodeSample.cpp and added the following typedef under the original:

    typedef char* (WINAPI* LPFN_MSGFUNC)(void);

    7) Added the following block of code under the original method call:

    LPFN_MSGFUNC getMsg = (LPFN_MSGFUNC)GetProcAddress(hMod, "GetMsg");
    if (getMsg != NULL)
    {
    char* msg = getMsg();
    cout << msg << endl;
    cout << GlobalFree((HGLOBAL) msg) << endl;
    }
    else
    {
    cout << "Failed to load function GetMsg" << endl;
    }

    Notice the call to GlobalFree? Since I allocated the memory using Marshal.StringToHGlobalAnsi – you have to use GlobalFree to deallocate the memory when your done with the string…

    Anyway, like I said – I’m not an expert at this, so this might not be the recommended way of accomplishing this 🙂 But, it seems to work ok.

    Comment by Tom Shelton — January 29, 2009 @ 12:13 am


RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Blog at WordPress.com.