3

I am new to C# but have worked extensively with C++. I have a C++ function that needs to be called from C#. After reading some answers from SO and some googling, I conclude that I need to make a pure C interface to the function. I have done this, but am still confused about how to call it from C#.

The function in C++ looks like this:

int processImages(
    std::string& inputFilePath,                      // An input file
    const std::vector<std::string>& inputListOfDirs, // Input list of dirs
    std::vector<InternalStruct>& vecInternalStruct,  // Input/Output struct
    std::vector<std::vector< int > >& OutputIntsForEachFile,
    std::vector< std::vector<SmallStruct> >& vecVecSmallStruct, // Output
    int verboseLevel
    );

The same function, converted in C, looks like this:

int processImagesC(
    char* p_inputFilePath,               // An input file
    char** p_inputListOfDirs,            // Input list of dirs
    size_t* p_numInputDirs,              // Indicating number of elements
    InternalStruct* p_vecInternalStruct, // Input/Output struct
    size_t* p_numInternalStructs, 
    int** p_OutputIntsForEachFile,       // a 2d array each row ending with -1
    size_t* p_numOutputIntsForEachFile //one number indicating its number of rows
    SmallStruct** p_vecVecSmallStruct,   // Output
    size_t* p_numInVecSmallStruct,
    int verboseLevel
    );

This is based on this advice.

Now I need to call this from C#, which is where the confusion is. I have tried my best to convert the structures.

The C# code looks like this:

[DllImport(
    @"C:\path\to\cppdll.dll", CallingConvention=CallingConvention.Cdecl, 
    EntryPoint="processImagesC", SetLastError=true)]
[return: MarshalAs(UnmanagedType.I4)]
unsafe public static extern int processImagesC(
    String inputFilePath,
    String[] inputListOfDirs,
    ref uint numInputListOfDirs,

    // Should I use ref InternalStruct * vecInternalStruct?
    ref InternalStruct[] vecInternalStruct, 

    ref uint numInternalStruct,

    // Or ref int[] or ref int[][] or int[][]?
    ref int[][] OutputIntsForEachFile, 

    ref uint numOutputIntsForEachFile,

    // again, ref ..[], [][], or ref [][]?
    ref SmallStruct[][] vecVecSmallStruct, 

    int verboseLevel
);

There are memory allocations for all the output variables (pointers) done within the C/C++ code. This likely means we need to declare the code as unsafe, correct?

How do we handle memory deallocation? Should I write another API (function) that does the deallocation of objects/arrays allocated by C/C++?

The C++ code needs to be standard compliant and platform independent, so I cannot insert any windows-specific things in it.

I hope someone could make sense of this and provide an answer or at least point me in the right direction.

6
  • You should NEVER call C++ from C#. The name mangling format is not garenteed to be the same between compiles. Commented Mar 28, 2013 at 1:09
  • 5
    If it's anything too complex you could create a managed C++/CLI wrapper around the native C++ to make it easier to call from C#. Commented Mar 28, 2013 at 1:43
  • 1
    Going to have to agree with the C++/CLI wrapper approach. There are some utilities in the framework for converting to/from STL types. Commented Mar 28, 2013 at 1:58
  • @Romoku can you explain how I can go about creating this managed C++/CLI wrapper? I have no experience with managed C++... Commented Mar 28, 2013 at 2:53
  • 1
    C++/CLI is the answer. Commented Mar 28, 2013 at 10:46

4 Answers 4

4

