用汇编访问COM对象

发布时间:2011年12月19日 作者:未知 查看次数:1461

用汇编访问COM对象


 

翻译:taowen

用汇编访问COM对象

Ernest Murphy ernie@surfree.com

Revised Dec 26 2000 for inclusion as part of MASM32

Revised July 10 2000 for the new form of coinvoke.

Sample code for this article is available at ...\COM\examples\shortcut


Abstract:

原理:

The COM (Component Object Model) is used by the Windows operation system in increasing ways. For example, the shell.dll uses COM to access some of its API methods. The IShellLink and IPersistFile interfaces of the shell32.dll will be demonstrated to create a shortcut shell link. A basic understanding of COM is assumed. The code sample included is MASM specific.

COM(组件对象模型)越来越多地被Windows操作系统使用。例如,shell.dll使用COM来访问一些它自己的API函数。shell32.dll的IShellLink和IPersistFile接口将被用来演示如何创建一个快捷方式。COM的基本理解是需要的。包含的示例代码是MASM专用的。


Introduction:

导言:

COM may seem complicated with it's numerous details, but in use these complications disappear into simple function calls. The hardest part is understanding the data structures involved so you can define the interfaces. I apologize for all the C++ terminology used in here. While COM is implementation neutral, it borrows much terminology from C++ to define itself.

COM由于其众多的细节可能看上去复杂,但在实际使用中这些复杂因素消失于简单的函数调用之中。最困难的部分是理解其中用到的数据结构,知道了它你旧可以定义接口(interfaces)。我对在此处使用的C++术语表示抱歉。虽然COM被实现为中立的,但它从C++借用了许多术语来定义自身。

In order to use the COM methods of some object, you must first instance or create that object from its coclass, then ask it to return you a pointer to it's interface. This process is performed by the API function CoCreateInstance. When you are done with the interface you call it's Release method, and COM and the coclass will take care of deleting the object and unloading the coclass.

为了使用一些对象的COM函数(method),你必须首先根据它的coclass具像化(示例化)或者说是创建这个对象。当你用完了这个接口,你调用它的Release函数,COM和coclass将会处理删除对象和卸载coclass的任务。

A COM object is referred to as the SERVER. The program that calls up a COM object so it may use it is referred to as the CLIENT.

COM对象被称为SERVER(服务器)。调用COM对象的程序被称为CLIENT(客户)


Assessing COM Methods

访问COM函数

To use COM methods you need to know before hand what the interface looks like. Even if you "late bind" through an IDispatch interface, you still need to know what IDispatch looks like. A COM interface is just table of pointers to functions. Let's start with the IUnknown interface. If you were to create a component that simply exports the IUnknown interface, you have a fully functional COM object (albeit on the level of "Hello World"). IUnknown has the 3 basic methods of every interface, since all interfaces inherit from IUnknown. Keep in mind all an interface consists of is a structure of function pointers. For IUnknown, it looks like this:

为了使用COM函数你需要事先知道接口(interface)的模样。即使你通过一个IDispatch接口“动态绑定”,你仍然需要知道IDispatch的模样。一个COM接口就是一个函数指针的表格。让我们从IUnknown接口开始。如果你要创建一个仅仅导出(export)IUnknown接口的组件,你将得到的是一个完善的COM对象(虽然仅是在“Hello World”的层次上)。每个接口有IUnknown的3个基本的函数,因为所有的接口都从IUnknown继承而来。始终记得所有的接口是包含函数指针的结构体(structure)。对于IUnknown来说,它就这模样:

IUnknown STRUCT DWORD 
; IUnknown methods 
IUnknown_QueryInterface  QueryInterface_Pointer ? 
IUnknown_AddRef   AddRef_Pointer ? 
IUnknown_Release   Release_Pointer ?
IUnknown ENDS

That's it, just 12 bytes long. It holds 3 DWORD pointers to the procedures that actually implement the methods. It is the infamous "vtable" you may have heard of. The pointers are defined as such so we can have MASM do some type checking for us when compiling our calls. Since the vtable holds the addresses of functions, or pointers, these pointers are typedefed in our interface definition as such:

