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:
Return a
BSTR
string from the C-interface DLL. UseSysAllocString
to create theBSTR
object from a raw C-style string pointer. Note thatBSTR
s "naturally" store Unicode UTF-16 encoded strings, so please make sure to convert your string to this encoding. The CLR is able to manageBSTR
strings just fine, so you don't have to pay attention to release the string memory: this will be the CLR's job.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 aStringBuilder
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
List<T> Readonly with a Private Set
How to Set Datagridview Textbox Column to Multi-Line
SQL Server Blocked Access to Procedure 'Sys.Sp_Oacreate' of Component 'Ole Automation Procedures'
Filesystemwatcher to Watch Unc Path
Retrieve an Object from Entityframework Without One Field
Is Idependencyresolver an Anti-Pattern
Difference Between SQLdatareader.Read and SQLdatareader.Nextresult
Bind 5 Items in Each Row of Repeater
How Get List of Local Network Computers
Open Source HTML to PDF Renderer with Full CSS Support
How to Implement the Equivalent of SQL In() Using .Net
How to Bind an Objective-C Static Library to Xamarin.iOS
Plink Returning Unwanted Characters via C#
How to Get Error Information When Httpwebrequest.Getresponse() Fails
System.Data.Sqlclient.Sqlexception: Invalid Column Name 'Phone_Types_Phone_Type_Id'