views:

256

answers:

2

Hi all!

I am using Delphi 2010 and my program wants to get the system's temp path. I am using TPath.GetTempPath and everything is working fine... at least for me and my coworkers. But on some customer machines this method returns a cropped path which is (of course) not existing. I found out that the problem seems to be the result from underlying call to GetLongPathName().

The complete code looks like this:

[...]
var
 TmpDir : String;
 Len : Integer;
begin

 [... Call to GetTempPath succeeds and we have a valid temp directory in short "~" notation in var TmpDir ...]

 Len := GetLongPathName(PChar(TmpDir), nil, 0);      // Len = 37
    SetLength(TmpDir, Len - 1);                         // We want to set the len of TmpDir to 37 - 1.
    GetLongPathName(PChar(TmpDir), PChar(TmpDir), Len); // Only 32 (instead of 36) characters are copied - so we have a cropped path - But why?!

end;
[...]

This only happens on some systems and I don't know why. I found a nasty workaround for this, but I would like to know what's going on here.

Can somebody put some light on this?

+3  A: 

There is a note about this Windows API function on the Homeland Security pages:

"The return buffer for GetLongPathName() and similar functions might return a truncated path and lead to hard-to-find errors."

https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/753-BSI.html

If you have the source code, you could check if the problem described in this article exists in the Delphi 2010 implementation.

mjustin
You are right, I found that too and my "nasty workaround" is to set Len to MAX_PATH initially and checking for result of GetLongPathName(), but I am wondering why Embarcadero is using the other approach... but thank you for posting this, because it verified my approach.
Patrick
You should report this to QC.
Mason Wheeler
So what's the problem? The code in the question correctly calls the function twice, doesn't it? Once to find out the required length and then a second time to fill the buffer for real. The code you cite allocates the buffer first so that the first call to the function might work, but if it fails, it allocates a new buffer and uses that instead. Is that really an important difference? The article is warning against assuming the first call will always succeed when given a MAX_PATH-sized buffer. How does that warning apply to this situation?
Rob Kennedy
+3  A: 

What happens if you try:

var
  longpath : string;

SetLength(longpath,MAX_PATH);
SetLength(longpath, GetLongPathName(PChar(TmpDir),PChar(LongPath),MAX_PATH));

This worked for me, your version truncated the path.

MarkRobinson
That's what I am doing in my "workaround" - if the problem occurs on your machine: it would be interesting if the TPath.GetTempPath() function is working for you?
Patrick
This code may leave you with a string that's longer than Max_Path, but with only Max_Path characters assigned. That is, `Length(longpath) > StrLen(PChar(longpath))`. You might also be left with a string with garbage in it -- the docs don't say what the function stores in the buffer when the buffer's too small. The security bulletin and the documentation both describe the way to avoid that problem.
Rob Kennedy
Ok, we should mention here, that the return value of GetLongPathName is important and should be checked.
Patrick
GetTempPath() works fine for me. Looks like I will have to modify my current code to check for the correct length AND then check that the result is >0 just to be sure.
MarkRobinson