Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement OLE server in C#

The question I want to ask is:

(1) If I interact with clipboard by Clipboard class, what should be in "Embed Source" and "MetaFilePict" streams?

(2) If I interact with clipboard by COM interface IDataObject, what should I do with the handle in "Embed Source" and "MetaFilePict"?

(3) Is there better methods to implement OLE (either client or server will help me) in C#?

More details:

I am seeking for an implementation of OLE Server in C# (extern Windows APIs and other methods that work on x86 are all OK for me, as long as they can be in one C# project). I can not find any examples of OLE that does not involve MFC. So I first tried a little bit.

My first step is to see what other OLE servers do in order to put their data into the clipboard. I have tried 2 methods to interact with clipboard: NET Clipboard class which gives me three Streams as described later, and IDataObject interface returned by OleGetClipboard which gives me pointers.

I found that Origin puts three data entries: Object Descriptor, Embed Source and MetaFilePict. They appear to be Stream. If I read all bytes from the three Streams, put them back to a new DataObject and put the DataObject to clipboard again, I am able to paste the original object in, for example, Word, which means the contents of the three Streams are enough for an OLE container to paste.

What I find further is that Object Descriptor contains the OBJECTDESCRIPTOR structure with guid of the data class and some names, and I am able to instantiate the object and convert it to an IOLEObject. But I don't know what should be in the other two stream, Embed Source and MetaFilePict. From my understanding, Embed Source should contain data that is passed to IOLEObject (probably by InitFromData) after creating it, but I did not succeed in doing so. And reagrding MetaFilePict, it seems that without this entry, the other two can not work properly (paste in Word end up with nothing if only Object Descriptor and Embed Source exist). But again, I don't know what is in it. It seems that it begins with an ASCII string (in my case it is "CPYA 4.2878 724#") and does not look like a WMF file.

If I use COM object IDataObject (in either System.Runtime.InteropServices.ComTypes or Microsoft.VisualStudio.OLE.Interop namespace), I can get HMETAFILEPICT from it, but I don't know how to use the handle. PlayMetaFile does not work on it.

EDIT

In MFC, the example uses OleCreateFromData to create the object directly from the IDataObject got from clipboard. This should also work in C#. But this is only the implementation of client. I will try to find out how to implement a server.

like image 862
Wu Zhenwei Avatar asked Dec 01 '16 02:12

Wu Zhenwei


Video Answer


1 Answers

I don't know why my question is down voted. Actually I can find many threads on the Internet that asks for similar question but none of them ends up with a complete answer. This is a shame.

Finally I find the answer myself, after looking through MFC example code. OLE is really hard for a new person to learn, especially without MFC. There is simply no one who can tell you what to do in detail.

First, both method should work in the same way. For "Object Descriptor", HGlobal is just a memory block, which just contains OBJECTDESCRIPTOR structure. For "Embed Source", the IStorage is usually created with ILockBytes, which just has a memory block. The question is what kind of data is on it. Actually on the block is a format called OLE Compound File. It contains two parts of information: a guid and data used to initialize IOLEObject. The guid is very important because it is actually the one that is used to create IOLEObject. The GUID in "Object Descriptor" is just used to show Special Paste dialog. The GUID can be added into IStorage with WriteClassStg API function. Other parts of data contained in the IStorage is usually a single stream called "Contents" (at least this is the implementation of MFC example I have).

In conclusion, what I have to do to prepare for a paste is:

  1. Have a OLEObject class that implements IOLEObject, IDataObject, IPersistStorage and IViewObject (not quite sure about IViewObject).
  2. Have a DataObject class that implements IDataObject (do same thing as the object in 1, but only the IDataObject interface).
  3. In the implementation for IDataObject of the 2 objects, at least have data for type "Object Descriptor", "Embed Source" and "MetaFilePict". MetaFile can be created by the method provided in Convert an image into WMF with .NET?.
  4. Ensure the classes can be accessed by COM and is properly registered. https://limbioliong.wordpress.com/2011/08/30/creating-a-com-server-using-c/
  5. When performing the copy, construct an IStorage by StgCreateDocfile, set GUID of the OLEObject with WriteClassStg, and write other data required by OLEObject in it.
  6. Construct the DataObject class and provide it with the IStorage as "Embed Source".
  7. Set the DataObject to clipboard with OleSetClipboard.

It seems that MFC registers a IClassFactory that allows the OLEObject to be created. I am not sure how to achieve this in C#.

I will add some code here after I finish so that someone else who looks for the details of OLE can easily find it.

A (barely) working example: https://github.com/acaly/SharpOle

like image 154
Wu Zhenwei Avatar answered Sep 28 '22 21:09

Wu Zhenwei