这就是它,仅仅12字节长。它保存着3个双字大小(DWORD)的指针,指向实际实现的函数。它是你可能听说过的不著名的“vtable”。指针被如下定义,使得MASM可以在编译我们的调用的时候为你作一些类型检查。既然vtable保存了函数的地址,或者说是指针,这些指针在我们的接口定义中被如此定义:

QueryInterface_Pointer  typedef ptr QueryInterface_Proto
AddRef_Pointer   typedef ptr AddRef_Proto 
Release_Pointer   typedef ptr Release_Proto

Finally, we define the function prototypes as follows:

最后,我们如下定义函数原型:

QueryInterface_Proto  typedef PROTO :DWORD, :DWORD, :DWORD 
AddRef_Pointer   typedef PROTO :DWORD 
Release_Pointer   typedef PROTO :DWORD 

In keeping with the MASM32 practice of "loose" type checking, function parameters are just defined as DWORDs. Lots of work to set things up, but it does keeps lots of errors confined to compile time, not run time. In practice, we will wrap up these interface definitions in include files and keep them from cluttering up the source code.

为了保持MASM32的“松散”类型检查的原则,函数参数只是被定义为DWORD。为了把东西搞定要作不少的工作,但它确实把许多错误限定在了编译期,而不是在运行时。原则上,我们要把这些接口定义包装在包含文件中,避免他们在源代码中被搞混乱了。

One rather big compilation on defining an interface: MASM cannot resolve forward references like this, so we have to define them backwards, by defining the function prototype typedefs first, and the interface table last. The include files for the example program later on defines the interfaces this way.

定义一个接口的大问题时:MASM不能决议(resolve)像这样的前向参考,因而我们必须在后面定义他们,先定义函数原型后定义接口表(interface table)。后面的示例程序的包含文件用这种方式定义接口。

To actually use an interface, you need a pointer to it.

为了实际使用一个接口,你需要一个指向它的指针。

The CoCreateInstance API can be used to return us this indirect pointer to an interface structure. It is one level removed from the vtable itself, and actually points to the "object" that holds the interface. The final structure looks like this: CoCreateInstance

API可以被用来返回间接指向一个接口结构的指针。它从vtable本身移开了一个层次,而实际是指向一个“对象”,该对象保存了接口。最终的结构就是这模样:

There is a lot of indirection using this structure, it can drive you batty trying to write code to properly reference and de-reference these elements. Macros to simplify this task will be defined.

使用这个结构其中有很多间接,如果你尝试着写代码来恰当的引用(reference)和取消引用(de-reference)这些元素,它会使你抓狂。将定义一些宏来简化这样的工作。

When the client makes a call to the COM library to create a COM object, it passes in the address where it wants the object pointer to be placed. This initial pointer is generically referred to as "ppv," from the C++ speak "pointer to pointer to (void)," where (void) means an unspecified type. It holds the address of another pointer ("pv"), and this pointer refers to a whole table of pointers, one table entry for each function of the interface.

当用户调用COM库来创建一个COM对象,它传递过去一个地址,在那保存对象的指针。这个最初的指针一般被称为“ppv”,从C++的观点来说就是“指向 指向void类型的指针的指针”,其中void意为未指定的类型。它保存了另外一个指针(“pv”)的地址,而那个指针指向一整个指针表格,对应一个接口中的函数有一个表格项。

For example, say we used CoCreateInstance and successfully got an interface pointer ppv, and wanted to see if it supports some other interface. We can call its QueryInterface method and request a new ppv (ppv2, pointer to an Interface) to the other interface (pIID, pointer to a Interface Identifying GUID) we are interested in. In C, QueryInterface has a prototype that would look like so:

例如,我们用CoCreateInstance成功地获得一个接口指针ppv,并且想要看它是否支持其他地一些接口。我们可以调用它地QueryInterface函数来请求一个新的指向其他我们感兴趣的接口(pIID,接口指针标识GUID)的ppv(ppv2,一个接口的指针)。用C,QueryInterface有一个如下的原型:

