PInvoking function with output parameter - c#

I have the following C++ function:
int my_func(char* error) {
// Have access here to an Exception object called `ex`
strcpy(error, ex.what());
return 0;
}
I am PInvoking it like this in C#:
[DllImport("pHash.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int my_func(
[MarshalAs(UnmanagedType.LPStr)]
StringBuilder error);
And using like this in the code (always C# of course):
StringBuilder error = new StringBuilder();
int returnValue = my_func(error);
If I run this, the program crashes terribly (meaning that is crashes with no exception. Just closes, that's it). What am I doing wrong?

The question here is: How does your code know how large the string buffer should be?
Normally you'd have some way of finding out. In the absence of this information, the only thing you can do is to initialise the StringBuilder to be as large as the largest string you expect, before calling the function.
For example:
StringBuilder error = new StringBuilder(1024); // Assumes 1024 chars max.
Your code is passing a StringBuilder with a default capacity, which is (I think) 16, so any string larger than that will cause a crash.

Related

Improving performance of switch from managed to unmanaged code

I have built a C# wrapper for a commercial third-party DLL written in C++. What I've got works, but it does not perform as fast as expectations. The company that make the DLL also use it to power a piece of their own software: mine runs at about a sixth of the speed using the wrapper.
To improve performance I used a profiler to find the bottlenecks. This suggested that about 80% of the runtime was taken up in making calls direct to the third-party DLL. So, for example this function, which just returns an integer:
[DllImport("MyDllPath.dll", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern int QABatchWV_FormattedLineCount(int handle, ref int count);
private int LineCount(int searchHandle)
{
int layoutLineCount = 0;
int layoutLineCountReturn = QABatchWV_FormattedLineCount(searchHandle, ref layoutLineCount);
return layoutLineCount;
}
Takes about 25% of the processing time. While this one:
[DllImport("MyDllPath.dll", SetLastError = true, CharSet = CharSet.Ansi)]
public static extern int QABatchWV_Clean(int handle, StringBuilder searchString, ref int searchHandle, StringBuilder returnPostcode, int postcodeLength, StringBuilder isoCode, StringBuilder returnCode, int returnLength);
private int AddressClean(StringBuilder returnPostcode, StringBuilder countryISO, StringBuilder returnCode, StringBuilder inputAddress, out int searchHandle)
{
searchHandle = 0;
int searchResult = 0;
searchResult = QABatchWV_Clean(ApiHandle, inputAddress, ref searchHandle, returnPostcode, 20, countryISO, returnCode, 256);
return searchResult;
}
Takes about 35%.
The handle in the code suggests the dll has an initialisation overhead. However, I've tried to engineer my code to minimize this. My calling code sets up a batch of "pre-initalised" DLL threads, each with their own handle. It then uses these for the run before closing them all down. The DLL seems to have been built to pass handles around freely: so, you get a handle when you start a session, pass that down into a clean function to use that session to clean which in turns gives you "clean" handle which you then pass back to the DLL to get the line count and so on.
I'm new to working with unmanaged code and I'm at a loss as to what - if anything - I can do here. Are these sorts of bottlenecks an intrinsic cost of passing variables between managed and unmanaged code? Or are there any tricks I can employ to speed up the process?

Marshalling char * to StringBuilder in C#

I know this has already been discussed but after 3days I haven't figured out why I keep getting a blank string after calling a C function wrapped in a DLL in C# :
[DllImport(DllName, CharSet = CharSet.Ansi)]
public static extern int QA_FormatExample(int viHandle, int viExample [MarshalAs(UnmanagedType.LPStr)]StringBuilder rsComment)
In Main :
StringBuilder rsComment = new StringBuilder(256);
apiWrap.QA_FormatExample(currentInstance, viExample, rsComment);
System.Diagnostics.Debug.WriteLine(rsComment);
Function's signature :
int __stdcall QA_FormatExample(int, int, char*);
The function QA_FormatExample initializes rsComment once called but I keep getting rsComment blank. Any suggestion would be really appreciated, thank you.
In order to match the C++ the function should be declared like this:
[DllImport(DllName)]
public static extern int QA_FormatExample(
int viHandle,
int viExample,
StringBuilder rsComment
);
And you call it like this:
StringBuilder comment = new StringBuilder(256);
int res = QA_FormatExample(handle, example, comment);
Which is very close to what you present in the question. So, if your calls to the function do not work, then your problems lie in the code that we cannot see. What I show above is the correct way to marshal text from native to managed via char*.
I would point out that it is a little unusual that you do not pass the length of buffer to the function. Without that information, the maximum length must be hardcoded.

Calling C++ code from C# error using references in c++

In my C++ dll named "scandll.dll" I have the following function
extern "C" __declspec(dllexport) void scanfile(char * returnstring)
{
strcpy(returnstring, "return string");
}
in my C# code I'm doing this
[DllImport("scandll.dll", CharSet = CharSet.Ansi, SetLastError = true )]
public static extern int scanfile(ref IntPtr strReturn);
and this is the method that I'm using to get the value from the dll
public void Scan()
{
string filename = "";
IntPtr ptr = new IntPtr();
scanfile(ref ptr);//Here i get the error
filename = Marshal.PtrToStringAnsi(ptr);
}
I get an exception thrown as "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I'm following this link
Calling C++ code from C# error using references in c++ ref in c#
Any help would be appreciated.
Thanks
Your C++ code is wrong - all it's doing is changing the value of the pointer which has been passed to it to point to a constant string. The caller knows nothing about that change.
It's the same as if you'd done this:
void MyCfunction(int number)
{
number = 3;
}
and then hoped that a caller of that function would somehow see the number '3' anywhere.
You could do something like this in C++:
void MyCFunction(char* pReturnString)
{
strcpy(pReturnString, "Hello, world");
}
but you also need to make sure on the C# side that you had provided a buffer with pReturnString pointing to it. One way to do this by using a StringBuilder, and then having your C# declaration of the C function take a parameter like this:
[MarshalAs(UnmanagedType.LPStr)] StringBuilder returnString
You need to reserve space in the StringBuilder before calling into the C++ function.
In real life, C/C++ functions like this pretty much always take an additional length parameter, which allows the caller to tell the callee the maximum length of string it's allowed to copy into the buffer. There is no consistency of convention about whether the length includes the terminating \0 or not, so people are often careful about running right up to the end of the buffer.
memcpy ( destination, * source, size_t num );
Try this to assign the value to returnstring = "rturn name";

Marshalling string from c# to c++

I am new in microsoft world.
I have lot of problem trying to pass a simple string from c# to dll/c++
I have read a lot of post and documentation about but the problem is the same.
C++ code
extern "C" __declspec(dllexport) int Init( long l , char* url );
C# code
[DllImport("MCRenderer.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = false)]
public static extern int Init(long a, StringBuilder url);
Init(hndl.ToInt64(), str );
what haeppen is that long value is passed correctly while string parameter is
0x00000000 <Bad Ptr>
can you help me ... Iam really confused
thanks!!
AG
You should pass a string, url should be of type string and not StringBuilder.
It's because you're marshalling incorrectly: long and int in C++ are both (usually) int in C#.
From MSDN, you need to:
The only caveat is that the StringBuilder must be allocated enough space for the return value, or the text will overflow, causing an exception to be thrown by P/Invoke
Also from Marshaling between Managed and Unmanaged Code
Don't pass StringBuilder by reference (using out or ref). Otherwise, the CLR will expect the signature of this argument to be wchar_t ** instead of wchar_t *, and it won't be able to pin StringBuilder's internal buffer. Performance will be significantly degraded.
Use StringBuilder when the unmanaged code is using Unicode. Otherwise, the CLR will have to make a copy of the string and convert it between Unicode and ANSI, thus degrading performance. Usually you should marshal StringBuilder as LPARRAY of Unicode characters or as LPWSTR.
Always specify the capacity of StringBuilder in advance and make sure the capacity is big enough to hold the buffer. The best practice on the unmanaged code side is to accept the size of the string buffer as an argument to avoid buffer overruns. In COM, you can also use size_is in IDL to specify the size.
So in your example, you need to specify the native size as the parameter of the StringBuilder like this StringBuilder str = new StringBuilder(SIZE_OF_NATIVE_STRING);
Try using LPCSTR in the dll function parameter
extern "C" __declspec(dllexport) int Init( long l , **LPCTSTR** url );
Here is a good example that I found on how to do this.
C++:
extern "C" __declspec(dllexport) void doSomething(LPCTSTR asString)
{
std::cout << "here" << (wchar_t)asString << endl;
system ("pause");
}
C#:
class Program
{
static void Main(string[] args)
{
String myString = "String in C#";
doSomething(myString);
}
private const String path = #"C:\testdll2.dll"; //Make sure that the DLL is in this location
[DllImport(path)]
private static extern void doSomething(string asString);
}

Calling DLL function with char* param from C#?

I have a C++ dll that I need to call from C#. One of the functions in the dll requires a char* for an input parameter, and another function uses a char* as an output parameter.
What is the proper way to call these from C#?
string should work if the parameter is read-only, if the method modifies the string you should use StringBuilder instead.
Example from reference below:
[DllImport ("libc.so")]
private static extern void strncpy (StringBuilder dest,
string src, uint n);
private static void UseStrncpy ()
{
StringBuilder sb = new StringBuilder (256);
strncpy (sb, "this is the source string", sb.Capacity);
Console.WriteLine (sb.ToString());
}
If you don't know how p/invoke marshaling works you could read http://www.mono-project.com/Interop_with_Native_Libraries
If you are only conserning with strings, read only the section: http://www.mono-project.com/Interop_with_Native_Libraries#Strings
Just using strings will work fine for input parameters, though you can control details about the string with the MarshalAs attribute. E.g.
[DllImport("somedll.dll", CharSet = CharSet.Unicode)]
static extern void Func([MarshalAs(UnmanagedType.LPWStr)] string wideString);
As for returning char* parameters, that's a little more complex since object ownership is involved. If you can change the C++ DLL you can use CoTaskMemAllocate, with something like:
void OutputString(char*& output)
{
char* toCopy = "hello...";
size_t bufferSize = strlen(toCopy);
LPVOID mem = CoTaskMemAlloc(bufferSize);
memcpy(mem, toCopy, bufferSize);
output = static_cast<char*>(mem);
}
The C# side then just uses an 'out string' parameter, and the garbage collector can pick up the ownership of the string.
Another way of doing it would be to use a StringBuilder, but then you need to know how big the string will be before you actually call the function.
Not sure this works, but have you tried with
StringObject.ToCharArray();
Not sure about initialising the String from char * tho. Mybe just assign to a string object, its worth a try.
Have you tried StringBuilder? I found this in a Google search:
[DllImport("advapi32.dll")]
public static extern bool GetUserName(StringBuilder lpBuffer, ref int nSize);
If you post the call you're making we can help you assemble it.
If the DLL function is expecting an allocated buffer of char* (not a wide/multibyte buffer) then the following will work:
[DllImport("somedll.dll", CharSet = CharSet.Ansi)]
static extern void TheFunc(byte[] someBuffer, int someSize);
Here a buffer allocated in c# is passed to TheFunc which fills it with a string of characters (of type char). Bytes aren't "interpreted" by C# they are treated like 8 bit integers, so are perfect for holding 8 bit characters.
An example code snipped would therefore be:
byte[] mybuffer;
int bufSize;
bufSize = 2048;
mybuffer = new byte[bufSize];
TheFunc(mybuffer, bufSize);
string value;
for(value = "", int ix = 0; (mybuffer[ix] != 0) && (ix < bufSize); ix++)
value += (char) mybuffer[ix];
DoSomethingWithTheReturnedString(value);

Categories