I would like to know what the difference is in using a ref return versus a pointer when doing pinvokes. The native method signature that I am calling is:
typedef struct Buffer {
const void* Data;
size_t Length;
} Buffer;
__declspec(dllexport) extern Buffer* GetNewBuffer();
On the C# end I have the Buffer struct mapped one to one.
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Buffer
{
public IntPtr Data;
public UIntPtr Length;
}
I also have two static methods that call the native function implemented like so:
[DllImport("testlib", EntryPoint = "GetNewBuffer")]
public static extern unsafe Buffer* GetNewBufferPointer();
[DllImport("testlib", EntryPoint = "GetNewBuffer")]
public static extern ref Buffer GetNewBufferReference();
These two methods compile down to the following MSIL:
.method public hidebysig static pinvokeimpl("testlib" as "GetNewBuffer" winapi)
valuetype TestInterop.Buffer* GetBufferPointer () cil managed preservesig
{
} // end of method Native::GetBufferPointer
.method public hidebysig static pinvokeimpl("testlib" as "GetNewBuffer" winapi)
valuetype TestInterop.Buffer& GetBufferReference () cil managed preservesig
{
} // end of method Native::GetBufferReference
Using these methods and accessing the returned structs members would look something like this:
unsafe
{
Buffer* bufferPointer = Native.GetBufferPointer();
var dataFromPointer = bufferPointer->Data;
}
ref Buffer bufferReference = ref Native.GetBufferReference();
var dataFromReference = bufferReference.Data;
Is there any difference between these two methods besides the different syntax used to store the return value and access its members? When using ref return with pinvoke, does the GC do anything special that I should be aware of?
Related
I am trying to access and make changes to elements in a struct that are in a DLL. I have followed this example on how to use struct from a DLL: http://nawatt.com/index.php/corporate/blog/78-using-c-dlls-in-c
I have been successful in making changes to non-array variables, but whenever I try to make changes to an array I get a Runtime Error.
This is an example of my C DLL code:
//lib_qrs.dll
#ifdef __cplusplus
extern "C" {
#endif
typedef struct TEST_STRUCT
{
unsigned short check[5];
} test_struct;
__declspec(dllexport) void __stdcall test(test_struct *test, unsigned short val){
// This is an example of what DOES NOT WORK
test->check[0]=val+1;
test->check[1]=val+2;
test->check[2]=val+3;
test->check[3]=val+4;
test->check[4]=val+5;
}
#ifdef __cplusplus
}
#endif
This is an example of my C# code:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT
{
public UInt16[] check;
}
public class Program
{
[DllImport("lib_qrs.dll", EntryPoint="test", CallingConvention = CallingConvention.StdCall)]
public static extern void test(ref TEST_STRUCT test, int val);
public TEST_STRUCT testStruct = new TEST_STRUCT();
static void Main(string[] args)
{
testStruct.check=new UInt16[5];
// WHERE ERROR OCCURS
test(ref testStruct, 5);
}
}
The error that I get is:
*"An unhandled exception of type 'System.AccessViolationException' occurred in Test.exe
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I understand that I have to be very careful with memory allocation when I replicate my structure in C#, but I don't know what I am doing wrong or how I can fix this array issue.
Does anyone have an idea of how I could get around this?
The default marshaling for that array is unsigned short*, not unsigned short[]. You'll need to apply the [MarshalAs] attribute to tell the pinvoke marshaller about it. Fix:
[StructLayout(LayoutKind.Sequential)]
public struct TEST_STRUCT {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public UInt16[] check;
}
And since you are returning values in the array, you also have to tell the pinvoke marshaller that it needs to copy the array values back. That requires the [Out] attribute:
[DllImport("lib_qrs.dll")]
public static extern void test([Out] ref TEST_STRUCT test, int val);
Do note that neither is necessary at all if the you just declare the argument as ushort[], assuming that the structure doesn't contain any additional fields.
I have this structure in C
struct system_info
{
const char *name;
const char *version;
const char *extensions;
bool path;
};
And this function signature
void info(struct system_info *info);
I'm trying to use this function like this:
[DllImport("...")]
unsafe public static extern void info(info *test);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public unsafe struct info
{
public char *name;
public char *version;
public char *extensions;
public bool path;
}
And on my main:
info x = new info();
info(&x);
I'm getting an error, pointers cannot reference to marshaled structures, how can I manage this?
There's no need at all to use unsafe here. I would do it like this:
public struct info
{
public IntPtr name;
public IntPtr version;
public IntPtr extensions;
public bool path;
}
And then the function is:
[DllImport("...")]
public static extern void getinfo(out info value);
You may need to specify the Cdecl calling convention, depending on the native code.
Call the function like this:
info value;
getinfo(out value);
string name = Marshal.PtrToStringAnsi(value.name);
// similarly for the other two strings fields
Since there is no mention of string length in the native code you posted, I'm assuming that the strings are allocated by the native code and that you don't need to to anything to deallocate them.
Solved by using ref instead of *test like Hans Passant mentioned
[DllImport("...")]
unsafe public static extern void info(ref system_info test);
I want to pass a structure to C function and I write the following code.
When I run it, the first function - Foo1 is working and then function Foo gets an exception. Can you help me to understand what the problem is?...
The C code:
typedef struct
{
int Size;
//char *Array;
}TTest;
__declspec(dllexport) void Foo(void *Test);
__declspec(dllexport) int Foo1();
void Foo(void *Test)
{
TTest *X = (TTest *)Test;
int i = X->Size;
/*for(int i=0;i<Test->Size;Test++)
{
Test->Array[i] = 127;
}*/
}
int Foo1()
{
return 10;
}
The C# code:
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
[StructLayout(LayoutKind.Sequential)]
public class TTest
{
public int Size;
}
class Program
{
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern void Foo(
[MarshalAs(UnmanagedType.LPStruct)]
TTest lplf // characteristics
);
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto)]
public static extern int Foo1();
static void Main(string[] args)
{
TTest Test = new TTest();
Test.Size = 25;
int XX = Program.Foo1();
Program.Foo(Test);
}
}
}
To the downvoters: This answer solves two issues: the immediate issue of the calling convention/the MarhsalAs attribute, and the issue he will soon find where his TTest parameter won't work if he takes my suggestion of turning TTest into a struct.
Your native code is asking for a void*, which in C# is an IntPtr. First you should define TTest as a struct and not a class. Second, you should change the declaration of Foo to:
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(IntPtr lplf);
And third, you should pin the TTest using the fixed keyword and pass it's pointer to Foo. If you're using a class, you can use Marhsal.StructureToPtr to get an IntPtr from your TTest.
This provides the same functionality on both sides, where a pointer to any type can be passed in. You can also write overloads with all the class types that you want to use since they all equate to void* on the native side. With a struct, your parameters would be prepended with a ref.
What I'm curious about is why your native code wants a void* instead of a TTest* when the first thing you do in the unmanaged code is cast to a TTest*. If you switched the parameter to a TTest*, then providing identical functionality becomes simpler. You declaration would become:
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void Foo(ref TTest lplf);
And you would call the function as Program.Foo(ref Test);
If you're using the class, the ref isn't necessary as classes are reference types.
You are using C call so you need to specify CallingConvention.Cdecl
[DllImport(#"C:\.net course\unmanaged1\unmanaged3\Debug\unmanaged3.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
By default its stdcall in C# pinvoke as i remember; You can also do change C code instead and leave your C# code as is like in below
__declspec(dllexport) void __stdcall Foo(void *Test);
But for me best is to both declare __cdecl (or stdcall) in your C export and CallingConvention.Cdecl (or stdcall) in your C# code to keep convenience. You can check https://learn.microsoft.com/en-gb/cpp/cpp/argument-passing-and-naming-conventions?view=vs-2017 and https://learn.microsoft.com/en-gb/dotnet/api/system.runtime.interopservices.callingconvention?view=netframework-4.7.2 for further info
I am attempting to call a C++ function in a DLL from C# via interop. The DLL was written by someone else. The function signature is as follows:
AXI_DLL_EXPORT int __stdcall GetFileType(StringParam *stringParam, int *fileType)
StringParam is a struct defined as follows:
struct StringParam
{
int size; // 4 byte integer for the size of the string buffer
LPWSTR buff; // Pointer to a wide char buffer
}
This struct is used for passing strings back and forth. It can be used as both in and out parameters. The purpose of the size field in the struct is primarily for returning strings to the caller and to indicate the size of the buffer required. If the size of the buffer (provided by the caller) is too small to hold the string the caller can be informed of this and can then provide a larger buffer in a successive call to the function.
In C# I created a corresponding struct as follows:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct StringParam
{
public int Size;
[MarshalAs(UnmanagedType.LPWStr)]
public string Text;
public StringParam(string text)
{
this.Text = text;
this.Size = text.Length;
}
}
I declared the DllImport function call as follows:
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetFileType(StringParam stringParam, out int fileType);
The call fails with the following error:
A call to PInvoke function '... GetFileType' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
How should I declare the struct and call the native function from C#?
public static extern int GetFileType(ref StringParam stringParam, out int fileType);
Have you tried :
[DllImport("Custom.dll", CharSet = CharSet.Unicode)]
public static extern int GetFileType(StringParam stringParam, ref int fileType);
And I think you have to pin the ref int before you pInvoke.
I have a C++ struct
struct UnmanagedStruct
{
char* s;
// Other members
};
and a C# struct
struct ManagedStruct {
[MarshalAs(UnmanagedType.LPStr)]
string s;
// Other members
}
the C++ library exposes
extern "C" UnmanagedStruct __declspec(dllexport) foo( char* input );
And it is imported like
[DllImport("SomeDLL.dll", CharSet = CharSet.Ansi)]
static extern ManagedStruct foo( string input );
However when I call this function I get
MarshalDirectiveException was unhandled
Method's type signature is not PInvoke compatible.
The thing is, this function call works if I remove the char* s and the string s from the structs.
For this type of scenario, do not use a String directly. Instead switch the type to be an IntPtr value and use Marshal.PtrToStringAuto/Ansi/Uni as appropriate. In this case, since your native code uses char*, PtrToStringAnsi is the best choice.
struct ManagedStruct {
IntPtr s;
public string sAsString { get { return Marshal.PtrToStringAnsi(s); } }
}