(HRESULT) SomeObject::QueryInterface (this:pObject, IID:pGUID, ppv2:pInterface)

Such a call would look like this:

这么一个调用要这样作:

; get pointer to the object 
mov eax, ppv 
; and use it to find the interface structure
mov edx, [eax] 
; push the function parameters onto the stack
push OFFSET ppv2
push OFFSET IID_ISomeOtherInterface
push dword ppv
; and then call that method 
call dword ptr [eax + 0]

This may be accomplished using the built-in MASM 'invoke' macro as such:

这个可以使用MASM内建的“invoke”宏来完成,就像这样:

; get pointer to the object 
mov eax, ppv 
; and use it to find the interface structure
mov edx, [eax] 
; and then call that method 
invoke (IUnknown PTR [edx]).IUnknown_QueryInterface, ppv,
    ADDR IID_SomeOtherInterface, ADDR ppv_new

I hope you find this as wonderfully simple as I do.

我希望你能和我一样发现这些相当简单。

Note we must pass in the pointer we used, this lets the interface know which object (literally "this" object) we are using.

注意我们必须传递我们使用的指针,这个使得接口知道哪个对象(严格的说,“这个”对象)我们正在使用。

Note the register must be type cast (IUnknown PTR [edx]). This lets the compiler know what structure to use to get the correct offset in the vtable for the .QueryInterface function (in this case it means an offset of zero from [edx]). Actually, the information contained by the interface name and function name called disappear at compile time, all that is left is a numeric offset from an as of yet value unspecified pointer.

注意寄存器必须转换类型(IUnknown PTR [edx])。这个让编译器知道使用什么结构在QueryInterface函数的vtable中来获得正确的偏移位置(在这例中,它意为从[edx]开始的0偏移位置)。实际上,由接口名和函数名所包含的信息在编译的时候就消失了,所有剩下的就是从一个还没有指定值的指针开始的一个数字偏移位置。

One more semi-obscure point. Notice I changed the interface method name from simply "QueryInterface" to "IUnknown_QueryInterface". This is a bit of name decoration I've found necessary. When you get to larger COM projects with many similar interfaces you will run into a problem, that is different interfaces with identical method names. This is quite valid, in fact it's called polymorphism, but can confuse the compiler a bit.

另一个有些晦涩不明的地方。注意我把接口函数名从简单的“QueryInterface”改变为“IUnknown_QueryInterface”。这是我发现必要的一点名称装饰。当你要进行一个大一点的COM工程,它有许多可能使你遇到麻烦的很相象的接口。也就是不同的接口有相同的函数名。这个是绝对有效的,实际上它被称为多态,但可能会使得编译器不明白。

Without this name decoration scheme things will be safe until you have two different interfaces with identical method names but different parameters to that method. This is more common then you might first think, but just consider how many interfaces might have a PRINT method.

不加入名称装饰,事情在你遇到这么两个不同接口之前事情还是安全,它们包含有相同函数名但参数又是不一样的函数。这种情况比你一想之下的还要常见,只要考虑可能会有多少个接口有PRINT函数。


The coinvoke Macro

coinvoke宏

We can simplify a COM invoke further with a macro. This coinvoke macro is part of the oaidl.inc file.

我们可以通过宏来进一步简化COM的调用。这个coinvoke宏是oaidl.inc文件的一部分。

