I have a C#.NET 2.0 CF application that imports a function from a native DLL with the following signature:
__declspec( dllexport ) void DLL_Foo( int count, ... );
My C# application P/Invokes that function as follows:
public sealed class MyObject
{
public void Foo()
{
NativeMethods.DLL_Foo(2, __arglist("a","b"));
}
internal static class NativeMethods
{
[DllImport("My.dll")]
internal static extern void DLL_Foo(int count, __arglist);
}
}
But, when I invoke MyObject.Foo, I get a System.MissingMethodException.
What do I need to change to make this work?
Thanks,
PaulH
Edit: If I change the import definition to:
internal static extern void DLL_Foo(int count, [MarshalAs(UnmanagedType.LPWStr)]string a, [MarshalAs(UnmanagedType.LPWStr)]string b);
then, call:
NativeMethods.DLL_Foo(2, "a", "b");
It works with no problems, so it's something with my __arglist usage.
I am not sure (and I have never done it) if you can have params args in P/Invoke but you might give it a try.
internal static extern void DLL_Foo(int count, params string[] args);
You should be using CallingConvention = CallingConvention.Cdecl for your DllImport. The CallingConvention.Cdecl description states it.
using LPWORD = System.IntPtr;
using LPVOID = System.IntPtr;
[DllImport("foo.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern LPVOID extFunc(LPWORD lpdwMandatory,__arglist);
And then You can call extFunc function:
extFunc(lp1,__arglist( 0xFF,0x6A,0xAA));
Related
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!
}
}
The System.Windows.Forms.dll
I wish to wrap a few functions in this file in a C# class.
Specifically these: https://msdn.microsoft.com/en-us/library/system.windows.forms.cursor(v=vs.110).aspx
But I'm not sure how to get a list of functions. I've tried programs that returned no results. I was wondering if someone could give me 1 and only 1 example?
This, for example, returns an EntryPointNotFoundException
[DllImport("System.Windows.Forms.dll")]
public static extern void SetCursor(String s);
First, you need to enable interop services in order to call into windows functions:
using System.Runtime.InteropServices;
Then, you simply declare the methods that you want to import as such:
[DllImport("winmm.dll")]
public static extern bool PlaySound(string filename,long hmodule, int dword );
This creates a "mapping" to the PlaySound method in the unmanaged winmm.dll
The method is created as static and the use of the extern keywords tells the compilar that the method is external to your class (not running inside it)
To get list / search of unmanaged functions you can use
http://www.pinvoke.net
For instance, for SetCursor you can call
http://www.pinvoke.net/search.aspx?search=SetCursor&namespace=[All]
E.g. to place cursor at a position provided SetCursorPos you can do
using System.Runtime.InteropServices;
...
// Wrapper
class CursorNativeMethods {
[DllImport("User32.dll",
EntryPoint = "SetCursorPos",
CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean SetCursorPos(Point point);
...
[DllImport("User32.dll",
EntryPoint = "GetCursorPos",
CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean GetCursorPos([Out] out Point point);
...
}
// Your Routine
public static class MyCursor {
public Point Location {
get {
Point pt = new Point(-1, -1);
if (CursorNativeMethods.GetCursorPos(out pt))
return pt;
else
return new Point(-1, -1);
}
set {
CursorNativeMethods.SetCursorPos(value);
}
}
...
}
Please note, that System.Windows.Forms.dll is managed assembly (you should not interop with), and User32.dll is unmanaged library.
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.
How do you directly call a native function exported from a DLL? Can someone just give me a small example?
This is Microsoft example:
class PlatformInvokeTest
{
[DllImport("msvcrt.dll")]
public static extern int puts(string c);
[DllImport("msvcrt.dll")]
internal static extern int _flushall();
public static void Main()
{
puts("Test");
_flushall();
}
}
If you need to generate C# DLLImport declarations from a native dll, watch this post: Generate C# DLLImport declarations from a native dll
Depends on what exactly you want ... I have something like this in my code but this uses the Win32 API dll's
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
then just call
GetForegroundWindow()
as if defined inside the class
Here’s a quick example of the DllImport attribute in action:
using System.Runtime.InteropServices;
class C
{
[DllImport("user32.dll")]
public static extern int MessageBoxA(int h, string m, string c, int type);
public static int Main()
{
return MessageBoxA(0, "Hello World!", "Caption", 0);
}
}
This example shows the minimum requirements for declaring a C# method that is implemented in a native DLL. The method C.MessageBoxA() is declared with the static and external modifiers, and has the DllImport attribute, which tells the compiler that the implementation comes from the user32.dll, using the default name of MessageBoxA.
Refer this link
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