`
yingyingol
  • 浏览: 744571 次
文章分类
社区版块
存档分类
最新评论

COM自动化,第一部分

 
阅读更多
在这部分和以后的内容中,我们将深入COM 自动化世界。希望用一个简要的专栏来研究这个题目。我们将谈论怎样进行自动化(IDispatch)调用和处理自动化对象需要做什么。然后,我们将讨论用于自动化的特殊COM数据类型和研究双重接口。

自动化(从前叫做OLE自动化)是一个和迄今为止我们曾认为标准COM vtable接口完全不同的客户调用服务器方法。

自动化是使用标准COM接口IDispatch来存取对象的自动化接口。因此,我们说任何实现IDispatch的对象实现了自动化。

为什么要自动化?

最初开发自动化是作为一种应用程序(例如Word和Excel)用以把其功能显露给其他应用,包括脚本语言的方式。目的是提供一种简单方式来访问属性和调用方法,这种方式尽可能少的占用自动化客户的资源,并且不需要被访问对象的类型信息就可以进行调用的方法。

在C++头文件中描绘接口的类型信息决不是浪费时间,描绘方法的vtable偏移量也很重要,最困难的是,设置正确的C++堆栈框架以便正确的执行方法调用。对一个基于文本的解释语言所有这些尤其需要技巧。

如果每个脚本语言都不得不做这个机灵的程序,那么很少有能存取COM对象的了。使用自动化,对象就可以提供一个简单的自动化接口,这样脚本语言作者只需掌握IDispatch和几个COM应用程序接口就可以了。

Visual Basic的第一个32位版使用自动化存取OLE控件(现在叫ActiveX控件),他代替了16位的Visual Basic的VBX控件。Visual Basic仍然可以使用自动化存取一个控件的属性和方法,但是更近的版本也支持使用标准COM vtable接口。这次我们创建的例子将使用自动化接口。

脚本语言,例如Visual Basic for Applications、VBScript和J/Script,以独占模式使用自动化。所以如果你想要你的对象可以被脚本语言使用,你必须实现一个自动化接口。

对象和属性和方法,噢,我的上帝!
世界上关于自动化有三个主要概念。对象是最重要的概念。对象显露属性和方法。
图1. 自动化对象的属性和方法
把这个与更复杂的世界的COM观点对比,在这种观点中,是接口,而不是对象,是第一位的,而属性是不存在的,并且每个对象能有多个包含多个方法的接口。
图2. COM对象,接口,方法(包括没有标签的IUnknown)

方法与C++成员函数相似,而自动化的属性则与C++数据成员和实例数据(也叫属性)相似。注意接口没有独立的概念,每个对象有一个自动化接口。进一步注意到COM接口没有属性的概念,它们只有方法。(但是我们可以使用get/set方法对模拟属性。)

自动化对象怎样被创建?

创建一个自动化对象是一个简单的操作。这儿我将使用Visual Basic作为例子,但是在任何兼容自动化的语言中,方法基本上一样。

在Visual Basic,你应先创建一个对象变量:
Dim Beeper as Object
……接着设置它指向一个特殊的对象:
Set Beeper = CreateObject("BeepCntMod.BeepCnt")

在这个例子中我们创建了一个BeepCnt对象

我们可以接着调用对象上的方法控制它的属性,就像我们不久将看到的。

但是首先,让我们讨论Visual Basic(或者任何自动化客户程序)在幕后真正做什么。

我们早已知道我们将通过IDispatch COM接口访问自动化对象。所以DIM语句只显示集合至少需要的内存,因此Visual Basic能为我们即将创建的对象访问IDispatch指针。

CreateObject调用需要有一点技巧。首先,GUID在哪里?对象的CLSID没有GUID我们怎样创建它?

你可以重新调用,这样我们可以通过对象的ProgID引用对象类型。你也可以重新调用我们在注册表用ProgID作为键名注册的一个键。该键用一个CLSID作为子键。

COM提供一个叫CLSIDFromProgID的函数,它根据给出的ProgID查找CLSID。Visual Basic用我们传送到CreateObject的字符串调用这个函数。在这个例子中,Visual Basic将传送"BeepCntMod.BeepCnt"。CLSIDFromProgID查阅那个键和返回与它相关的CLSID。(顺便说一句,ProgID的第一部分是模块或应用程序名,第二部分是模块或应用程序中的对象名。)

在这一点上Visual Basic调用我们的老朋友CoCreateInstanceEx,传送CLSID和请求IDispatch接口。如果CoCreateInstanceEx成功,VB创建一个包含由CoCreateInstanceEx收到的IDispatch指针的对象变量,并且把它分配给我们的对象变量。

如果因任何原因创建失败:对象不存在,或者它没实现IDispatch,则对CreateObject的调用失败。

就像你所看到的,Visual Basic(或任何自动化客户)的开销是最小的,所有必须知道的是用两个简单的COM函数创建对象。

那么你怎样访问自动化属性和方法?

访问我们的对象的Visual Basic源代码可能像下面这样:
BC = Beeper.Count
Beeper.Count = 5
Beeper.Beep

这三个语句分别访问一个属性、设置一个属性和调用一个方法,都只使用了两种IDispatch方法:GetIDsOfNames和Invoke。IDispatch::GetIDsOfNames获得与方法或属性的文本名有关的整型ID。Visual Basic调用它发现"Beep(嘟嘟响)"对应ID 1和"Count(计数)"对应ID 2。当我们调用IDispatch::Invoke时,我们需要这些叫做dispids的ID。

所有现行的自动化属性和方法访问都是通过调用IDispatch::Invoke实现。换句话说,你的自动化客户要访问自动化对象所必须知道是几个简单的COM调用。如果你的执行语言不是C或C++,你可以为你的运行时间编写帮助者来做那些调用,所以从任何程序使用自动化是简单的。

也许很简单,但并不是不重要:IDispatch::Invoke接收一批参数,所有的参数必须被正确设置。最重要的是:

  • 一个叫dispid的整型ID,它指定要被访问的属性和方法(我们通过调用包含属性或方法名的字符串的GetIDsOfNames获得它)。
  • 一个包含一列参数指针的结构。(每个参数被存储到包含一个典型标记和一个叫variant的共用体的结构中。)
  • 一个包含指向属性(设置它、获得它、用一个引用设置它)或者方法(调用它)数列的指针的结构。
  • 一个作为属性获取的或者是方法调用返回的返回值参数,也是一个变量。

噢,万一你想本地化方法、属性、名字化参数名或参数值,Invoke和GetIDsOfNames都接受一个本地ID。

Invoke也有几个其他参数可以把错误信息传递给自动化客户。在这里我们将假定我们处在一个完美的世界,暂时不考虑它们。

变量(Variant)以16个字节存储。前两个字节是一个标记,包含一个代表变量类型的数,其次的六个字节填满,最后的八个字节是变量的值。值的格式取决于标记的值。在C/C++中,我们使用一个共用体表示变量的值。变量可以拥有大多数C++数据类型,包括指针、数组、串、日期和当前对象。下一次我们将对包括变量COM数据类型做完整的处理。

那么为什么它比常规接口容易?

注意不一定是必须通过IDispatch::Invoke进行一次调用:

  • 没有偏移量--我们使用一个dispid,我们通过请求对象本身获得它。
  • 没有C/C++参数列表和调用约定--我们使用变量的数组。
  • 不用C/C++头文件告诉你上面的东西--但是典型库是可选择的。

你不能完全使用C/C++来进行分发。很显然,这四个调用需要使用C/C++调用约定完成。但这是你作为一个自动化客户唯一需要担心的地方。

那么,所有需要为调用做的是一个对象的IDispatch指针,你想访问的属性、方法名和参数的列表。

出于同样的原因,如果你想用脚本语言中编写COM对象,只有用IDispatch(噢,一种创建对象的方式)代替试图处理常规接口的无数变量的无数细节,这会使你的语言运行时间实现更方便。

COM接口和自动化间的不同

从前面的描述,你可以直接的看到自动化与COM接口间不同的几个方式:

  • 自动化接口不必是永恒的,虽然在运行的时候你不必改变它们,因为客户能缓存dispids。但是自动化接口的改变是平常的,尤其是从对象的一个版本到另一个版本时添加方法。(如果你删除方法或改变参数,你可能破坏现有的客户代码。)
  • 自动化方法(和属性)可以获取包括不同类型的可变长度参数列表。在运行时解析参数和执行任何必需的类型转变是IDispatch::Invoke的事。(如果不能实现参数的转换,对象的IDispatch::Invoke实现将返回一个错误,HRESULT。)
  • 自动化方法和属性的访问是最后限制的;换句话说,确定要被访问的方法/属性被推迟到调用时才进行。
  • 因为所有这个后期连接,自动化方法和属性是多形态的,在某种意义上更像Smalltalk而非C++;你可以存取任何对象上任何方法或属性,拒绝有害的调用是对象的责任。例如,你可以在一个任意对象上调用一个Print方法。任何对象调用Print方法大概都会打印它的值;没有任何对象会发生调用失败。在C++中,你只能在那些定义了Print方法的类的对象上调用Print:在编译时检查名字和参数,而不是运行时。


那么看一些例子怎么样?

作为一个快速例子,让我们着眼于刚展示的三个调用怎样使用。

调用一个方法

因为没有参数传递到Beep方法,也没有从Beep方法或返回值,让我们先调用它。记住调用被写作:
Beeper.Beep

首先你需要知道一件事:参数指针传递到Invoke实际上是一个指向一个DISPPARAMS结构的指针,就像下面定义的:

  • 属性的新值有一个参数。
  • 使用dispid DISPID_PROPERTYPUT为参数命名。
  • 返回的值参可以忽略。

设置一个属性与调用一个方法的不同之处在于属性设置的参数使用特殊的dispid命名。

为什么我们不得不进行这么详细的讨论?记住属性可以被参数化的。属性的值有一个特殊的名字使自动化对象容易认出哪个参数是属性的值。(好大夫真正想避免使用命名的参数,但是这是我们绝对需要的例子)

回想在Beeper对象上设置Count属性的VB代码是:
Beeper.Count = 5

调用的C++代码是:



所有这些代码是一个小调用!

对于实现自动化调用需要多少代码和为什么自动化可能很缓慢,你可能开始有一个概念了。如果有更多的参数,这三行设置每个变量:
VariantInit(&rgvarg[0]);
dispparams.rgvarg[0].vt = VT_I4; // 32-bit integer
dispparams.rgvarg[0].iVal = 5; // here's our 5!

……每个参数会被重复--一个语句初始化变量、一个语句设置它的类型、一个语句设定它的值。如果参数需被命名,在rgdispidNamedArgs中有一个附加的语句设置每个参数名。并且参数的大小应被正确设置。

所以自动化是用代码的尺寸作为代价实现了灵活调用。并且它还放弃一件事情:你唯一能传递的参数是那些能在变量(variant)中被描述的参数。而最大的损失是你不能使用变量描述结构。

我们不久将讨论变量。如果你想知道更多关于自动化参数传递的信息,查找你最喜欢的COM参考书。

准备写你自己的IDispatch::Invoke调用?阅读本部分……

除非你正在编写你自己的脚本语言或直接调用IDispatch::Invoke,否则你不会遇到我们将要讨论的问题,对于一个C++客户来讲是一个纯自动化对象。

在阅读关于可选择的自变量时,会发现一个令人迷惑的情形:现在对IDispatch::Invoke的COM文档中,你不得不为每个遗漏的没命名的变量传送带有VT_ERROR标记的变量。如果你跳过一个没命名的自变量,就像在object.method(a,,c)中,是明显正确的。但是如果可选择的自变量是在最后呢?脚本语言能否根本不使用类型库就知道要传递多少哑元自变量?答案:不能。

如果你最后想省略没命名的可选择的自变量,你可以省略它们。使用实现了IDispatch的COM对象将会通过向在IDL中指定的双重接口函数提供自变量来正确处理这种情况。但是有一个gotcha:如果你在调用的对象本身实现了IDispatch::Invoke本身,而不依赖COM,它可以被写入,所以它假设所有可选择的和缺省值的自变量为哑元自变量,--所以如果你能获取类型库信息,你应该可以确定参数的数目,,因此你能提供哑元自变量。

自动化:服务器端

关于怎样调用方法、设置和获取属性、存取返回值和传送参数,我们现在看到的比我们曾经想知道的还要多。自动化对象怎样处理所有这些问题?

简而言之,对象通过实现IDispatch::Invoke(当然还有IDispatch其他部分,包括GetIDsOfNames)处理所有这些问题。但是让我们假定你正在实现这个对象。你怎样实现这些方法?

实现IDispatch困难的方式

如果你的方法没有任何参数(或者可能只有一个参数)和没有参数化的属性,你自己实现IDispatch::Invoke就非常简单。全部需要做的是一个开关语句,它为每个dispid和类型的组合调用正确的函数。(你也可以使用一个调用表。)你将不得不把参数转换为你需要的类型,但是调用VariantChangeType可以很容易的从任何类型转换变量到你需要的任何类型。(如果转换失败,就返回一个错误给调用者。)

但是可以想象如果你有多个参数的混乱状况。首先,未命名的参数在数组中是颠倒次序的。如果他们被命名,顺序由数组的dispids决定,意味着你将不得不把它挑选出。但是等一下--那不是全部--你可能需要支持可选择的参数和带有缺省值可选择的参数。给出所有的这些的描绘是一巨大的混乱。它不仅仅困难--你很可能在做它时产生错误。如果你去做,在你能处理的调用上你将多半以对一个你本来能处理的调用给出一个错误而结束---这不是在你的用户当中鼓舞信心的一种伟大的方式。

有比较容易的方式吗?你可以打赌一定有。

不要用困难方式做它。让COM帮你做它!


如果你的服务器遇到两个相对简单的要求,你能利用COM的内建的IDispatch的实现。要求是:
· 你的自动控制接口必须是一个双重接口(作为ATL产生的),不是一个纯粹的dispinterface。 
· 你必须产生和利用一个类型库告诉COM是哪些方法和属性。

ATL甚至不支持引入的纯disp接口,所以你的选择是双重的(包括自动控制)和自定义接口。一个双重接口和等价的常规接口之间主要的差别是双重接口派生自IDispatch而不是IUnknown。这意味着除了QueryInterface、Release和AddRef外,双重接口也必须实现所有的IDispatch方法(包括GetIDsOfNames和Invoke)。正如ATL为你提供了IUnknown方法的实现,它也提供了IDispatch方法的实现。

双重接口的IDL

双重接口的IDL看起来非常像常规接口的IDL。对BeepCnt对象,IbeepCnt接口的IDL是:



注意双重接口的IID和接口是一个双重接口的事实在接口属性中被指定。同时也应该注意到接口派生自IDispatch,而非IUnknown。

方法属性中的IDs是自动化接口的dispids。注意实现属性的方法有一个特别的属性--在这个情况中,Count属性有两种方法,一个是获取值和另一个是设置它。当MIDL产生C++头文件时,这两种方法将被命名为get_Count和put_Count。 

顺便说一句,这个接口的vtable将有10个入口:三个为IUnknown,四个为IDispatch,和三个IBeepCount方法。 
 
类型库

类型库由IDL文件的库部分产生:



库属性指定LIBID、版本和帮助字符串。该库引入标准类型库,接着为这个对象指定了coclass。

注意对象的CLSID被GUID在coclass属性表中指定。因为这是一个简单的对象,剩下的只是指定对象实现接口。(IUnknown和IDispatch被继承性所覆盖。)

MIDL在一个.tlb文件里产生类型库。你可以独立地使用这个文档,但是你通常不必这样。这文件在资源部分里被包含在DLL内,所以它建立到DLL中。

类型库的原始使用也很重要的:它们是一些像Visual Basic、Visual J++和Visual C++的智能指针(#输入)的工具,常用于描绘对象有什么方法和属性。

ATL怎样使用COM实现IDispatch

在这一点上,ATL的IDispatch实现非常简单。首先,在DllMain函数(当DLL载入时运行)中,Module对象的Init方法在其他事务中载入类型库。COM允许我们通过标准COM接口ITypeLib和ITypeInfo访问类型库。Init为对象的类型库ITypeInfo接口存取一个指针。

我们的好朋友ITypeInfo

ITypeInfo接口有两个叫做GetIDsOfNames和Invoke的方法。ITypeInfo::GetIDsOfNames运用类型库信息获取你发送的名字的正确的调度IDs。这个方法的实现是COM一个内置部分,所以你不必写它--只需提供类型库。

ITypeInfo::Invoke更有趣:它解析dispid和参数(等等),并且实际上通过你的双重接口的vtable调用适当的C++方法。它使用类型库信息决定参数将被转换成什么类型;怎样处理缺省的、可选择的和指定的参数;vtable偏移量是多少。然后它为参数建立一个堆栈框架和执行调用。

因为COM为你做所有这些工作,在IDispatchImpl里的IDispatch方法的ATL实现很简单:当对象被初始化时,它只是通过存储的ITypeInfo接口指针把调用传送到类型库。例如,IDispatchImpl::Invoke的编码很简单:


ITypeInfo指针将被贮存到tih去。我们要做的全部是把指针传送到传递给我们的对象和参数上,COM做其余的工作。不可能更简单了。 

好的,那么有什么收获呢?

但是COM实现被优化到最小的管理开销--因为在调用结束必要的全部工作(例如数组设置和调用堆栈设置,像刚刚叙述的),IDispatch调用在任何情况下都很慢。所以差别没有那么巨大--尤其考虑到如果你自己执行IDispatch::Invoke你不得不做相同数量的工作,除去挖掘类型库。

什么时候你的组件应该支持自动化?

那么什么时候你应该和不应该支持自动化?


明显地,如果你需要你的组件被脚本语言和其他只能自动化的客户应用,你必须支持自动化。那么如果你的组件将用于网页,被Windows脚本主机,或者Office应用程序和许多其他应用程序中Visual Basic for Applications使用,你必须支持自动化。
 
换句话说,如果你不因为你的组件而彻底的分割市场,你需要支持自动化。它和在ATL里单击正确按钮一样容易,所以没有太多的理由不去做它。

另一方面,有一些组件你将只能从vtable兼容的语言(Visual C++,Visual J++,Visual Basic,等等)中调用。这里有一个好例子,如果因为你的系统设计成为组件化(一个很好的东西),而你正在编写组件。像这样的组件经常不能用于它们被设计的系统外,因为它们是专用的--所以使用这些组件可能没有意义,比如说,在网页上。

支持自动化将耗费你什么?

一些与支持自动化相关的费用。

大小和速度


如果你提供一个双重接口,你的ATL组件的大小将有一个小的增长。通常这不重要,但是如果你在一个一个的计算字节,那就有必要看看两种方式产生组件的差别是否重要了。

IDispatch调用速度慢,但是如果你用一个双重接口,你可以让顾客选择是用快的(vtable)还是慢的(自动化)调用。所以性能不是一个真正的大问题。

但是如果涉及到编组(marshalling),就有一个微妙的性能问题会减慢你的调用,比如客户端和服务器在不同的进程(或者在不同的机器)中时。双重接口需要使用COM的通用编组器(marshaller),它是由类型库信息驱动。这个编组器(marshaller)比用MIDL更慢几分。然而,花费在编组器(marshaller)调用上的额外时间与花费在转换过程或者和网络上另一个机器通信上的时间相比是可以忽略的。

对于同一个进程中(DLL)服务器编组(marshalling)通常不是必需的,所以这个问题在大多数情况下不影响性能。然而,如果客户和对象在不同的"房间"里,编组(marshalling)也被用于进程内服务器,因为它们经常处在多线程的应用程序中。在进程内部跨房间的情况下,额外的编组(marshalling)时间是明显的。

没有C++灵活

较大的问题是处理自动化接口模型的和标准COM接口模型之间的差别。首先,对一个特别的对象通常只有唯一的自动化接口。在某些设计中可能存在问题。

更坏的是,你的参数和返回值受限于可以被放入变量(Variant)的那些类型。我们在下一专栏里将讨论变量,但是现在你应该知道变量支持大多数类型,比如字符串和数组。所以它包括了要用到的大部分类型。

但是自动化接口不支持结构。你可以不去考虑它,但它确实是一件痛苦的事。顺便说一句,那意味着连接数据结构也是困难的。

所以如果你需要传送高级C++数据结构,自动化不适合你。但是假如你无论如何都要支持自动化呢?

智能选择

如果你需要支持自动化,即使你有一些好理由不支持它,有一个办法:实现一个双重接口和一个等价的(在函数性方面)常规接口。

常规接口可以利用C++强大的数据构造,并且不需要通用的编组(marshaller)--事实上,你可以定制的编组器(marshaller)使性能达到最优。这将成为你的C和C++客户使用的接口。

每个人愿意使用双重接口。你可能不得不做一些充满想象力的工作来描绘出怎样把一个复杂的C++数据结构转换成一个可以放入变量(Variant)的类型,但是一旦你实现了它,你将拥有一个任何客户都能用的对象。

给一个机会!

如果你不知道是否你真的明白这一部分所谈论的内容,除非你试一下--所以给一个机会!

· 通过编写IDispatch::Invoke调用(和相关的代码),独立写一个应用程序调用一个简单的COM自动化组件。你不需要经常做,但是做一次它将会很好的帮助你--如果没有更好的理由帮你评价你的脚本语言为你做了什么。

· 独立写一个简单的自动化组件,执行IDispatch,包括可怕的Invoke方法。有两个简单的方法我们没有提及--检查文档找出有关的信息。如果你愿意,使用COM的标准化IDispatch实现改变你的解决方案。

这次,我们通过讨论自动化属性和方法如何被使用,创建了一个COM自动化的诀窍。在下一部分里,我们将谈论有关自动化数据类型和更深入的钻研双重接口。

分享到:
评论

相关推荐

    《.NET软件测试自动化之道》源代码

    全书内容由Windows UI测试、Web应用程序测试和存储过程及XML测试三部分组成,全面介绍了如何利用.NET平台提供的一系列技术(而不是依赖于第三方的商业自动化测试工具),采用C#语言编写轻量级的、功能强大的自动化...

    Windows窗口自动化操作类forVB6_V2.0 clsWindow源码

    这个类楼主很早就开始封装了,原本打算做成类似DOM对象那样,通过一堆getElmentByXXX等方法实现对桌面程序下各个窗口以及里面各个控件对象的自由访问,但是具体要做的工作太多,目前只实现了一部分,期待大家一起...

    Windows窗口自动化操作类forVB6_V1.8 源代码

    这个类楼主很早就开始封装了,原本打算做成类似DOM对象那样,通过一堆getElmentByXXX等方法实现对桌面程序下各个窗口以及里面各个控件对象的自由访问,但是具体要做的工作太多,目前只实现了一部分,期待大家一起...

    php OA 源码 办公自动化源码

    新闻:有相关权限的用户可以发送各种业务、行政、人事等单位的相关新闻,被指定接收的用户可以随时查看,第一时间了解单位动态。支持新闻评论,可以直接在新闻及新闻评论中上传文件和图片,可以对重要的新闻进行置顶...

    Windows窗口自动化操作类forVB6_V1.7纯净版 源码

    这个类楼主很早就开始封装了,原本打算做成类似DOM对象那样,通过一堆getElmentByXXX等方法实现对桌面程序下各个窗口以及里面各个控件对象的自由访问,但是具体要做的工作太多,目前只实现了一部分,期待大家一起...

    wohnungsbot:公寓机器人-克莱门斯·舍尔(ClemensSchöll)的三幕“从一个搬出去到柏林找公寓的人”对自动化戏剧的第二幕(机器人的承诺)的应用

    三种动作的自动化戏剧-第2幕:机器人的承诺 信息 提示 Windows的32位版本 住房ia32可以在Windows上为ia32构建。 但是,默认情况下会禁用此功能,因为尚未找到单独播放32位版本的方法,因此将创建一个更大的安装文件...

    Windows窗口自动化操作类forVB6_V1.9beta clsWindow源码

    这个类楼主很早就开始封装了,原本打算做成类似DOM对象那样,通过一堆getElmentByXXX等方法实现对桌面程序下各个窗口以及里面各个控件对象的自由访问,但是具体要做的工作太多,目前只实现了一部分,期待大家一起...

    第一部分 界面设计

    第一部分 界面设计 实例001 如何实现程序闪屏效果 实例002 如何实现程序窗口闪烁 实例003 如何制作吸附窗口程序 实例004 如何制作透明程序窗口 实例005 如何制作半透明程序窗口 实例006 如何制作不规则程序...

    Linux_unix_shell第一部分编程(共两部分)

    第一部分 shell 第1章 文件安全与权限 1 1.1 文件 1 1.2 文件类型 2 1.3 权限 2 1.4 改变权限位 4 1.4.1 符号模式 4 1.4.2 chmod命令举例 5 1.4.3 绝对模式 5 1.4.4 chmod命令的其他例子 6 1.4.5 可以选择使用符号...

    .bulan:大部分yudha自动化脚本的第三级存储库

    自动化脚本 yudha。词典 培养阅读: 要使用此脚本,必须满足以下要求: 1.) Install Phython36 (https://www.python.org/ftp/python/3.6.4/python-3.6.4.exe) 2.) Unduh kbbi5.py, sinonim.py, wikipedia.py, dan ...

    COM与COM+从入门到精通(pdf版本,含源码)

    第一部分 了解COM 第1章 COM概述 何谓CoM COM术语 COM利与弊 COM的好处 COM的局限性 COM组件与接口 何谓接口 接口特征 接口类型 接口规则 接口设计 COM组件的实现规则 实现IUnknown规则 内存管理...

    visual c++6.0技术内幕 带有NLC的文件查看器

    第一部分 windows、V和应用程序框架基础  第1章 Microsoft Windows和VisuaI C++  第2章 Microsoft基本类库应用程序框架 第二部分 MFC库视图类  第3章 用AppWizard开始——“Hello,world!”  第4章 基本事件处理...

    精通MFC 原版书 超星

    第一部分介绍掌握MFC必须具备的C++和面向对象的基础知识。第二部分详细阐述了MFC应用框架、文档视图结构、消息处理机制、窗口的原理、技术及相关实现。每一个技术难点,在进行理论阐述的同时,都给出了典型示例,以...

    [详细完整版]5软件工程.docx

    支撑软件:文本编辑程序,文件格式化程序,程序库系统等 应用软件:商业数据处理软件,工程与科学计算软件,计算机辅助设计/制造软件,系统仿真软件,智能嵌入软件,医疗、制药软件,事务管理、办公自动化软件。...

    深入解析MFC

    本书的第一部分包含了核心的MFC图形用户界面类以及支持它们的类,第二部分包含了像OLE这种扩展基本Windows支持的主题。如果做到以下几点,你就可以成为一位透彻理解MFC实现细节的专家:探索MFC文档/视图结构的内幕,...

    C++ Builder 5 编程实例与技巧

    第五部分 组件对象模型(COM) 组件对象模型(COM)包括:COM和OLE自动化等内容。 第六部分 分布式程序设计分布式程序设计包括:DCOM、MIDAS、ActiveForm、CORBA与分布式应用程序,以及其他的一些编程方法与技巧。 阅读...

    C++ Builder_5 编程实例与技巧(源代码)

    第五部分 组件对象模型(COM) 组件对象模型(COM)包括:COM和OLE自动化等内容。 第六部分 分布式程序设计分布式程序设计包括:DCOM、MIDAS、ActiveForm、CORBA与分布式应用程序,以及其他的一些编程方法与技巧。 阅读...

    MFC Windows程序设计(第2版修订版)--详细书签版1卷

    MFC不是类似活动模板库(ATL)的通用COM框架,但是MFC使编写某些COM程序更容易,使编写ActiveX控件的过程更简单,而且它使编写自动化(Automation)服务器程序(使用COM技术来供脚本客户调用的程序)变成了二件轻而易举的...

    物联网与智能建筑.pptx

    智能建筑与物联网 Intelligent Building & IOT 物联网与智能建筑全文共14页,当前为第1页。 content 物联网 智能建筑 物联网在智能建筑中应用 1 2 3 目录 物联网与智能建筑全文共14页,当前为第2页。 Internet of ...

Global site tag (gtag.js) - Google Analytics