Shell 链接

Shell 链接 是一个数据对象,其中包含用于访问 Shell 命名空间中另一个对象的信息,即通过 Windows 资源管理器可见的任何对象。 可通过 Shell 链接访问的对象类型包括文件、文件夹、磁盘驱动器和打印机。 Shell 链接允许用户或应用程序从命名空间中的任何位置访问对象。 用户或应用程序不需要知道对象的当前名称和位置。

用户通过从对象的快捷菜单中选择 创建快捷方式 命令来创建 Shell 链接。 系统通过将对象的图标与显示在图标左下角的系统定义链接覆盖图标(称为系统定义的链接覆盖图标)组合在一起,自动为 Shell 链接创建图标。 具有图标的 Shell 链接称为快捷方式;但是,术语 Shell 链接和快捷方式通常可互换使用。 通常,用户会创建快捷方式,以便快速访问存储在子文件夹或其他计算机上的共享文件夹中的对象。 例如,用户可以创建位于子文件夹中的 Microsoft Word 文档的快捷方式,并将快捷方式图标放在桌面上。 然后,用户可以通过双击快捷图标打开文档。 如果在创建快捷方式后移动或重命名文档,系统将在下次用户选择该文档时尝试更新该快捷方式。

应用程序还可以创建和使用 Shell 链接和快捷方式。 例如,字处理应用程序可能会创建 Shell 链接来实现最近使用的文档的列表。 应用程序通过使用 IShellLink 接口创建 Shell 链接对象来创建 Shell 链接。 应用程序使用 IPersistFileIPersistStream 接口将对象存储在文件或流中。

注意

无法使用 IShellLink 创建 URL 链接。

 

本概述介绍了 IShellLink 接口,并说明了如何使用它从基于 Microsoft Win32 的应用程序中创建和解析 Shell 链接。 由于 Shell 链接的设计基于 OLE 组件对象模型 (COM),因此在阅读本概述之前,应熟悉 COM 和 OLE 编程的基本概念。

如果用户创建对象的快捷方式,并且对象的名称或位置后来发生更改,则系统会在下次用户选择该快捷方式时自动执行更新或解析的步骤。 但是,如果应用程序创建 Shell 链接并将其存储在流中,则系统不会自动尝试解析链接。 应用程序必须通过调用 IShellLink::Resolve 方法来解析链接。

创建 Shell 链接时,系统会保存有关该链接的信息。 解析链接时(自动或使用 IShellLink::Resolve 调用)时,系统首先使用指向 Shell 链接标识符列表的指针检索与 Shell 链接关联的路径。 有关标识符列表的详细信息,请参阅 项标识符和标识符列表。 系统在该路径中搜索关联的对象,如果找到该对象,则解析链接。 如果系统找不到对象,它将调用 分布式链接跟踪和对象标识符(DLT)服务(如果可用)来查找对象。 如果 DLT 服务不可用或找不到对象,则系统会在同一目录中查找具有相同文件创建时间和属性但名称不同的对象。 这种类型的搜索将解析已重命名的对象的链接。

如果系统仍然找不到该对象,它将搜索目录、桌面卷和本地卷,但以递归方式查找同名或创建时间相同的对象的目录树。 如果系统仍然找不到匹配项,则会显示一个对话框,提示用户输入位置。 应用程序可以通过在调用 IShellLink::Resolve中指定 SLR_NO_UI 值来禁止显示对话框。

组件对象库的初始化

在应用程序可以创建和解析快捷方式之前,它必须通过调用 CoInitialize 函数来初始化组件对象库。 每次调用 CoInitialize 都需要对 CoUninitialize 函数进行相应的调用,应用程序在终止时应调用该函数。 调用 CoUninitialize 可确保应用程序在收到其所有挂起的消息之前不会终止。

Location-Independent 名称

系统为 Shell 提供了与位置无关的名称,这些链接指向存储在共享文件夹中的对象。 如果对象存储在本地,则系统提供对象的本地路径和文件名。 如果对象远程存储,则系统为该对象提供通用命名约定(UNC)网络资源名称。 由于系统提供与位置无关的名称,Shell 链接可用作可传输到其他计算机的文件的通用名称。

