最近工作比较忙,在粗略分析了CoInitialize之后我们一直没有再深入研究,下面言归正传。前面我们初步了解到了CoInitialize其实是通过调用CoInitializeEx来实现功能的,而后者最终调用了wCoInitializeEx函数,如果能进一步了解这个函数的内部实现,那么我们对COM环境的初始化过程就比较清晰了。好,我们下面继续看wCoInitializeEx的汇编代码,这次我们分段来看。

769AF092 arg_0           = dword ptr  8
769AF092 arg_4           = byte ptr  0Ch
769AF092                 mov     edi, edi
769AF094                 push    ebp
769AF095                 mov     ebp, esp
769AF097                 push    ebx
769AF098                 push    esi
769AF099                 push    edi
769AF09A                 mov     edi, ds:__imp__InterlockedIncrement@4 ; InterlockedIncrement(x)
769AF0A0                 push    offset ?g_cProcessInits@@3KA ; lpAddend
769AF0A5                 call    edi ; InterlockedIncrement(x) ; InterlockedIncrement(x)
769AF0A7                 cmp     eax, 1
769AF0AA                 mov     esi, [ebp+arg_0]
769AF0AD                 jz      loc_769CA020
函数一开始先将全局变量g_cProcessInits加1,后面还比较了加1后变量的值是否为1,因此这应该是一个计数器,并且在第一次执行函数时会进行一些额外的操作,具体如下:

769CA020 loc_769CA020:
769CA020                 call    ?ProcessInitialize@@YGJXZ ; ProcessInitialize(void)
769CA025                 test    eax, eax
769CA027                 mov     [ebp+arg_0], eax
769CA02A                 jge     loc_769AF0B3
769CA030                 jmp     loc_76A0B96F
果然,在g_cProcessInits为1的时候,也就是第一次执行wCoInitializeEx的时候,会调用ProcessInitialize,在这里执行了进程范围内全局性的初始化操作,具体是什么操作我们暂时不关心,初始化成功则返回值大于等于0,否则小于0,我们沿着正确的路径往下看。

769AF0B3 loc_769AF0B3:
769AF0B3                 test    [ebp+arg_4], 2
769AF0B7                 mov     eax, [esi]
769AF0B9                 jz      loc_769DAFB7
判断第二个参数,即传入的dwCoInit是否设置了COINIT_APARTMENTTHREADED标识,这里应该是整个函数的一个分水岭,单线程套间和多线程套间的执行从此开始走向不同的方向,本文只研究单线程套间的后续执行情况。

769AF0BF                 or      byte ptr [eax+0Ch], 80h
769AF0C3                 test    [ebp+arg_4], 4
769AF0C7                 jz      short loc_769AF0CF
将在CoInitializeEx中分配的SOleTlsData的dwReserved0[1]域的第7位(由低到高从第0位开始)置1后,检查dwCoInit参数是否设置了COINIT_DISABLE_OLE1DDE标识,该标识的意义是为支持Ole1而禁用DDE,如果设置了,我们会看到。

769AF0C9                 mov     eax, [esi]
769AF0CB                 or      dword ptr [eax+0Ch], 40h
上面只是简单地将在CoInitializeEx中分配的SOleTlsData的dwReserved0[1]域的第2位(由低到高从第0位开始)置1,因此我认为SOleTlsData中dwReserved0[1]域的第7位标识了COM套间是单线程套间还是多线程套间,第2位标识了DDE是否禁用,上述操作只有在dwCoInit参数的COINIT_DISABLE_OLE1DDE标识被设置时才执行。

769AF0CF loc_769AF0CF:
769AF0CF                 mov     eax, [esi]
769AF0D1                 push    4
769AF0D3                 push    1
769AF0D5                 push    esi
769AF0D6                 lea     ecx, [eax+50h]
769AF0D9                 push    ecx
769AF0DA                 add     eax, 3Ch
769AF0DD                 push    eax
769AF0DE                 call    ?InitThreadCtx@@YGJAAPAVCObjectContext@@AAPAVCComApartment@@AAVCOleTls@@HW4tagAPTKIND@@@Z ; InitThreadCtx(CObjectContext * &,CComApartment * &,COleTls &,int,tagAPTKIND)
769AF0E3                 test    eax, eax
769AF0E5                 mov     [ebp+arg_0], eax
769AF0E8                 jl      loc_76A0B96F
我们可以看到后面又调用了InitThreadCtx,这个线程中分配了CObjectContext对象实例以及CComApartment对象实例并将其指针分别传入的前两个参数中。从这里我们大概可以猜测到SOleTlsData向后偏移60个字节和80个字节处分别存放了CObjectContext指针和CComApartment指针。不过SOleTlsData结构自身的大小只有60字节,因此在SOleTlsData结构后面肯定还有其他数据结构存在,这部分内存在什么地方分配的呢?再回到CoInitializeEx函数中调用的COleTls::TLSAllocData观察,发现其中分配了288个字节,根据前面的分析,这里分配的应该是COleTls对象,而SOleTlsData是其中第一个成员,其第二个成员应该就是CObjectContext,而CComApartment指针在CObjectContext指针之后16个字节。