Since there seems to be some interest in using It Just Works (IJW) with C++/CLI, I'll post some info about that, further google searches and research will need to be done to figure it all. C++/CLI can be enabled with a single compiler flag (/CLI, enabled through Property Page->General->Common Language Runtime Support). C++/cli is NOT c++, but rather just another managed language. C++/CLI classes can be compiled into dll's and called directly from other .NET projects (C#, VB.NET, ect). However, unlike the other .NET languages it can directly interact with C++ code.

This is an ok start to learning C++/CLI. The big thing to learn is the decorations that tell you the class is managed (.NET class) and not Vanila C++. The "ref" keyword decalres the definition as a .NET definition:

public ref class Foo{ public: void bar();};//Managed class, visible to C#
public ref struct Foo{};//Managed struct, visible to C#

All reference classes are referred to with Handles rather than pointers or references. A handle is denoted by the ^ operator. To make a new handle, you use gcnew, and to access functions/members of the handle, use the -> operator.

//in main
Foo^ a = gcnew Foo();
a->bar();

You often have to move structures common from C# to native types and back again. (such as managed Array^ or String^ to void* or std::string). This process is called Marshaling. This handy table is pretty useful for figuring that out.

A common task is to create a wrapper for a native class, done like this:

//Foo.h
#include <string>
namespace nativeFoo
{
    class Foo
    {
     private:
        std::string fooHeader;
     public:
        Foo() {fooHeader = "asdf";}
        std::string Bar(std::string& b) {return fooHeader+b;}
    };
}
//ManagedFoo.h
#include "foo.h"
namespace managedFoo
{
    public ref class Foo
    {
        private:
             nativeFoo::Foo* _ptr;
        public:
             Foo(){_ptr = new nativeFoo::Foo();}
             ~Foo(){!Foo();}
             !Foo(){if (_ptr){delete ptr;ptr = NULL;}}

             String^ bar(String^ b)
             {
                 return marshal_as<String^>(_ptr->bar(marshal_as<std::string>(b)));
             }
    };
}

Warning: I am totally missing a bunch of #include and #using statements, this is just to give a general gist of how to use this.

Sign up to request clarification or add additional context in comments.

3 Comments

You did help him with std::string type, His function has complicated types like std::vector<std::vector<SmallStruct>>&, std::vector<std::vector<int>>, he needs to access them from C# as well. Would you be able to do the same in C++/CLI? Thanks!
@Dave, In general, it depends how you want to do that. You can only really marshal structs: classes (such as std::vector) are a little trickier. If you can get away with a deep copy to System::Generic::List, I suggest that: (vector<NativeStruct> s; List<ManagedSmallStruct>^ l = gcnew List<ManagedSmallStruct>(); l->add((ManagedSmallStruct)s[0]);). This can lead to performance issues for large arrays. Otherwise, you'll have to make a wrapper class (just like we did for Foo) for the vector, which gets old fast.
2 minor points: The compiler switch is /clr, not /cli. And C++/CLI is different from other .NET languages in that it can produce mixed mode assemblies, that contain both managed and native code. In your example nativeFoo::Foo compiles to native code. If you want to create a pure managed assembly using C++/CLI you would have to use /clr:pure or /clr:safe.
1

Begin from this:

And something about marshalling in this:

Note that Marshal.Copy also overloads for arrays use. With marshalling you can get rid of ref excepting that you do want to. Just write C/C++ in their way.

And following is a little bit complicated:

Comments

0

The 2 ways I've often seen this handled is to either have a 'FreeResource' style API, or specify in the function the size of output buffers.

Method 1

C++

void GetName(char ** _str)
{
    if (!_str)
        return; // error
    *_str = new char[20];
    strcpy(*str, "my name");
}

void FreeString(char * _str)
{
    delete str;
}

Client (any language)

char * name;
GetName(&name);
...
FreeString(name);

Method 2

C++

void GetName(char * _str, size_t _len)
{
    if (_len < 20)
        return; // error
    strcpy(str, "my name");
}

Client (any language)

char * name = new char[20];
GetName(name, 20);
...

1 Comment

Your answer makes things more clear, however, it would help to see the specific C# code used for calling the dll function.
0

If you are willing to use third party tool, there is a tool named C#/.NET PInvoke Interop SDK may be helpful to you. But you can do it yourself as well. For simple classes with a few methods, you can write your own code in managed C# code.

The basic idea of instantiating a C++ object from .NET world is to allocate exact size of the C++ object from .NET, then call the constructor which is exported from the C++ DLL to initialize the object, then you will be able to call any of the functions to access that C++ object, if any of the method involves other C++ classes, you will need to wrap them in a C# class as well, for methods with primitive types, you can simply P/Invoke them. If you have only a few methods to call, it would be simple, manual coding won't take long. When you are done with the C++ object, you call the destructor method of the C++ object, which is a export function as well. if it does not have one, then you just need to free your memory from .NET.

Here is an example.

public class SampleClass : IDisposable
{    
    [DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi,          CallingConvention=CallingConvention.ThisCall)]
    public extern static void SampleClassConstructor(IntPtr thisObject);

    [DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi,      CallingConvention=CallingConvention.ThisCall)]
    public extern static void DoSomething(IntPtr thisObject);

    [DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi,      CallingConvention=CallingConvention.ThisCall)]
    public extern static void DoSomething(IntPtr thisObject, int x);

    IntPtr ptr;

    public SampleClass(int sizeOfYourCppClass)
    {
        this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
        SampleClassConstructor(this.ptr);  
    }

    public void DoSomething()
    {
        DoSomething(this.ptr);
    }

    public void DoSomethingElse(int x)
    {
        DoSomethingElse(this.ptr, x);
    }

    public void Dispose()
    {
        Marshal.FreeHGlobal(this.ptr);
    }
}

For the detail, please see the below link,

C#/.NET PInvoke Interop SDK

The tool, xInterop NGen++ 2.0 has been released. Please check it out if you are interested in creating C# wrapper for native C++ DLL.

(I am the author of the SDK tool)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.