Issues with SHGetPathFromiDList

Posted on 2013-05-22
Last Modified: 2013-08-22
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;
stgMedium: TStgMedium;
formatEtc: TFormatEtc;
i: integer;
lpRootPidl: PItemiDList;
lpPidl: PItemiDList;
lpFileName: array[0..MAX_PATH] of Char;
    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
                lpIDA := GlobalLock(stgMedium.hGlobal);
                    lpRootPidl := PItemiDList(LPBYTE(lpIDA) + lpIDA.aoffset[0]);
                    for i := 1 to lpIDA.cidl do
                            lpPidl := ILCombine(lpRootPidl, PItemiDList(LPBYTE(lpIDA) + lpIDA.aoffset[i]));
                            if lpPidl <> nil then
                                    SHGetPathFromiDList(lpPidl, lpFileName);
                    Result := NOERROR;
            Result := NOERROR;

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));

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?

Question by:thebeersmith
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 3
  • 2
LVL 35

Assisted Solution

mccarl earned 100 total points
ID: 39189745
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!
LVL 34

Expert Comment

ID: 39190072
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.


Author Comment

ID: 39190093
@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.
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

LVL 34

Assisted Solution

sarabande earned 400 total points
ID: 39190147
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


Author Comment

ID: 39206034
@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.
LVL 34

Accepted Solution

sarabande earned 400 total points
ID: 39209070
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.


Featured Post

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

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

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
In this post we will learn how to connect and configure Android Device (Smartphone etc.) with Android Studio. After that we will run a simple Hello World Program.
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …

739 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question