📄 article1.htm
字号:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML><HEAD><TITLE>Article 1. Stealing Code with Type Libraries</TITLE></HEAD>
<!--DocHeaderStart-->
<BODY bgcolor="#ffffff" text="#000000" leftmargin=0 topmargin=0 link="#0080C0" vlink="gray">
<BR>
<table width=100% border=0 cellspacing=0 cellpadding=0>
<TR>
<TD WIDTH="10" VALIGN=TOP> </TD>
<TD VALIGN="TOP">
<!--DocHeaderEnd-->
<!-- This is a PANDA Generated HTML file. The source is a WinWord Document. -->
<P><h1>Article 1. Stealing Code with Type Libraries</h1>
<P>Bruce McKinney<P><!--DATE-->April 18, 1996<P><!--/DATE-->
<p>Just because you didn't write it doesn't mean you can't use it. This series of articles is about how to how to write OLE dynamic-link libraries (DLLs) for Visual Basic® programs, but this particular article is about how to fake it. You can take system DLLs that know nothing about OLE, add a simple type library, and there you have it. Your system DLLs look and work as if they were filled with OLE-aware Visual Basic functions. <P>Even if you don't need to steal other peoples' DLLs with type libraries, you may want to at least skim this article. It introduces some tools and a definition language that are used in all aspects of OLE programming. You'll also need this information in later articles when we talk about creating DLLs, and you'll need to understand type libraries to effectively create OLE objects or ActiveX™ controls (subjects not covered in this series). <P><h2>The Win32 Type Library</h2><P>We'll be using the Windows® API Functions type library (WIN32.TLB) to illustrate the Object Description Language (ODL). If you have my book <i>Hardcore Visual Basic</i>, you're already familiar with this type library. Both 32- and 16-bit versions were included on the book's CD-ROM. The source file, WIN.ODL, was also provided on disc, but not discussed in the book. An updated version is supplied in the sample code files associated with this article.<P>The source files for this article and a sample Visual Basic program that uses some Windows API calls are included. When you download the sample files, you'll get a directory called \Cpp4VB. In it you'll find a Visual Basic project called Cpp4VB.Vbp and two subdirectories. The \WinTlb subdirectory contains the type library source; the \VBUtil subdirectory contains the DLL source described in Articles 2 through 5. The sample project is primarily designed to test the DLL rather than the type library, although it does use a few API functions. <P>If you have <i>Hardcore Visual Basic</i>, the version of the type library created by the sample project completely replaces the version supplied with the book. This new version contains numerous fixes and improvements. You can use it with any of the Visual Basic samples in the book. If you don't have <i>Hardcore Visual Basic</i>, you can use the Windows API type library to replace most <b>Declare</b> statements in your current Visual Basic programs. Load the type library in the References dialog (you can put it in AUTO32LD.VBP). After it is loaded, you can look at the contents in the Object Browser. <P><blockquote><b>Note:</b> As noted in the introduction, this series is 32-bit only. I hate to start off with an exception, but the original WIN.ODL was designed to be compiled for either 16- or 32-bit. It didn't make sense to spend a lot of time and effort crippling that part of the file just to make it fit into this article. Therefore, you'll still see conditional compilation for 16- and 32-bit code in the source file. I won't be talking about or updating the 16-bit branch. If you find bugs in that code, you'll have to fix them yourself. </blockquote><P>To summarize briefly for those completely unfamiliar with the concept, a type library can eliminate about 80 percent of the <b>Declare</b> and <b>Const</b> statements you would normally use to access Windows API functions and other DLL functions from Visual Basic. Loading WIN32.TLB into your project is equivalent to loading the WIN32API.TXT file supplied with Visual Basic, except that with the type library you pay the memory penalty only for the functions you use. <P>For example, WIN32API.TXT contains about 1500 <b>Declare</b> statements, most of which you would never use from Visual Basic even in your wildest dreams. The hardest of hardcore Visual Basic programmers would be lucky to use 100 in a single project. If you were foolish enough to load WIN32API.TXT into a project, you would pay a memory penalty of about 20 bytes minimum for each <b>Declare</b> in the file--and that's not even counting the constants. So nobody actually loads the more-than-30,000 bytes of WIN32API.TXT data directly into their programs. Instead, they cut and paste the parts they need for each project.<P>WIN32.TLB isn't nearly as comprehensive as WIN32API.TXT. It contains about 475 function entries focused specifically on those that Visual Basic programmers are most likely to need. You'll pay about the same memory price for the functions you use from a type library, but if you only use three, you only pay for three. If this sounds too good to be true, well, it is. Type libraries have some significant limitations that make it impossible to provide some of the most useful API functions. We'll get to those limitations later. <P>
<h2>The Object Description Language</h2><P>The Object Description Language (ODL) is one of two languages you can use to describe OLE interfaces. The other is the Interface Definition Language (IDL), based on the Open Software Foundation's (OSF) Distributed Computing Environment (DCE). IDL is a general language for specifying remote procedure call (RPC) interfaces (which includes OLE interfaces). ODL is a specific language for OLE. ODL and IDL intersect in their ability to define OLE interfaces, but IDL doesn't support the features we'll be talking about in this article--those that allow you to define type libraries for system DLLs. <P>Microsoft is in the process of merging IDL and ODL. Soon you'll be able to compile your current ODL source with an IDL compiler called MIDL. MKTYPLIB, the ODL compiler described in this article, will go the way of the dodo, and none too soon for me. We'll be talking a lot about the limitations of MKTYPLIB. I'll also give a preview of the MIDL compiler where appropriate. <P>Although ODL (like IDL) is called a language, it's not a computer language in the normal sense. Real computer languages allow you to both declare and implement features. ODL only lets you declare features that are implemented elsewhere--except that there are exceptions to the rule. You can, for example, implement constants in ODL. An ODL source file looks a lot like a C++ include file with a lot of extraneous square brackets. Square brackets are used to enclose lists of attributes that apply to the C-like code blocks following the final bracket. It works something like this: <P><PRE><FONT FACE="COURIER" SIZE="2">// Attributes
[
blue,
long,
weight("heavy")
]
// Code block
keyword BlockName {
// Contents of block (can include nested blocks)
}
</FONT></PRE><P>Of course that's not legal ODL, but it gives you a feel for the attribute-block sequence. <P><h3>A GUID Strategy</h3><P>Another thing you'll see a lot in ODL source is Globally Unique Identifiers--GUIDs. Let me indulge readers from my part of the world by describing the pronunciation of GUID as geoduck without the "uck". Those of you who don't know a geoduck from a mallard can just say "Goo-Id." A GUID is a 128-bit number uniquely distinguishing some OLE element from all other OLE elements that ever have or ever will be defined. If you want to master OLE, you'll have to get used to GUIDs. When defining type libraries for system DLLs, GUIDs are more a hoop to be jumped through than a useful feature. But if jump we must, let's do it right. <P>Every block of related definitions in an ODL file must have its own GUID. The groups we'll be talking about in this article are the one for the entire library (called a LIBID) and ones for each module (associated with a single DLL). Visual C++® comes with two GUID generation tools: a Windows program called GUIDGEN and a command-line program called UUIDGEN. You can also generate GUIDs programmatically by calling the OLE <b>CoCreateGuid</b> function. The strategy I recommend for a type library is to generate only one GUID with GUIDGEN or UUIDGEN, and then use increments of its first number group for nested elements. For example, the GUID for the Windows API type library is:<P><PRE><FONT FACE="COURIER" SIZE="2">54674040-3A82-101B-8181-00AA003743D4 // Library LIBID
</FONT></PRE><P>For the modules in the library, I use an incremented sequence:<P><PRE><FONT FACE="COURIER" SIZE="2">54674042-3A82-101B-8181-00AA003743D3 // Windows Kernel Functions
54674043-3A82-101B-8181-00AA003743D3 // Windows Kernel Constants
54674044-3A82-101B-8181-00AA003743D3 // Windows GDI Functions
54674045-3A82-101B-8181-00AA003743D3 // Windows GDI Constants
54674046-3A82-101B-8181-00AA003743D3 // Windows User Functions
. . .
</FONT></PRE><P>Each module starts on an even number. Some have separate odd-numbered sections for constants; some don't. During development of the library, I reorganized modules and changed the GUID numbers several times. But once I published them, something very important happened to them that you need to understand right up front. <P>GUIDs are eternal. A published GUID is carved in stone. The original GUID was generated on a machine that I don't use anymore, and part of the number came from the network adapter on that machine. I could never generate that GUID on my current development machine no matter how many billions of times I ran GUIDGEN. Yet I'm bound by the immutable laws of OLE to keep using that GUID for every version of the Windows API type library until the mountains tumble to the sea. <P>As a practical matter, this principle doesn't matter much for type libraries that expose DLL functions. The OLE world wouldn't come to an end if I changed a few digits in one of the type library GUIDs just to prove I could get away with it. The OLE world (or at least my part of it) <i>would</i> come to an end if I changed the GUID of an OLE interface or coclass. We won't get around to talking about those elements in this article, but keep in mind that in most parts of OLE, GUIDs never change. <P><h3>ODL File Organization</h3><P>Every type library starts off with a library block, which can in turn contain other blocks. Let's check out the whole file, and then look at specific parts. <P><PRE><FONT FACE="COURIER" SIZE="2">// WIN.ODL
[
uuid(54674040-3A82-101B-8181-00AA003743D3),
helpstring("Windows API Functions"),
lcid (0x00000000),
version(1.1)
]
library Win
{
#ifdef WIN32
importlib("stdole.tlb");
#endif
// Include standard windows types.
#include "wintype.odl"
#ifdef WIN32
#define H8000 0x8000 // Hack to get around sign bug in MKTYPLIB.
#else
#define H8000 -32768
#endif
// NOTE: All messages below 0x0400 are RESERVED by Windows.
#define WM_USER 0x0400
#include "intrface.odl"
#include "kernel.odl"
#include "gdi.odl"
#include "user.odl"
#include "winbase.odl"
#include "oleauto.odl"
#include "winmsg.odl"
#include "winmm.odl"
#include "network.odl"
#include "shell.odl"
#include "registry.odl"
#include "comctl.odl"
#include "winerr.odl"
#include "comconst.odl"
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -