Solved

Issues with SHGetPathFromiDList

Posted on 2013-05-22
6
460 Views
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;
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
Comment
Question by:thebeersmith
[X]
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
6 Comments
 
LVL 35

Assisted Solution

by:mccarl
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!
0
 
LVL 34

Expert Comment

by:sarabande
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.

Sara
0
 

Author Comment

by:thebeersmith
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.
0
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

by:sarabande
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

Sara
0
 

Author Comment

by:thebeersmith
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.
0
 
LVL 34

Accepted Solution

by:
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.

Sara
0

Featured Post

Industry Leaders: 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!

Question has a verified solution.

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

This article will show, step by step, how to integrate R code into a R Sweave document
This is about my first experience with programming Arduino.
With the power of JIRA, there's an unlimited number of ways you can customize it, use it and benefit from it. With that in mind, there's bound to be things that I wasn't able to cover in this course. With this summary we'll look at some places to go…

749 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