2

Looking for help from experience people who have dealt with C++ and C#

I am now writing a C# project which requires calling an existing C++ function in the internal library, and encountering some difficulties.

C++ Code Function Header (Cannot be changed)

typedef int (*PTR_func) 
(const char*             param1,      // input
 const char*             param2,      // input
 VirtualInternalString&  po_output);  // Third param is actually the output

C++ Code Class for the internal string object (Cannot be changed)

namespace InternalLib
{

    class VirtualInternalString
    {
        public :
            virtual ~VirtualInternalString(){};
            virtual void _cdecl SetString(const char *) =0;
            virtual const char * _cdecl c_str() const =0;
    };

    class SafeInternalString :public VirtualInternalString
    {
        public :
            SafeInternalString():TheString(""){};
            virtual ~SafeInternalString(){};
            virtual void _cdecl SetString(const char *  chararray) { TheString = chararray;};
            virtual const char * _cdecl c_str() const { return TheString.c_str() ;} ;           // This is the std::string

        protected:
            std::string TheString;
    };

    class SafePtrString :public VirtualInternalString
    {
        public :
            SafePtrString():PtrString(0){};
            SafePtrString(std::string &str):PtrString(&str){};
            virtual ~SafePtrString(){}; 
            virtual void _cdecl SetString(const char *  chararray) { (* PtrString) = chararray;};
            virtual const char * _cdecl c_str() const { return PtrString->c_str() ;} ;

        protected :
            std::string * PtrString;
    };
}

Now I have to utilize this "func" in my C# code. I can call and use the C++ function properly, but problem occurs during output return (the third param). It should be problem related return type.

C# (Can be changed)

[DllImport("xxxxlib", CallingConvention = CallingConvention.Cdecl)]
private extern static int func(string param1, 
                               string param2, 
                               [MarshalAs(UnmanagedType.LPStr)] StringBuilder res); // problem here

I know I have to create an appropriate wrapper to decopose the c++ object into C# string but I don't have previous experience about it. I have tried several solutions from the internet but not working.

Does any one know how I can implement this? Would you mind showing me some simple C# skeleton code?

passenger
  • 23
  • 4
  • 4
    Didn't I see just this exact question a few minutes ago? Was it you that posted it and then deleted it? If so then please don't to that. Instead you should edit the question you already have. – Some programmer dude Aug 22 '17 at 07:49
  • 1
    Possible duplicate of [Passing strings from C# to C++ dll and back -- minimal example](https://stackoverflow.com/questions/20752001/passing-strings-from-c-sharp-to-c-dll-and-back-minimal-example) – vasek Aug 22 '17 at 07:54
  • 1
    Sorry about that... Do you have any idea about this post? The target is to get the string inside the returned c++ object. It should not be difficult but I really tried different ways and struggled a lot. It still doesnt work – passenger Aug 22 '17 at 08:07
  • 1
    P/Invoke is only intended for API that are similar to those used by Win32 API. It will only be able to translate C style functions and structures that conforms to what it knows how to translate. For anything complex like this, you need. C++/CLI bridge. Or you could expose functions that are conformant to what P/Invoke support. – Phil1970 Aug 22 '17 at 11:12
  • Suggested duplicate by @vasek does not cover this case as object in the post is not marshallable between C++ and C#. More information needed to explain why it is not possible and how to bridge the gap. – Alexei Levenkov Aug 23 '17 at 06:52
  • Thanks all for helping!! It finally worked =D We implemented a C++/CLI bridge and inside the function wrapper making the object content returned as String^ %. int funcWrapper (String^ param1, String^ param2, String^% po_output ) – passenger Aug 25 '17 at 07:30

4 Answers4

3

vasek's possible duplicate won't work if you only change the C# side. But what you can do is create wrappers in C++ that create raw char * with associated length parameter.

You create an additional C++ header and source which contains:

int func 
(const char*             param1,      // input
 const char*             param2,      // input
 char*                   po_output,   // Third param is actually the output
 int                     length);     // Length of the buffer allocated by caller  

And then you make your func call PTR_func.

CodeMonkey
  • 4,067
  • 1
  • 31
  • 43
  • 1
    The difficult part is that c++ function returns a VirtualInternalString& object, and to use this function requires not simple C# initialtion. Do you mean that I can create another c++ function that expose the char * in the object to the outsideworld? – passenger Aug 22 '17 at 08:25
  • Exactly, the extra `func` wrapper that you create exposes the `char *` to the outside world, while dealing with the `VirtualInternalString&` object on the inside. – CodeMonkey Aug 22 '17 at 08:28
  • 1
    @passenger Exactly! To be more precise, you need to _copy_ data from `VirtualInternalString&` to the buffer provided by C#. Don't forget to check buffer length! – vasek Aug 22 '17 at 08:28
  • @passenger Glad to help, if this answer solved your problem please mark it as accepted by clicking the check mark next to the answer. see: [How does accepting an answer work?](https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work) for more information – CodeMonkey Aug 28 '17 at 14:16
1

This is a case in which I think using C++/CLI does make sense. You may want to build a tiny C++/CLI bridge between the native C++ DLL and your C# code.

The C++/CLI layer is responsible to take your custom C++ string class as output parameter from the DLL function, and convert from this custom string to a C#/.NET string object.

Your custom C++ string class seems to wrap std::string; on the other hand, C#/.NET strings use Unicode UTF-16 as their text encoding, so you may need to convert between Unicode UTF-8 (stored in std::string and in your custom string class) and UTF-16 (for C#/.NET strings).

Similarly, you also need to convert C# UTF-16 strings to UTF-8 in this C++/CLI bridging layer, and pass these UTF-8-encoded strings as your const char* input parameters to the native DLL function.

Mr.C64
  • 41,637
  • 14
  • 86
  • 162
  • Would you mind showing some simple code structure? That will be great. – passenger Aug 22 '17 at 11:48
  • Yes, thanks so much for all of you! I have some clue now and am trying the C++/CLI bridge to expose the const char* – passenger Aug 22 '17 at 11:55
  • @passenger: For the Unicode encoding conversions, you may find [this MSDN article](https://msdn.microsoft.com/magazine/mt763237) interesting. Moreover, please take a look also at this [marshaling library](https://learn.microsoft.com/cpp/dotnet/overview-of-marshaling-in-cpp). – Mr.C64 Aug 22 '17 at 17:12
  • Thanks so much! The C++/CLI bridge worked super fine though we encountered function pointer some other obstacles later. Now all solved! – passenger Aug 25 '17 at 07:39
0

Use C++ COM and consume from C# code. Only if u are on windows platform.

0

In past experience, I did use SWIG tool for generation wrapper implementation It has support for C++ to wrapper generation to several languages, where Java the most popular one There is a great tutorial - http://www.swig.org/tutorial.html C# is also supported - http://www.swig.org/Doc3.0/CSharp.html

/* InternalLib.i */
%module InternalLib
%{
/* include header with a VirtualInternalString class*/
#include "InternalLib.h"
%}

then call swig to generate wrapper files:

swig -csharp InternalLib.i
N0dGrand87
  • 727
  • 1
  • 9
  • 16