none
How to compile C++ function to be used by C# ?

    Question

  • I want to write a native C++ function that I can later access from C#.

    Please keep in mind I do not have much experience developing on windows, (mostly unix) and that my only windows compiler is VS2005.

    I tried creating a MFC DLL.  I am able to access this from a C++ .net program, but not from a C# program using P/Invoke.  It seems that a plain MFC DLL is not "good enough" for use with DllImport.  Is this correct?

    It seems that I have two options:

      1) compile the C++ function as a COM DLL (not sure how to do this from VS2005 - I don't see an option for that).  I see an option for an ActiveX DLL...I hope I don't need to do that.

     2) write a C++ .net wrapper class, create a .net assembly, and use that assembly from C#.

    I would prefer option 1...how do I create a DLL that C# can use via [DllImport] ?

     

    Tuesday, June 13, 2006 4:42 PM

Answers

  • You are confusing Platform/Invoke with COM interop.  These are two separate interop techniques.  As far as which one you should use I would lean toward PInvoke given your newness to Windows programming.  PInvoke meets most needs and is simple to use.

    For PInvoke all you need is the C signature for a method to call in a DLL.  If you want to write your code in C++ then you must: a) create a static or global function in C++ (no class methods), and b) you must mark the function with extern "C" in order to change the namemangling from C++ to C.  You can not access C++ classes or class members through PInvoke.  Theoretically you can but honestly it is simply too difficult and not worth the effort.

    Here is a function you might write in C++:

    //In DLL: MyDll.dll
    extern "C" double SquareRoot ( double value )
    {
       //Do work
    }

    In C# you would do this:

    public class MyClass
    {
       public double SqrRoot ( double value )
       {
          return SquareRoot(value);
       }

       [DllImport("MyDll.dll")]
       private static extern double SquareRoot ( double value );
    }

    The general rule of thumb is to make these functions private and put a .NET wrapper around it like in the example.  DllImport accepts a lot of optional parameter to control character sets, error handling, etc.  Furthermore each parameter can be attributed to control marshaling and whatnot but the above example gives you the basics.

    If you want to expose an entire class and its methods then you are forced to use COM interop.  This requires that you create a COM interface and class using ATL or something and then register the COM object.  You then have to import the COM object into a .NET project (or use tlbimp) to generate the COM proxy that .NET will use.  You can then use the auto-generated proxy to talk with the COM object.  However I'd avoid this if at all possible since it complicates issues and adds to deployment requirements.  For more information use MSDN to look up Platform/Invoke and COM Interop.

    Michael Taylor - 6/13/06

    Wednesday, June 14, 2006 3:04 AM
    Moderator

