2017-09-05 281 views
1

我有一个.lnk文件创建一个IShellItem对象,我想获得它指向的文件,无论是作为另一个的IShellItem或一个PIDL。由于目标可能不是实际的文件(仅仅是虚拟文件系统中的一个项目),所以我想完全避免使用任何路径。得到一个.lnk文件BHID_LinkTargetItem如果IsShortcut从注册表中删除

我发现的最接近的方法是IShellItem::BindToHandler,它接受BHID_LinkTargetItem作为参数,并且获得所述目标作为一个的IShellItem。这很完美,但是存在一个小问题 - 如果我删除HKCL中的IsShortcut注册表项(lnkfile等),则不起作用。在这种情况下调用IShellItem::GetAttributes不会返回SFGAO_LINK,并且该方法失败。

当篡改注册表时,系统功能停止工作并不奇怪,但删除此特定键以从快捷方式图标中隐藏箭头是常见的事情。我知道可能有更好的方法来做到这一点,但我不希望我的代码在用户完成之后失败。

我目前有决心,是构建IPersistFile在它的位置和铸造IShellLink。现在我只需拨打IShellLink::GetIDList,我很高兴。如果外壳项目不是真实文件,我尝试获取第二段中指定的链接。

但是,我必须获得链接文件的路径这一事实令我担忧了一下。有没有一些情况下,这可能会失败的一个有效的链接对象?它可以完成而不必使用路径?

+0

继续为用户创建的混乱提供解决方法的路径只会导致黑暗的一面;-)。相反,你应该告诉他们隐藏快捷箭头的正确方法是[用一个透明图标代替它](http://www.theeldergeek.com/windows_7/shortcut_remove_arrow_overlay.htm)。我认为这仍然适用于Win 10。甚至有免费工具可以做到这一点,而无需手动篡改注册表。 – zett42

+0

@ zett42好点,但我不是在寻找“黑客”或解决方法,只是解决问题的另一种方法。也能够直接从* IShellItem *获得* IShellLink *将会很有用。 – IllidanS4

+0

够公平的。 [Raymond Chen通过'IShellFolder :: GetUIObjectOf()')(https://blogs.msdn.microsoft.com/oldnewthing/20100702-00/?p=13523/),在结果中获得一个'IShellLink'。当你从一个'IShellItem'开始时,你可以用'BHID_SFUIObject'来合理地调用'IShellItem :: BindToHandler()'来获得'IShellLink'。我不知道这是否也取决于'IsShortcut'注册表项,但它可能值得一试。 – zett42

回答

1

你可以通过调用IShellItem::BindToHandler()并通过BHID_SFUIObjectrbhid参数获得直接从IShellItemIShellLink。然后,请拨打IShellLink::GetIDList()以获取目前解决方案中已经完成的链接目标。

在我的Windows 10测试中,它独立于IsShortcut注册表项的存在。

例子:

#include <ShlObj.h>  // Shell API 
#include <atlcomcli.h> // CComPtr 
#include <atlbase.h> // CComHeapPtr 
#include <iostream> 
#include <system_error> 

// Throw a std::system_error if the HRESULT indicates failure. 
template< typename T > 
void ThrowIfFailed(HRESULT hr, T&& msg) 
{ 
    if(FAILED(hr)) 
     throw std::system_error{ hr, std::system_category(), std::forward<T>(msg) }; 
} 

// RAII wrapper to initialize/uninitialize COM 
struct CComInit 
{ 
    HRESULT hr = ::CoInitialize(nullptr); 
    CComInit() { ThrowIfFailed(hr, "CoInitialize failed"); } 
    ~CComInit() { ::CoUninitialize(); } 
}; 

int main() 
{ 
    try 
    { 
     CComInit init; 

     // Create a shell item from a file for testing purposes. 
     CComPtr<IShellItem> pItem; 
     ThrowIfFailed(
      SHCreateItemFromParsingName(L"C:\\test.lnk", nullptr, IID_PPV_ARGS(&pItem)), 
      "Could not create shell item from path"); 

     // Obtain a link object from the shell item. 
     CComPtr<IShellLink> pLink; 
     ThrowIfFailed(
      pItem->BindToHandler(nullptr, BHID_SFUIObject, IID_PPV_ARGS(&pLink)), 
      "Could not obtain IShellLink"); 

     // Get the link target as PIDL. 
     CComHeapPtr<ITEMIDLIST> pidlTarget; 
     ThrowIfFailed(
      pLink->GetIDList(&pidlTarget), 
      "Could not get link target as PIDL"); 

     // Create a shell item from the link target PIDL... 
     CComPtr<IShellItem> pItemTarget; 
     ThrowIfFailed(
      SHCreateShellItem(nullptr, nullptr, pidlTarget, &pItemTarget), 
      "Could not create shell item for link target"); 

     //... to obtain the display name (we may use SIGDN_FILESYSPATH to obtain a 
     // path only if we know the target is a filesystem item). 
     CComHeapPtr<wchar_t> pDisplayName; 
     ThrowIfFailed(
      pItemTarget->GetDisplayName(SIGDN_DESKTOPABSOLUTEEDITING, &pDisplayName), 
      "Could not get display name of link target"); 

     std::wcout << L"Link target (display name): " << pDisplayName.m_pData << '\n'; 

     return 0; 
    } 
    catch(std::system_error const& e) 
    { 
     std::cout << "ERROR: " << e.what() << ", error code: " << e.code() << "\n"; 

     return 1; 
    } 
} 

注:

  • 代码使用ATL COM智能指针异常安全,干净的代码。 CComPtr用于管理COM对象(当对象超出作用域时,其析构函数自动调用Release()方法)。 CComHeapPtr用于管理由shell分配的“原始内存”(其析构函数自动调用::CoTaskMemFree())。
+0

我认为它可能不适用于自定义shell命名空间扩展,因为IShellLink实现不是强制性的。在这种情况下,IShellLink实现由shell本身提供,我怀疑在这种情况下我们会回到原来的问题。 –