How to Call a Function of a C++ Dll That Accepts a Parameter of Type Stringstream from C#

Calling a C dll from C# windows application causes the svchost.exe to crash

I see that you specified the calling convention to Cdecl (CallingConvention = CallingConvention.Cdecl) in your C# PInvoke declaration; since this is the default calling convention in C++ code as well, you shouldn't have any calling convention mismatch in this case. Although, please note that the common calling convention for C-interface DLLs is __stdcall.

The problem I see is the way you return the string from the C-interface API

CDLL_API const char* Classify(const char * img_path, int N)
{
...
return ss.str().c_str();
}

(BTW I assume ss is something like a std::ostringstream object.)

You build a string using an output string stream (calling its str method), then you get a raw C-style string pointer calling c_str. But when the function exits, the string object is destroyed, so the C-style raw string pointer is not valid anymore.

To return a string from C-interface DLL APIs to C#, you can consider one of these options:

  1. Return a BSTR string from the C-interface DLL. Use SysAllocString to create the BSTR object from a raw C-style string pointer. Note that BSTRs "naturally" store Unicode UTF-16 encoded strings, so please make sure to convert your string to this encoding. The CLR is able to manage BSTR strings just fine, so you don't have to pay attention to release the string memory: this will be the CLR's job.

  2. Add to the C-interface DLL function a couple of parameters: a pointer to a buffer, and a buffer size. This will be an output string buffer, that is allocated by the caller (e.g. C#), and the C-interface API exported from the DLL will write the result string to that caller-provided buffer. This is what e.g. the GetWindowText Win32 API does (on the C# side, the output string buffer can be represented by a StringBuilder object).

Creating c++ DLL without static methods

As side note I did 1 experiment few days ago with mingw/c++ wich can be clarifying for you.

I had a Global reference counter for find out memory leaks in my program,

class ReferenceCounter /** other implementations details are omitted.*/
{
public:

static int GlobalReferenceCounter;

//version 1
static int getReferenceCount1() { return GlobalReferenceCounter;}

//verison 2
static int getReferenceCount2(); //same code of version 1 but moved into .cpp file
};

When compiling my library using reference counter into a DLL, then the variable is duplicated, 1 version is compiled into the DLL, and one version is compiled in client code.

When I ask istances of reference counted stuff from the factory methods of the DLL, only the reference counter inside the DLL is increased/reduced. When client code is using its own classes inherited from Ref Counter, then the client reference counter is increased/reduced.

So for check for memory leaks I must do at end of the program

assert(ReferenceCounter.getReferenceCount1() == 0);
assert(ReferenceCoutner.getReferenceCount2() == 0);

that's because in case of memory leak one of those values will be greater than 0. If first value greater than 1, then the memory leak is caused by unallocated user classes, if second value is greater than 0, then memory leak is caused by library classes.

Note that if leak is caused by unallocated library's classes, this is not necessarily a bug of the library, since user is still able to leak that classes, even if that should mean a lack of design in the library, since ideally everythin should be returned in proper smart pointers for safety.)

Of course you should specify that "GlobalReferenceCoutner" is duplicated in the documentation, else some unaware user can just think that 2 getters are redundant and will think you did some mistake. (if possible avoid to do something like that, is obscure and unclear)

That should also warn you that accessing static method through DLLs is highly unsafe. For example If In my class I wanted to have only 1 reference counter instead of 2 I had to do that for grant safety:

class ReferenceCounter
{
public:

static int GlobalReferenceCounter;

static void duplicate() { increaseGlobalCounter(); }

static void release() { decreaseGlobalCounter(); }

static int getGlobalCounter() { return privateGetGlobalCounter(); }

private:

static increaseGlobalCounter(); // implementation into Cpp file

static decreaseGlobalCounter(); // implementation into Cpp file

static privateGetGlobalCounter(); // implementation into Cpp file

};

doing that will grant you that the same value is used across DLL bounduaries and in User application. so instead of have 2 different variables here we use 1 variable (probably the GlobalCounter is still compiled into user executable, but no one is using it removing the "clone effect")

Return string from c++ dll export function called from c#

How about this (Note, it assumes correct lengths - you should pass in the buffer length and prevent overflows, etc):

extern "C" __declspec(dllexport)  void  __cdecl getDataFromTable(char* tableName, char* buf)
{
std::string st = getDataTableWise(statementObject, columnIndex);
printf(st.c_str());

strcpy(buf, st.c_str());
}

Then in C#:

[DllImport("\\SD Card\\ISAPI1.dll")]
private static extern string getDataFromTable(byte[] tablename, byte[] buf);
static void Main(string[] args)
{
byte[] buf = new byte[300];
getDataFromTable(byteArray, buf);
Console.writeLine(System.Text.Encoding.ASCII.GetString(buf));
}

Note, this does make some assumptions about character encodings in your C++ app being NOT unicode. If they are unicode, use UTF16 instead of ASCII.

StringStream in C#

You can use tandem of MemoryStream and StreamReader classes:

void Main()
{
string myString;

using (var stream = new MemoryStream())
{
Print(stream);

stream.Position = 0;
using (var reader = new StreamReader(stream))
{
myString = reader.ReadToEnd();
}
}
}


Related Topics



Leave a reply



Submit