如何在C#中实现OLE服务器
How to implement OLE server in C#
我想问的问题是:
(1) 如果我通过剪贴板 class 与剪贴板交互,"Embed Source" 和 "MetaFilePict" 流中应该包含什么?
(2)如果我通过COM接口IDataObject与剪贴板交互,我应该如何处理"Embed Source"和"MetaFilePict"中的句柄?
(3) 有没有更好的方法在 C# 中实现 OLE(客户端或服务器都可以帮助我)?
更多详情:
我正在寻找一种在 C# 中实现 OLE 服务器的方法(extern Windows API 和其他适用于 x86 的方法对我来说都可以,只要它们可以在一个 C# 项目中)。我找不到任何不涉及 MFC 的 OLE 示例。所以我先尝试了一下。
我的第一步是查看其他 OLE 服务器如何将它们的数据放入剪贴板。我尝试了 2 种与剪贴板交互的方法:NET Clipboard class 为我提供了三个流,如后所述,以及 OleGetClipboard 返回的 IDataObject 接口为我提供了指针。
我发现Origin 放置了三个数据条目:Object Descriptor、Embed Source 和MetaFilePict。它们似乎是 Stream。如果我从三个 Streams 中读取所有字节,将它们放回一个新的 DataObject 并将 DataObject 再次放入剪贴板,我可以将原始对象粘贴到例如 Word 中,这意味着三个 Streams 的内容就足够了用于要粘贴的 OLE 容器。
我进一步发现对象描述符包含 OBJECTDESCRIPTOR 结构和数据 class 的 guid 和一些名称,我能够实例化对象并将其转换为 IOLEObject。但我不知道其他两个流 Embed Source 和 MetaFilePict 中应该有什么。根据我的理解,Embed Source 应该包含在创建后传递给 IOLEObject(可能由 InitFromData)的数据,但我没有成功这样做。并且reagrding MetaFilePict,似乎没有这个条目,其他两个不能正常工作(如果只有Object Descriptor和Embed Source存在,在Word中粘贴最终什么也没有)。但同样,我不知道里面有什么。它似乎以 ASCII 字符串开头(在我的例子中是 "CPYA 4.2878 724#")并且看起来不像 WMF 文件。
如果我使用 COM 对象 IDataObject(在 System.Runtime.InteropServices.ComTypes 或 Microsoft.VisualStudio.OLE.Interop 命名空间中),我可以从中获取 HMETAFILEPICT,但我不知道如何使用句柄。 PlayMetaFile 对其不起作用。
编辑
在MFC中,该示例使用OleCreateFromData直接从从剪贴板获取的IDataObject创建对象。这也应该适用于 C#。但这只是客户端的实现。我将尝试找出如何实现服务器。
我不知道为什么我的问题被否决了。实际上,我可以在 Internet 上找到许多提出类似问题的帖子,但其中 none 最终得到了完整的答案。这是一个耻辱。
在查看 MFC 示例代码后,我终于找到了答案。 OLE对于新手来说真的很难学,尤其是没有MFC的时候。根本没有人能详细告诉你怎么做。
首先,这两种方法的工作原理应该相同。对于"Object Descriptor",HGlobal只是一个内存块,里面只包含OBJECTDESCRIPTOR结构。对于"Embed Source",IStorage通常是用ILockBytes创建的,它只有一个内存块。问题是上面有什么样的数据。实际上在块上是一种称为 OLE 复合文件的格式。它包含两部分信息:用于初始化 IOLEObject 的 guid 和数据。 guid 非常重要,因为它实际上是用来创建 IOLEObject 的。 "Object Descriptor" 中的 GUID 仅用于显示特殊粘贴对话框。可以使用 WriteClassStg API 函数将 GUID 添加到 IStorage 中。 IStorage 中包含的其他数据部分通常是称为 "Contents" 的单个流(至少这是我拥有的 MFC 示例的实现)。
综上所述,准备贴子我要做的是:
- 有一个实现 IOLEObject、IDataObject、IPersistStorage 和 IViewObject 的 OLEObject class(对 IViewObject 不太确定)。
- 有一个实现 IDataObject 的 DataObject class(与 1 中的对象做同样的事情,但只有 IDataObject 接口)。
- 在2个对象的IDataObject的实现中,至少有"Object Descriptor"、"Embed Source"和"MetaFilePict"类型的数据。 MetaFile可以通过Convert an image into WMF with .NET?.
中提供的方法创建
- 确保 classes 可以被 COM 访问并正确注册。 https://limbioliong.wordpress.com/2011/08/30/creating-a-com-server-using-c/
- 复制时,通过StgCreateDocfile构造一个IStorage,通过WriteClassStg设置OLEObject的GUID,写入OLEObject需要的其他数据。
- 构造 DataObject class 并将其与 IStorage 一起提供为 "Embed Source"。
- 使用 OleSetClipboard 将 DataObject 设置为剪贴板。
似乎MFC 注册了一个允许创建OLEObject 的IClassFactory。我不确定如何在 C# 中实现这一点。
完成后我会在这里添加一些代码,以便其他寻找 OLE 详细信息的人可以轻松找到它。
一个(勉强)工作示例:https://github.com/acaly/SharpOle
我想问的问题是:
(1) 如果我通过剪贴板 class 与剪贴板交互,"Embed Source" 和 "MetaFilePict" 流中应该包含什么?
(2)如果我通过COM接口IDataObject与剪贴板交互,我应该如何处理"Embed Source"和"MetaFilePict"中的句柄?
(3) 有没有更好的方法在 C# 中实现 OLE(客户端或服务器都可以帮助我)?
更多详情:
我正在寻找一种在 C# 中实现 OLE 服务器的方法(extern Windows API 和其他适用于 x86 的方法对我来说都可以,只要它们可以在一个 C# 项目中)。我找不到任何不涉及 MFC 的 OLE 示例。所以我先尝试了一下。
我的第一步是查看其他 OLE 服务器如何将它们的数据放入剪贴板。我尝试了 2 种与剪贴板交互的方法:NET Clipboard class 为我提供了三个流,如后所述,以及 OleGetClipboard 返回的 IDataObject 接口为我提供了指针。
我发现Origin 放置了三个数据条目:Object Descriptor、Embed Source 和MetaFilePict。它们似乎是 Stream。如果我从三个 Streams 中读取所有字节,将它们放回一个新的 DataObject 并将 DataObject 再次放入剪贴板,我可以将原始对象粘贴到例如 Word 中,这意味着三个 Streams 的内容就足够了用于要粘贴的 OLE 容器。
我进一步发现对象描述符包含 OBJECTDESCRIPTOR 结构和数据 class 的 guid 和一些名称,我能够实例化对象并将其转换为 IOLEObject。但我不知道其他两个流 Embed Source 和 MetaFilePict 中应该有什么。根据我的理解,Embed Source 应该包含在创建后传递给 IOLEObject(可能由 InitFromData)的数据,但我没有成功这样做。并且reagrding MetaFilePict,似乎没有这个条目,其他两个不能正常工作(如果只有Object Descriptor和Embed Source存在,在Word中粘贴最终什么也没有)。但同样,我不知道里面有什么。它似乎以 ASCII 字符串开头(在我的例子中是 "CPYA 4.2878 724#")并且看起来不像 WMF 文件。
如果我使用 COM 对象 IDataObject(在 System.Runtime.InteropServices.ComTypes 或 Microsoft.VisualStudio.OLE.Interop 命名空间中),我可以从中获取 HMETAFILEPICT,但我不知道如何使用句柄。 PlayMetaFile 对其不起作用。
编辑
在MFC中,该示例使用OleCreateFromData直接从从剪贴板获取的IDataObject创建对象。这也应该适用于 C#。但这只是客户端的实现。我将尝试找出如何实现服务器。
我不知道为什么我的问题被否决了。实际上,我可以在 Internet 上找到许多提出类似问题的帖子,但其中 none 最终得到了完整的答案。这是一个耻辱。
在查看 MFC 示例代码后,我终于找到了答案。 OLE对于新手来说真的很难学,尤其是没有MFC的时候。根本没有人能详细告诉你怎么做。
首先,这两种方法的工作原理应该相同。对于"Object Descriptor",HGlobal只是一个内存块,里面只包含OBJECTDESCRIPTOR结构。对于"Embed Source",IStorage通常是用ILockBytes创建的,它只有一个内存块。问题是上面有什么样的数据。实际上在块上是一种称为 OLE 复合文件的格式。它包含两部分信息:用于初始化 IOLEObject 的 guid 和数据。 guid 非常重要,因为它实际上是用来创建 IOLEObject 的。 "Object Descriptor" 中的 GUID 仅用于显示特殊粘贴对话框。可以使用 WriteClassStg API 函数将 GUID 添加到 IStorage 中。 IStorage 中包含的其他数据部分通常是称为 "Contents" 的单个流(至少这是我拥有的 MFC 示例的实现)。
综上所述,准备贴子我要做的是:
- 有一个实现 IOLEObject、IDataObject、IPersistStorage 和 IViewObject 的 OLEObject class(对 IViewObject 不太确定)。
- 有一个实现 IDataObject 的 DataObject class(与 1 中的对象做同样的事情,但只有 IDataObject 接口)。
- 在2个对象的IDataObject的实现中,至少有"Object Descriptor"、"Embed Source"和"MetaFilePict"类型的数据。 MetaFile可以通过Convert an image into WMF with .NET?. 中提供的方法创建
- 确保 classes 可以被 COM 访问并正确注册。 https://limbioliong.wordpress.com/2011/08/30/creating-a-com-server-using-c/
- 复制时,通过StgCreateDocfile构造一个IStorage,通过WriteClassStg设置OLEObject的GUID,写入OLEObject需要的其他数据。
- 构造 DataObject class 并将其与 IStorage 一起提供为 "Embed Source"。
- 使用 OleSetClipboard 将 DataObject 设置为剪贴板。
似乎MFC 注册了一个允许创建OLEObject 的IClassFactory。我不确定如何在 C# 中实现这一点。
完成后我会在这里添加一些代码,以便其他寻找 OLE 详细信息的人可以轻松找到它。
一个(勉强)工作示例:https://github.com/acaly/SharpOle