P/Invoke? No way! (Pt. 2)
[7/20/05: Additional comment added for Marshal::GetFunctionPointerForDelegate()]
There are some instances where Win32 requires a callback function pointer. This frequently comes up in Enumeration APIs, such as EnumWindows. In this post we’ll look at some code that makes this possible in Managed C++ (2005 -- C++/CLI).
One could create an unmanaged callback, but we’ll look at the completely managed route here. (One could also use P/Invoke, but you know how I feel about that, no?) (Don’t forget to read my earlier post on how to set up a C++ project for interop with the Win32 API if you haven’t already.)
You need to do just a few basic things:
- Create a method that matches the desired callback signature.
- Create a delegate type that matches the above method.
- Create an instance of the delegate that points to your callback method.
- Pin the delegate.
- Convert the delegate into a function pointer.
- Use it.
Here’s some sample code:
public ref class Window
{
private:
// Private flag for keeping track of when we're enumerating windows.
static bool enumeratingWindows = false;
// Private list of top level windows;
static List<IntPtr>^ topLevelWindows = gcnew List<IntPtr>();
// Delegate type for the EnumWindowsProc callback.
// Matches the required signature of the API. Note the use of ‘BOOL’, not ‘bool’.
delegate BOOL EnumWindowsDelegate(HWND hwnd, LPARAM lParam);
// Private method used for EnumWindows calls.
static BOOL EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
// Not much to this. Note the use of ‘TRUE’, not ‘true’.
Window::topLevelWindows->Add((IntPtr)hwnd);
return TRUE;
}
public:
// Returns the top-level window handles.
// Will return empty List if currently enumerating.
static property List<IntPtr>^ TopLevelWindowHandles
{
List<IntPtr>^ get()
{
List<IntPtr>^ foundWindows = gcnew List<IntPtr>();
if (Window::enumeratingWindows == true)
{
return foundWindows;
}
// Flag that we're currently enumerating windows.
Window::enumeratingWindows = true;
Window::topLevelWindows->Clear();
// Setup the managed callback. Need to create the delegate we need. (Step 1.)
// (This is a delegate to the managed method defined above.)
EnumWindowsDelegate^ enumWindowsDelegate = gcnew EnumWindowsDelegate(Window::EnumWindowsProc);
// Pin the delegate so the GC won’t move it. (Step 2.)
pin_ptr<EnumWindowsDelegate^> pinnedDelegate = &enumWindowsDelegate;
// Get the function pointer for the delegate and cast it to the type the API expects. (Step 3.)
// 7/20: Note that you can also pass *pinnedDelegate here. It doesn't matter as they represent the
// same object. Having a pin_ptr in scope is what keeps the object itself pinned.
IntPtr delegatePointer = Marshal::GetFunctionPointerForDelegate(enumWindowsDelegate);
WNDENUMPROC enumWindowsProc = static_cast<WNDENUMPROC>(delegatePointer.ToPointer());
if (!EnumWindows(enumWindowsProc, 0))
{
// Failed! Throw appropriate fit here...
}
// Add what we found and return them.
foundWindows->AddRange(Window::topLevelWindows);
Window::enumeratingWindows = false;
return foundWindows;
}
}
};
No unmanaged code used, no P/Invoke, I like it. :) Hopefully you’ll find it useful too.
Some other potentially useful links:
- Implementing Callback functions using IJW (avoiding DllImport)
- Using Delegates with Native Function Callbacks in Managed C++
- How to: Marshal Callbacks and Delegates Using C++ Interop
Comments
- Anonymous
September 07, 2005
I've already given the steps necessary to make this happen in an earlier post, but as this will come... - Anonymous
July 23, 2010
Hey people ,the thins is so silmles as you mean Get in the count C++ let you write non - class functions and classes not clr!!! So in these objects you just dive in the C++ native world It Just Work !!!(IJW), pivoke NO WAY!!!