So there is a beautiful technique for acquiring a COM pointer cross-process (same machine) from an Excel.exe session if you know its Hwnd using the Accessibility API. The specific Windows API function is AccessibleObjectFromWindow; if called with parameter of OBJID_NATIVEOM then Excel.exe marshals back a COM pointer to an Excel.Window object. Very cool.
So I was wondering if developers can implement the same technique for their own applications. The answer is yes, they respond to a certain message, WM_GETOBJECT, in their message pump code. Whilst this is doable for a C++ application, I am puzzled as how to do this for a C# application.
I'm presuming the answer is to do something to get access to the message pump handling code and alter it. It maybe the case that some magic attribute could be used. I'm open to either technique so long as it works.
Here is the code that acquires COM pointer from Excel
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("oleacc.dll", SetLastError = true)]
internal static extern int AccessibleObjectFromWindow(IntPtr hwnd, uint id, ref Guid iid,
[In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject);
bool IXlMoniker.GetExcelByHwnd(int lhwndApp2, ref object appRetVal)
{
bool bRetVal = false;
IntPtr lhwndApp = (IntPtr)lhwndApp2;
IntPtr lHwndDesk = FindWindowEx(lhwndApp, IntPtr.Zero, "XLDESK", "");
if (lHwndDesk != IntPtr.Zero)
{
IntPtr lHwndExcel7 = FindWindowEx(lHwndDesk, IntPtr.Zero, "EXCEL7", null);
if (lHwndExcel7 != IntPtr.Zero)
{
Guid IID_IDispatch = new Guid("{00020400-0000-0000-C000-000000000046}");
const uint OBJID_NATIVEOM = 0xFFFFFFF0;
object app = null;
if (AccessibleObjectFromWindow(lHwndExcel7, OBJID_NATIVEOM, ref IID_IDispatch, ref app) == 0)
{
dynamic appWindow = app;
appRetVal = appWindow.Application;
return true;
}
}
}
return bRetVal;
}
This looks promising Return an IOleCommandTarget from processing WM_GETOBJECT in a NativeWindow