正常情况下在Richedit中使用OLE,如果需要OLE支持复制粘贴,那么这个OLE对象必须是已经注册的COM对象。

注册COM很简单,关键问题在于注册时需要管理员权限,这样一来,如果希望APP做成绿色版本就不好使了。

为什么需要注册成COM?因为在粘贴时Richedit需要能够从COM对象的GUID实例化出你的OLE对象。

从一个COM的GUID创建一个COM对象,必然需要通过CoCreateInstance(Ex)这个系统API。那么我们是不是只要Hook到这个API就可以不需要注册了呢?

通过Hook CoCreateInstance,我们发现创建已经注册的COM确实会到CoCreateInstance这个API里来。然而在试图粘贴未注册的COM对象时,确并没有走到自己Hook的CoCreateInstance,粘贴并没有成功。

为什么呢?

既然是粘贴,我们可以先看一下剪贴板里有些什么东西,随便找一个剪贴板查看的工具,看一下里面的RTF格式里有些什么东西。当复制的是注册的COM时,RTF里有这个COM的字符串ID,而当复制没有注册的COM时,剪贴板里没有这些信息。

问题出在哪呢?

有Richedit的原代码就好了。

正好有一份Wince里的Richedit的源代码,通过分析Richedit的复制的代码,可以发现在复制OLE对象时,先要调用ProgIDFromCLSID来查询这个对象的ProgID。

如果一个OLE对象没有注册,那么ProgIDFromCLSID会返回失败,从而导致复制阶段就失败了。

知道了这个流程就好办了,继续HOOK,把ProgIDFromCLSID加到HOOK表就好了。

HOOK了这个函数后发现粘贴时能够执行CoCreateInstance了,我们也可以自己实例化这个等待粘贴的对象了,但事实是还是粘贴失败了,因为在执行这个对象的Load方法前还没有设置ClientSite对象,而我的这个表情对象需要从这个ClintSite来QueryInterface出一个自己定义的接口,没有这个接口对象就没有办法初始化。

为什么呢?为什么呢?

如果有Windows的原代码查一下就好了。

对了,可以看看Wine里OleLoad是怎么做的(通过调用栈可以知道Richedit里直接调用的是OleLoad)。Wine是一个在Linux上运行Windows程序的开源框架,里面有各种Windows API的实现,虽然和Windows还是不一样,但大体流程差不多了。

https://source.winehq.org/ 这个网站不错,想看哪个API的实现搜索一下就出来。

/******************************************************************************
* OleLoad [OLE32.@]
*/
HRESULT WINAPI OleLoad(
LPSTORAGE pStg,
REFIID riid,
LPOLECLIENTSITE pClientSite,
LPVOID* ppvObj)
{
IPersistStorage* persistStorage = NULL;
IUnknown* pUnk;
IOleObject* pOleObject = NULL;
STATSTG storageInfo;
HRESULT hres; TRACE("(%p, %s, %p, %p)\n", pStg, debugstr_guid(riid), pClientSite, ppvObj); *ppvObj = NULL; /*
* TODO, Conversion ... OleDoAutoConvert
*/ /*
* Get the class ID for the object.
*/
hres = IStorage_Stat(pStg, &storageInfo, STATFLAG_NONAME);
if (FAILED(hres))
return hres; /*
* Now, try and create the handler for the object
*/
hres = CoCreateInstance(&storageInfo.clsid,
NULL,
CLSCTX_INPROC_HANDLER|CLSCTX_INPROC_SERVER,
riid,
(void**)&pUnk); /*
* If that fails, as it will most times, load the default
* OLE handler.
*/
if (FAILED(hres))
{
hres = OleCreateDefaultHandler(&storageInfo.clsid,
NULL,
riid,
(void**)&pUnk);
} /*
* If we couldn't find a handler... this is bad. Abort the whole thing.
*/
if (FAILED(hres))
return hres; if (pClientSite)
{
hres = IUnknown_QueryInterface(pUnk, &IID_IOleObject, (void **)&pOleObject);
if (SUCCEEDED(hres))
{
DWORD dwStatus;
hres = IOleObject_GetMiscStatus(pOleObject, DVASPECT_CONTENT, &dwStatus);
}
} /*
* Initialize the object with its IPersistStorage interface.
*/
hres = IUnknown_QueryInterface(pUnk, &IID_IPersistStorage, (void**)&persistStorage);
if (SUCCEEDED(hres))
{
hres = IPersistStorage_Load(persistStorage, pStg); IPersistStorage_Release(persistStorage);
persistStorage = NULL;
} if (SUCCEEDED(hres) && pClientSite)
/*
* Inform the new object of its client site.
*/
hres = IOleObject_SetClientSite(pOleObject, pClientSite); /*
* Cleanup interfaces used internally
*/
if (pOleObject)
IOleObject_Release(pOleObject); if (SUCCEEDED(hres))
{
IOleLink *pOleLink;
HRESULT hres1;
hres1 = IUnknown_QueryInterface(pUnk, &IID_IOleLink, (void **)&pOleLink);
if (SUCCEEDED(hres1))
{
FIXME("handle OLE link\n");
IOleLink_Release(pOleLink);
}
} if (FAILED(hres))
{
IUnknown_Release(pUnk);
pUnk = NULL;
} *ppvObj = pUnk; return hres;
}

上面是Wine 1.9.4里OleLoad的源代码。

可以看到在执行IPersistStorage_Load前会先调用IOleObject_GetMiscStatus这个方法,然而从代码来看它并没有什么用啊?

哦,我差点忘记了,这是Wine,并不是真正的Windows的代码。赶紧查一下IOleObject_GetMiscStatus应该返回什么。