当用户通过从对象的快捷菜单中选择 “创建快捷方式” 命令来创建对象的快捷方式时,Windows 将存储访问链接文件中的对象所需的信息(具有.lnk文件扩展名的二进制文件)。 链接文件包含以下信息:

  • 快捷方式(称为相应对象)引用的对象的位置(路径)。
  • 相应对象的工作目录。
  • 当为快捷方式激活 IContextMenu::InvokeCommand 方法时,系统传递给相应对象的参数列表。
  • 用于设置相应对象的初始显示状态的 show 命令。 这是 ShowWindow中所述的SW_值之一。
  • 快捷方式图标的位置(路径和索引)。
  • 快捷方式的说明字符串。
  • 快捷方式的键盘快捷方式。

删除链接文件时,相应的对象不会受到影响。

如果创建指向另一个快捷方式的快捷方式,则系统只需复制链接文件,而不是创建新的链接文件。 在这种情况下,快捷方式将互不独立。

应用程序可以将文件扩展名注册为快捷方式文件类型。 如果文件具有已注册为快捷文件类型的文件扩展名,则系统会自动将系统定义的链接覆盖图标(小箭头)添加到文件的图标。 若要将文件扩展名注册为快捷文件类型,必须将 IsShortcut 值添加到文件扩展名的注册表说明中,如以下示例所示。 请注意,必须重启 Shell 才能使覆盖图标生效。 IsShortcut 没有数据值。

HKEY_CLASSES_ROOT
   .xyz
      (Default) = XYZApp
   XYZApp
      IsShortcut

快捷方式名称

快捷方式的名称(显示在 Shell 链接图标下方的字符串)实际上是快捷方式本身的文件名。 用户可以通过选择并输入新字符串来编辑说明字符串。

命名空间中快捷方式的位置

快捷方式可以存在于桌面上或 Shell 命名空间中的任意位置。 同样,与快捷方式关联的对象也可以存在于 Shell 命名空间中的任意位置。 应用程序可以使用 IShellLink::SetPath 方法设置关联对象的路径和文件名,IShellLink::GetPath 方法检索对象的当前路径和文件名。

快捷方式工作目录

工作目录是当用户不标识特定目录时快捷方式的相应对象加载或存储文件的目录。 链接文件包含相应对象的工作目录的名称。 应用程序可以使用 IShellLink::SetWorkingDirectory 方法为相应对象设置工作目录的名称,并使用 IShellLink::GetWorkingDirectory 方法检索相应对象的当前工作目录的名称。

快捷方式命令行参数

链接文件包含 Shell 在用户选择链接时传递给相应对象的命令行参数。 应用程序可以使用 IShellLink::SetArguments 方法设置快捷方式的命令行参数。 当相应的应用程序(如链接器或编译器)采用特殊标志作为参数时,设置命令行参数非常有用。 应用程序可以使用 IShellLink::GetArguments 方法从快捷方式中检索命令行参数。

快捷方式显示命令

当用户双击快捷方式时,系统会启动与相应对象关联的应用程序,并根据快捷方式指定的 show 命令设置应用程序的初始显示状态。 show 命令可以是 ShowWindow 函数的说明中包含的任何SW_值。 应用程序可以使用 IShellLink::SetShowCmd 方法设置快捷方式的 show 命令,并使用 IShellLink::GetShowCmd 方法检索当前显示命令。

快捷图标

与其他 Shell 对象一样,快捷方式具有图标。 用户通过双击快捷方式的图标访问与快捷方式关联的对象。 当系统为快捷方式创建图标时,它将使用相应对象的位图,并将系统定义的链接覆盖图标(小箭头)添加到左下角。 应用程序可以使用 IShellLink::SetIconLocation 方法设置快捷方式图标的位置(路径和索引)。 应用程序可以使用 IShellLink::GetIconLocation 方法检索此位置。

快捷方式说明

快捷方式具有说明,但用户永远不会看到它们。 应用程序可以使用说明来存储任何文本信息。 说明是使用 IShellLink::SetDescription 方法设置的,并使用 IShellLink::GetDescription 方法进行检索。

快捷键盘快捷方式

快捷对象可以具有与之关联的键盘快捷方式。 键盘快捷方式允许用户按组合键来激活快捷方式。 应用程序可以使用 IShellLink::SetHotkey 方法设置快捷方式的键盘快捷方式,并且可以使用 IShellLink::GetHotkey 方法检索当前键盘快捷方式。

项标识符和标识符列表

Shell 使用 Shell 命名空间中的对象标识符。 Shell(文件、目录、服务器、工作组等)中可见的所有对象在其父文件夹中的对象中都具有唯一标识符。 这些标识符称为项标识符,它们具有 shtypes.h 头文件中定义的 SHITEMID 数据类型。 项标识符是可变长度字节流,其中包含标识文件夹中对象的信息。 只有项标识符的创建者知道标识符的内容和格式。 Shell 使用的项标识符的唯一部分是前两个字节,用于指定标识符的大小。

