Hello, I need to maintain a list of PIDL's (ITEMIDLIST's) between sessions.
The only way I can do it so far is to create a shortcut (.lnk) on the disk for every PIDL... something like this (extended from shell_COM_example.eba):
' ---------------------------------------------------------------------------------------------
' DECLARATIONS
' ---------------------------------------------------------------------------------------------
$INCLUDE "ishelllink.inc"
CONST CSIDLDESKTOP = 0x0000
CONST CSIDLPROGRAMS = 0x0002
CONST CSIDLCONTROLS = 0x0003
CONST CSIDLPRINTERS = 0x0004
CONST CSIDLPERSONAL = 0x0005
CONST CSIDLFAVORITES = 0x0006
CONST CSIDLSTARTUP = 0x0007
CONST CSIDLRECENT = 0x0008
CONST CSIDLSENDTO = 0x0009
CONST CSIDLBITBUCKET = 0x000a
CONST CSIDLSTARTMENU = 0x000b
CONST CSIDLDESKTOPDIRECTORY = 0x0010
CONST CSIDLDRIVES = 0x0011
CONST CSIDLNETWORK = 0x0012
CONST CSIDLNETHOOD = 0x0013
CONST CSIDLFONTS = 0x0014
CONST CSIDLTEMPLATES = 0x0015
CONST CSIDLCMNSTARTMENU = 0x0016
CONST CSIDLCMNPROGRAMS = 0x0017
CONST CSIDLCMNSTARTUP = 0x0018
CONST CSIDLCMNDESKTOPDIRECTORY = 0x0019
CONST CSIDLAPPDATA = 0x001a
CONST CSIDLPRINTHOOD = 0x001b
CONST CP_ACP = 0
CONST CLSCTX_INPROC_SERVER = 1
$define STGM_READ 0x00000000
DECLARE IMPORT,SHGetSpecialFolderLocation(hwnd as UINT,nFolder as INT,ppITEMIDLIST as POINTER),int
DECLARE IMPORT,SHGetPathFromIDList(pITEMIDLIST:POINTER,PATH:STRING),int
DECLARE IMPORT,CoTaskMemFree(pidl:POINTER)
DECLARE IMPORT,MultiByteToWideChar(codepage as UINT,dwFlags as UINT,lpMBS as STRING,cchMB as INT,lpWCS as POINTER,cchWC as INT)
' ---------------------------------------------------------------------------------------------
' MAIN
' ---------------------------------------------------------------------------------------------
OPENCONSOLE
PRINT "Save and load PIDL's (using shortcuts)..."
PRINT ""
DoExample()
PRINT ""
PRINT "Press any key to close"
DO:UNTIL INKEY$ <> ""
CLOSECONSOLE
SUB DoExample()
ISTRING lnkPath[260, 3]
pointer pidl[3]
'Make sure COM is active
CoInitialize(NULL)
'the .lnk target files
lnkPath[0, 0] = GETSTARTPATH + "my_pidl1.lnk"
lnkPath[0, 1] = GETSTARTPATH + "my_pidl2.lnk"
lnkPath[0, 2] = GETSTARTPATH + "my_pidl3.lnk"
' fill the PIDL's with some locations
SHGetSpecialFolderLocation(0, CSIDLPERSONAL, &pidl[0])
SHGetSpecialFolderLocation(0, CSIDLFAVORITES, &pidl[1])
SHGetSpecialFolderLocation(0, CSIDLDESKTOP, &pidl[2])
FOR j = 0 TO 2
'Save PIDL's to file (.lnk)
IF SavePIDL(lnkPath[0, j], pidl[j]) = 0 THEN
CoTaskMemFree(pidl[j])
'Load PIDL's back from the file
pidl[j] = LoadPIDL(lnkPath[0, j])
IF pidl[j] THEN
lnkPath[0, j] = ""
' show to where the PIDL's points to...
' ... cuz the tree PIDL's have physical address
' this is just to check
SHGetPathFromIDList(pidl[j], lnkPath[0, j])
PRINT "PIDL[" + STR$(j) + " ] points to: " + lnkPath[0, j]
CoTaskMemFree(pidl[j])
ENDIF
ENDIF
NEXT j
'Shut down COM
CoUninitialize()
ENDSUB
' ---------------------------------------------------------------------------------------------
' FUNCTIONS
' ---------------------------------------------------------------------------------------------
SUB GetFolderLocation(nFolder:INT),STRING
ISTRING path[260] = ""
POINTER pidl = 0
POINTER ppidl = 0
ppidl = &pidl
SHGetSpecialFolderLocation(NULL,nFolder,ppidl)
SHGetPathFromIDList(pidl,path)
CoTaskMemFree(pidl)
RETURN path
ENDSUB
SUB SavePIDL(STRING lpszPathLink, pointer pidl),INT
INT hres = 0
IShellLinkA psl = NULL
IPersistFile ppf = NULL
WORD wsz[260]
'Get a pointer to the IShellLink interface.
hres = CoCreateInstance(_CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, _IID_IShellLinkA, psl)
if (hres = 0)
'set the PIDL of the IShellLink
psl->SetIDList(*<ITEMIDLIST>pidl)
'Query IShellLink for the IPersistFile interface for saving the
'shortcut in persistent storage.
hres = psl->QueryInterface(_IID_IPersistFile,ppf)
if (hres = 0)
'Ensure that the string is ANSI unicode.
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1,wsz, 260)
'Save the link by calling IPersistFile->Save.
hres = ppf->Save(wsz, TRUE)
ppf->Release()
endif
psl->Release()
endif
return hres
ENDSUB
SUB LoadPIDL(STRING lpszPathLink), pointer
INT hres = 0
IShellLinkA psl = NULL
IPersistFile ppf = NULL
WORD wsz[260]
pointer pidl = 0
'Get a pointer to the IShellLink interface.
hres = CoCreateInstance(_CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, _IID_IShellLinkA, psl)
if (hres = 0)
'set the PIDL of the IShellLink
psl->SetIDList(*<ITEMIDLIST>pidl)
'Query IShellLink for the IPersistFile interface for saving the
'shortcut in persistent storage.
hres = psl->QueryInterface(_IID_IPersistFile,ppf)
if (hres = 0)
'Ensure that the string is ANSI unicode.
MultiByteToWideChar(CP_ACP, 0, lpszPathLink, -1,wsz, 260)
'load the link by calling IPersistFile->Load.
hres = ppf->Load(wsz, STGM_READ)
if (hres = 0) THEN psl->GetIDList(&pidl)
ppf->Release()
endif
psl->Release()
endif
return pidl
ENDSUB
But it would be great to save and load all PIDL's from just one single file. Is it possible?
Fasecero, an ITEMIDLIST is just a memory block allocated by CoTaskMemAlloc, or Alloc method from IMalloc interface. It contains an array of SHITEMID structures (size+string), and it is well explained here (http://msdn.microsoft.com/en-us/library/cc144090(VS.85).aspx).
Give it a try.
Iterating through each SHITEMID I get the total size in bytes, then I save the size (as integer) and after all those bytes.
To read the ITEMIDLIST again, I read the size and with CoTaskMemAlloc(...) I make the allocation again, then I read the bytes.
It works well, thanks Sapero.