Cannot pass SafeHandle instance in ReleaseHandle to native method - c#

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);
}

Related

Calling IsWowProcess2 from C# .NET (P/Invoke)

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();

Do not declare visible instance fields warning in sequential struct

I'm using some DllImports in a wpf application to capture the screen. I'm calling GetWindowRect in user32.dll. It requires a rect struct passed to it. The layout of the struct matters, since it's a native call.
I'm trying out VS 2019 preview 2 which gives me warnings I hadn't seen before. All the fields in rect generate the same warning:
CA1051 Do not declare visible instance fields
In the rest of the code, I fixed this by turning the field into a property by appending {get;set;} to it. I don't know if I can safely do this in a struct where layout matters.
Rect is also giving me a warning that I should override Equals.
CA1815 Rect should override Equals.
CA1815 Rect should override the equality (==) and inequality (!=) operators.
I never compare it though and definitely don't need to, I just want to fix the warning.
public static class NativeMethods
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
public static IntPtr _GetForegroundWindow()
{
return GetForegroundWindow();
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern IntPtr GetDesktopWindow();
public static IntPtr _GetDesktopWindow()
{
return GetDesktopWindow();
}
//Am unable to get code analysis to shut up about this.
[DllImport("user32.dll")]
private static extern int GetWindowRect(IntPtr hWnd, ref Rect rect);
public static IntPtr _GetWindowRect(IntPtr hWnd, ref Rect rect)
{
return (IntPtr)GetWindowRect(hWnd, ref rect);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
How can I fix these warnings?
The documentation of CA1051: Do not declare visible instance fields says:
Cause
An externally visible type has an externally visible instance field.
The key point for both type and field is external. Hence the fix (since this is supposed to be used only inside your application) is to make the struct (and the class that exposes it) internal:
[StructLayout(LayoutKind.Sequential)]
internal struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
internal static class NativeMethods
{
// ...
}
Please note that CA1051 warning is not generated by the C# compiler, but Code Analysis, hence can be excluded or ignored from the CA rule set (although the documentation suggests to not suppress it).
You can suppress warnings in a file like this:
#pragma warning disable CA1051, CA1815
or disable it in csproj file for the whole project
<NoWarn>CA1051, CA1815</NoWarn>
EDIT If you want to fix the warning instead of suppress it, you should follow the warning message.
I never compare it though and definitely don't need to, I just want to fix the warning.
The warning will appear unless you add operators like that suggested by the message. The warning means that "it probably works for you now, but not the best practice". Overriding equal operators for structs improves the readability and performance, also structs are supposed to be immutable, public fields break the immutability and hide potential bugs.

I wish to wrap a DLL in C#

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.

AccessViolationException with GLFW in C#

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);

How to convert IntPtr to Cursor or SafeHandle?

I'm in .NET 3.5, I have found
CursorInteropHelper.Create()
method here. However it is absolutely unclear how do I convert IntPtr for cursor to SafeHandle. The list of implementations of SafeHandle listed here does not include SafeCursorHandle and others are abstract or unrelated. Is the only way to go is to create my own implementation of SafeHandle?
SafeHandle is an abstract class. It wants you to provide an object of a concrete SafeHandle derived class that can release the handle. Unfortunately you forgot to mention how you obtained that IntPtr so we cannot know how it should be released.
I'll take a guess and assume it is a GDI cursor, the one you get from the CreateCursor() winapi function. Which requires calling DestroyCursor() to release the handle. Such a class could look like this:
class SafeCursorHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid {
public SafeCursorHandle(IntPtr handle) : base(true) {
base.SetHandle(handle);
}
protected override bool ReleaseHandle() {
if (!this.IsInvalid) {
if (!DestroyCursor(this.handle))
throw new System.ComponentModel.Win32Exception();
this.handle = IntPtr.Zero;
}
return true;
}
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyCursor(IntPtr handle);
}
Tweak the ReleaseHandle() override as necessary to release the handle in your case.
According to the MSDN doc, ReleaseHandle() must never fail:
http://msdn.microsoft.com/de-de/library/system.runtime.interopservices.safehandle.releasehandle%28v=vs.110%29.aspx
"Because one of the functions of SafeHandle is to guarantee prevention of resource leaks, the code in your implementation of ReleaseHandle must never fail."
IMO, this means that it must not throw - just like native destructors.

Categories