每个父文件夹都有自己的项标识符,用于在自己的父文件夹中标识它。 因此,任何 Shell 对象都可以由项标识符列表唯一标识。 父文件夹保留其包含的项目的标识符列表。 该列表具有 ITEMIDLIST 数据类型。 项标识符列表由 Shell 分配,可以跨 Shell 接口传递,例如 IShellFolder。 请务必记住,项标识符列表中的每个标识符仅在其父文件夹的上下文中有意义。

应用程序可以使用 IShellLink::SetIDList 方法设置快捷方式的项标识符列表。 此方法在设置不是文件的对象(如打印机或磁盘驱动器)的快捷方式时非常有用。 应用程序可以使用 IShellLink::GetIDList 方法检索快捷方式的项目标识符列表。

本部分包含的示例演示如何从基于 Win32 的应用程序中创建和解析快捷方式。 本部分假定你熟悉 Win32、C++ 和 OLE COM 编程。

创建文件的快捷方式和文件夹快捷方式

以下示例中的 CreateLink 示例函数创建快捷方式。 这些参数包括指向要链接到的文件名称的指针、指向要创建的快捷方式名称的指针,以及指向链接说明的指针。 说明包含字符串“文件名快捷方式”,其中 文件名 是要链接到的文件的名称。

若要使用 CreateLink 示例函数创建文件夹快捷方式,请使用 CLSID_FolderShortcut 调用 CoCreateInstance,而不是CLSID_ShellLink(CLSID_FolderShortcut支持 IShellLink)。 所有其他代码保持不变。

由于 CreateLink 调用 CoCreateInstance 函数,因此假定已调用 CoInitialize 函数。 CreateLink 使用 IPersistFile 接口保存快捷方式和 IShellLink 接口来存储文件名和说明。

// CreateLink - Uses the Shell's IShellLink and IPersistFile interfaces 
//              to create and store a shortcut to the specified object. 
//
// Returns the result of calling the member functions of the interfaces. 
//
// Parameters:
// lpszPathObj  - Address of a buffer that contains the path of the object,
//                including the file name.
// lpszPathLink - Address of a buffer that contains the path where the 
//                Shell link is to be stored, including the file name.
// lpszDesc     - Address of a buffer that contains a description of the 
//                Shell link, stored in the Comment field of the link
//                properties.

#include "stdafx.h"
#include "windows.h"
#include "winnls.h"
#include "shobjidl.h"
#include "objbase.h"
#include "objidl.h"
#include "shlguid.h"

HRESULT CreateLink(LPCWSTR lpszPathObj, LPCSTR lpszPathLink, LPCWSTR lpszDesc) 
{ 
    HRESULT hres; 
    IShellLink* psl; 
 
    // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
    // has already been called.
    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); 
    if (SUCCEEDED(hres)) 
    { 
        IPersistFile* ppf; 
 
        // Set the path to the shortcut target and add the description. 
        psl->SetPath(lpszPathObj); 
        psl->SetDescription(lpszDesc); 
 
        // Query IShellLink for the IPersistFile interface, used for saving the 
        // shortcut in persistent storage. 
        hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf); 
 
        if (SUCCEEDED(hres)) 
        { 
            WCHAR wsz[MAX_PATH]; 
 
            // Ensure that the string is Unicode. 
            MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1, wsz, MAX_PATH); 
            
            // Add code here to check return value from MultiByteWideChar 
            // for success.
 
            // Save the link by calling IPersistFile::Save. 
            hres = ppf->Save(wsz, TRUE); 
            ppf->Release(); 
        } 
        psl->Release(); 
    } 
    return hres; 

解析快捷方式

应用程序可能需要访问和作之前创建的快捷方式。 此作称为解析快捷方式。

以下示例中应用程序定义的 ResolveIt 函数解析快捷方式。 其参数包括窗口句柄、指向快捷方式路径的指针,以及接收对象新路径的缓冲区的地址。 窗口句柄标识 Shell 可能需要显示的任何消息框的父窗口。 例如,如果链接位于未共享媒体上,则 Shell 可以显示一个消息框;如果出现网络问题,如果用户需要插入软盘,等等。

