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.
Related
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);
}
I've been doing quite a bit of googling trying to find the standard way to get an instance of a COM interface.
Microsoft provides an example of this in their article COM Interop Part 1: Client Tutorial:
// Create an instance of a COM coclass:
FilgraphManager graphManager = new FilgraphManager();
// See if it supports the IMediaControl COM interface.
// Note that this will throw a System.InvalidCastException if
// the cast fails. This is equivalent to QueryInterface for
// COM objects:
IMediaControl mc = (IMediaControl) graphManager;
// Now you call a method on a COM interface:
mc.Run();
However, it appears as though they are instantiating a COM object and casting it to a COM interface.
For the interface I am interested in, IDesktopWallpaper, there does not seem to be an implementing COM object to instantiate.
An example I found here defines some class that gets instantiated and then casts it to the interface the same way that the msdn example does:
[ComImport, Guid("C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD")]
internal class IDesktopWallpaper
{
}
[Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B"), //B92B56A9-8B55-4E14-9A89-0199BBB6F93B
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface DesktopWallpaperInterface
{
// declared members
}
I'm not understanding what the instantiated object is. It seems like an arbitrary object, it has a GuidAttribute which seems to indicate that it is an actual COM object.
Another example i found here System.Type and System.Runtime.InteropServices.Marshal to instantiate an object and then casts it to the interface:
IntPtr ptrRet;
SHGetMalloc(out ptrRet);
System.Type mallocType = System.Type.GetType("IMalloc");
Object obj = Marshal.GetTypedObjectForIUnknown(ptrRet,mallocType);
IMalloc pMalloc = (IMalloc)obj;
This method seems to be requesting a pointer to an existing instance of the interface. I can't find any methods like SHGetMalloc for IDesktopWallpaper in the Windows Shell documentation.
Question
So, long story short, what's the standard way to get an instance of a COM interface?
In the event that there is no one-size-fits-all solution, what are the standard ways that one can use to get an instance of a COM interface and in what circumstance is each of these ways most useful?
Edit
After downloading the Windows 10 SDK and referencing that against the Requirements section of the IDesktopWallpaper interface documentation, I have discovered that you can look up the MIDL from Shobjidl.h and use that in the GuidAttribute for your interface declaration and then look up the CLSID from Shobjidl.idl and use that in conjunction with Type.GetTypeFromCLSID(Guid) and Activator.CreateInstance(Type) to get an instance of an object that implements IDesktopWallpaper.
I also see now that the CLSID is what is used in the second method listed above for the GuidAttribute of the seemingly arbitrary object. It seems like this method allows you to mimic managed instantiation of the object by instantiating the class and then casting the instance to the COM interface.
However
I am still interested to know if this is the best way to do this and what pros and cons may be associated with this method vs others.
You can get a pointer to a COM object reference by a variety of methods:
P/Invoke CoCreateInstance
P/Invoke CLSIDFromProgID → CoCreateInstance
P/Invoke IRunningObjectTable.GetObject
Type.GetTypeFromCLSID → Activator.CreateInstance
Type.GetTypeFromProgID → Activator.CreateInstance
new SomeType() where SomeType is marked with ComImport
Activator.CreateInstance and new SomeType() eventually hit CoCreateInstance (if they do not get intercepted by various in-app-domain stuff). Calls to CoCreateInstance for an out-of-process server will eventually hit IRunningObjectTable with a class moniker (I think). The best option depends on what you are trying to do:
For an in-process server, just use ComImport
For an out-of-process server that is not implemented in .Net, ComImport will work, I would prefer to call CoCreateInstance to pass the right CLSCTX.
For a .net implemented out-of-process server implemented in .Net, you must call CoCreateInstance directly to avoid the "optimizations" added by ComImport that would result in the server being run in-process
If you are dealing with a moniker, use IRunningObjectTable
If you are starting out with a ProgID rather than a CLSID, use either CLSIDFromProgID or Type.GetTypeFromProgID
Regardless of how we get a reference to an object, we start out with IUnknown (object in .Net), and then have to call IUnknown->QueryInterface to get a pointer to a particular interface. Calling QueryInterface in .Net is effected by casting to an interface marked as ComVisible (and usually annotated with GuidAttribute).
In the example you named, you would end up with:
// based off of https://bitbucket.org/ciniml/desktopwallpaper
[ComImport]
[Guid("B92B56A9-8B55-4E14-9A89-0199BBB6F93B")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDesktopWallpaper
{
void SetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string monitorID, [MarshalAs(UnmanagedType.LPWStr)] string wallpaper);
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetWallpaper([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
[return: MarshalAs(UnmanagedType.LPWStr)]
string GetMonitorDevicePathAt(uint monitorIndex);
[return: MarshalAs(UnmanagedType.U4)]
uint GetMonitorDevicePathCount();
[return: MarshalAs(UnmanagedType.Struct)]
Rect GetMonitorRECT([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
void SetBackgroundColor([MarshalAs(UnmanagedType.U4)] uint color);
[return: MarshalAs(UnmanagedType.U4)]
uint GetBackgroundColor();
void SetPosition([MarshalAs(UnmanagedType.I4)] DesktopWallpaperPosition position);
[return: MarshalAs(UnmanagedType.I4)]
DesktopWallpaperPosition GetPosition();
void SetSlideshow(IntPtr items);
IntPtr GetSlideshow();
void SetSlideshowOptions(DesktopSlideshowDirection options, uint slideshowTick);
void GetSlideshowOptions(out DesktopSlideshowDirection options, out uint slideshowTick);
void AdvanceSlideshow([MarshalAs(UnmanagedType.LPWStr)] string monitorID, [MarshalAs(UnmanagedType.I4)] DesktopSlideshowDirection direction);
DesktopSlideshowDirection GetStatus();
bool Enable();
}
[ComImport]
[Guid("C2CF3110-460E-4fc1-B9D0-8A1C0C9CC4BD")]
public class DesktopWallpaper
{
}
[Flags]
public enum DesktopSlideshowOptions
{
None = 0,
ShuffleImages = 0x01
}
[Flags]
public enum DesktopSlideshowState
{
None = 0,
Enabled = 0x01,
Slideshow = 0x02,
DisabledByRemoteSession = 0x04
}
public enum DesktopSlideshowDirection
{
Forward = 0,
Backward = 1
}
public enum DesktopWallpaperPosition
{
Center = 0,
Tile = 1,
Stretch = 2,
Fit = 3,
Fill = 4,
Span = 5,
}
[StructLayout(LayoutKind.Sequential)]
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
An example use of which would be:
public partial class Form1 : Form
{
private IDesktopWallpaper Wallpaper;
public Form1()
{
InitializeComponent();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Wallpaper = (IDesktopWallpaper)new DesktopWallpaper();
uint monitorCount = Wallpaper.GetMonitorDevicePathCount();
for (uint i = 0; i < monitorCount; i++)
{
lbMonitors.Items.Add(Wallpaper.GetMonitorDevicePathAt(i));
}
}
private void lbMonitors_SelectedValueChanged(object sender, EventArgs e)
{
var path = (string)lbMonitors.SelectedItem;
tbWallpaper.Text = Wallpaper.GetWallpaper(path);
}
}
Which produces the form:
The following sample is from Microsoft's documentation:
public delegate bool CallBack(int handle, IntPtr param);
public class LibWrap
{
// passing managed object as LPARAM
// BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
[DllImport("user32.dll")]
public static extern bool EnumWindows(CallBack cb, IntPtr param);
}
public class App
{
public static void Main()
{
Run();
}
[SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
public static void Run()
{
TextWriter tw = System.Console.Out;
GCHandle gch = GCHandle.Alloc(tw);
CallBack cewp = new CallBack(CaptureEnumWindowsProc);
// platform invoke will prevent delegate to be garbage collected
// before call ends
LibWrap.EnumWindows(cewp, GCHandle.ToIntPtr(gch));
gch.Free();
}
private static bool CaptureEnumWindowsProc(int handle, IntPtr param)
{
GCHandle gch = GCHandle.FromIntPtr(param);
TextWriter tw = (TextWriter)gch.Target;
tw.WriteLine(handle);
return true;
}
}
There are two things that mystify me.
Firstly, the documentation of GCHandle.Alloc only talks about preventing a an object to be garbage collected. If that was all, you wouldn't need GCHandle.Alloc: Obviously in the sample, tw isn't going to be collected during the call to EnumWindows - there's a reference to it in the function scope.
The concern here is that one needs to make sure that it isn't moved. But the documentation of GCHandle.Alloc doesn't talk about that. So what's going on?
Secondly, what about the delegate? There may not be a problem in this sample, but what if the delegate is bound to an object (a lambda with a closure or a non-static method of a class)? In that case, one needs to take care of the delegate too, right? Is that another GCHandle.Alloc(myDelegate) or are there more things to consider?
Moving is not a concern. GCHandle.ToIntPtr promises to give you an integer value that you can, at a later date, pass to GCHandle.FromIntPtr to retrieve the original handle. That's all. If you need to stop the object moving in memory then you would have to pin it. But you don't actually need to pin the object, you just need to stop it being collected, and be able to retrieve it in your callback.
The delegate's lifetime is not an issue here because the p/invoke framework will make sure it is not collected during the external call to EnumWindows, as noted in the comment. If you pass a delegate to unmanaged code, and that unmanaged code holds a reference to the delegate, then you have work to do. You must ensure that the delegate outlives the unmanaged reference to it.
static class Class
{
public static void methodRequiringStuffFromKernel32()
{
// code here...
}
}
Where do I put [DllImport("Kernel32.dll")] here?
You put it on the method you're importing from Kernel32.dll.
For example,
static class Class
{
[DllImport("Kernel32.dll")]
static extern Boolean Beep(UInt32 frequency, UInt32 duration);
public static void methodRequiringStuffFromKernel32()
{
// code here...
Beep(...);
}
}
From #dtb: Note that the class should be named NativeMethods, SafeNativeMethods or UnsafeNativeMethods. See Naming Convention for Unmanaged Code Methods for more details.
CA1060: Move P/Invokes to NativeMethods class:
NativeMethods - This class does not suppress stack walks for unmanaged code permission. (System.Security.SuppressUnmanagedCodeSecurityAttribute must not be applied to this class.) This class is for methods that can be used anywhere because a stack walk will be performed.
SafeNativeMethods - This class suppresses stack walks for unmanaged code permission. (System.Security.SuppressUnmanagedCodeSecurityAttribute is applied to this class.) This class is for methods that are safe for anyone to call. Callers of these methods are not required to perform a full security review to make sure that the usage is secure because the methods are harmless for any caller.
UnsafeNativeMethods - This class suppresses stack walks for unmanaged code permission. (System.Security.SuppressUnmanagedCodeSecurityAttribute is applied to this class.) This class is for methods that are potentially dangerous. Any caller of these methods must perform a full security review to make sure that the usage is secure because no stack walk will be performed.
This is an example of DllImport:
using System;
using System.Runtime.InteropServices;
class MsgBoxTest
{
[DllImport("user32.dll")]
static extern int MessageBox (IntPtr hWnd, string text, string caption,
int type);
public static void Main()
{
MessageBox (IntPtr.Zero, "Please do not press this again.", "Attention", 0);
}
}
I suggest you to study Platform Invoke Tutorial.
static class Class
{
[DllImport("kerynel32.dll")]
public static extern void methodRequiringStuffFromKernel32();
}
It goes on the method itself that's P/Invoking outside methods. Make sure to add a reference to System.Runtime.InteropServices
I have a C# project in which i use several unmanaged C++ functions.
More so, I also have static IntPtr that I use as parameters for those functions. I know that whenever I use them, I should implement IDisposable in that class and use a destructor to invoke the Dispose method, where I free the used IntPtr, as is said in the MSDN page.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
if (disposing)
{
component.Dispose();
}
CloseHandle(m_InstanceHandle);
m_InstanceHandle = IntPtr.Zero;
disposed = true;
}
}
[System.Runtime.InteropServices.DllImport("Kernel32")]
private extern static Boolean CloseHandle(IntPtr handle);
However, when I terminate the application, I'm still left with a hanging process in TaskManager. I believe that it must be related to the used of the MarshalAs instruction in my structures:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SipxAudioCodec
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
public string CodecName;
public SipxAudioBandwidth Bandwidth;
public int PayloadType;
}
When I create such a structure should I also be careful to free the space it allocs using a destructor?
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct SipxAudioCodec
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
public string CodecName;
public SipxAudioBandwidth Bandwidth;
public int PayloadType;
~SipxAudioCodec()
{
Marshal.FreeGlobal(something...);
}
}
The proper way to handle IntPtr-style handles in p/Invoke interop is:
Define a class SafeMyHandle derived from SafeHandle. It should only override IsInvalid and ReleaseHandle, and do nothing else.
Define another class MyHandle that has methods for the public API for that handle.
MyHandle should have a private member of type SafeMyHandle.
MyHandle should implement IDisposable, and its Dispose method should just call SafeMyHandle.Dispose.
All of the p/Invoke methods should not use IntPtr directly; rather, they should pass and return instances of SafeMyHandle. The one exception is the "freeing function" that is called from SafeMyHandle.ReleaseHandle; it should take an IntPtr.
If you follow these conventions, your handles will be freed even if your AppDomain is rudely torn down.
I have had cases in the past where the application hangs after closing, usually because not all threads were terminated. How do you terminate your app? Maybe threads are the cause?