【发布时间】:2014-06-09 04:47:35
【问题描述】:
我在使用 Shell PIDL 时遇到了三个问题(仅在选择的控制面板项目中发生,一个示例是“通知区域图标”)。
首先,获取“通知区域图标”的 PIDL,并使用该 PIDL 和 SIGDN_NORMALDISPLAY 调用 SHGetNameFromIDList。我没有得到“通知区域图标”(就像我为其他人所做的那样),而是得到了 PIDL 的解析名称(例如 ::{control_panel_clsid}\0\::{notif_area_clsid})。对于其他控制面板项和其他 Shell 项,这没有问题。
其次,使用相同的 PIDL 并调用 ShellexecuteEx,其中 SHELLEXECUTEINFO.lpIDList 设置为该 PIDL,SHELLEXECUTEINFO.fMask 设置为 SEE_MASK_IDLIST。它不启动! Windows 弹出一个错误“没有关联的程序”或类似的东西。对于其他控制面板项和其他 Shell 项,这没有问题。
第三,使用相同的 PIDL 并使用该 PIDL 和包含 SHGFI_PIDL 的 uFlags 调用 SHGetFileInfo。结果是 Windows 通用空白纸图标。对于其他控制面板项和其他 Shell 项,这没有问题。
我很困惑。我不知道这里发生了什么。这可能是某些控制面板虚拟文件夹的错误吗?
我的意思是,完全相同的代码适用于所有其他 Shell 项目(库、文件夹,甚至 OneDrive),但一些(不是全部!)控制面板虚拟文件夹都显示完全相同和上面一样。如果需要,我可以得到一个有这种奇怪行为的列表,但“通知区域图标”是我记得的那个。
很可能我做错了什么,但为什么它对大多数其他事情都有效?为了记录,PIDL 来自 IPersistFolder2::GetCurFolder。
我有一个用纯 C 语言编写的程序,它重现了该问题(它演示了上述 SHGetNameFromIDList 的问题)。
代码如下(完全独立):
要使用它,您只需运行它,打开“通知区域图标”,然后按任意键。它将输出 PIDL“正常显示”名称,并输出 PIDL“解析名称”。对于“通知区域图标”,您会注意到它们是相同的,我认为这是错误的(SIGDN_NORMALDISPLAY 应该返回“通知区域图标”)。
它还输出 PIDL 的十六进制转储。
#include <tchar.h>
#include <Windows.h>
#include <Shlwapi.h>
#include <ShlObj.h>
#include <strsafe.h>
int _tmain( int argc, _TCHAR* argv[] )
{
if ( !SUCCEEDED( CoInitialize( NULL ) ) )
return 1;
system( "PAUSE" );
IShellWindows *psw = NULL;
IUnknown *punkEnum = NULL;
IEnumVARIANT *pEnumVar = NULL;
HRESULT hr = 0;
while ( 1 )
{
if ( SUCCEEDED( hr = CoCreateInstance( &CLSID_ShellWindows,
NULL,
CLSCTX_ALL,
&IID_IShellWindows,
( LPVOID ) &psw ) ) )
{
if ( FAILED( psw->lpVtbl->_NewEnum( psw, ( IUnknown** ) &punkEnum ) ) )
goto clean_main;
if ( FAILED( punkEnum->lpVtbl->QueryInterface( punkEnum,
&IID_IEnumVARIANT,
( void** ) &pEnumVar ) ) )
{
goto clean_main;
}
VARIANT v;
VariantInit( &v );
while ( pEnumVar->lpVtbl->Next( pEnumVar, 1, &v, NULL ) == S_OK )
{
IShellBrowser *psb = NULL;
IShellView *psv = NULL;
IFolderView *pfv = NULL;
IPersistFolder2 *ppf = NULL;
IShellItem2 *psi = NULL;
LPITEMIDLIST pidl = NULL;
LPWSTR pszParsingName;
LPWSTR pszName = NULL;
SFGAOF attributes = 0;
size_t dwLength = 0;
size_t dwLengthParsing = 0;
if ( SUCCEEDED( IUnknown_QueryService( ( IUnknown* ) v.pdispVal,
&SID_STopLevelBrowser,
&IID_IShellBrowser,
( void** ) &psb ) ) )
{
hr = psb->lpVtbl->QueryActiveShellView( psb, &psv );
if ( FAILED( hr ) )
goto clean_inner;
hr = psv->lpVtbl->QueryInterface( psv, &IID_IFolderView, ( void** ) &pfv );
if ( FAILED( hr ) )
goto clean_inner;
hr = pfv->lpVtbl->GetFolder( pfv, &IID_IPersistFolder2, ( void** ) &ppf );
if ( FAILED( hr ) )
goto clean_inner;
if ( SUCCEEDED( ppf->lpVtbl->GetCurFolder( ppf, &pidl ) ) )
{
size_t i;
size_t cb = 0;
byte *pb = NULL;
if ( FAILED( hr = SHCreateItemFromIDList( pidl, &IID_IShellItem2, ( void** ) &psi ) ) )
goto clean_inner;
if ( FAILED( hr = psi->lpVtbl->GetDisplayName( psi, SIGDN_NORMALDISPLAY, &pszName ) ) )
goto clean_inner;
if ( FAILED( hr = psi->lpVtbl->GetDisplayName( psi, SIGDN_DESKTOPABSOLUTEPARSING, &pszParsingName ) ) )
goto clean_inner;
if ( FAILED( hr = psi->lpVtbl->GetAttributes( psi, SFGAO_CANLINK, &attributes ) ) )
goto clean_inner;
if ( FAILED( hr = StringCchLength( pszName, STRSAFE_MAX_CCH, &dwLength ) ) )
goto clean_inner;
if ( FAILED( hr = StringCchLength( pszParsingName, STRSAFE_MAX_CCH, &dwLengthParsing ) ) )
goto clean_inner;
cb = ILGetSize( pidl );
pb = ( byte* ) pidl;
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), pszName, dwLength, NULL, NULL );
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), L"\n", 1, NULL, NULL );
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), pszParsingName, dwLengthParsing, NULL, NULL );
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), L"\n", 1, NULL, NULL );
for ( i = 0; i < cb; ++i )
{
WCHAR szHex[ 4 ];
StringCchPrintf( szHex, ARRAYSIZE( szHex ), L"%02X", pb[ i ] );
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), szHex, 3, NULL, NULL );
}
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), L"\n", 1, NULL, NULL );
WriteConsole( GetStdHandle( STD_OUTPUT_HANDLE ), L"\n", 1, NULL, NULL );
}
clean_inner:
if ( pszParsingName )
CoTaskMemFree( pszParsingName );
if ( pszName )
CoTaskMemFree( pszName );
if ( psv )
psv->lpVtbl->Release( psv );
if ( pfv )
pfv->lpVtbl->Release( pfv );
if ( ppf )
ppf->lpVtbl->Release( ppf );
if ( psi )
psi->lpVtbl->Release( psi );
if ( psb )
psb->lpVtbl->Release( psb );
}
}
}
clean_main:
if ( pEnumVar )
pEnumVar->lpVtbl->Release( pEnumVar );
if ( punkEnum )
punkEnum->lpVtbl->Release( punkEnum );
if ( psw )
psw->lpVtbl->Release( psw );
system( "PAUSE" );
}
CoUninitialize();
return 0;
}
有什么想法吗?提前致谢!
【问题讨论】:
-
您的代码是否有可能在 64 位 Windows 中作为 32 位进程运行? Explorer 仅部分支持这一点。
-
很难猜出你是如何编译这个程序的,它到处都是错误。
-
@HansPassant - 代码对我来说编译得很好,但它是 C,而不是 C++(您还需要链接到 Shlwapi.lib)。编译为 x64 时,它可以 100% 正常工作。
-
@SimonMourier - 它确实在 64 位 Windows 上以 32 位运行。为什么这会有所作为?我想我可以停止询问并尝试一下:P
-
@SimonMourier - 哇哦,它确实有效。好的。我在资源管理器中不知道这一点,仅部分支持来自 32 位应用程序的调用来处理这种事情。接下来我将测试 ShellExecuteEx 和 SHGetFileInfo 并确保它们现在也以相同的方式工作!谢谢大家。
标签: c winapi com windows-shell