⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 article1.htm

📁 The code for this article was written for version 1.0 of the Active Template Library (ATL). The cu
💻 HTM
📖 第 1 页 / 共 5 页
字号:
</FONT></PRE>
<P>This is a good illustration of how all type library blocks work. First, let's look at similarities between C++ and ODL. As in C++, blocks are enclosed in curly braces. For the most part, ODL handles numeric and string expressions the same as C++, but the H8000 macro gives a hint that some things aren't going to be exactly the same. In C++, hexadecimal constants are compatible with either signed or unsigned numbers, but in ODL the hexadecimal constants are unsigned, leading to unexpected results. This won't be the last ODL &quot;gotcha&quot; you'll encounter. (This problem seems to be fixed in pre-release versions of the MIDL compiler.)
<P>You can use C++ comments in either the single-line or enclosed block format. MKTYPLIB uses the C++ preprocessor and therefore the syntax of the <b>#define</b>, <b>#include</b>, and conditional compilation statements is exactly the same. You can confirm this by renaming C1.EXE (the Visual C++ preprocessor program) and then trying to compile an ODL file. It won't happen--because MKTYPLIB needs that program. 
<P>WIN.ODL uses the <b>#include</b> statement to fake separate modules. This technique makes the source easier to manage, but it doesn't do anything to speed compilation. You still recompile every module whenever any module changes. There's no way to compile files as separate compilation modules. The WINTYPE.ODL file defines Windows types such as HWND, DWORD, and so on. It is a real include file (rather than a fake module) in the C++ tradition, and should be placed in your include directory so that you can use the shared definitions from different projects--such as the VBUTIL type library discussed in the next article in this series. 
<P>Each ODL block starts with a list of attributes enclosed in square brackets. Some attributes (<b>uuid</b>, <b>helpfile</b>, <b>helpcontext</b>, <b>helpstring</b>) are standard and apply to many different kinds of blocks. Others (<b>lcid</b> and <b>version</b>) are specific to a particular block type. The block itself starts with a keyword (<b>library</b>, in this case) followed by the block name. Curly braces enclose the contents of each block. 
<P>Let's look at each element of the library block: 
<P>
<TABLE WIDTH=87% BORDER=1 CELLPADDING=5 CELLSPACING=0>
<TR VALIGN=TOP BGCOLOR="#DDDDDD">
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>Keyword</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>Description</b></FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>uuid</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>The name comes from Universally Unique Identifier (UUID), which is apparently a synonym for GUID. If there's a difference between a UUID and a GUID, it doesn't matter to us. </FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>helpfile</b> </FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>A help file containing a help topic about the block. </FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>helpcontext</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>The context number of the help topic. Object browsers can provide a button to reach this context.
</FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>helpstring</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>A one-line description that appears in object browsers that open the type library. </FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>lcid</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>The locale ID indicates the national language of the type library. Zero indicates language-neutral. I'm not going to address internationalization of type libraries here. If you need to translate, you're on your own. </FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>version</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>You should increment the version number for updates, although Visual Basic and its object browser won't know the difference. </FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>library</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>This statement defines the type library block, names it, and encloses all the other blocks. </FONT></TD></TR>
<TR VALIGN=TOP>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2><b>importlib</b></FONT></TD>
<TD><font face="Verdana, Arial, Helvetica, Sans-Serif" SIZE=2>This statement includes (imports) one type library into another. STDOLE.TLB contains standard OLE definitions. </FONT></TD></TR>
</TABLE><h3>Module Statements</h3><P>The library statement is just a formality. You use the <b>Module</b> statement to set up groups of functions associated with a particular DLL. This strategy is the opposite of the Visual Basic <b>Declare</b> syntax. In Visual Basic, you specify for each function what DLL it comes from. In ODL, you specify for each DLL what functions will be mapped. A module block looks like this: <P><PRE><FONT FACE="COURIER" SIZE="2">[
uuid(54674042-3A82-101B-8181-00AA003743D3),
helpstring(&quot;Windows Kernel Functions&quot;),
#ifdef WIN32
dllname(&quot;KERNEL32.DLL&quot;)
#else
dllname(&quot;KRNL386.EXE&quot;)
#endif
]
module Kernel {
</FONT></PRE><P><PRE><FONT FACE="COURIER" SIZE="2">    . 
    . // Module contents
    .
}
</FONT></PRE><P>The key element here is the <b>dllname</b>. You must supply one even if the module only contains constants and has no relation to any DLL. If the DLL name is never used, it doesn't have to make sense. The Visual Basic object browser won't care if you provide the following: <P><PRE><FONT FACE="COURIER" SIZE="2">dllname(&quot;YOUDUMMY.NODLL&quot;),
</FONT></PRE><P>The module name must be one word (no spaces). It will show up as the title for the module entries in object browsers. <P><h3>Procedure Entries</h3><P>The hard work of defining type libraries is in creating the procedure declarations, which are called <i>entries</i> in ODL. You define the attributes of the procedure, and of each of its parameters. Most of what this section has to say about procedures and parameters applies equally well to the method and property definitions in OLE interfaces. You can apply the knowledge you gain here when creating OLE objects such as ActiveX controls. <P>To become an expert in creating type libraries for Visual Basic, you need to understand two things: Visual Basic <b>Declare</b> statements and C function prototypes. ODL entries look a lot like C prototypes, but if your target is Visual Basic, you have to think of them more like <b>Declare</b> statements. <P>For this discussion, I'm going to assume that you understand Basic <b>Declare</b> statements, C prototypes, and how Windows API functions are matched in each. If you feel a little rusty on <b>Declare</b> statements, the Visual Basic documents explain them superficially and many other Visual Basic books give more detail. Chapter 2 of my book <i>Hardcore Visual Basic</i> contains an exhaustive explanation. <P>So let's take a few API functions and look at them as <b>Declare</b> statements, C prototypes, and ODL entries. We'll start with the humble <b>GetParent</b> function, not because a Visual Basic programmer would use it every day, but because it's simple. <b>GetParent</b> looks like this in the Win32 API include file WINUSER.H:<P><PRE><FONT FACE="COURIER" SIZE="2">WINUSERAPI HWND WINAPI GetParent(HWND hWnd);
</FONT></PRE><P>We'll ignore WINUSERAPI because it is usually just a <b>#define</b> to nothing. An HWND is a <b>typedef</b> to <i>void * </i>and WINAPI is a <b>#define</b> to the <i>__stdcall </i>keyword. So essentially we have: <P><PRE><FONT FACE="COURIER" SIZE="2">void * __stdcall GetParent(void * hWnd);
</FONT></PRE><P>As far as Basic is concerned, all pointers, void or otherwise, are just 32-bit integers--Longs--passed by value. This makes sense in the case of handles. Windows may treat them as pointers internally, but as far as users are concerned, they're just handles. It actually makes more sense to think of the prototype like this:<P><PRE><FONT FACE="COURIER" SIZE="2">long __stdcall GetParent(long hWnd);
</FONT></PRE><P>So what we come up with for a 32-bit <b>Declare</b> statement is:<P><PRE><FONT FACE="COURIER" SIZE="2">Declare Function GetParent Lib &quot;USER32&quot; (ByVal hWnd As Long) As Long
</FONT></PRE><P>An ODL entry looks like a C++ prototype. It makes sense to emulate some language, and why not C++? The ODL syntax can also provide a hint to a host compiler or interpreter about how the parameter should be handled. <P><PRE><FONT FACE="COURIER" SIZE="2">long __stdcall GetParent([in] long hWnd);
</FONT></PRE><P>The <i>[in] </i>attribute indicates that a value is passed into the function, but nothing will ever be read out of the parameter. All <i>ByVal</i> parameters are passed on the stack, and you can never get a changed value back, so <i>[in] </i>is always appropriate for <i>ByVal</i> numeric types. If you actually look in WIN.ODL, however, the <b>GetParent</b> entry doesn't look like quite like the one shown above. Instead it looks like this: <P><PRE><FONT FACE="COURIER" SIZE="2">    [
    usesgetlasterror,
    entry(&quot;GetParent&quot;),
    helpstring(&quot;Gets the parent window handle of the given window&quot;)
    ]
    HWND    WINAPI GetParent([in] HWND hWnd);