不看不知道,一看吓一跳,原来这里有一个OLEMISC_SETCLIENTSITEFIRST这个标志,看名字就知道有这个标志时,会先调用SetClientSite再调用Load。

好了,改写OLE的这个方法,不去查注册表,直接返回这个标志就好了。

到这里,一个不需要注册的OLE对象就完成了。

写这么多,希望看到的人能够有所启发。

不注册COM在Richedit中使OLE支持复制粘贴的更多相关文章

  1. [FMX]在你的跨平台应用中使用剪贴板进行复制粘贴

    [FMX]在你的跨平台应用中使用剪贴板进行复制粘贴 2017-08-10 • Android.C++ Builder.Delphi.iOS.教程 • 暂无评论 • swish •浏览 516 次 VC ...

  2. delphi edit 中undo 和clearundo 复制粘贴等总结

    edit 和memo都有undo功能, Undo:恢复到改动前. ClearUndo:撤销掉Undo缓冲区的内容,则将无法恢复到改动前的 从该文本框的撤销缓冲区中清除关于最近操作的信息,根据应用 程序 ...

  3. 实现一种快速查找Richedit中可见区域内OLE对象的方法

    Richedit是一个OLE容器,使用Richedit来显示IM聊天内容时,通常使用OLE对象来实现在Richedit中播放表情动画. 触发表情的绘制有两种途径: 1.来自Richedit的刷新消息. ...

  4. 怎么使Richedit中光标始终指到最后一行的最后面?

    Richedit中数据会不断增加.要始终能看到当前的数据.该怎么做? SendMessage(Memo->Handle, WM_VSCROLL, SB_BOTTOM, 0); SendMessa ...

  5. 一种快速刷新richedit中内嵌动画的方法的实现

    在IM中使用动画表情是一种非常有趣的方式,然而选择一种合适的方式来实现却并不容易. 一般来说,除了自己去实现一个富文本控件,目前主要的解决方案有3种: 1.使用浏览器做容器. 2.使用QT提供的Ric ...

  6. Django中使用富文本编辑器Uedit

    Uedit是百度一款非常好用的富文本编辑器 一.安装及基本配置 官方GitHub(有详细的安装使用教程):https://github.com/zhangfisher/DjangoUeditor 1. ...

  7. 将C注册到lua环境中使用

    注册到lua的方式有两种,一种是lua解释器,如果支持动态链接,使用动态链接机制,将函数接口编译成动态链接库,然后将动态链接库放到lua的C路径(LUA_CPATH)中,然后在lua文件中直接使用 r ...

  8. 在Visual Studio中使用用例图描述参与者与用例的关系

    在"在Visual Studio中使用用例图描述系统与参与者间的关系"中,使用用例图表示参与者与系统的关系,本篇体验参与者与用例(参与者要做的事情)的关系. 首先创建有关Custo ...

  9. Django后台管理admin或者adminx中使用富文本编辑器

    在admin或者adminx后台中使用富文本编辑器 一.建立模型:(安装django-tinymce==2.6.0) from django.db import models from tinymce ...

随机推荐

  1. C# 如何强制关闭WINWORD进程

    private void KillProcess(string processName) //调用方法,传参{try{ Process[] thisproc = Process.GetProcesse ...

  2. AD域控制器通过组策略禁止USB设备

    问题:域环境下如何禁用USB口设备? 第一种:用传统的办法,在Bios中禁用USB. 第二种: 微软技术支持回答:根据您的需求, Windows识别USB设备主要通过两个文件,一个是Usbstor.p ...

  3. [Tomcat] Tomcat的classloader

    定义 同其他服务器应用一样,tomcat安装了各种classloader(classes that implement java.lang.ClassLoader) Bootstrap | Syste ...

  4. 【学习笔记】Struts2之配置文件struts.xml

    在默认情况下,Struts2只自动加载类加载路径下的struts.xml.default-struts.xml和struts-plugin.xml三类文件.但是随着应用规模的增大,系统中Action数 ...

  5. vue2.0实战

    学了几周的vue2.0,终于有时间去做一个应用了. 为了全面联系相关知识,所以用到了vue-router,以及作者最新推荐的axios,组件库用的是饿了么的mint-ui2.0. 项目构建使用官方vu ...

  6. SLF4J: Class path contains multiple SLF4J bindings.

    库冲突导致的异常,由于多次引入SLF4j包导致. It seems you have several implementation of SLF4J; you should exclude all t ...

  7. 导出Excel通用工具类

    导出Excel的两种方法: 一,POI 导入poi包 poi-3.11-beta3-20141111.jar /** * */ package com.car.ots.mpckp.utils; imp ...

  8. 前端构建工具的用法—grunt、gulp、browserify、webpack

    随着前端项目的飞速发展,项目越来越大.文件越来越多,前端工程化的工具也越来越多.下面介绍目前最流行的四种构建工具——grunt.gulp.browserify.webpack 所有的构建工具都是基于N ...

  9. 使用vue1.0+es6+vue-cli+webpack+iview-ui+jQuery 撸一套高质量的后台管理系统

    首先按照vue.js官网的指令安装: 1.本地安装好node.js 2.根据官方命令行工具 详情 这样一个官方的脚手架工具就已经搭建好了:但是有一点需要注意的是由于现在按照官方的搭建方法是搭建vue2 ...

  10. MyBatis Like查询处理%_符号

    如果我们数据库中存的字段包含有"%_"这两个like查询的通配符,那么在查询的时候把"%_"当作关键字是查询不出来的,因为mybatis会把这两个字符当作通配符 ...