#import 指令
C++特殊处
#import指令用于从一个类型库中结合信息。该类型库的内容被转换为C++类,主要用于描述COM界面。
语法
#import "文件名" [属性]
#import <文件名> [属性]
属性:
属性1,属性2,...
属性1 属性2 ...
文件名是一个包含类型库信息的文件的名称。一个文件可为如下类型之一:
* 一个类型库(.TLB或.ODL)文件。
* 一个可执行(.EXE)文件。
* 一个包含类型库资源(如.OCX)的库文件(.DLL)。
* 一个包含类型库的复合文档。
* 其它可被LoadTypeLib API支持的文件格式。
文件名之前可以有一个目录规格。文件名必须是一个已存在文件的名称。两种格式的区别是当路径未完全说明时,预处理器检索类型库文件的顺序不同。
动作
语法格式
引号格式 这种格式让预处理器首先搜索与包含#import语句的文件同一目录的类型库文件,然后在所有包括(#include)该文件的目录中搜索,最后在如下路径中搜索
尖括号格式 这种格式指示预处理器沿以下路径搜索类型库文件
编译器在以下目录中搜索已命名的文件:
1. PATH环境变量路径表。
2. LIB环境变量路径表。
3. 用/I(额外的包括目录)编译器选项指定的路径。#import可以任选地包含一个或多个属性。这些属性使编译器改变类型库头文件的内容。一个反斜杠(\)符可用在一个单一的#import语句中包含额外的行,例如:
#import "test.lib" no_namespace \
rename("OldName","NewName")
#import属性列出如下:
exclude | high_method_prefix |
high_property_prefixes | implementation_only |
include(...) | inject_statement |
named_guids | no_auto_exclude |
no_implementation | no_namespace |
raw_dispinterfaces | raw_interfaces_only |
raw_method_prefix | raw_native_types |
raw_property_prefixes | rename |
rename_namespace |
#import指令可创建两个在C++源代码中重构类型库内容的头文件,第一个头文件和用Microsoft接口定义语言(MIDL)编译器生成的头文件类似,但有额外的编译器生成代码和数据。第一个头文件与类型库有相同的基本名,其扩展名为.TLH。第二个头文件也有与类型库相同的基本名,其扩展名为.TLI。它包括了编译器生成成员函数的实现,且被包含在(#include)的第一个头文件内。
两个头文件都在用/Fo(命名对象文件)选项指定的输出目录中。随后它们被读出和编译,就像第一个头文件被#include指令命名一样。
以下是伴随#import指令的编译器优化:
* 头文件被创建时,将被分配与类库相同的时间标志。
* 处理#import时,编译器首先测试头文件是否存在,是否过期。若条件为真,就不需重新创建。
* 编译器延迟对于OLE子系统的初始化,直到碰到第一个#import命令。
#import指令也可参与最小重建且可被置于一个预编译头文件中。
基本类型库头文件
基本类型库头文件由七个部分组成:
1. 头部固定正文:由注释、COMDEF.H(定义用在头部的一些标准宏)的#include语句和其它繁杂的安装信息组成。
2.前向引用和类型定义:由象struct IMyinterface之类的结构说明和用于一些TKIND_ALIAS项的类型定义组成。
3.灵敏指针说明:模块类_com_ptr_t是一个封装接口指针和消除调用AddRef、Release 和QueryInterface函数需求的灵敏指针实现。此外,它隐藏了创建一个新COM对象中的CoCreateInstance调用。此部分采用宏语句_COM_SMARTPTR_TYPEDEF将COM接口的类型定义创建为_com_ptr_t模板类的模板特例化。例如,对于界面IFoo,.TLH文件包含有:
_COM_SMARTPTR_TYPEDEF(IFoo,_ _uuidof(IFoo));
编译器将其扩展为:type def _com_ptr_t<_com_IIID<IFoo,_ _uuidof(IFoo) >> IFooPtr;
类型IFooPtr可以用在原始的界面指针IFoo*的地方。结果,就不需调用各种IUnknown成员函数。
4. 类型信息(typeinfo)说明:主要由类定义和其它项组成,这些项说明由ITyptLib:GetTypeInfo返回的单个的信息类型项目。在这部分,每个来自于类型库的信息类型都以一种依赖于TYPEKIND信息的格式反映在该头部。
5. 任选旧式GUID定义:包含命名的GUID常量的初始化过程,这些定义是格式CLSID_CoClass和IID_Interface的名称,与那些由MIDL编译器产生的类似。
6. 用于第二个类型库头部的#include语句。
7. 结尾固定正文:目前包括#pragma pack(pop)。
以上这些部分除头部固定正文和结尾固定正文部分之外,都被包括在原来的IDL文件中以library语句指定其名称的名称空间中。你可以通过用名称空间显式限定或包括如下语句从类型库头部使用该名称。
using namespace MyLib
在源代码的#import语句之后立即
名称空间可用#import指令的no_namespace属性来阻止。但阻止的名称空间可能导致名称冲突。名称空间也可用rename_namespace属性重新换名。
编译器提供完全路径给需要依赖当前正在处理的类型库的任何类型库。路径以注释格式写入到由编译器为每个处理的类型库生成的类型库头部(.TLH)。
如果一个类型库包含了对其它类型库定义的类型引用,.TLH文件将包括以下注释:
//
//Cross-referenced type libraries:
//
//#import "c:\path\typelib0.tlb"
//
在#import注释中的实际文件名是存储在寄存器中交叉引用的类型库全路径。如果你遇到由于省略类型定义的错误时,检查.TLH头部的注释,看哪一种依赖类型库需要先输入。在编译该.TLI文件时可能的错误有语法错误(例如C2143,C2146,C2321)、C2501(缺少说明指示符)或C2433(在数据说明中禁止′inline′)。
你必须确定哪些依赖注释是不被系统头部给出的,而是在依赖类型库的#import指令前的某处给出一个#import指令以消除这些错误。
exclude属性exclude(“称1”[,“名称2”,...])
名称1
被排斥的第一个项
名称2
被排斥的第二个项(如有必要)
类型库可能包含在系统头部或其它类型库内定义的项的定义。该属性可用于从生成的类型库头文件中排斥这些项。这个属性可带任意数目的参量,每个参量是一个被排斥的高级类型库项目:
high_method_prefix属性
high_method_prefix("Prefix")
Prefix
被使用的前缀
在缺省的情况下,高级错误处理属性和方法用一个无前缀命名的成员函数来展示。这个名称来自于类型库。high_method_prefix属性说明一个前缀以用于命名这些高级属性和方法。
high_property_prefixes属性
high_property_prefixes("GetPrefix,""PutPrefix,""PutRefPrefix")
GetPrefix
用于propget方法的前缀
PutPrefix
用于propput方法的前缀
PutRefPrefix
用于propputref方法的前缀
在缺省情况下,高级错误处理方法,如propget、propput和propputref,分别采用以前缀Get、Put和PutRef命名的成员函数来说明。high_property_prefixes属性用于分别说明这三种属性方法的前缀。
implementation_only属性
implementation_only属性禁止.TLH头文件(基本头文件)的生成。这个文件包括了所有用于展示类型库内容的说明。该.TLI头文件和wrapper成员函数的实现,将被生成且包含在编译过程中。
当指定该属性时,该.TLI头部的内容将和用于存放普通.TLH头部的内容放在相同的名称空间。此外,该成员函数不会作为联编说明。implementation_only属性一般希望与no_implementation属性配对使用,以跟踪预编译头文件(PCH)之外的实现。一个有no_implementation属性的#import语句被置于用来创建pch的源区域中,结果PCH将被一些源文件所用。一个带implementation_only属性的#import语句随后被用在PCH区域之外。在一个源文件里只需用一次这种语句。这将生成不需对每个源文件进行额外重编译的所有必要的wrapper成员函数。
注意:一个#import语句中的implementation_only属性必须和相同类型库中no_implementation属性的另一个#import语句配套使用。否则,将产生编译错误。这是因为带no_implementation属性的#import语句生成的wrapper类定义需要编译implementation_only属性生成的语句实现。
include(...)属性
Include(名称1[,名称2,...])
名称1
第一个被强制包含的项
名称2
第二个被强制包含的项(如果必要)
类型库可能包含在系统头部或其它类型库中定义的项的定义。#import指令试图用自动排斥这些项来避免多重定义错误。若这些项已经被排斥,象警告C4192所指出的那样,且它们不应该被排斥,则这个属性可用于禁止自动排斥。该属性可带任意数目的参量,每个参量应是被包括的类型库项的名称。
inject_statement属性
inject_statement("source_text")
source_text
被插入到类型库头文件的源文本。
inject_statement属性将其参量作为源文本插入类型库头部。此文本被置于包括头文件中类型库内容的名称空间说明的起始处。
named_guids属性
named_guids属性让编译器定义和初始化模板LIBID_MyLib、CLSID_MyCoClass、IID_MyInterface和DIID_MyDispInterface的旧式格式的GUID变量。
no_implementation属性
该属性阻止.TLI头文件的生成,这个文件包含wrapper成员函数的实现。如果指定这个属性,则展示类型库项说明的.TLH头将生成没有一个#include语句包括该.TLI头文件。
该属性与implementation_only属性配套使用。
no_auto_exclude属性
类型库可能包括在系统头部或其它类型库中定义的项的定义。#import试图通过自动排斥这些项来避免多重定义错误。当这样做时,每个被排斥的项都将生成一个C4192警告信息。你可禁止这个属性使用自动排斥。
no_namespace属性
#import头文件中的类型库内容一般定义在一个名称空间里。名称空间的名称在原来IDL文件的library语句中指定。如果指定no_namespace属性,编译器就不会生成这个名称空间。
如果你想使用一个不同的名称空间,应代替使用rename_namespace属性。
raw_dispinterfaces属性
raw_dispinterfaces属性让编译器生成一个低级wrapper函数。该函数用于调用IDispatch::Invoke和返回HRESULT错误代码的dispinterface方法和属性。如果未指定此属性,则只生成高级wrapper,它在失败时丢弃该C++异常。
raw_interfaces_only属性
raw_interfaces_only属性禁止生成错误处理wrapper函数以及使用这些wrapper函数的_ _declspec(属性)说明。
raw_interfaces_only属性也导致删除在命名non__property函数中的缺省前缀。通常该前缀是raw_。若指定此属性,函数名称将直接从类型库中生成。该属性只允许展示类型库的低级内容。
raw_method_prefix属性
raw_method_prefix("Prefix")
Prefix
被使用的前缀
用raw_作为缺省前缀的成员函数展示低层属性和方法,以避免与高级错误处理成员函数的名称冲突。raw_method_prefix属性用于指定一个不同的前缀。注意: raw_method_prefix属性的效果不会因raw_method_prefix属性的存在而改变。在说明一个前缀时,raw_method_prefix总是优先于raw_interfaces_only。若两种属性用在同一个#import语句中时,则采用raw_method_prefix指定的前缀。
raw_native_types属性
在缺省情况下,高级错误处理方法在BSTR和VARIANT数据类型和原始COM界面指针的地方使用COM支持类_bctr_t和_variant_t。这些类封装了分配和取消分配这些数据类型的存储器存储的细节,并且极大地简化了类型造型和转换操作。raw_native_types属性在高级wrapper函数中禁止使用这些COM支持类,并强制替换使用低级数据类型。
raw_property_prefix属性
raw_property_prefix("GetPrefix","PutPrefix","PutRefPrefix")
GetPrefix
用于propget方法的前缀
PutPrefix
用于propput方法的前缀
PutRefPrefix
用于propputref方法的前缀
在缺省情况下,低级方法propget、propput和propputref分别用后缀为get_、put_和putref_的成员函数来展示。这些前缀与MIDL生成的头文件中的名称是兼容的。raw_property_prefixes属性分别用于说明这三个属性方法的前缀。
rename属性
rename("OldName,""NewName")
OldName
类型库中的旧名
NewName
用于替换旧名的名称
rename属性用于解决名称冲突的问题。若该属性被指定,编译器将在类型库中的OldName的所有出现处用结果头文件中用户提供的NewName替换。
此属性用于类型库中的一个名称和系统头文件中的宏定义重合时。若这种情况未被解决,则将产生大量语法错误,如C2059和C2061。
注意:这种替换用于类型库的名称,而不是用于结果头文件中的名称。
这里有一个例子:假设类型库中有一个名称为MyParent的属性,且头文件中定义了一个用在#import之前的宏GetMyParent。由于GetMyParent是用于错误处理属性get的一个wrapper函数的缺省名称,所以将产生一个名称冲突。为解决这个问题,使用#import语句中的以下属性:
rename("MyParent","MyParentX")
该语句将重新命名类型库中的名称MyParent,而试图重新命名GetMyParentwrapper名称将会出错:
rename("GetMyParent","GetMyParentX")
这是因为名称GetMyParent只出现在结果类型库头文件中。
rename_namespace属性
rename_namespace("NewName")
NewName
名称空间的新名称
rename_namespace属性用于重新命名包含类型库内容的名称空间。它带有一个指定名称空间新名newname的参量。
消除名称空间可以使用no_namespace属性。
C++特殊处结束