I'd like to know a way how to marshal 2 Classes that I made in C++ in C# language.
(a tutorial link would be perfect, I tried to google this for myself, but my english knowledge is not that advance, to search for the right thing. I am new to marshaling from C++ to C#)
Well basically what I have is something like this:
C++
EXPORT_API CarClass* DLL_AddCar();
EXPORT_API CarWheel* DLL_AddWheel();
EXPORT_API void DLL_GiveWheelToCar(CarClass* car,CarWheel* wheel);
C#
public class Car
{
#region Members
private IntPtr nativeCarObject;
#endregion Members
public Car()
{
this.nativeCarObject = Sim.DLL_AddCar();
}
// ---> this part is not working
//#region Wrapper methods
//public void GiveWheel(Wheel myWheel){Sim.DLL_GiveWheelToCar(this.nativeCarObject,myWheel);}
//#endregion Wrapper methods
}
public class Wheel
{
#region Members
private IntPtr nativeCarObject;
#endregion Members
public Wheel()
{
this.nativeCarObject = Sim.DLL_AddWheel();
}
}
internal class Sim
{
public const string pluginName = "MyDLL";
#region PInvokes
[DllImport(pluginName)] public static extern IntPtr DLL_AddCar();
[DllImport(pluginName)] public static extern IntPtr DLL_AddWheel();
[DllImport(pluginName)] public static extern void DLL_GiveWheelToCar(IntPtr car,IntPtr wheel);
#endregion PInvokes
}
Now My question is where would I use "DLL_GiveWheelToCar" ?
The method I tryed is commented out, because it didnt work, is it a logic error or should I change the way I'm marshaling?
Thanks for advice.
public void GiveWheel(Wheel myWheel){
Sim.DLL_GiveWheelToCar(this.nativeCarObject, myWheel);
}
Here you are passing myWheel which is of type Wheel to a function that expects an IntPtr. You need to pass the private field of the Wheel class that is currently (mis)named nativeCarObject.
Other points:
As I hinted above, the nativeCarObject member of Wheel should be renamed as nativeWheelObject.
The DLL looks like it uses the cdecl calling convention. You should specify that in your p/invoke declarations.
It looks like you code leaks the objects that the DLL supplies. I suspect you need to supply some tidy up helpers.
The code might look like something this:
public class Car
{
public readonly IntPtr nativeCarObject = Sim.DLL_AddCar();
public void GiveWheel(Wheel myWheel)
{
Sim.DLL_GiveWheelToCar(this.nativeCarObject, myWheel.nativeWheelObject);
}
}
public class Wheel
{
public readonly IntPtr nativeWheelObject = Sim.DLL_AddWheel();
}
internal class Sim
{
public const string pluginName = "MyDLL";
[DllImport(pluginName, CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr DLL_AddCar();
[DllImport(pluginName, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr DLL_AddWheel();
[DllImport(pluginName, CallingConvention = CallingConvention.Cdecl)]
public static extern void DLL_GiveWheelToCar(IntPtr car, IntPtr wheel);
}
This code is still leaking those objects. I'll let you sort that out.
Related
I have never created a P/Invoke wrapper method in C# because there's a way to do it "the managed way" using .NET Framework classes.
However, I have the need to invoke Win32 API function and I discovered that it's not as easy as I thought.
The function in IsWowProcess2, and the code to call it from C++ is illustrated here.
http://www.rudyhuyn.com/blog/2017/12/13/how-to-detect-that-your-x86-application-runs-on-windows-on-arm/
I've created a silly program to test it, but of course, it doesn't work:
class Program
{
static void Main(string[] args)
{
var result = IsWowProcess2();
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = false, PreserveSig = true)]
private static extern MyStruct IsWowProcess2();
}
internal class MyStruct
{
public ushort One { get; set; }
public ushort Two { get; set; }
}
What's the correct way to create the wrapper method and how to invoke it?
Start by reading the documentation. This gives the signature as:
BOOL IsWow64Process2(
HANDLE hProcess,
USHORT *pProcessMachine,
USHORT *pNativeMachine
);
As a pinvoke that is:
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool IsWow64Process2(
IntPtr process,
out ushort processMachine,
out ushort nativeMachine
);
I've no idea where your attempt came from but it looks nothing like the correct declaration, and in fact even the name of the function is wrong. Interop programming is all about detail and matching one binary interface to another. You don't get to invent the interface. Details like the parameters to be passed, the name of the function, etc. all need to match.
You can call it like this:
ushort processMachine;
ushort nativeMachine;
if (!IsWow64Process2(GetCurrentProcess(), out processMachine, out nativeMachine))
throw new Win32Exception();
You also need a declaration for GetCurrentProcess. You can use this:
[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();
I have been given an external c++ dll that I need to load and use in my C# project. The dll comes with a header file, which is this (simplified / anonymized):
typedef struct
{
int (*GetVersion)();
int (*StartServer)(const char *ip, int port);
void (*OnRemoteError)(void *caller, int error);
} RemoteServerPluginI;
extern "C" __declspec(dllexport) RemoteServerPluginI* GetServerPluginInterface();
I have a few questions on how to use this in my C# project:
do I translate "void*" with object?
do I translate the char* array to a string or to a char[] ?
OnRemoteError is supposed to be a callback; to register my callback, should I simply assign my callback function to this field?
Any link to the relevant documentation is most appreciated.
I might have figured it out, after a ton of reading and helpful pointers both here on SO and reddit (a special thank you to this comment).
BIG DISCLAIMER: at this time I haven't been able to interface with the actual system, so this might be wrong. However I've successfully loaded the dll and read the version, which makes me think I might have solved it. If anything comes up I will update the answer.
First thing is to declare a struct to map the C++ struct into our C# code.
We can use the MarshalAs attribute to tell the marshaller that these delegates are really just function pointers:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetVersionT();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int StartServerT(string ip, int port);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void OnRemoteErrorT(object caller, int error);
public struct RemoteServerPluginI
{
[MarshalAs(UnmanagedType.FunctionPtr)] public GetVersionT GetVersion;
[MarshalAs(UnmanagedType.FunctionPtr)] public StartServerT StartServer;
[MarshalAs(UnmanagedType.FunctionPtr)] public OnRemoteErrorT OnRemoteError;
// a lot of other methods not shown
}
Then we make a helper class that loads the DLL using DLLImport and calls the method that was defined in the dll.
This is easily done using an extern method:
[DllImport("data/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetServerPluginInterface();
Note that we had to specify the calling convention.
Another important thing to note: this method returns an IntPtr object.
Luckily we are done now, we just need to cast this to the correct type:
var ptr = GetServerPluginInterface();
var server = (RemoteServerPluginI)Marshal.PtrToStructure(ptr, typeof(RemoteServerPluginI));
At this point I just wrapped everything into a convenience class to manage access, and voilĂ !
Here is the final code:
public static class IntPtrExtensions
{
// I'm lazy
public static T ToStruct<T>(this IntPtr ptr)
=> (T)Marshal.PtrToStructure(ptr, typeof(T));
}
public static class RemoteControlPlugin
{
[DllImport("path/to/remoteplugin.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr GetServerPluginInterface();
private static RemoteServerPluginI? _instance = null;
public static RemoteServerPluginI Instance =>
(RemoteServerPluginI)(
_instance ??
(_instance = GetServerPluginInterface().ToStruct<RemoteServerPluginI>())
);
}
internal static class Program
{
private static void Main(string[] args)
{
var remoteServer = RemoteControlPlugin.Instance;
Console.WriteLine(remoteServer.GetVersion()); // Easy!
}
}
I have a problem with the glfwSetCharCallback function. Whenever I call it, the glfwPollEvents throws an AccessViolationException saying: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I have created a very simple wrapper just to demonstrate the problem:
using System;
using System.Runtime.InteropServices;
using System.Security;
namespace Test
{
public delegate void GlfwCharCallback(GlfwWindow window, Char character);
[StructLayout(LayoutKind.Explicit)]
public struct GlfwMonitor
{
private GlfwMonitor(IntPtr ptr)
{
_nativePtr = ptr;
}
[FieldOffset(0)] private readonly IntPtr _nativePtr;
public static readonly GlfwMonitor Null = new GlfwMonitor(IntPtr.Zero);
}
[StructLayout(LayoutKind.Explicit)]
public struct GlfwWindow
{
private GlfwWindow(IntPtr ptr)
{
_nativePtr = ptr;
}
[FieldOffset(0)] private readonly IntPtr _nativePtr;
public static GlfwWindow Null = new GlfwWindow(IntPtr.Zero);
}
public class Wrap
{
[DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
private static extern Int32 glfwInit();
[DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
internal static extern GlfwWindow glfwCreateWindow(Int32 width, Int32 height,
[MarshalAs(UnmanagedType.LPStr)] String title,
GlfwMonitor monitor, GlfwWindow share);
[DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
internal static extern void glfwSetCharCallback(GlfwWindow window, GlfwCharCallback callback);
[DllImport("GLFW3", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
internal static extern void glfwPollEvents();
public static Boolean Init()
{
return glfwInit() == 1;
}
public static GlfwWindow CreateWindow(Int32 width, Int32 height, String title, GlfwMonitor monitor,
GlfwWindow share)
{
return glfwCreateWindow(width, height, title, monitor, share);
}
public static void SetCharCallback(GlfwWindow window, GlfwCharCallback callback)
{
glfwSetCharCallback(window, callback);
}
public static void PollEvents()
{
glfwPollEvents();
}
}
}
And I call it like I would GLFW:
using System;
namespace Test
{
class Program
{
static void Main()
{
Wrap.Init();
var window = Wrap.CreateWindow(800, 600, "None", GlfwMonitor.Null, GlfwWindow.Null);
Wrap.SetCharCallback(window, (glfwWindow, character) => Console.WriteLine(character));
while(true)
{
Wrap.PollEvents();
}
}
}
}
The character is printed into the console and I get the AccessViolationException.
What am I doing incorrectly? All the DllImports specify the CDecl calling convention (I've tried the others on both PollEvents and SetCharCallback), I've tried all CharSets on the SetCharCallback function and nothing worked.
Could someone please help me?
Edit:
Here is the GLFW3 Dll that I'm using:
http://www.mediafire.com/?n4uc2bdiwdzddda
Edit 2:
Using the newest "lib-msvc110" DLL made glfwSetCharCallback work. glfwSetKeyCallback and glfwSetCursorPosCallback do not work and continue to throw AccessViolationException. I will attempt to figure out what makes CharCallback so special, but honestly, I've gone through the GLFW source code through and through and all the functions seem to do their thing in the same way. Maybe there's something I'm overlooking.
Edit 3:
I've tried everything, cdecl, stdcall, all the charsets, all versions of the dll, all combinations of arguments, etc. I've made sure that the callbacks are not disposed by storing a reference to them. I've also tried to purposefully crash the application by not reserving any space of arguments of glfwSetCharCallback (which works as of now) and I haven't managed to do it. This leads me to believe that the arguments themselves have no bearing on the application.
The thing that really makes me think the fault is with GLFW itself is the fact that if I compile against the x86-64 dll, everything works perfectly. It is a little bit strange to use cdecl on x86-64, because MSDN specifically states that the only calling convention is fastcall.
It took me a while, but I solved it. All the delegates are describing C# functions, which cannot be called from C. The solution is to make the delegates describe C-like functions, which can be achieved via the UnmanagedFunctionPointer attribute.
Adding the attribute to all delegates (as shown below) solved the problem.
[UnmanagedFunctionPointer(CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public delegate void GlfwCharCallback(GlfwWindow window, Char character);
I have a c# managed project that imports an unmanaged c++ dll. What I wanted was to get logging to work using the logging function I wrote in the C# code. So I added the following to my C# side:
public struct API
{
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void FunctionPointer(string msg);
[DllImport("mydll.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
unsafe public static extern void setLoggerPointer(IntPtr fctPointer);
[DLLImport("mydll.dll", SetLastError = true)]
[return: MarshalAsAttribute(UnmanagedType.I1)] unsafe public static extern bool init();
}
public unsafe class MyInterface
{
public MyInterface()
{
API.FunctionPointer loggerDelegate;
loggerDelegate = new API.FunctionPointer(Logger.LogMessage);
IntPtr loggerPtr = Marshal.GetFunctionPointerForDelegate(loggerDelegate);
API.setLoggerPointer(loggerPtr);
if (API.init())
{
//do stuff
}
}
}
Here is my Logger class definition:
public static class Logger
{
public static void LogMessage(string msg)
{
Console.WriteLine(msg);
fileStream.Write(msg);
}
}
I have the following on the c++ side header:
#define MY_C_API extern "C" __declspec(dllexport);
MY_C_API __declspec(dllexport) void __stdcall setLoggerPointer( void *fctPointer(LPCTSTR msg) );
MY_C_API __declspec(dllexport) bool __stdcall init();
And in the C++ source:
//global variable
void *(*logger)(LPCTSTR msg);
void __stdcall setLoggerPointer( void *fctPointer(LPCTSTR msg) )
{
logger = fctPointer;
}
bool __stdcall init()
{
logger("WOOO");
return true; //I'm getting the AccessViolation right here
}
I'm getting a System.AccessViolationException upon returning from the init() function in atlsimpstr.h Release() function inside the mfc100.dll
Anybody know what I'm doing wrong? All the questions I've seen about this sort of thing have been how to do the reverse P/Invoke without access violation but it's working fine for me, it's just when returning from the other invocation it is messed up, as if that section of memory is now considered part of the C# application.
The problem is that the calling conventions for the callback do not match. Your native code expects the callback to be cdecl, but the managed code declares it to be stdcall. You can fix this either in the managed code or in the native code. For simplicity, I'll show how to fix it in the managed code.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FunctionPointer(string msg);
Some other points:
Don't set the SetLastError parameter to true since your functions are not setting the Win32 last error.
Don't use unsafe anywhere in this code. As a general rule you should avoid unsafe and you just don't need it for any of this.
I'm importing several unmanaged c++ DLL's into my project however the imported DLLs have the same method name which causes compiler issues. For example;
unsafe class Myclass
{
[DllImport("myfirstdll.dll")]
public static extern bool ReturnValidate(long* bignum);
[DllImport("myseconddll.dll")]
public static extern bool ReturnValidate(long* bignum);
public Myclass
{
int anum = 123;
long passednum = &anum;
ReturnValidate(passsednum);
}
}
Now what I'd like to do would be rename the method on import. Something like;
[DllImport("myseconddll.dll")]
public static extern bool ReturnValidate(long bignum) AS bool ReturnValidate2(long bignum);
Is this possible?
Use the EntryPoint property of DllImport attribute.
[DllImport("myseconddll.dll", EntryPoint = "ReturnValidate")]
public static extern bool ReturnValidate2(long bignum);
Now when you call ReturnValidate2 in your C# code, you will effectively call ReturnValidate on myseconddll.dll.
You could provide any name for your imported function, you should only specify in DllImport the name of the function in it, using EntryPoint property. So you code could look like:
[DllImport("myfirstdll.dll", EntryPoint="ReturnValidate")]
public static extern bool ReturnValidate1(long bignum);
[DllImport("myseconddll.dll", EntryPoint="ReturnValidate")]
public static extern bool ReturnValidate2(long bignum);
Use the EntryPoint parameter:
[DllImport("myfirstdll.dll", EntryPoint="ReturnValidate")]
public static extern bool ReturnValidate1(long bignum);
[DllImport("myseconddll.dll", EntryPoint="ReturnValidate")]
public static extern bool ReturnValidate2(long bignum);
Documentation:
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.aspx
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.dllimportattribute.entrypoint.aspx