SCHangeNotify 未更新 URL= 我的 .url 快捷方式文件中的更改
SHChangeNotify not updating URL= change in my .url shortcut file
我有一个简单的 Delphi 应用程序,可以为 URL 创建桌面快捷方式。它在用户的桌面文件夹中创建一个带有 .url
文件扩展名的两行文本文件:
[InternetShortcut]
URL=http://127.0.0.1/admin
效果很好。当我需要用新的 URL 更新文件时,我会覆盖旧文件。但是 Windows 在我重新启动资源管理器或重新启动之前不会识别更改。于是了解到SHChangeNotify()
,覆盖文件后调用:
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH or SHCNF_FLUSH, PChar(Path), nil);
但是没有效果:
- 我试过使用和不使用
SHCNF_FLUSH
标志;
SHCNF_FLUSHNOWAIT
标志也没有区别。
- 我也试过先删除文件,然后使用
SHCNE_DELETE
事件,然后重新创建文件。这也不起作用,它只是继续使用旧的 URL.
如何强制资源管理器在不重新启动的情况下从文件中重新加载 URL?
虽然文件的内容可以像任何 INI 文件一样处理,但我还没有找到直接的方法来控制对它的操作:
- 创建文件时,其内容按预期方式读取:系统针对
URL=
协议的默认应用程序已启动(即,对于 http
,它很可能是互联网浏览器)。
- 修改每个文件系统的文件没有效果 - MSIE 本身维护缓存或 COM 的魔法。
可以通过以下方式进行间接操作:
- 清空文件的现有内容。为什么?因为后面的步骤将再次添加具有
URL=
值的相同 INI 部分,但第一部分的 URL=
值仍然被考虑在内。
- 访问每个 COM 的文件并更改其属性。遗憾的是,这会在文件中写入更多内容——在我的例子中,outcome/file 的内容是:
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2
[InternetShortcut]
URL=http://127.0.0.1/index.php
IDList=
但是,它的“工作原理”如下:更改(说:不同的 URL)被识别。将它们放在一起,我在 Windows 7 上的 Delphi 7 的以下代码也应该对您有用 - 只需调用函数:
uses
ShlObj, ActiveX, ComObj;
const
SID_IUniformResourceLocatorA= '{FBF23B80-E3F0-101B-8488-00AA003E56F8}';
SID_IUniformResourceLocatorW= '{CABB0DA0-DA57-11CF-9974-0020AFD79762}';
SID_InternetShortcut= '{FBF23B40-E3F0-101B-8488-00AA003E56F8}';
type
PUrlInvokeCommandInfoA= ^TUrlInvokeCommandInfoA;
TUrlInvokeCommandInfoA= record
dwcbSize,
dwFlags: DWORD; // Bit field of IURL_INVOKECOMMAND_FLAGS
hwndParent: HWND; // Parent window. Valid only if IURL_INVOKECOMMAND_FL_ALLOW_UI is set.
pcszVerb: LPCSTR; // Verb to invoke. Ignored if IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB is set.
end;
PUrlInvokeCommandInfoW= ^TUrlInvokeCommandInfoW;
TUrlInvokeCommandInfoW= record
dwcbSize,
dwFlags: DWORD;
hwndParent: HWND;
pcszVerb: LPCWSTR;
end;
IUniformResourceLocatorA= interface( IUnknown )
[SID_IUniformResourceLocatorA]
function SetURL( pcszURL: LPCSTR; dwInFlags: DWORD ): HRESULT; stdcall;
function GetURL( ppszURL: LPSTR ): HRESULT; stdcall;
function InvokeCommand( purlici: PUrlInvokeCommandInfoA ): HRESULT; stdcall;
end;
IUniformResourceLocatorW= interface( IUnknown )
[SID_IUniformResourceLocatorW]
function SetURL( pcszURL: LPCWSTR; dwInFlags: DWORD ): HRESULT; stdcall;
function GetURL( ppszURL: LPWSTR ): HRESULT; stdcall;
function InvokeCommand(purlici: PUrlInvokeCommandInfoW ): HRESULT; stdcall;
end;
function SetURL( sFile, sUrl: Widestring ): Integer;
const
CLSID_InternetShortCut: TGUID= SID_InternetShortcut;
var
oUrl: IUniformResourceLocatorW;
oFile: IPersistFile;
hFile: THandle;
begin
// First, the existing file's content should be emptied
hFile:= CreateFileW( PWideChar(sFile), GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0 );
if hFile= INVALID_HANDLE_VALUE then begin
result:= 1; // File might not exist, sharing violation, etc.
exit;
end;
// Initial file pointer is at position 0
if not SetEndOfFile( hFile ) then begin
result:= 2; // Missing permissions, etc.
CloseHandle( hFile );
exit;
end;
// Gracefully end accessing the file
if not CloseHandle( hFile ) then begin
result:= 3; // File system crashed, etc.
exit;
end;
// Using COM to access properties
result:= 0;
try
oUrl:= CreateComObject( CLSID_InternetShortCut ) as IUniformResourceLocatorW;
except
result:= 4; // CLSID unsupported, COM not available, etc.
end;
if result<> 0 then exit;
// Opening the file again
oFile:= oUrl as IPersistFile;
if oFile.Load( PWideChar(sFile), STGM_READWRITE )<> S_OK then begin
result:= 5; // Sharing violations, access permissions, etc.
exit;
end;
// Set the property as per interface - only saving the file is not enough
if oUrl.SetURL( PWideChar(sUrl), 0 )<> S_OK then begin
result:= 6;
exit;
end;
// Storing the file's new content - setting only the property is not enough
if oFile.Save( PWideChar(sFile), TRUE )<> S_OK then begin
result:= 7;
exit;
end;
// Success!
result:= 0;
end;
根据我的桌面防火墙,执行过程会在 IPersistFile.Save()
时修改 explorer.exe
的内存 - 之后执行 URL 文件应该反映其新内容,而在此之前的任何尝试仍应根据旧文件的内容采取行动。
我有一个简单的 Delphi 应用程序,可以为 URL 创建桌面快捷方式。它在用户的桌面文件夹中创建一个带有 .url
文件扩展名的两行文本文件:
[InternetShortcut]
URL=http://127.0.0.1/admin
效果很好。当我需要用新的 URL 更新文件时,我会覆盖旧文件。但是 Windows 在我重新启动资源管理器或重新启动之前不会识别更改。于是了解到SHChangeNotify()
,覆盖文件后调用:
SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH or SHCNF_FLUSH, PChar(Path), nil);
但是没有效果:
- 我试过使用和不使用
SHCNF_FLUSH
标志; SHCNF_FLUSHNOWAIT
标志也没有区别。- 我也试过先删除文件,然后使用
SHCNE_DELETE
事件,然后重新创建文件。这也不起作用,它只是继续使用旧的 URL.
如何强制资源管理器在不重新启动的情况下从文件中重新加载 URL?
虽然文件的内容可以像任何 INI 文件一样处理,但我还没有找到直接的方法来控制对它的操作:
- 创建文件时,其内容按预期方式读取:系统针对
URL=
协议的默认应用程序已启动(即,对于http
,它很可能是互联网浏览器)。 - 修改每个文件系统的文件没有效果 - MSIE 本身维护缓存或 COM 的魔法。
可以通过以下方式进行间接操作:
- 清空文件的现有内容。为什么?因为后面的步骤将再次添加具有
URL=
值的相同 INI 部分,但第一部分的URL=
值仍然被考虑在内。 - 访问每个 COM 的文件并更改其属性。遗憾的是,这会在文件中写入更多内容——在我的例子中,outcome/file 的内容是:
[{000214A0-0000-0000-C000-000000000046}] Prop3=19,2 [InternetShortcut] URL=http://127.0.0.1/index.php IDList=
但是,它的“工作原理”如下:更改(说:不同的 URL)被识别。将它们放在一起,我在 Windows 7 上的 Delphi 7 的以下代码也应该对您有用 - 只需调用函数:
uses
ShlObj, ActiveX, ComObj;
const
SID_IUniformResourceLocatorA= '{FBF23B80-E3F0-101B-8488-00AA003E56F8}';
SID_IUniformResourceLocatorW= '{CABB0DA0-DA57-11CF-9974-0020AFD79762}';
SID_InternetShortcut= '{FBF23B40-E3F0-101B-8488-00AA003E56F8}';
type
PUrlInvokeCommandInfoA= ^TUrlInvokeCommandInfoA;
TUrlInvokeCommandInfoA= record
dwcbSize,
dwFlags: DWORD; // Bit field of IURL_INVOKECOMMAND_FLAGS
hwndParent: HWND; // Parent window. Valid only if IURL_INVOKECOMMAND_FL_ALLOW_UI is set.
pcszVerb: LPCSTR; // Verb to invoke. Ignored if IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB is set.
end;
PUrlInvokeCommandInfoW= ^TUrlInvokeCommandInfoW;
TUrlInvokeCommandInfoW= record
dwcbSize,
dwFlags: DWORD;
hwndParent: HWND;
pcszVerb: LPCWSTR;
end;
IUniformResourceLocatorA= interface( IUnknown )
[SID_IUniformResourceLocatorA]
function SetURL( pcszURL: LPCSTR; dwInFlags: DWORD ): HRESULT; stdcall;
function GetURL( ppszURL: LPSTR ): HRESULT; stdcall;
function InvokeCommand( purlici: PUrlInvokeCommandInfoA ): HRESULT; stdcall;
end;
IUniformResourceLocatorW= interface( IUnknown )
[SID_IUniformResourceLocatorW]
function SetURL( pcszURL: LPCWSTR; dwInFlags: DWORD ): HRESULT; stdcall;
function GetURL( ppszURL: LPWSTR ): HRESULT; stdcall;
function InvokeCommand(purlici: PUrlInvokeCommandInfoW ): HRESULT; stdcall;
end;
function SetURL( sFile, sUrl: Widestring ): Integer;
const
CLSID_InternetShortCut: TGUID= SID_InternetShortcut;
var
oUrl: IUniformResourceLocatorW;
oFile: IPersistFile;
hFile: THandle;
begin
// First, the existing file's content should be emptied
hFile:= CreateFileW( PWideChar(sFile), GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0 );
if hFile= INVALID_HANDLE_VALUE then begin
result:= 1; // File might not exist, sharing violation, etc.
exit;
end;
// Initial file pointer is at position 0
if not SetEndOfFile( hFile ) then begin
result:= 2; // Missing permissions, etc.
CloseHandle( hFile );
exit;
end;
// Gracefully end accessing the file
if not CloseHandle( hFile ) then begin
result:= 3; // File system crashed, etc.
exit;
end;
// Using COM to access properties
result:= 0;
try
oUrl:= CreateComObject( CLSID_InternetShortCut ) as IUniformResourceLocatorW;
except
result:= 4; // CLSID unsupported, COM not available, etc.
end;
if result<> 0 then exit;
// Opening the file again
oFile:= oUrl as IPersistFile;
if oFile.Load( PWideChar(sFile), STGM_READWRITE )<> S_OK then begin
result:= 5; // Sharing violations, access permissions, etc.
exit;
end;
// Set the property as per interface - only saving the file is not enough
if oUrl.SetURL( PWideChar(sUrl), 0 )<> S_OK then begin
result:= 6;
exit;
end;
// Storing the file's new content - setting only the property is not enough
if oFile.Save( PWideChar(sFile), TRUE )<> S_OK then begin
result:= 7;
exit;
end;
// Success!
result:= 0;
end;
根据我的桌面防火墙,执行过程会在 IPersistFile.Save()
时修改 explorer.exe
的内存 - 之后执行 URL 文件应该反映其新内容,而在此之前的任何尝试仍应根据旧文件的内容采取行动。