ResolveIt 函数调用 CoCreateInstance 函数,并假定已调用 CoInitialize 函数。 请注意,ResolveIt 需要使用 IPersistFile 接口来存储链接信息。 IPersistFileIShellLink 对象实现。 必须先加载链接信息,然后才能检索路径信息,此示例稍后会显示该信息。 未能加载链接信息会导致调用 IShellLink::GetPathIShellLink::GetDescription 成员函数失败。

// ResolveIt - Uses the Shell's IShellLink and IPersistFile interfaces 
//             to retrieve the path and description from an existing shortcut. 
//
// Returns the result of calling the member functions of the interfaces. 
//
// Parameters:
// hwnd         - A handle to the parent window. The Shell uses this window to 
//                display a dialog box if it needs to prompt the user for more 
//                information while resolving the link.
// lpszLinkFile - Address of a buffer that contains the path of the link,
//                including the file name.
// lpszPath     - Address of a buffer that receives the path of the link
                  target, including the file name.
// lpszDesc     - Address of a buffer that receives the description of the 
//                Shell link, stored in the Comment field of the link
//                properties.

#include "stdafx.h"
#include "windows.h"
#include "shobjidl.h"
#include "shlguid.h"
#include "strsafe.h"
                            
HRESULT ResolveIt(HWND hwnd, LPCSTR lpszLinkFile, LPWSTR lpszPath, int iPathBufferSize) 
{ 
    HRESULT hres; 
    IShellLink* psl; 
    WCHAR szGotPath[MAX_PATH]; 
    WCHAR szDescription[MAX_PATH]; 
    WIN32_FIND_DATA wfd; 
 
    *lpszPath = 0; // Assume failure 

    // Get a pointer to the IShellLink interface. It is assumed that CoInitialize
    // has already been called. 
    hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl); 
    if (SUCCEEDED(hres)) 
    { 
        IPersistFile* ppf; 
 
        // Get a pointer to the IPersistFile interface. 
        hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf); 
        
        if (SUCCEEDED(hres)) 
        { 
            WCHAR wsz[MAX_PATH]; 
 
            // Ensure that the string is Unicode. 
            MultiByteToWideChar(CP_ACP, 0, lpszLinkFile, -1, wsz, MAX_PATH); 
 
            // Add code here to check return value from MultiByteWideChar 
            // for success.
 
            // Load the shortcut. 
            hres = ppf->Load(wsz, STGM_READ); 
            
            if (SUCCEEDED(hres)) 
            { 
                // Resolve the link. 
                hres = psl->Resolve(hwnd, 0); 

                if (SUCCEEDED(hres)) 
                { 
                    // Get the path to the link target. 
                    hres = psl->GetPath(szGotPath, MAX_PATH, (WIN32_FIND_DATA*)&wfd, SLGP_SHORTPATH); 

                    if (SUCCEEDED(hres)) 
                    { 
                        // Get the description of the target. 
                        hres = psl->GetDescription(szDescription, MAX_PATH); 

                        if (SUCCEEDED(hres)) 
                        {
                            hres = StringCbCopy(lpszPath, iPathBufferSize, szGotPath);
                            if (SUCCEEDED(hres))
                            {
                                // Handle success
                            }
                            else
                            {
                                // Handle the error
                            }
                        }
                    }
                } 
            } 

            // Release the pointer to the IPersistFile interface. 
            ppf->Release(); 
        } 

        // Release the pointer to the IShellLink interface. 
        psl->Release(); 
    } 
    return hres; 
}

创建非文件对象的快捷方式

创建非文件对象的快捷方式(如打印机)类似于创建文件的快捷方式,不同之处在于,必须将标识符列表设置为打印机,而不是设置文件的路径。 若要设置标识符列表,请调用 IShellLink::SetIDList 方法,指定标识符列表的地址。

Shell 命名空间中的每个对象都有一个项标识符。 Shell 通常将项标识符串联成以 null 结尾的列表,其中包含任意数量的项标识符。 有关项标识符的详细信息,请参阅 项标识符和标识符列表

一般情况下,如果需要设置没有文件名的项目(例如打印机)的快捷方式,则已经有指向对象的 IShellFolder 接口的指针。 IShellFolder 用于创建命名空间扩展。

IShellFolder的类标识符后,可以调用 CoCreateInstance 函数来检索接口的地址。 然后,可以调用接口来枚举文件夹中的对象,并检索要搜索的对象的项目标识符的地址。 最后,可以使用调用 IShellLink::SetIDList 成员函数中的地址来创建对象的快捷方式。