(SST) ShlWAPI.pas Version 1.08

Developer Reference
(SST)ShlWAPI PathAddBackslash Function
Appends a backslash to a path string.
Scope
Global (i.e. this function can be called/accessed from code in any unit that includes/uses (SST)ShlWAPI.pas).
Syntax
function PathAddBackslash(pszPath : LPSTR) : LPSTR;
Parameters
pszPath [in/out] A pointer to a NULL terminated ANSI (PathAddBackslash and PathAddBackslashA) or Unicode (PathAddBackslashW) character buffer, that on input contains the path to which the backslash ("\") should be added. If the function adds a backslash to the path, it is appended to the string in this buffer. The buffer must therefore be large enough to hold the input string, the added backslash and, depending on which version of the function is called, a terminating ANSI (PathAddBackslash & PathAddBackslashA) or Unicode (PathAddBackslashW) NULL character.
Return Values
If the buffer containing the in- and output string is smaller than MAX_PATH (= 260) characters in length, the function always returns a pointer to the ANSI or Unicode character (depending on which version of the function was called), following the last backslash in the provided buffer (but see remarks, below). If the buffer can hold MAX_PATH (= 260) or more characters, the function returns NIL, if appending a backslash would mean replacing either the terminating null (i.e. the contents of buffer[MAX_PATH - 1]) or one of the following characters, otherwise the address of the character following the last backslash.
Remarks
This function is the Windows API equivalent to the Delphi SDK function IncludeTrailingBackslash.
Contrary to what may be assumed, given the wording of the description in the documentation accompanying Microsoft SDK Versions up to and including 6.1, the function does not check if the buffer is large enough to accomodate an additional ANSI (PathAddBackslash & PathAddBackslashA) or Unicode (PathAddBackslashW) character. It merely checks if the input string has a trailing backslash. If not, it replaces the next single or double byte character through a backslash, irregardless of whether this is the terminating null character of the buffer or not. As it also does not resize the buffer, nor add a terminating null character if the string is no longer terminated by one, the returned, modified string may extend into memory ranges that belong to other parts of the application. This may result in a corrupted stack or heap and cause access violations that can crash the application, and possibly even the system.
However, it should be noted on behalf of the function and its documentation, that it does state that the buffer should be set to a size capable of storing MAX_PATH (= 260) characters (ANSI or Unicode, depending on which version of the function is called). Nonehteless, because of the way it was implemented, calling this function remains somewhat risky.
It is therefore absolutely imperative, when calling this function, that the necessary code be implemented that safeguards against the errors described above occurring. Here some suggestions:
As, for security reasons, it is not advisable to declare large buffers, such as the character arrays in the examples below (and in the Microsoft documentation) on the stack, the provided buffer should be dynamically allocated on the heap; either using functions from the SDK, such as GetMem, or the Windows API, such as LocalAlloc/GlobalAlloc, etc.. If you're using a Delphi SDK, the former is preferable, because the Delphi memory management (and garbage collection) routines will generally prevent worse.
If possible, compile the entire project or at least those parts in which the function call is implemented with structured exception handling turned on.
Unless memory usage is (already) an issue, such buffers should (always) have a size of at least MAX_PATH (= 260) characters, even if less is actually required.
The buffer should always be explicitly filled wiht null characters, prior to initializing it with the path string (i.e. don't rely on the memory allocation functions having (already) performed this task for you).
Determine the length of the path string and verify that the buffer is large enough to hold it, including the backslash that may be added, and the terminting null character.
Evaluate the pointer returned by the function.
GetLastError returns error code 5 (ERROR_ACCESS_DENIED) if the function overwwrites the terminating null or one or more bytes in the adjaccent memory.
Example
PROCEDURE TForm4.TestShlWAPIPathAddBackslash(Sender : TObject); VAR pathstrbuf : ARRAY[0.. MAX_PATH - 1] OF CHAR; VAR wcharpathbufp : PWideChar; VAR pathstrbuf3 : ARRAY[0..20] OF CHAR; VAR wcharpathbuf2 : ARRAY[0..22] OF WideChar; VAR bufsize : INTEGER; VAR apiretval : POINTER; VAR lastchar : CHAR; VAR lastwchar : WideChar; VAR lastcharaddr : POINTER; VAR newinfoline : STRING; BEGIN FillChar(pathstrbuf, Length(pathstrbuf), #0); wcharpathbufp := NIL; FillChar(pathstrbuf3, Length(pathstrbuf3), #0); ZeroMemory(@wcharpathbuf2, SizeOf(wcharpathbuf2)); bufsize := 0; apiretval := NIL; lastchar := #0; lastwchar := #0; lastcharaddr := NIL; newinfoline := ''; newinfoline := 'Example 1.'; Memo1.Lines.Add(newinfoline); pathstrbuf := 'C:\Hello\World'; newinfoline := 'PathAddBackslash called with ' + pathstrbuf; Memo1.Lines.Add(newinfoline); apiretval := PathAddBackslash(pathstrbuf); IF apiretval <> NIL THEN newinfoline := 'Returned : 0x' + IntToHex(INTEGER(apiretval), 8) + ' and the modified string : ' + pathstrbuf ELSE newinfoline := 'Failed, returning NIL !'; Memo1.Lines.Add(newinfoline); bufsize := 2 * (Length('C:\Hello\Unicode\World') + 2); GetMem(wcharpathbufp, bufsize); ZeroMemory(wcharpathbufp, bufsize); wcharpathbufp := StringToWideChar('C:\Hello\Unicode\World', wcharpathbufp, bufsize); newinfoline := 'Example 2.'; Memo1.Lines.Add(newinfoline); newinfoline := 'PathAddBackslashW called with ' + wcharpathbufp; Memo1.Lines.Add(newinfoline); newinfoline := 'The address and size of which are : 0x' + IntToHex(INTEGER(wcharpathbufp), 8); newinfoline := newinfoline + ' ' + IntToStr(bufsize) + ' bytes'; Memo1.Lines.Add(newinfoline); apiretval := PathAddBackslashW(wcharpathbufp); IF apiretval <> NIL THEN newinfoline := 'Returned : 0x' + IntToHex(INTEGER(apiretval), 8) + ' and the modified Unicode string : ' + wcharpathbufp ELSE newinfoline := 'Failed, returning NIL !'; Memo1.Lines.Add(newinfoline); FreeMem(wcharpathbufp); //String which already has a trailing backslash FillChar(pathstrbuf, Length(pathstrbuf), #0); apiretval := NIL; bufsize := Length(pathstrbuf); newinfoline := 'Example 3.'; Memo1.Lines.Add(newinfoline); pathstrbuf := 'C:\Hello\World\With\Backslash\'; lastchar := pathstrbuf[30]; newinfoline := 'PathAddBackslash called with ' + pathstrbuf + ' and a buffer size of ' + IntToStr(bufsize) + ', '; Memo1.Lines.Add(newinfoline); newinfoline := 'the 30th char of which is #' + IntToStr(Ord(lastchar)); Memo1.Lines.Add(newinfoline); lastcharaddr := @pathstrbuf[30]; newinfoline := 'Address of 30th character : 0x' + IntToHex(INTEGER(lastcharaddr), 8); Memo1.Lines.Add(newinfoline); apiretval := PathAddBackslash(pathstrbuf); IF apiretval <> NIL THEN BEGIN newinfoline := 'Returned : 0x' + IntToHex(INTEGER(apiretval), 8) + ' and the buffer contents : ' + pathstrbuf + ', '; Memo1.Lines.Add(newinfoline); lastchar := pathstrbuf[30]; newinfoline := 'the 30th char of which is now "' + lastchar + '"'; END ELSE newinfoline := 'Failed, returning NIL !'; Memo1.Lines.Add(newinfoline); //Buffer too small (should fail, but doesn't !) apiretval := NIL; newinfoline := 'Example 4.'; Memo1.Lines.Add(newinfoline); pathstrbuf3 := 'C:\Hello\World\Four4'; //Text length : 19 characters bufsize := Length(pathstrbuf3);//Text length + terminating null character lastchar := pathstrbuf3[20]; //terminating null newinfoline := 'PathAddBackslash called with ' + pathstrbuf3 + ' and a buffer size of ' + IntToStr(bufsize) + ', '; Memo1.Lines.Add(newinfoline); newinfoline := 'the last char of which is #' + IntToStr(Ord(lastchar)); Memo1.Lines.Add(newinfoline); lastcharaddr := @pathstrbuf3[20]; //address of terminating null newinfoline := 'Address of last character : 0x' + IntToHex(INTEGER(lastcharaddr), 8); Memo1.Lines.Add(newinfoline); apiretval := PathAddBackslash(pathstrbuf3); IF apiretval <> NIL THEN BEGIN newinfoline := 'Returned : 0x' + IntToHex(INTEGER(apiretval), 8) + ' and the modified buffer contents : ' + pathstrbuf3 + ', '; Memo1.Lines.Add(newinfoline); lastchar := pathstrbuf3[20]; newinfoline := 'the last char of which is now "' + lastchar + '"'; END ELSE newinfoline := 'Failed, returning NIL !'; Memo1.Lines.Add(newinfoline); //Buffer too small, Unicode version (should fail, but doesn't !) apiretval := NIL; wcharpathbufp := NIL; bufsize := 0; newinfoline := 'Example 5.'; Memo1.Lines.Add(newinfoline); //Text : 'C:\Hello\Unicode\Error' //Text length : 22 characters bufsize := Length(wcharpathbuf2); //Text length + terminating, null, Unicode character wcharpathbufp := StringToWideChar('C:\Hello\Unicode\Error', @wcharpathbuf2, bufsize); lastwchar := wcharpathbuf2[22]; //terminating null newinfoline := 'PathAddBackslash called with ' + wcharpathbuf2 + ' and a buffer size of ' + IntToStr(bufsize) + ', '; Memo1.Lines.Add(newinfoline); newinfoline := 'the last char of which is #' + IntToStr(WORD(lastwchar)); Memo1.Lines.Add(newinfoline); lastcharaddr := @wcharpathbuf2[22];//address of terminating null newinfoline := 'Address of last Unicode character : 0x' + IntToHex(INTEGER(lastcharaddr), 8); Memo1.Lines.Add(newinfoline); apiretval := PathAddBackslash(pathstrbuf3); IF apiretval <> NIL THEN BEGIN newinfoline := 'Returned : 0x' + IntToHex(INTEGER(apiretval), 8) + ' and the modified buffer contents : ' + wcharpathbuf2 + ', '; Memo1.Lines.Add(newinfoline); lastwchar := wcharpathbuf2[22]; newinfoline := 'the last Unicode char of which is now "' + lastwchar + '"'; END ELSE newinfoline := 'Failed, returning NIL !'; Memo1.Lines.Add(newinfoline); //Buffer size MAX_PATH but still too small (fails !) apiretval := NIL; newinfoline := 'Example 6.'; Memo1.Lines.Add(newinfoline); newinfoline := 'X:\' + StringOfChar('A', 63) + '\' + StringOfChar('b', 63) + '\' + StringOfChar('C', 63) + '\' + StringOfChar('d', 64); bufsize := Length(newinfoline); StrCopy(pathstrbuf, PChar(newinfoline)); //Text length : 259 characters bufsize := Length(pathstrbuf); //Text length + terminating null character lastchar := pathstrbuf[MAX_PATH - 1];//terminating null newinfoline := 'PathAddBackslash called with ' + pathstrbuf + ' and a buffer size of ' + IntToStr(bufsize) + ', '; Memo1.Lines.Add(newinfoline); newinfoline := 'the last char of which is #' + IntToStr(Ord(lastchar)); Memo1.Lines.Add(newinfoline); lastcharaddr := @pathstrbuf[MAX_PATH - 1]; //address of terminating null newinfoline := 'Address of last character : 0x' + IntToHex(INTEGER(lastcharaddr), 8); Memo1.Lines.Add(newinfoline); apiretval := PathAddBackslash(pathstrbuf); IF apiretval <> NIL THEN BEGIN newinfoline := 'Returned : 0x' + IntToHex(INTEGER(apiretval), 8) + ' and the modified buffer contents : ' + pathstrbuf + ', '; Memo1.Lines.Add(newinfoline); lastchar := pathstrbuf[MAX_PATH - 1]; newinfoline := 'the last char of which is now "' + lastchar + '"'; END ELSE newinfoline := 'Failed, returning NIL !'; Memo1.Lines.Add(newinfoline); Memo1.Lines.Add(''); END;
 
Example 1. PathAddBackslash called with C:\Hello\World Returned : 0x0012F4A7 and the modified string : C:\Hello\World\ Example 2. PathAddBackslashW called with C:\Hello\Unicode\World The address and size of which are : 0x012D7EB8 48 bytes Returned : 0x012D7EE6 and the modified Unicode string : C:\Hello\Unicode\World\ Example 3. PathAddBackslash called with C:\Hello\World\With\Backslash\ and a buffer size of 260, the 30th char of which is #0 Address of 30th character : 0x0012F4B6 Returned : 0x0012F4B6 and the buffer contents : C:\Hello\World\With\Backslash\, the 30th char of which is now " Example 4. PathAddBackslash called with C:\Hello\World\Four4 and a buffer size of 21, the last char of which is #0 Address of last character : 0x0012F497 Returned : 0x0012F498 and the modified buffer contents : C:\Hello\World\Four4\, the last char of which is now "\" Example 5. PathAddBackslash called with C:\Hello\Unicode\Error and a buffer size of 23, the last char of which is #0 Address of last Unicode character : 0x0012F480 Returned : 0x0012F498 and the modified buffer contents : C:\Hello\Unicode\Error, the last Unicode char of which is now " Example 6. PathAddBackslash called with X:\AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd and a buffer size of 260, the last char of which is #0 Address of last character : 0x0012F59B Failed, returning NIL !
Requirements
Unit: Declared and imported in (SST)ShlWAPI.pas
Library: (SST)ShlWAPI.dcu/(SST)ShlWAPI.obj
Unicode: Implemented as ANSI (PathAddBackslash and PathAddBackslashA) and Unicode (PathAddBackslashW) functions.
Min. ShlWAPI.dll version according to MS SDK doc.: 4.71
Min. ShlWAPI.dll version based on SST research: 4.71
Min. OS version(s) according to Microsoft SDK doc.: Windows 2000, Windows NT 4.0 with Internet Explorer 4.0, Windows 98, Windows 95 with Internet Explorer 4.0
Min. OS version(s) according to SST research.: Windows NT 4.0 with IE 4.0, Windows 95 with IE 4.0, Windows 98, Windows 2000 and later
See Also
PathGetCharType.
 
Windows APIs: PathAddBackslash, PathGetCharType.


Document/Contents version 1.01
Page/URI last updated on June 02, 2022
 
Copyright © Stoelzel Software Technologie (SST) 2010 - 2022
Suggestions and comments mail to:
webmaster@stoelzelsoftwaretech.com