</FONT></PRE>
<P>Let's skip over the optional <i>usesgetlasterror </i>for now and talk about the required <i>entry</i>. It indicates the exact name of the function as it appears in the DLL. If the DLL specified in this entry's module statement doesn't contain this name, you'll get rude errors when you try to call the function. As we'll see in detail later, aliases are created by giving the entry name the official DLL name and giving the prototype name as the name you want the user to see in the object browser and use in code. In other words, <i>entry </i>is equivalent to the Alias attribute in a <b>Declare</b> statement, except that in ODL, <i>entry </i>is always required even if it has the same value as the prototype name. 
<P>We've already talked about <i>helpstring </i>so I won't say anything more here except that users of object browsers will consider you the lowest scum of the earth if you (like the Visual C++ class wizard) refuse to provide at least a <i>helpstring </i>for each entry. 
<P>The actual entry definition comes at the end, after the parameter list, and it gives the information needed for the host environment to actually call the function. ODL doesn't know a thing about HWND or WINAPI, but it does know about the C <b>#define</b> and <b>typedef</b> statements. This particular definition works because I defined HWND and WINAPI in my standard definition include file, WINTYPE.ODL. I'll talk more about standard definitions later.
<P><h3>A Type Library Strategy</h3>
<P>At this point, you know enough about type libraries to start thinking about strategies for creating and maintaining them. We'll get back to more entry specifics soon. 
<P>The Windows API type library was a huge undertaking that involved translating most of the C include files for the Windows API. If I were starting over, I might do it differently. For example, I would reconsider writing a program that translates include files to type libraries. This would have been a difficult undertaking, and I didn't attempt it because, although most of the work was mechanical, there seemed to be an awful lot of exceptions and special cases. 
<P>Instead, I use the following method for enhancing the Windows type library. Let's say, for example, that I decided to add ZAPI to the many APIs supported by WIN.TLB. 
<P><OL><LI>   Copy ZAPI.H to ZAPI.ODL. 
<LI>   Include ZAPI.ODL in WIN.ODL. 
<LI>   Comment out all the ZAPI definitions and constants. Windows include files are often messy, with lots of conditional compilation, so this isn't always as easy as it sounds.
<LI>   Make sure the whole blank mess compiles with MKTYPLIB.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -