I’m trying to add a HTML capable editor to a WPF application. The obvious choice is using the default WebBrowser control, and putting its document into “design mode”. So far, so good.
Now, it’s getting tricky, because I need to be able to intercept content that get pasted into the editor from the clipboard to make sure it is in a usable format. For example, when pasting a snippet from a Word document containing an image, the image is represented by a piece of Office XML that the Browser control doesn’t understand, so I would need to replace that with a simple <img> tag before it is inserted into the document.
After realizing that there is no way to accomplish this on the pure WPF level, I had to drill down to the ActiveX component that the WebBrowser control is only a wrapper for. It appears as if my problem (and some others that I am also facing, but that are not relevant for this question) could be solved by providing a custom implementation of the IDocHostUIHandler interface.
This interface contains a method called FilterDataObject which apparently gets called whenever a paste operation takes place, giving the implementation a chance to inspect and, if necessary, change the data received from the clipboard by the browser.
For reference, here’s my the definition of the method in the IDocHostUIHandler interface:
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("bd3f23c0-d43e-11cf-893b-00aa00bdce1a")]
public interface IDocHostUIHandler {
// ...
[PreserveSig]
int FilterDataObject(
[In, MarshalAs(UnmanagedType.Interface)] object pDO,
[Out, MarshalAs(UnmanagedType.Interface)] out object ppDORet
);
}
The COM object that gets passed in can be cast to System.Runtime.InteropServices.ComTypes.IDataObject, and works fine for accessing the clipboard contents. But when I actually try to return a replacement IDataObject through the out parameter, I get an access violation exception.
For example, a naive attempt to simply return a new instance of the existing class System.Windows.DataObject (which also implements ...ComTypes.IDataObject, so my hope was that the CLR would be able to properly marshal it out-of-the-box) like this …
public class MyDocHostUIHandler : IDocHostUIHandler {
// ...
public int FilterDataObject(object pDO, out object ppDORet) {
ppDORet = new System.Windows.DataObject("Text", "New Clipboard Content!");
return S_OK; // S_OK means that the data object has been replaced
}
}
… doesn’t work (AccessViolationException as soon as I try to paste anything into the editor).
Has anyone ever done something like this? What might I be doing wrong? Is the definition of my ComImport-ed interface broken (it was pretty difficult to find a version on the web that actually works – at least some of the time – to begin with)? Is what I am trying to do even possible from managed code?
Try [return: MarshalAs(UnmanagedType.I4)] int FilterDataObject(System.Runtime.InteropServices.ComTypes.IDataObject pDO, out System.Runtime.InteropServices.ComTypes.IDataObject ppDORet); }
If you have problem generating signatures for COM interfaces, often you can generate from Windows SDK tools using midl and tlbimp.