;---------------------------------------------------------------------
; coinvoke MACRO 
;
; invokes an arbitrary COM interface 
;
; revised 12/29/00 to check for edx as a param and force compilation error
;                   (thanks to Andy Car for a how-to suggestion)
; revised 7/18/00 to pass pointer in edx (not eax) to avoid confusion with
;   parmas passed with ADDR  (Jeremy Collake's excellent suggestion)
; revised 5/4/00 for member function name decoration
; see http://ourworld.compuserve.com/homepages/ernies_world/coinvoke.htm
;
; pInterface    pointer to a specific interface instance
; Interface     the Interface's struct typedef
; Function      which function or method of the interface to perform
; args          all required arguments 
;                   (type, kind and count determined by the function)
;
coinvoke MACRO pInterface:REQ, Interface:REQ, Function:REQ, args:VARARG
    LOCAL istatement, arg
    FOR arg,      ;; run thru args to see if edx is lurking in there
        IFIDNI <&arg>, 
            .ERR 
        ENDIF
    ENDM
    istatement CATSTR ,<_>,<&Function, pInterface>
    IFNB      ;; add the list of parameter arguments if any
        istatement CATSTR istatement, <, >, <&args> 
    ENDIF 
    mov edx, pInterface
    mov edx, [edx]
    istatement
ENDM
;---------------------------------------------------------------------

Thus, the same QueryInterface method as before can be invoked in a single line:

因此,和之前一样的QueryInterface函数可以用一行来调用:

coinvoke ppv ,IUnknown, QueryInterface, ADDR IID_SomeOtherInterface, ADDR ppnew

Note that now the name decoration is done for us by the macro.

注意同样的名字装饰由宏来替我们完成了。

The only 'gotcha' (well, the most obvious) is that no parameters to a COM call should be passed in edx as this register is used to handle 'this' the object reference. Using edx as a parameter will generate a compile error.

唯一的已经找出的麻烦(呃,最明显的)是调用COM的参数中不能有一个是通过edx传递的,因为这个寄存器被用来处理指向对象的this。使用edx作为一个参数会产生一个编译错误。


Using IShellFile and IPersistFile from shell32.dll

使用shell32.dll中IShellFile和IPersistFile

The shell32.dll provides a simple. easy way to make shell links (shortcuts). However, it uses a COM interface to provide this service. The sample below is based on the MSDN "Shell Links" section for "Internet Tools and Technologies."

shell32.dll提供了一个简单容易的办法来创建快捷方式。然而,它使用COM接口来提供这项服务。下面的例子是基于MSDN的“Internet Tools and Technologies”的“Shell Links”部分。

This may be a strange place to find documentation, but there it is.

这可能是一个找到文档的奇怪的地方,但它就在那。

The "Shell Links" article may be found at: http://msdn.microsoft.com/library/psdk/shellcc/shell/Shortcut.htm

“Shell Links”的文章可以在http://msdn.microsoft.com/library/psdk/shellcc/shell/Shortcut.htm找到。

For this tutorial we will access the following members of the IShellLink and the IPersistFile interfaces. Note every interface includes a "ppi" interface parameter, this is the interface that we calling to (it is the THIS parameter). (The following interface information is a copy of information published by Microsoft)

在本教程中,我们将访问IShellLink和IPersistFile这两个接口的一下成员。注意每个接口都包括一个“ppi”接口参数,这个是我们正在调用的接口的指针(它是THIS参数)。(下面的接口信息是由微软发布的信息的复制)

IShellLink::QueryInterface, ppi, ADDR riid, ADDR ppv 
  * riid: The identifier of the interface requested. To get access to the 
  * ppv: The pointer to the variable that receives the interface. 
  Description: Checks if the object also supports the requested interface. If so, 
  signs the ppv pointer with the interface's pointer. 

IShellLink::Release, ppi Description: Decrements the reference count on the IShellLink interface.
IShellLink:: SetPath, ppi, ADDR szFile * pszFile: A pointer to a text buffer containing the new path for the shell link object. Description: Defines where the file the shell link points to.
IShellLink::SetIconLocation, ppi, ADDR szIconPath, iIcon * pszIconPath: A pointer to a text buffer containing the new icon path. * iIcon: An index to the icon. This index is zero based. Description: Sets which icon the shelllink will use.
IPersistFile::Save, ppi, ADDR szFileName, fRemember * pszFileName: Points to a zero-terminated string containing the absolute path of the file to which the object should be saved. * fRemember: Indicates whether the pszFileName parameter is to be used as the current working file. If TRUE, pszFileName becomes the current file and the object should clear its dirty flag after the save. If FALSE, this save operation is a "Save A Copy As ..." operation. In this case, the current file is unchanged and the object should not clear its dirty flag. If pszFileName is NULL, the implementation should ignore the fRemember flag. Description: Perform a save operation for the ShellLink object, or saves the shell link are creating.
IPersistFile::Release, ppi Description: Decrements the reference count on the IPersistFile interface.

These interfaces contain many many more methods (see the full interface definitions in the code below), but we only need concentrate on those we will actually be using.

这些接口包含比这更多的函数(参考下面代码中的接口定义),但我们仅仅需要集中注意我们将实际使用的那几个。

A shell link is the MS-speak name for a shortcut icon. The information contained in a link (.lnk) file is:

shell link是快捷方式的微软说法。包含在一个link(.lnk)文件中的信息有:

  • 1 - The file path and name of the program to shell.
  • 2 - Where to obtain the icon to display for the shortcut (usually from the executable itself), and which icon in that file to use. We will use the first icon in the file
  • 3 - A file path and name where the shortcut should be stored.
  • 1 - 指向的程序的路径和名字。
  • 2 - 快捷方式显示的图标的路径(通常是执行文件本身),以及那是文件中的哪个图标。我们将使用文件的第一个图标。
  • 3 - 快捷方式应该被存放在的文件路径和名字。

The use of these interfaces is simple and straightforward. It goes like this:

这些接口的用法是相当简单明了的。整个过程是这样:

Call CoCreateInstance CLSID_ShellLink for a IID_IShellLink interface

调用CoCreateInstance CLSID_ShellLink得到一个IID_IShellLink接口

QueryInterface IShellLink for an IID_IPersistFile interface.

QueryInterface IShellLink得到IID_IPersistFile接口。

Call IShellLink.SetPath to specify where the shortcut target is

调用IShellLink.SetPath 来指定快捷方式指向的目标。

Call IShellLink.SetIconLocation to specify which icon to use

调用IShellLink.SetIconLocation来指定使用哪个图标。

Call IPersistFile.Save to save our new shortcut .lnk file.

调用IPersistFile.Save来保存我们的新的快捷方式.lnk文件。

finally,

最后

Call IPersistFile.Release

调用IPersistFile.Release

Call IShellLink.Release

调用IShellLink.Release

This releases our hold on these interfaces, which will automatically lead to the dll that supplied them being unloaded. Again, the hard part in this application was finding documentation. What finally found broke the search open was using Visual Studio "Search in Files" to find "IShellLink" and " IPersistFile" in the /include area of MSVC. This lead me to various .h files, from which I hand translated the interfaces from C to MASM.

这样作释放了我们保存的接口。而这将自动卸载提供他们的dll。此外,写程序中最困那的部分是查找文档。最终打破寻找僵局的是用Visual Studio的“在文件中查找”在MSVC的include目录中查找“IShellLink”和“IPersistFile”。这把我引向了好几个.h文件,在那我手工把接口定义从C翻译为MASM。

Another handy tool I could have used is the command line app "FindGUID.exe," which looks through the registry for a specific interface name or coclass, or will output a list of every class and interface with their associated GUIDs.

另一个我使用的便利的工具是命令行程序“FindGUID.exe”,它在整个注册表中查找一个指定的接口名或coclass,或者输出每个类和接口以及他们所关联的GUID。

Finally, the OLEView.exe application will let you browse the registry type libraries and mine them for information. However, these tools come with MSVC and are proprietary.

最后,OLEView.exe程序让你浏览注册表的类型库,从中挖掘出信息。然而,这个工具是随MSVC一同发售的,是有版权的。

Take care when defining an interface. Missing vtable methods lead to strange results. Essentially COM calls, on one level, amount to "perform function (number)" calls. Leave a method out of the vtable definition and you call the wrong interface. The original IShellLink interface definition I used from a inc file I downloaded had a missing function. The calls I made generated a "SUCCEEDED" hResult, but in some cases would not properly clean the stack (since my push count did not match the invoked function's pop count), thus lead to a GPF as I exited a procedure. Keep this in mind if you ever get similar "weird" results.

当定义一个接口的时候要小心。缺失vtable的函数导致奇怪的结果。

本质上COM调用,从一个层面上来说,相当于“执行 function(number)”。把一个函数缺失在vtable的定义之外导致你调用了错误的接口。我使用的IShellLink接口的最初的定义是来自于一个下载的inc文件,它少了一个函数。我作出的调用产生了“SUCCEEDED”的hResult,但在某些情况下可能不能正常的清栈(因为我的push的次数和调用的函数的pop的次数不相匹配),因而在我退出一个函数的时候导致GPF。牢记这一点,当你得到类似的“奇异”结果的时候。


MakeLink.asm, a demonstration of COM

MakeLink.asm,com的一个演示

This program does very little, as a good tutorial program should. When run, it creates a shortcut to itself, in the same directory. It can be amusing to run from file explorer and watch the shortcut appear. Then you can try the shortcut and watch it's creation time change.

这个程序没有干什么太多的事情,就像一个好的教程中的程序应该作的。运行之后,它在同一个目录中创建了一个指向自身的快捷方式。在文件管理器中运行并观察快捷方式的产生可能会很有趣。然后你可以试一试快捷方式并看到它的创建时间被改变了。

The shell link tutorial code is in ...\COM\examples\shortcut. It begins with some "hack code" to get the full file name path of the executable, and also makes a string with the same path that changes the file to "Shortcut To ShellLink.lnk" These strings are passed to the shell link interface, and it is saved (or persisted in COM-speak).

快捷方式的示例代码位于...\COM\examples\shortcut。它由一些为了获得可执行文件的文件名的完整路径的“hack code”开始,而且用相同的路径创建了一个字符串。它把文件改变为"Shortcut To ShellLink.lnk"。这几个字符串被传递给shell link接口,而且它被保存了(用COM的语言,被持久化了)。

The CoCreateLink procedure used to actually perform the COM methods and perform this link creation has been kept as general as possible, and may have reuse possibilities in other applications.

用来实际执行COM函数和实现快捷方式创建的CoCreateLink函数被设计为尽可能的通用,因而可能在其他程序中有重用的可能。

This program is similar to earlier published tutorial, but has been edited for some additional clarity. The interfaces are defined in a separate include file to reduce clutter. It may be built in MASM32 by using the ...\COM\bin\BLDDLL.BAT file supplied.

这个程序和早先发布的教程很相象,但为了更加清晰而重新进行了编辑。为了避免混乱接口在独立的包含文件中定义。它可以通过使用提供的...\com\bin\bldll.bat文件来建立。

Additional note: Iczelion has quite a useful file in his tutorials named resource.h. It is quite useful when using rc.exe to compile resource files. I use it so much I have moved it to my /masm32/include/ folder. You need to either move your copy there, or change the path in the rsrc.rc file to build it properly.

附加的说明:Iczelion在它的教程中有一个名为resource.h的相当有用的文件。在使用rc.exe来编译资源文件的时候,这个相当有用。我用它太频繁了以至于我把它移动到了我的/masm32/include文件夹。你需要要么把你的那份拷贝移到那儿,或者改变rsrc.rc文件中的路径使得能够正常的建立。


Bibliography:

参考书目:

"Inside COM, Microsoft's Component Object Model" Dale Rogerson Copyright 1997,

Paperback - 376 pages CD-ROM edition

Microsoft Press; ISBN: 1572313498

(THE book for understanding how COM works on a fundamental level. Uses C++ code to illustrate basic concepts as it builds simple fully functional COM object)

"Automation Programmer's Reference : Using ActiveX Technology to Create

Programmable Applications" (no author listed)

Copyright 1997,

Paperback - 450 pages

Microsoft Press; ISBN: 1572315849

(This book has been available online on MSDN in the past, but it is cheap enough for those of you who prefer real books you can hold in your hand. Defines the practical interfaces and functions that the automation libraries provide you, but is more of a reference book then a "user's guide")

Microsoft Developers Network

http://msdn.microsoft.com/

"Professional Visual C++ 5 ActiveX/Com Control Programming" Sing Li and Panos Economopoulos

Copyright April 1997,

Paperback - 500 pages (no CD Rom, files available online)

Wrox Press Inc; ISBN: 1861000375

(Excellent description of activeX control and control site interfaces. A recent review of this book on Amazon.com stated "These guys are the type that want to rewrite the world's entire software base in assembler." Need I say more?)

"sean's inconsequential homepage" http://ript.net/~spec/

Various hardcore articles on low-level COM and ATL techniques. Coded in C++

"Using COM in Assembly Language" Bill Tyler

http://thunder.prohosting.com/~asm1/

Assembly Language Journal, Apr-June 99


代码

MakeLink.asm

;---------------------------------------------------------------------
; MakeLink.asm ActiveX simple client to demonstrate basic concepts
;               written & (c) copyright April 5, 2000 by Ernest Murphy
;
;               contact the author at ernie@surfree.com
;
;               may be reused for any educational or
;               non-commercial application without further license
;---------------------------------------------------------------------
.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\ole32.inc
include \masm32\com\include\oaidl.inc
include \masm32\com\include\shlobj.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\ole32.lib
;---------------------------------------------------------------------
CoCreateLink      PROTO :DWORD, :DWORD
MakeMessage MACRO Text:REQ
    ; macro to display a message box
    ; the text to display is kept local to
    ; this routine for ease of use
    LOCAL lbl
    LOCAL sztext
    jmp lbl
sztext:  
    db Text,0
lbl:
    invoke MessageBox,NULL,sztext,ADDR szAppName,MB_OK
    ENDM
  ; IPersistFile Interface 
  IPersistFile            STRUCT DWORD
       IPersistFile_QueryInterface       comethod3       ?
       IPersistFile_AddRef               comethod1       ?
       IPersistFile_Release              comethod1       ?
       IPersistFile_GetClassID           comethod2       ?
       IPersistFile_IsDirty              comethod1       ?
       IPersistFile_Load                 comethod3       ?
       IPersistFile_Save                 comethod3       ?
       IPersistFile_SaveCompleted        comethod2       ?
       IPersistFile_GetCurFile           comethod2       ?
 IPersistFile            ENDS
;---------------------------------------------------------------------
.data
szAppName         BYTE     "Shell Link Maker", 0
szLinkName        BYTE     "Shortcut to MakeLink.lnk", 0
szBKSlash   BYTE   "\", 0
hInstance         HINSTANCE   ?
Pos               DWORD       ?
szBuffer1   BYTE   MAX_PATH DUP(?)
szBuffer2   BYTE   MAX_PATH DUP(?)
;---------------------------------------------------------------------
.code
start:
;---------------------------------------------------------------------
;  this bracketed code is just a 'quick hack'
;  to replace the filename from the filepathname
;  with the 'Shortcut to' title
;
    invoke GetModuleHandle, NULL
    mov hInstance, eax
    invoke GetModuleFileName, NULL, ADDR szBuffer1, MAX_PATH
    invoke lstrcpy, ADDR szBuffer2, ADDR szBuffer1
    ; Find the last backslash '\' and change it to zero
    mov edx, OFFSET szBuffer2
    mov ecx, edx
    .REPEAT
        mov al, BYTE PTR [edx]
        .IF al == 92 ; "\"
            mov ecx, edx
        .ENDIF
        inc edx
    .UNTIL  al == 0
    mov BYTE PTR [ecx+1], 0   
    invoke lstrcpy, ADDR szBuffer2, ADDR szLinkName
;---------------------------------------------------------------------
; here is where we call the proc with the COM methods
    invoke CoInitialize, NULL
    MakeMessage "Let's try our Createlink."
    invoke CoCreateLink, ADDR szBuffer1, ADDR szBuffer2
    MakeMessage "That's all folks !!!"
    invoke CoUninitialize
invoke ExitProcess, NULL
;---------------------------------------------------------------------
CoCreateLink PROC pszPathObj:DWORD, pszPathLink:DWORD
 ; CreateLink - uses the shell's IShellLink and IPersistFile interfaces 
 ;   to create and store a shortcut to the specified object. 
 ; Returns the hresult of calling the member functions of the interfaces. 
 ; pszPathObj - address of a buffer containing the path of the object. 
 ; pszPathLink - address of a buffer containing the path where the 
 ;   shell link is to be stored. 
 ; addapted from MSDN article "Shell Links"
 ;  deleted useless "description" method
 ;  added set icon location method
    LOCAL   pwsz    :DWORD         
    LOCAL   psl     :DWORD         
    LOCAL   ppf     :DWORD       
    LOCAL   hResult :DWORD       
    LOCAL   hHeap   :DWORD       
.data
CLSID_ShellLink     GUID       sCLSID_ShellLink
IID_IShellLink      GUID       sIID_IShellLink
IID_IPersistFile    GUID       {00000010bH, 00000H, 00000H, \ 
                               {0C0H, 000H, 000H, 000H, 000H, 000H, 000H, 046H}}
.code
    ; first, get some heap for a wide buffer
    invoke GetProcessHeap
    mov hHeap, eax
    invoke HeapAlloc, hHeap, NULL, MAX_PATH * 2
    mov pwsz, eax
    ; Get a pointer to the IShellLink interface. 
    invoke CoCreateInstance, ADDR CLSID_ShellLink, NULL, 
                             CLSCTX_INPROC_SERVER, 
                             ADDR IID_IShellLink, ADDR psl
    mov hResult, eax
    test eax, eax
    .IF SUCCEEDED 
        ; Query IShellLink for the IPersistFile 
        ; interface for saving the shortcut
        coinvoke psl, IShellLink, QueryInterface, ADDR IID_IPersistFile, ADDR ppf
        mov hResult, eax
        test eax, eax
        .IF SUCCEEDED 
            ; Set the path to the shortcut target 
            coinvoke psl, IShellLink, SetPath, pszPathObj
            mov hResult, eax
            ; add the  description, use first icon found
            coinvoke psl, IShellLink, SetIconLocation, pszPathObj, 0 
            mov hResult, eax
            ; change string to Unicode. 
            ; (COM typically expects Unicode strings)
            invoke MultiByteToWideChar, CP_ACP, 0, pszPathLink, 
                                        -1, pwsz, MAX_PATH
            ; Save the link by calling IPersistFile::Save
  coinvoke ppf, IPersistFile, Save, pwsz, TRUE
            mov eax, hResult
            ; release the IPersistFile ppf pointer
            coinvoke ppf, IPersistFile, Release
            mov hResult, eax
        .ENDIF
        ; release the IShellLink psl pointer
        coinvoke psl, IShellLink, Release
        mov hResult, eax
    .ENDIF
    ; free our heap space
    invoke HeapFree, hHeap, NULL, pwsz
    mov eax, hResult    ; since we reuse this variable over and over,
                        ;  it contains the last operations result
    ret
CoCreateLink ENDP
;---------------------------------------------------------------------
end start 

rsrc.rc

// resource.h may be found in Iczelion's tut #10-1
// it is well worth moving to your /masm32/include/ folder
#include "\masm32\include\resource.h"
#define IDC_ICON1              1001
#define IDC_ICON2              1002

IDI_ICON1     ICON   MOVEABLE PURE LOADONCALL DISCARDABLE "PLANE.ICO"
IDI_ICON2     ICON   MOVEABLE PURE LOADONCALL DISCARDABLE "TRFFC14.ICO"


版权所有!www.sieye.cn
E.Mail:sieye@sohu.com QQ:66697110