How to pass String into integer Pointer in C# - c#

I have method which has IntPtr as parameter. Now I have a string "EXT101" which I need to pass into the function parameter as argument. Following is my function and what I tried but its not working.
unsafe private static extern int lineDevSpecific(IntPtr hLine);
string vline="Ext101";
int* hline=&vline;
How to pass hline as argument?

Note that IntPtr isn't a pointer to int. It is pointer represented by signed native integer. Maybe you want to obtain the pointer to this string in memory:
string vline="Ext101";
IntPtr hline = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(vline);
lineDevSpecific(hline);

At a guess, I'd say EXT101 is a pre-processor defined constant. There's no easy way of getting that, as it ceases to exist as a name after being compiled. You'll need to take a look at the header file for whatever library you're trying to link with to find the value.

Related

What is the equivalence of LPCTSTR * in C#

I have an unmanaged C++ function residing inside a Dll that I call from my C# application. Here is the signature of the function:
GetCrashMeasurement(LPCTSTR channelName, LPCTSTR properties, LPCTSTR * Values, HANDLE error)
where channelName and properties are input parameter [in]; and Values is an output parameter[out].
I'm also using the Platform invoke from my C# application as follows:
[DllImport("DrvCrashHAL.dll", EntryPoint = "coCRAL_GetCrashMeasurements")]
public static unsafe extern CoStatus GetCrashMeasurements(string sChannel, string sMeasurements, ref string sValues, IntPtr hError);
From my C# application I call the function in the following way:
string Text = "";
intptr herror = intptr.zero;
GetCrashMeasurements("channelname","",ref Text,herror);
But then my program stops exactly at this line without throwing any exception, and all I see in the output window is the following message:
Critical error detected c0000374
Critical error detected c0000374
The program '[4964] ProjectX.exe: Managed' has exited with code 0 (0x0).
The program '[4964] ProjectX.exe: Native' has exited with code 0 (0x0).
My guess is, the problem lies in the marshalling type of LPCTSTR.
Could anyone please tell what I`m doing wrong or point me to the right direction?
Thanks in advance.
As far as I can tell from what you've provided, parameters 1, 2 and 4 should marshall just fine. The problem is likely to be parameter 3.
Error c0000374 is heap corruption. That would fit.
The issue in trying to solve your problem is to know what that 3rd parameter does. It's declared as a kind of char**, and as an [out] parameter I would expect it to be output as a char*. In other words, I would expect the function to actually output a pointer, not a string, with the expectation that the caller will receive that pointer and copy a (null-terminated) string from it.
You can investigate calls like this by redefining the entry point as an int* or int** and passing in an intptr. That way you should (a) fix the crash and (b) get to see the output value (as an int or pointer). You may have to do your own marshalling for this one. Given a pointer to a COM BSTR you can easily retrieve the value you need.
However, according to my reading of the Interop spec, this should work correctly with a declaration of out string sValues and not ref. The heap corruption would be happening when the function is called, not when it returns.
This is helpful, if rather technical: http://msdn.microsoft.com/en-us/magazine/cc164193.aspx.
I suggest you try that first. If not, maybe try the debugging route to see what pointer you are getting.
As #jester noticed LPCTSTR* Values sounds more like an array of strings. Can you try to change your third parameter to be an array of strings?
public static unsafe extern CoStatus GetCrashMeasurements(string sChannel, string sMeasurements, string[] sValues, IntPtr hError);
and call it as follows
GetCrashMeasurements("channelname","",new[] { Text },herror);
Also you can specify MarshalAs on sValues.
public static unsafe extern CoStatus GetCrashMeasurements(string sChannel,
string sMeasurements,
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPTStr)]
string[] sValues,
IntPtr hError);

Trying to get a string value from native dll

I have a native dll that I need to set a string value of a parameter. Below is the c++ method signature
__declspec(dllexport) void __stdcall getDetails(_Out_ LPTSTR lpDetails, _In_ UINT uSize)
c++ method
I've hardcoded this for the return lpDetails=L"test";
My c# signature
[return: MarshalAs(UnmanagedType.I1)]
[DllImport("test.dll",CharSet=CharSet.Auto)]
static extern bool getDetails(StringBuilder result, System.UInt32 uSize);
c# code
StringBuilder b = new StringBuilder(1000);
getDetails(b, 255);
m = b.ToString(); //always ""
Console.WriteLine(m);
When I step through the code I see the value (lpDetails) get set to the correct value. The problem is on the c# all I ever see is an empty string.
I have tried a things I found from other posts but so far no luck.
There are few things wrong here:
native method is shown with C signature, not C++ signature (I am
nitpicking here :)
lPDetails parameter is IN, not OUT, if you want
function to write details to a preallocated location (as it seems
from the second argument which should denote the length of the
allocated buffer)
your implementation lpDetails=L"test" is broken. If you provide the preallocated buffer to the function (as your signature implies) you need to _tcscpy the details into lpDetails. If you want to allocate the string inside the function (as your implementation implies, you need to declare lpDetails as LPTSTR*, but this is a bad design as caller might not now how to free it (and in this case indeed it cant as "test" points to a static memory in data segment and not to the heap.
This wouldn't work even without Interop:
LPTSTR pCallerData; // a pointer variable
void getDetails(LPTSTR lpData, /*what do you use second param for? */)
{
// lpData is a COPY of pCallerData, not a pointer to it,
// so you are not modifying pCallerData, but a local variable
lpData = "test";
// after the function exits pCallerData still has the same value as before
}_
better implementation would be:
LPTSTR txt[256];
getDetails(txt, 256);
// ----
void getDetails(LPTSTR lpData, int size)
{
_tcscpy(lpDetails, _T("Test"));
// or more realistically _tcsncpy and properly check
// if details string is longer than size argument
}_

How do I marshal wchar_t* from C++ to C# as an out parameter or return value?

I have tried to do this in many ways, but none is working. Does anyone have a correct example for this? I just want to move the wchar_t* value from a function to the C# level.
This isn't as difficult as you think it is... What is wchar_t*? What value does that type typically represent? A string. It's the equivalent to the LPWSTR type defined in windows.h.
So, you marshal it as a string type. However, since it's an out parameter (or a return value), you'll need to use the StringBuilder class on the C# end, rather than the string type.
The P/Invoke syntax would look something like this:
[DllImport("MyLib.dll")]
public static extern void MyFunction(StringBuilder str);
And to use it, you first declare an instance of the StringBuiler class with the appropriate capacity, and then call the function:
StringBuilder myString = new StringBuilder(255);
MyFunction(myString);
Remember that the unmanaged C++ code must free the string in order to prevent a memory leak. It's the only one with access to the unmanaged memory area where the string was allocated.

Passing pointer to a pointer to a struct as a function parameter in C#

How do I pass a pointer to a pointer to a struct as a function parameter in C#?
I am trying to use a function from a DLL which has one of its arguments as a double pointer. For e.g.,
struct mystruct
{
int a;
char b;
};
[DllImport("mydll.dll")]
private static extern int func_name(int a, char b, mystruct** c);
I guess the correct way to use a pointer like int *a is ref int a. Is there anything similar for int **a?
The CLR equivalent for a pointer to a pointer is ref IntPtr.
An IntPtr is a pointer to an integer, and when you pass it by ref, it's transferred by reference, making it a pointer to a pointer.
You may also need to use the Marshal.PtrToStructure method to convert the pointer to your structure object.
See the section entitled "Manual marshalling" near the bottom of this article for more information.
I would work differently.
Any pointer should be used as
System.IntPtr
and I would then use
Marshal.PtrToStructure
for the actual conversion of the pointer to actual structure. Otherwise I would also consider to copy field by field with offsets, which is sometimes easier to debug.

char * in managed code?

I have a c++ plus method that generates a value. i am calling this method from a c# application.
The C++ method is like this:
extern "C" REGISTRATION_API char * generate(char dIn[],char dOut[])
The generate method returns an array of chars (sOut[]=returnvalue; return sOut;)
Now I'm calling this method from my c# app:
[DllImport("mydll.dll")]
static extern string generate(string sIn, string sOut);
As you can see, the return type in c# is string. What is happening is that the returned value in c# is not correct and it is corrupted. (The value is correct inside the generate method, but whenever i call it from c# to extract it, i get some erroneous value.)
Is it OK that my method in C# has a string return value while in c++ it's a char*?
Please share your comments, it is urgent, thanks.
Unfortunately, char* can be somewhat ambiguous. (Literally, it's a pointer to a single character, where you may or may not be able to get at other characters by dereferencing the pointer like an array.) Usually, though, it's a pointer to a null-terminated string of single bytes in ANSI encoding (what Microsoft calls an LPStr). By default, P/Invoke marshals instances of System.String as the BStr type, which is like a Unicode Pascal string (with a length at the beginning). You'll need to use the MarshalAs attribute to explicitly specify how you want the strings marshaled between managed and unmanaged code.
Most likely, you'll want to declare it like so:
[DllImport("mydll.dll")]
[return: MarshalAs(UnmanagedType.LPStr)]
static extern string generate(
[MarshalAs(UnmanagedType.LPStr)] string sIn,
[MarshalAs(UnmanagedType.LPStr)] out string sOut);
However, your function signature is confusing: is it returning the string, or is it setting an output variable? You may have to adjust the P/Invoke declaration to fit your needs.
Is it OK that my method in C# has a
string return value while in c++ it's
a char*?
It depends really.
If that C++ method is allocating the memory for the string and expecting the caller to deallocate it then you have problems...big problems. You could try calling Marshal.FreeHGlobal on it, but there is no guarentee that the C++ method actually allocated the memory on the global heap. Most APIs have you feed the pointer back into one of their methods to free the memory.
It is also possible that the char * is pointing to memory which does not need to be freed. In that case using a string should be fine.
You could have the C# declaration return an IntPtr and then call one of the Marhsal.PtrToStringXXX methods.
This method cannot be called safely from a native C++ application either. It returns a pointer to an array on the stack frame. Any call to another function will overwrite that stack frame and corrupt the array.
This probably works by accident in a C++ program right now because there is no function call after obtaining the return value of this function. This kind of luck is no longer available when the P/Invoke marshaller sits in between.
You will have to redesign the C++ function. You should give it arguments that allows the caller to pass its own buffer to be filled with the result. For example:
extern "C" void __stdcall generate(const char* input, char* output, int outputSize)
Call this in C# code by passing a StringBuilder for the output argument, properly initialized with a sufficient Capacity to receive the string. The outputSize argument ensures that the C++ function can avoid writing past the end of the buffer and corrupt the garbage collected heap. Don't ignore it.

Categories

Resources