Well, setting aside the obfuscation of burying the real work of the routine as a side effect of code that exists merely to provide the scaffolding required to make that call, this code actually contains an error:
setlength(temp, 100); //has to be big enough first
Since GetEnvironmentVariable() works with a plain null terminated buffer, the length of the buffer that you indicate in the call to it needs to reflect the expected presence of that terminator in the buffer. But the null terminator on a String type variable is implicit, and not part of the payload and so not reflected in the length.
If the USERNAME were exactly 100 characters in length, your call to GetEnvironmentVariable() would fail, even though the buffer you are passing is actually the right size.
This to my mind perfectly illustrates the problem: treating one type (a string) as if it were another in a situation where the differences are actually important.
Passing a String value to an external routine that simple READS from a null terminated buffer is much more straightforward and less error prone (imho – due to being reminded of what you are working with by the very nature of what you are working with) than trying to pass a pointer which is actually an offset into a complex String type to a function that expects to modify a plain null terminated buffer.
When reading from a string, the payload can be safely naively regarded as a null terminated buffer by a function that is unaware of the greater complexity of the type of value it is actually being passed.
But when writing to a null terminated buffer, there be dragons when that buffer isn’t just a null terminated buffer but actually the payload of a far more complex type.
If an external routine expected a pointer to an unsigned 32 bit value I doubt you would consider it sensible to contrive to pass a pointer to an signed 16-bit variable, even if you always got the desired results (at least, up to now... ). J
To: 'NZ Borland Developers Group - Delphi List' <mailto:delphi@delp...>
Subject: Re: [DUG] Variable in String
Sorry Bob, I meant to include an example of your code tweaked to use “raw” a char array with the Windows API routine. Here it is (this version displays results rather than storing in a variable, but you get the idea J ) :
dir: array of Char;
s := ‘’;
SetLength(dir, MAX_PATH + 1);
if Succeeded(SHGetFolderPath(0, CSIDL_Program_Files, 0, 0, @dir)) then
s := PChar(dir);
ShowMessageFmt(s + ' (%d chars)', [Length(s)]);
// Outcome: s has both the right length *and* is null terminated correctly