769AF0EE                 mov     ebx, offset ?g_cSTAInits@@3KA ; ulong g_cSTAInits
769AF0F3                 push    ebx             ; lpAddend
769AF0F4                 call    edi ; InterlockedIncrement(x) ; InterlockedIncrement(x)
769AF0F6                 cmp     eax, 1
769AF0F9                 jz      loc_769CA351
线程上下文初始化成功后,g_cSTAInits加1并判断其加1后的值是否等于1,这里应该又是判断是否第一个执行,我们继续看。

769CA351 loc_769CA351:
769CA351                 call    ?RegisterOleWndClass@@YGJXZ ; RegisterOleWndClass(void)
769CA356                 test    eax, eax
769CA358                 mov     [ebp+arg_0], eax
769CA35B                 jge     loc_769AF0FF
769CA361                 jmp     loc_76A0B956
这里注册了一个名为“OleMainThreadWndClass”的窗口类并将返回的窗口类的唯一标识存放在全局变量中供后续程序使用。注册成功后来到这里:

769AF0FF loc_769AF0FF:
769AF0FF                 cmp     ?gdwMainThreadId@@3KA, 0 ; ulong gdwMainThreadId
769AF106                 jz      loc_769CA3CD
可以看到判断保存线程ID的全局变量是否为0,如果为0则:

769CA3CD loc_769CA3CD:
769CA3CD                 call    ?InitMainThreadWnd@@YGJXZ ; InitMainThreadWnd(void)
769CA3D2                 test    eax, eax
769CA3D4                 mov     [ebp+arg_0], eax
769CA3D7                 jge     loc_769AF10C
769CA3DD                 jmp     loc_76A0B95F
又执行了一个函数,这个函数中先检查当前线程是否某个特殊线程,即满足某个全局变量为非空且当前线程的SOleTlsData.dwReserved0[1]的第4位被置1。然后就创建一个之前注册的窗口类的隐藏窗口并保存该窗口的句柄,接着保存当前线程ID到全局变量后函数执行结束。创建窗口调用的是CreateWindowEx函数,第9个参数即父窗口的句柄为HWND_MESSAGE,也就是说创建了一个message-only的窗口。这种窗口可以用来发送和接收消息,不可见、没有Z-Order属性、不能被枚举、不能接收广播消息,Windows仅仅是简单地向其派发消息。由此,我们可以看出对于单线程套间,第一个调用CoInitialize函数的线程中会创建一个隐藏窗口,后续对COM组件的调用都是以消息的形式将请求发送至该隐藏窗口,在其消息处理函数中进行处理,也就是说Windows利用消息机制将对COM组件的调用请求进行了串行化,这就降低了对COM组件的多线程同步要求,但反过来也影响了效率。后续我们会进一步分析来验证单线程套间中,COM请求是否最终通过消息机制得以实现的。

769AF10C loc_769AF10C:
769AF10C                 cmp     ?gpNTAApartment@@3PAVCComApartment@@A, 0 ; CComApartment * gpNTAApartment
769AF113                 jz      loc_769CA22A
这里比较某个全局指针是否为空,如果为空也就是说在第一次调用CoInitialize时要对其进行初始化,若不为空则wCoInitializeEx就直接返回0结束了。我们继续看看该指针为空的情况:

769CA22A loc_769CA22A:
769CA22A                 call    ?InitializeNTA@@YGJXZ ; InitializeNTA(void)
769CA22F                 test    eax, eax
769CA231                 mov     [ebp+arg_0], eax
769CA234                 jge     loc_769AF119
这里主要是调用了一个InitializeNTA函数,这个函数中只是重新分配了一个CObjectContext对象和CComApartment对象,并将CObjectContext指针保存到SOleTlsData的pCurrentCtx中,随后还调用了其某个成员函数;将CComApartment指针保存到全局变量中;这个函数的最终用意还不是非常了解。

后续的工作就比较简单了,主要是释放之前分配的对象,置一些标志位等操作,这里就不再一一细究了。

