• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 517
  • Last Modified:

Issues with SHGetPathFromiDList

I'm trying to pull the paths from an IDataObject, however the string added to the list is always empty. The following method is being called from "IShellExtInit.Initialize".

function ProcessItemIDListData(lpdobj: IDataObject; filesList: TStringList): HResult;
var
stgMedium: TStgMedium;
formatEtc: TFormatEtc;
i: integer;
lpIDA: PIDA;
lpRootPidl: PItemiDList;
lpPidl: PItemiDList;
lpFileName: array[0..MAX_PATH] of Char;
begin
    formatEtc.cfFormat := RegisterClipboardFormat(CFSTR_SHELLIDLIST);
    formatEtc.ptd := nil;
    formatEtc.dwAspect := DVASPECT_CONTENT;
    formatEtc.lindex := -1;
    formatEtc.tymed := TYMED_HGLOBAL;
    Result := lpdobj.GetData(formatEtc, stgMedium);

    if not Failed(Result) then
        begin
            try
                lpIDA := GlobalLock(stgMedium.hGlobal);
                try
                    lpRootPidl := PItemiDList(LPBYTE(lpIDA) + lpIDA.aoffset[0]);
                    for i := 1 to lpIDA.cidl do
                        begin
                            lpPidl := ILCombine(lpRootPidl, PItemiDList(LPBYTE(lpIDA) + lpIDA.aoffset[i]));
                            if lpPidl <> nil then
                                begin
                                    SHGetPathFromiDList(lpPidl, lpFileName);
                                    filesList.Add(StrPas(lpFileName));
                                end;
                        end;
                    Result := NOERROR;
                finally
                    GlobalUnlock(stgMedium.hGlobal);
                end;
            finally
                ReleaseStgMedium(stgMedium);
            end;
            Result := NOERROR;
        end;
end;

Open in new window


There are a few things I have noted.
1) SHGetPathFromiDList always returns false??
2) If I change the code to something like (where s is a string)
SetLength(s, 1024);
SHGetPathFromiDList(lpPidl, PChar(s));
filesList.Add(s);

Open in new window

I actually get a path for the object, except the string is not trimmed correctly (and I have also tried filesList.Add(StrPas(PChar(s))); etc)

Can anyone help, or point me in the right direction?

Thanks
0
thebeersmith
Asked:
thebeersmith
  • 3
  • 2
3 Solutions
 
mccarlIT Business Systems Analyst / Software DeveloperCommented:
It's been a while since I have done anything Delphi related so this may not be anything close to the answer, but in case you get nothing else, here are my thoughts...

Could the problem be to do with mismatches between 1-byte characters and 2-byte characters, eg. Unicode or wide chars? In my dealings with this a long time ago, I do remember there being issue when interfacing my code (which always just used 1-byte chars) and System calls such as SHGetPath... that used 2-byte chars. Are you able to do low level debugging and see exactly what memory is getting allocated for lpFileName and exactly how that memory is being filled?

Because it has been a while for me, your code might even be correctly taking all that into account, but I just thought I would throw that idea out there, in case it can be of some use to you!
0
 
sarabandeCommented:
if the SHGetPathFromIDList returns 0  (false), it has failed. the contents of the buffer you provided was not (likely to be) filled.

you may call GetLastError after call what should give a more specific error.

Sara
0
 
thebeersmithAuthor Commented:
@Sara - Well that is what you would expect, however GetLastError reports "The operation completed successfully". This is why I removed the checking of the result (as a side note, all sample code I've looked at, in C++ and Delphi does not check the result of this call, which is odd)... this also does not explain why using the string method returns something... albeit a bit wacky.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
sarabandeCommented:
ok. i wonder why SHGetPathFromIDList returns 0  what is FALSE if there is no error.

anyhow, mccarl probably is on the right track. the SHGetPathFromIDList takes a LPTSTR as second argument. that is a pointer to TCHAR what is 8-bit char if the project settings are 'multibyte characterset' or 16-bit wchar_t if 'unicode characterset' is the default.

when using StrPas function the argument expected is a PAnsiChar what is char (ansi == 8-bit) while the SHGetPathFromIDList might expect a wide char buffer. if using a string s with sufficient size, the pointer you were passing to SHGetPathFromIDList at least points to a sufficiently large buffer. however a pascal string obviously doesn't contain only a buffer but also has length attributes which were not properly filled in your case. that's why you see the string 'is not trimmed correctly'.

you might go one of the following ways:

- try to change from UNICODE to ANSI if that is possible
- use an array of WideChar characters for lpFileName and assign it to a string
- try to 'trim' the string you passed as buffer to SHGetPathFromIDList after call by
  iterating all characters and check for two zero bytes. that is the end of the
  path string and you could set your string length accordingly

Sara
0
 
thebeersmithAuthor Commented:
@Sara - Thanks for the input. I'm using Delphi XE3 which supports unicode, and as such a PChar is in fact a PWideChar (so lpFileName is a PWideChar) and SHGetPathFromIDList is expecting PWideChar. The same is true of StrPas, this is overloaded to handle either ansi or unicode.
0
 
sarabandeCommented:
then i would recommend to using second choice 'use an array of WideChar characters and assign it to a string'.

the received string should not need any further conversions and should print properly after assignment.

Sara
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now