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);
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!
}
}
I just recently learned about SafeHandle and, for a test, I implemented it for the SDL2 library, creating and destroying a window:
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
internal static extern IntPtr SDL_CreateWindow(
[MarshalAs(UnmanagedType.LPStr)] string title, int x, int y, int w, int h, uint flags);
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
internal static extern void SDL_DestroyWindow(IntPtr window);
public class Window : SafeHandleZeroOrMinusOneIsInvalid
{
public Window() : base(true)
{
SetHandle(SDL_CreateWindow("Hello", 400, 400, 800, 600, 0));
}
protected override bool ReleaseHandle()
{
SDL_DestroyWindow(handle);
return true;
}
}
This works fine, then I learned of another advantage of using SafeHandle: The possibility to use the class in the p/invoke signature directly, like so:
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
internal static extern Window SDL_CreateWindow(
[MarshalAs(UnmanagedType.LPStr)] string title, int x, int y, int w, int h, uint flags);
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
internal static extern void SDL_DestroyWindow(Window window);
This is of course much better than generic IntPtr parameters / returns, because I have type safety passing / retrieving actual Window (handles) to / from those methods.
While this works for SDL_CreateWindow, which correctly returns a Window instance now, it doesn't work for SDL_DestroyWindow, which is called by me inside of Window.ReleaseHandle like this:
public Window() : base(true)
{
SetHandle(SDL_CreateWindow("Hello", 400, 400, 800, 600, 0).handle);
}
protected override bool ReleaseHandle()
{
SDL_DestroyWindow(this);
return true;
}
When trying to pass this to SDL_DestroyWindow, I get an ObjectDisposedException: Safe handle has been closed. Indeed the IsClosed property is true, which I didn't expect to be at this time. Apparently it internally tries to increase a reference count, but notices IsClosed is true. According to the documentation, it has been set to true because "The Dispose method or Close method was called and there are no references to the SafeHandle object on other threads.", so I guess Dispose was implicitly called before in the call stack to invoke my ReleaseHandle.
ReleaseHandle apparently isn't the correct place to clean up if I want to use a class parameter in the p/invoke signature, so I wonder if there is any method where I could clean up without breaking SafeHandle internals?
My question above is slightly misguided by wrong information I learned about SafeHandle (via some blog posts I won't mention). While I was told that replacing IntPtr parameters in P/Invoke methods with class instances is "the main advantage provided by SafeHandle" and definitely nice, it turns out to be only partly useful:
Careful with automatic SafeHandle creation by the marshaller
For one, I say this because my code above has a big issue I didn't see at first. I have written this code:
void DoStuff()
{
Window window = new Window();
}
public class Window : SafeHandleZeroOrMinusOneIsInvalid
{
public Window() : base(true)
{
// SDL_CreateWindow will create another `Window` instance internally!!
SetHandle(SDL_CreateWindow("Hello", 400, 400, 800, 600, 0).handle);
}
protected override bool ReleaseHandle()
{
SDL_DestroyWindow(handle); // Since "this" won't work here (s. below)
return true;
}
// Returns Window instance rather than IntPtr via the automatic SafeHandle creation
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
private static extern Window SDL_CreateWindow(
[MarshalAs(UnmanagedType.LPStr)] string title, int x, int y, int w, int h, uint flags);
// Accept Window instance rather than IntPtr (won't work out, s. below)
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
private static extern void SDL_DestroyWindow(Window window);
}
When the marshaller calls the P/Invoke method of SDL_CreateWindow in the Window constructor, it internally creates another instance of the Window class for the return value (calling the required parameterless constructor and then setting the handle member internally). This means I now have two SafeHandle instances:
one returned by the SDL_CreateWindow method - which I don't use anywhere (only stripping out the handle property)
one created by my user code calling new Window(), the SafeHandle class itself
The only correct way to implement the SafeHandle here is to let SDL_CreateWindow return an IntPtr again, so no internal marshalling SafeHandle instances are created anymore.
Can't pass SafeHandle around in ReleaseHandle
As Simon Mourier explained / cited in the comments, the SafeHandle itself cannot be used at all anymore when cleaning up in ReleaseHandle, since the object is garbage collected and trying to do "fancy" things like passing it to a P/Invoke method is not safe anymore / doomed to fail. (Given I were told that the replacement of IntPtr parameters in the P/Invoke is one of "the main features" of SafeHandle, it surprised me first that this is not supported and deemed "fancy"). That is also why the ObjectDisposedException I receive is very justified.
I can still access the handle property here, but then, again, my P/Invoke method no longer accepts a Window instance, but the "classic" IntPtr.
Am I better off with IntPtr for P/invoke parameters again?
I'd say so, my final implementation looks like this and solves the above two problems while still using the advantages of SafeHandle, just without the fancy P/Invoke argument replacements. And as an extra feature, I can still connote the IntPtr parameters to "accept" an SDL_Window (the native type pointed to) with a using alias.
using SDL_Window = System.IntPtr;
public class Window : SafeHandleZeroOrMinusOneIsInvalid
{
private Window(IntPtr handle) : base(true)
{
SetHandle(handle);
}
public Window() : this(SDL_CreateWindow("Hello", 400, 400, 800, 600, 0)) { }
protected override bool ReleaseHandle()
{
SDL_DestroyWindow(handle);
return true;
}
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
private static extern SDL_Window SDL_CreateWindow(
[MarshalAs(UnmanagedType.LPStr)] string title, int x, int y, int w, int h, uint flags);
[DllImport(_libName, CallingConvention = CallingConvention.Cdecl)]
private static extern void SDL_DestroyWindow(SDL_Window window);
}
Intro
My program allows other developers to write plugins. These plugins(which are dll files) are placed in a certain directory and my program is able to load them at runtime like this:
Assembly assembly = Assembly.LoadFrom("MyPluginPath");
object Plugin = assembly.CreateInstance("Plugins.Main", true);
Then the program will be able to call the main method from the plugin this way:
var pluginType = Plugin.GetType();
var methodOutput = pluginType.GetMethod("MyInternalMethod").Invoke(Plugin, new object[] {param});
This works fine for all the plugins I've wrote so far that followed a certain structure as below:
namespace Plugins
{
public class Main : IDisposable
{
public Bitmap MyInternalMethod(Bitmap param)
{
// method body
}
}
}
Problem
Now there is this new plugin I've to wrote which loads an external dll at runtime itself:
namespace Plugins
{
public class Main : IDisposable
{
[System.Runtime.InteropServices.DllImport("MyExternalDLL.dll")]
unsafe private static extern int Initialize(int* arg1, char* arg2);
public Bitmap MyInternalMethod(Bitmap param)
{
// method body
Initialize(x,y);
}
}
}
Initialize(x,y) method in the above code gives me an error that says
Failed to initialize com library.
Notes
I created an executable test application for my plugin and it worked fine, so I know the problem occurs when using it as a dll at runtime.
I thought maybe I should load my external dll in the main program and then pass it as a Func<> object to my plugin but Func<> doesn't allow pointer variables (Initialize method contains arguments like int* and char*) and even if it does, I'm not sure if this solution is gonna work.
I tried using winApi as "Sinatr" suggested in the comments by following this answer but the error stays. I even tried loading MyExternalDLL.dll in the main program using winApi and passing the retrieved address of Initialize method to my plugin like the following code but the error remains the same (this way the error happens on func(x,y)):
Main program:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string name);
IntPtr dllHandle = IntPtr.Zero;
IntPtr addr = IntPtr.Zero;
private void RunPlugin()
{
dllHandle = LoadLibrary("MyExternalDLL.dll");
addr = GetProcAddress(dllHandle, "Initialize");
var methodOutput = pluginType.GetMethod("MyInternalMethod").Invoke(Plugin, new object[] {param, addr});
}
Plugin:
public class Main : IDisposable
{
private unsafe delegate byte initialize_api(int* arg1, char* arg2);
public Bitmap MyInternalMethod(Bitmap param, IntPtr addr)
{
// method body
//initialize_api is a delegate with the same signature as Initialize(x,y);
var func = (initialize_api)Marshal.GetDelegateForFunctionPointer(addr, typeof(initialize_api));
func(x,y);
}
}
Since we are talking about plugins here, I don't think implementing a common Interface which both my program and the plugins can use is possible.
Question
Is it even possible to use a dynamically loaded dll that contains a dynamically loaded dll the way I described? if it is, what should I do to make this work?
Currently I'm trying to use a C++ library under C# using DLL importation. Library is called Interception.
The problem is that I don't know how to translate #define entries and typedef declaration of the header file:
https://github.com/oblitum/Interception/blob/master/include/interception.h
I tried to use "using" directive, but with no success (I can't access to the void definition).
Moreover, I didn't understood the role of __declspec(dllimport) in this header. In my c# project, I just ignored it? Is it good to do that?
This is the code I want to use in c# (it's a sample of the library)
https://github.com/oblitum/Interception/blob/master/samples/hardwareid/main.cpp
EDIT:
What I've tried: basic importation:
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
void interception_set_filter(void* context, InterceptionPredicate predicate, ushort filter);
I don't know ho to convert InterceptionPredicate. According the header file, InterceptionFilter is a ushort, and InterceptionContext is a void pointer (void*).
The C++ library should be compiled as a .DLL file. This .DLL file should have exported functions. You can use the Depends tool to check what's exported from a .DLL. .NET code can call C++ exported functions using what's called "Platform Invoke".
Now, I strongly suggest you take a deep look at this Platform Invoke Tutorial that will guide you.
PS: void * should be declared in c# as IntPtr. enums should be redeclared as enums. Functions should be declared as static extern methods marked with the DllImport attribute.
First, it looks like you're trying to implement a global keyboard/mouse hook .. if that's the case, I'd recommend googling 'C# low level keyboard and mouse hook'.
Now for your question, first is the __declspec(dllimport) issue: this would be if you were actually using the header in a C++ application, that is the C++ equivilent of the C# DllImport .. so in effect you didn't ignore it, you implemented it. In C++ it just tells the linker that the function declared as such will be imported from a specific DLL instead of it being a local function (pretty similar to what the C# DllImport directive does)
Next is for function pointer issue (InterceptionPredicate). In the header it is defined as such:
typedef int (*InterceptionPredicate)(InterceptionDevice device);
And InterceptionDevice is just an 'int'. So the InterceptionPredicate is just a function pointer type (or Delegate in C#), so your delegate definition for InterceptionPredicate would look like this:
// [UnmanagedFunctionPointer(CallingConvention.Winapi)]
public delegate int InterceptionPredicate (int device);
A note about the UnmanagedFunctionPointer calling convention descriptor: IF you know what kind of calling convention (stdcall, fastcall, cdecl) the exported function might be using, you could specify here so that the .NET marshaler will know how to pass the data between the managed/unmanaged code, but if you don't know it or it's not specified typically you can just leave that off.
Also, as others have mentioned, unless you have the 'unsafe' flag specified in your C# properties, a void* type should always be an IntPtr in C#.
Also, be sure to mark the dll function in your C# code as public static extern, see example below.
So to make an example of the function you've specified, here's what could be done:
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace InterceptorTest
{
public class Interceptor : IDisposable
{
#region DllImports
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr interception_create_context();
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void interception_destroy_context(IntPtr context);
[DllImport("interception.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void interception_set_filter(IntPtr context, InterceptionPredicate predicate, ushort filter);
// The function pointer type as defined in interception.h that needs to be defined as a delegate here
public delegate int InterceptionPredicate(int device);
#endregion
#region private members
private InterceptionPredicate m_PredicateDelegate { get; set; }
private IntPtr m_Context { get; set; }
#endregion
#region methods
public Interceptor(ushort filter)
{
// be sure to initialize the context
this.m_PredicateDelegate = new InterceptionPredicate(this.DoSomethingWithInterceptionPredicate);
this.m_Context = interception_create_context();
interception_set_filter(this.m_Context, this.m_PredicateDelegate, filter);
}
private void Cleanup()
{
interception_destroy_context(this.m_Context);
// the next line is not really needed but since we are dealing with
// managed to unmanaged code it's typically best to set to 0
this.m_Context = IntPtr.Zero;
}
public void Dispose()
{
this.Cleanup();
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing) { this.Cleanup(); }
}
public int DoSomethingWithInterceptionPredicate(int device)
{
// this function is something you would define that would do something with
// the device code (or whatever other paramaters your 'DllImport' function might have
// and return whatever interception_set_filter is expecting
return device;
}
#endregion
}
static class Program
{
[STAThread]
private static void Main(string[] argv)
{
Interceptor icp = new Interceptor(10);
// do something with the Interceptor object
}
}
}
Hope that gets you on the right track.
Ok, I found an example code that was hidden on the GIT. Thank you google!
https://gist.github.com/1959219
It precises all function from DLL importation, with a working example.
I am using DllImport in my solution.
My problem is that I have two versions of the same DLL one built for 32 bit and another for 64 bit.
They both expose the same functions with identical names and identical signatures.
My problem is that I have to use two static methods which expose these and then at run time use IntPtr size to determine the correct one to invoke.
private static class Ccf_32
{
[DllImport(myDllName32)]
public static extern int func1();
}
private static class Ccf_64
{
[DllImport(myDllName64)]
public static extern int func1();
}
I have to do this because myDllName32 and myDllName64 must be constant and I have not found a way to set it at run time.
Does anyone have an elegant solution for this so I could get rid of the code duplication and the constant IntPtr size checking.
If I could set the file name, I would only have to check once and I could get rid of a ton of repeated code.
I prefer to do this by using the LoadLibrary call from kernel32.dll to force a particular DLL to load from a specific path.
If you name your 32-bit and 64-bit DLLs the same but placed them in different paths, you could then use the following code to load the correct based on the version of Windows you are running. All you need to do is call ExampleDllLoader.LoadDll() BEFORE any code referencing the ccf class is referenced:
private static class ccf
{
[DllImport("myDllName")]
public static extern int func1();
}
public static class ExampleDllLoader
{
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
private extern static IntPtr LoadLibrary(string librayName);
public static void LoadDll()
{
String path;
//IntPtr.Size will be 4 in 32-bit processes, 8 in 64-bit processes
if (IntPtr.Size == 4)
path = "c:/example32bitpath/myDllName.dll";
else
path = "c:/example64bitpath/myDllName.dll";
LoadLibrary(path);
}
}
You can probably achieve this with the #if keyword. If you define a conditional compiler symbol called win32, the following code will use the win32-block, if you remove it it will use the other block:
#if win32
private static class ccf_32
{
[DllImport(myDllName32)]
public static extern int func1();
}
#else
private static class ccf_64
{
[DllImport(myDllName64)]
public static extern int func1();
}
#endif
This probably means that you can remove the class wrapping that you have now:
private static class ccf
{
#if win32
[DllImport(myDllName32)]
public static extern int func1();
#else
[DllImport(myDllName64)]
public static extern int func1();
#endif
}
For convenience, I guess you could create build configurations for controlling the compilation symbol.
I know this is a really old question (I'm new - is it bad to answer an old question?), but I just had to solve this same problem. I had to dynamically reference a 32-bit or 64-bit DLL based on OS, while my .EXE is compiled for Any CPU.
You can use DLLImport, and you don't need to use LoadLibrary().
I did this by using SetDLLDirectory. Contrary to the name, SetDLLDirectory adds to the DLL search path, and does not replace the entire path. This allowed me to have a DLL with the same name ("TestDLL.dll" for this discussion) in Win32 and Win64 sub-directories, and called appropriately.
public partial class frmTest : Form
{
static bool Win32 = Marshal.SizeOf(typeof(IntPtr)) == 4;
private string DLLPath = Win32 ? #"\Win32" : #"\Win64";
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
[DllImport("TestDLL.dll", SetLastError = true)]
static extern IntPtr CreateTestWindow();
private void btnTest_Click(object sender, EventArgs e)
{
string dllDir = String.Concat(Directory.GetCurrentDirectory(), DLLPath);
SetDllDirectory(dllDir);
IntPtr newWindow = CreateTestWindow();
}
}
Why not wrap them into a method?
private static class ccf_32_64
{
private static class ccf_32
{
[DllImport(myDllName32)]
private static extern int func1();
}
private static class ccf_64
{
[DllImport(myDllName64)]
private static extern int func1();
}
public static int func1()
{
if (32bit)
{
return ccf_32.func1();
}
else
{
return ccf_64.func1();
}
}
}
One alternative option is to have both the 32- and 64-bit versions of the unmanaged DLL have the same name, but have them live in separate folders in your build output (say, x86\ and x64\).
Then, your installer or however else you're distributing this is updated so it knows to install the proper DLL for the platform it's installing on.
you can create two methods and choose one in a runtime, so you can keep Any CPU
public static class Ccf
{
[DllImport(myDllName32)]
private static extern int func32();
[DllImport(myDllName64)]
private static extern int func64();
public static int func()
{
if(Environment.Is64BitProcess)
{
return func64();
}
return func32();
}
}
You can't do this the way you want. You need to think of the DllImport attribute as metadata that is used at compile time. As a result you can't change the DLL it is importing dynamically.
If you want to keep your managed code targeted to "Any CPU" then you either need to import both the 32-bit and 64-bit libraries wrapped as two different functions that you can call depending on the runtime environment or use some additional Win32 API calls to late-load the correct version of the unmanaged assembly at runtime and additional Win32 calls to execute the required methods. The drawback there is that you won't have compile time support for any of that type of code for type safety, etc.
Hmm, I'm wondering if you could create an interface and then a class with the methods based on the 32 bit and 64 bit dlls.
I'm not sure if there is an explicit method to determine if you are running 64 bit, but the following might work: allow unsafe code and have an unsafe function that gets a pointer to some address and then determine whether the pointer is 4 or 8 bytes in size. Based on the result determine which implementation of the interface to create.
You can determine whether you are running 64Bits or not by checking the size of the IntPtr type (which is called native int anyways).
Then you can load the approriate DLL using an imported LoadLibraryW call, get the function pointer using GetProcAddress, and then, check out Marshal.GetDelegateForFunctionPointer
This not nearly as complicated as it might look like. You have to DllImport both LoadLibraryW and GetProcAddress.