CoInitialize浅析二的更多相关文章

  1. InnoDB的锁机制浅析(二)—探索InnoDB中的锁(Record锁/Gap锁/Next-key锁/插入意向锁)

    Record锁/Gap锁/Next-key锁/插入意向锁 文章总共分为五个部分: InnoDB的锁机制浅析(一)-基本概念/兼容矩阵 InnoDB的锁机制浅析(二)-探索InnoDB中的锁(Recor ...

  2. EM算法浅析(二)-算法初探

    EM算法浅析,我准备写一个系列的文章: EM算法浅析(一)-问题引出 EM算法浅析(二)-算法初探 一.EM算法简介 在EM算法之一--问题引出中我们介绍了硬币的问题,给出了模型的目标函数,提到了这种 ...

  3. ReentrantLock和condition源码浅析(二)

    转载请注明出处... 接着上一篇的ReentrantLock和condition源码浅析(一),这篇围绕着condition 一.condition的介绍 在这里为了作对比,引入Object类的两个方 ...

  4. 以太网驱动的流程浅析(二)-Ifconfig的详细代码流程【原创】

    以太网驱动流程浅析(二)-ifconfig的详细代码流程 Author:张昺华 Email:920052390@qq.com Time:2019年3月23日星期六 此文也在我的个人公众号以及<L ...

  5. IOS RunLoop浅析 二

    上一篇我们说了runloop 的几种模式,那么我们在模式中又要做些什么呢??? 模式中有三个模块: 事件源(输入源) Source Source: 按照官方文档分类 Port-Based Custom ...

  6. CoInitialize浅析一

    大家都知道程序中若要使用COM组件则必须要先调用CoInitialize,该函数主要是用来初始化COM运行环境.但这个函数的作用域是以线程为单位还是以进程为单位呢?也许大家已经通过测试程序摸索出答案, ...

  7. JDK8 BigDecimal API-创建BigDecimal源码浅析二

    第二篇,慢慢来 根据指数调整有效小数位数 // 上一篇由字符串创建BigDecimal代码中,有部分代码没有给出,这次补上 // 这个是当解析字符数组时存在有效指数时调整有小小数位数方法 privat ...

  8. iOS-静态库,动态库,framework浅析(二)

    创建.a静态库 第一步,新建工程.     一般使用工程名就使用库的名称,比如我这里用FMDB来创建静态库,我的工程名就取名为FMDB,创建的.a静态库就是libFMDB.a.             ...

  9. NIO浅析(二)

    一:前言 在(一中了解了NIO中的缓冲区和通道),通过本文章你会了解阻塞和非阻塞,选择器,管道 二:完成NIO通信的三要素 * 1.通道(Channel):负责连接* java.nio.channel ...

随机推荐

  1. ListView

    ListView:列表展示数据1.视图 - 在其右上方小箭头点击将视图改为Largelcon:或右键属性在外观View将其改为Details2.设置列头 - 在其右上方小箭头点击选择编辑列,然后添加列 ...

  2. Android数据存储方式

    Android提供了5种方式存储数据: 1.使用SharedPreferences存储数据:它是Android提供的用来存储一些简单配置信息的一种机制,采用了XML格式将数据存储到设备中.只能在同一个 ...

  3. 主板BIOSCOMS故障解决三例

    主板故障中因为BIOS/COMS设置不当或者因为主板电池引起的coms故障而导致主板无法正常工作的比例占了不小.今天我们就来说说主板BIOS/COMS的故障和解决.声卡维修 硬盘安装设置 CMOS设置 ...

  4. ORACLE行转列通用过程

    create or replace procedure row_to_col(tabname in varchar2,                                   group_ ...

  5. win764位下安装mysql-5.6.22-x64启动服务报 系统错误 1067的解决办法

    本人电脑win7,64位,需要安装mysql服务器.版本:mysql-5.6.22-x64.安装完成后,在服务里面并没有mysql.于是在百度上搜了下,好多信息,最后把解决方法自己总结下. 在${pr ...

  6. EasyUI表单内容整理

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...

  7. GPS定位 测试

    public class MainActivity extends Activity { private final String TAG = "BX"; private Loca ...

  8. (DFS、全排列)POJ-3187 Backward Digit Sums

    题目地址 简要题意: 输入两个数n和m,分别表示给你1--n这些整数,将他们按一定顺序摆成一行,按照杨辉三角的计算方式进行求和,求使他们求到最后时结果等于m的排列中字典序最小的一种. 思路分析: 不难 ...

  9. Socket Receive 避免 Blocking

    我们知道 Socket Blocking 属性默认true . 表明Socket 处于同步调用 , Connect , 或 Send , Receive 需等待动作 完成才能继续执行. 有一种应用场景 ...

  10. NPOI 2.0 创建Excel文件

    如果只是简单的处理的话,只需要引用下载压缩包里的 NPOI.dll (office 2003)或 NPOI.OOXML.dll (office 2007) 文件而已. using System; us ...