Во, сделал, правда все равно не уверен что интерфейсные указатели освобождаются до конца. Кому интересно как создать ярлык сетевого подключения на рабочем столе.
Проверял на Windows 2000, Windows XP, Windows 7. Проверьте кто разбирается в COM интерфейсах.
[more=">>>"]function GetNextItemID(pidl: PItemIDList): PItemIDList;
var
cb: DWORD;
begin
Result := nil;
if (pidl = nil) then
Exit;
cb := pidl.mkid.cb;
if (cb = 0) then
Exit;
pidl := PItemIDList(Cardinal(pidl) + cb);
if (pidl.mkid.cb <> 0) then
Result := pidl;
end;
//
function GetPIDSize(pidl: PItemIDList): DWORD;
begin
Result := 0;
if (pidl <> nil) then
begin
Result := SizeOf(pidl.mkid.cb);
while (pidl <> nil) do
begin
Inc(Result, pidl.mkid.cb);
pidl := GetNextItemID(pidl);
end;
end;
end;
//
function IsDesktopFolder(pidl: PItemIDList): Boolean;
begin
if Assigned(pidl) then
Result := (pidl.mkid.cb = 0)
else
Result := FALSE;
end;
//
function ConcatPIDL(destpidl, srcpidl: PItemIDList): PItemIDList;
var
cb1: DWORD;
cb2: DWORD;
pmc: IMalloc;
hr : HRESULT;
begin
Result := nil;
hr := SHGetMalloc(pmc);
if SUCCEEDED(hr) then
begin
cb1 := 0;
cb2 := 0;
if Assigned(destpidl) then
begin
if not IsDesktopFolder(destpidl) then
cb1 := GetPIDSize(destpidl) - SizeOf(destpidl^.mkid.cb);
end;
if Assigned(srcpidl) then
cb2 := GetPIDSize(srcpidl);
Result := pmc.Alloc(cb1 + cb2);
if Assigned(Result) then
begin
if Assigned(destpidl) then
CopyMemory(Result, destpidl, cb1);
if Assigned(srcpidl) then
CopyMemory(Pointer(DWORD(Result) + cb1), srcpidl, cb2);
end;
pmc := nil;
end;
end;
//
procedure CreateShellVpnLink(szEntryName: WideString);
var
pMalloc : IMalloc;
Desktop : IShellFolder;
pidlDesktop: PItemIDList;
pszPath : Array [0..MAX_PATH] of WideChar;
pidlConnect: PItemIDList;
Network : IShellFolder;
Items : IEnumIDList;
pidl2 : PItemIDList;
dwFetched : Cardinal;
Connection : STRRET;
ObjectName : WideString;
pfLink : IUnknown;
isLink : IShellLink;
ipFile : IPersistFile;
pidl3 : PItemIDList;
szFileName : WideString;
begin
// acquire shell's allocator
if (SHGetMalloc(pMalloc) = S_OK) then
try
// acquire shell namespace root folder
if (SHGetDesktopFolder(Desktop) = S_OK) then
try
if (SHGetSpecialFolderLocation(0, CSIDL_DESKTOP, pidlDesktop) = S_OK) then
try
ZeroMemory(@pszPath, SizeOf(pszPath));
SHGetPathFromIDListW(pidlDesktop, @pszPath);
if (SHGetSpecialFolderLocation(0, CSIDL_CONNECTIONS, pidlConnect) = S_OK) then
try
Desktop.BindToObject(pidlConnect, nil, IID_IShellFolder, Network);
Network.EnumObjects(0, SHCONTF_NONFOLDERS, Items);
while (Items.Next(1, pidl2, dwFetched) = S_OK) do
try
if (dwFetched > 0) and Assigned(pidl2) then
try
Network.GetDisplayNameOf(pidl2, SHGDN_NORMAL, Connection);
ObjectName := Connection.pOleStr;
if (ObjectName = szEntryName) then
try
CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IUnknown, pfLink);
isLink := pfLink as IShellLink;
ipFile := pfLink as IPersistFile;
pidl3 := ConcatPIDL(pidlConnect, pidl2);
isLink.SetIDList(pidl3);
szFileName := FormatW('%s\%s.lnk', [ExcludeTrailingPathDelimiterW(pszPath), szEntryName]);
ipFile.Save(@szFileName[1], FALSE);
pMalloc.Free(pidl3);
finally
// для Delphi необязательно вызывать Release у интерфейса, так
// как для них предусмотрена compiler magic и компилятор все
// сделает сам, иначе мы получим ошибку - метод Release будет
// вызван несколько раз
//pfLink := nil;
//isLink := nil;
//ipFile := nil;
end;
finally
pMalloc.Free(pidl2); // release folder
end;
finally
end;
finally
Network := nil;
pMalloc.Free(pidlConnect); // release folder
end;
finally
pMalloc.Free(pidlDesktop); // release folder
end;
finally
Desktop := nil; // release shell namespace root folder
end;
finally
pMalloc := nil; // release shell's allocator
end;
end;[/more]