All replies

  • You are confusing Platform/Invoke with COM interop.  These are two separate interop techniques.  As far as which one you should use I would lean toward PInvoke given your newness to Windows programming.  PInvoke meets most needs and is simple to use.

    For PInvoke all you need is the C signature for a method to call in a DLL.  If you want to write your code in C++ then you must: a) create a static or global function in C++ (no class methods), and b) you must mark the function with extern "C" in order to change the namemangling from C++ to C.  You can not access C++ classes or class members through PInvoke.  Theoretically you can but honestly it is simply too difficult and not worth the effort.

    Here is a function you might write in C++:

    //In DLL: MyDll.dll
    extern "C" double SquareRoot ( double value )
    {
       //Do work
    }

    In C# you would do this:

    public class MyClass
    {
       public double SqrRoot ( double value )
       {
          return SquareRoot(value);
       }

       [DllImport("MyDll.dll")]
       private static extern double SquareRoot ( double value );
    }

    The general rule of thumb is to make these functions private and put a .NET wrapper around it like in the example.  DllImport accepts a lot of optional parameter to control character sets, error handling, etc.  Furthermore each parameter can be attributed to control marshaling and whatnot but the above example gives you the basics.

    If you want to expose an entire class and its methods then you are forced to use COM interop.  This requires that you create a COM interface and class using ATL or something and then register the COM object.  You then have to import the COM object into a .NET project (or use tlbimp) to generate the COM proxy that .NET will use.  You can then use the auto-generated proxy to talk with the COM object.  However I'd avoid this if at all possible since it complicates issues and adds to deployment requirements.  For more information use MSDN to look up Platform/Invoke and COM Interop.

    Michael Taylor - 6/13/06

    Wednesday, June 14, 2006 3:04 AM
    Moderator
  • I finally got this to work, but there are some critical steps that have not been mentioned.  I will document them in case someone else has the same problem.

    First, create the native win32 dll using VS2005:

    Create a C++ win32 project.  (I called my project "MyWin32Function"). In the wizard, set application type to "DLL".  Check the "export symbols" box.

    This creates a template file "MyWin32Function.cpp".  The template contains an example function, class, and variable to be exported.  I deleted the variable and the class.  I modified the function to extern "C" and wrote code to simply multiply two floats.

    The other important thing in the template (which nobody mentions) is the DllMain at the top.  Frankly, I have no idea what that is (I come from a unix background) but it seems to be necessary if you want to link to the dll from another program.

    Here is the complete MyWin32Function.cpp:

    // MyWin32Function.cpp : Defines the entry point for the DLL application.
    //

    #include "stdafx.h"
    #include "MyWin32Function.h"


    #ifdef _MANAGED
    #pragma managed(push, off)
    #endif

    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }

    #ifdef _MANAGED
    #pragma managed(pop)
    #endif


    // This is an example of an exported function.
    extern "C" MYWIN32FUNCTION_API float fnMyWin32Function(float a,float b)
    {
        return a*b;
    }


    I then compiled the file and copied the resulting dll to a directory in my PATH.

    The C# side is now fairly easy.  I created a C# console project.  I did not need to modify any of the default project properties.  However, you do need the using Sytem.Runtime.InteropServices!  Here is the complete C# file:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;

    namespace MyCsharpTest
    {
        class Program
        {
            [DllImport("MyWin32Function.dll")]
            private static extern Single fnMyWin32Function(Single a, Single b);
            static void Main(string[] args)
            {
                Single a = 10.0f;
                Single b = 20.0f;
                Single c = fnMyWin32Function(a,b);
                System.Console.WriteLine(c);
            }
        }
    }


    Hopefully this is useful to someone else.
    Thursday, June 15, 2006 12:35 PM
  • DllMain is not needed in order to export functions.  DllMain is a helper routine that DLLs can implement to be notified when they are loaded and unloaded in the process and for each thread.  This function is normally used to initialize global variables or run global functions prior to the DLL being used.  DllMain is called by Windows after the DLL is loaded but before control returns to the loader (technically the function called is an RTL function that initializes the C++ library and then calls DllMain).  The problem with DllMain is the fact that it is loaded within a synchronization block that the loader uses (at least it use to be, not sure whether this is still true in XP) so there are certain things that can't be done within DllMain.

    Most DLLs implement this function simply because each time you create a thread or a thread terminates every DLL loaded in the process has its DllMain called with the appropriate reason code.  This is wasteful if the DLL doesn't care about threads (the general case).  Therefore the defacto implementation of this function looks like this:

    BOOL WINAPI DllMain ( HMODULE hModule, DWORD dwReason, LPVOID reserved )
    {
       switch(dwReason)
       {
          case DLL_PROCESS_ATTACH :
          case DLL_PROCESS_DETACH :
          case DLL_THREAD_DETACH : break;
          case DLL_THREAD_ATTACH :
          {
             //Disable thread notifications
             DisableThreadLibraryCalls(hModule);
          };
       };
    }

    The other important use of this function is to get the HMODULE for the DLL which you need for some API calls.  Note that some frameworks like MFC and ATL use DllMain to perform framework initialization as well. 

    Michael Taylor - 6/15/06

    Thursday, June 15, 2006 12:46 PM
    Moderator
  • This might be over-simplifying things, but if you're writing a C++ module that will be called by C#, why not write the C++ as a .NET Assembly and eliminate the whole PInvoke thing entirely?

     

    . . . or this an exercise in getting a Win32 C++ library to play nice with C#

    Thursday, June 15, 2006 1:40 PM
  • I tried your initial example, but was unable to get it to work.  At one point, I got an error something like "could not initialize DLL".  (I'm not at that computer right now, I can't get the exact error).  I then created a completely new project, modified it, and had success.

    I can see two possibly significant differences between the version that worked and the one that did not:

    1) DllMain

    2) instead of

    extern "C" float fnMyWin32Function(float,float)

    the template creates

    extern "C" MYWIN32FUNCTION_API float fnMyWin32Function(float a,float b)

    where MYWIN32FUNCTION_API is defined in the header (which I don't have handy).  It may be defined as something like __declspec(dllexport) ?

     

     

     


    Thursday, June 15, 2006 2:17 PM
  • I have some image processing routines written in C++ (currently in a unix environment) that I would like to be able to call from C#.  As a test, I plan to implement some of them both as native and as .NET and see if there is any noticable performance advantage to "going native."
    Thursday, June 15, 2006 2:35 PM
  • Ah yes.  The DllExport is important.  I forgot about that, sorry.  To expose a function from a Dll you need to use __declspec(dllexport) to expose the function from the Dll.  If you don't then the Dll should compile but you won't be able to call it from an external application.  The complication (and hence the macro in the template) is the fact that you only want it to say __declspec(dllexport) when you are compiling your DLL.  The header file should say __declspec(dllimport) when others call it (not an issue in .NET).  Therefore the macro is generally defined as follows:

    #if defined(MYDLL_EXPORT)
    #define DllExport __declspec(dllexport)
    #else
    #define DllExport __declspec(dllimport)
    #endif

    Then in the project settings for your DLL you specify MYDLL_EXPORT as a compile-time definition.  Your function then becomes:

    extern "C" DllExport void Foo ( void );

    Then when you compile your DLL it is exported but when others use your header file it is imported.

    Michael Taylor - 6/15/06

    Thursday, June 15, 2006 2:37 PM
    Moderator
  • I'm new to C++ but I'm trying to follow these examples and discussion and I keep getting this error.

    error C2059: syntax error : 'string'

    It happens on a file parsing routine the sig is...

    extern "C" DllExport void ParseFile( char* filePath, char* outputDir )

    Can anyone point out the obvious to me? I have a feeling it a compiler option but I haven't been able to find the right one.

    Monday, December 04, 2006 5:17 PM
  • It should be :

    #if defined(MYDLL_EXPORT)
    #define DllExport __declspec(dllexport)
    #else
    #define DllImport __declspec(dllimport)
    #endif

    -Deva Wijewickrema 1/12/2007
    Saturday, January 13, 2007 3:25 AM
  • thanks Rick,

    very useful posting and yes it helped me

    cheers

     

    Wednesday, March 07, 2007 9:02 AM
  • either  use extern "C"  or DLLExport as mentioned in above post!
    Wednesday, March 07, 2007 10:30 AM
  • I have a unmanaged c++ dll which I need to use from a C# application. I used all the tricks mentioned above. But I'm getting an exception something like this...

    ......." Unable to find an entry point named 'SomeFunction' in DLL 'SomeDll' "

    Here are the things that I have done :

    i> The functions in C++ are prefixed with _declspec(dllexport) and extern "C"
    ii> I have used a .def file in C++ to avoid name mangling
    iv> I have copied the unmanaged dll in the bin directory of my C# app.
    iii> I have tried with / without DllMain

        I have failed to obtain a solution to this problem. It would be really helpful if anyone can enlighten me in this matter. thanks in advance .

    Tuesday, April 03, 2007 10:42 AM
  • And how do you "write the C++ as a .NET Assembly" and invoke it in C#?!?  From what I can tell, you have to take about 15 steps just to do that, so why not just use P/Invoke?  It seems equally difficult to do it either way (and both methods seem extremely inelegant).

    Monday, August 20, 2007 1:20 PM
  • I have the same problem. Did you find any solution?
    Friday, October 15, 2010 11:10 AM
  • This is an old thread.  Please raise your question as a new thread, providing details of how it affects you.


    Answering policy: see profile.
    Friday, October 15, 2010 11:19 AM