Recent

Author Topic: Trying to get My Documents folder  (Read 19898 times)

Josh

  • Hero Member
  • *****
  • Posts: 1271
Trying to get My Documents folder
« on: July 18, 2017, 11:29:26 pm »
Hi

I am using latest trunk to test some old units out and have come across a problem, with my routine that get user's Documents Folder.

The routine is working fine, when user has standard characters; but when user has character like áé etc, then it fails and raises the exception in the code.


Code: Pascal  [Select][+][-]
  1. function GetMyDocuments: string;
  2. var
  3.   r: Bool;
  4.   path: array[0..Max_Path] of Char;
  5. begin
  6.   r := ShGetSpecialFolderPath(0, path, CSIDL_Personal, False) ;
  7.   if not r then raise Exception.Create('Could not find MyDocuments folder location.') ;
  8.   path:=SysToUTF8(path);
  9.   Result := Path;
  10. end;
  11.  
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

Handoko

  • Hero Member
  • *****
  • Posts: 5132
  • My goal: build my own game engine using Lazarus
Re: Trying to get My Documents folder
« Reply #1 on: July 19, 2017, 02:58:42 am »

PatBayford

  • Full Member
  • ***
  • Posts: 125
Re: Trying to get My Documents folder
« Reply #2 on: July 19, 2017, 05:25:04 am »
The problem you are having josh, is that most versions of Windows DO NOT "speak" UTF8/16, or even necessarily Unicode correctly. You will need to ensure that any strings passed to Windows API functions are converted to old-fashioned ASCII code strings.
PS - don't forget that Windows strings need to be enclosed in quotes if they contain spaces.
Lazarus 1.8.0 FPC 3.0.2 SVN 56594 Windows 10 64bit (i386-win32-win32/win64)

balazsszekely

  • Guest
Re: Trying to get My Documents folder
« Reply #3 on: July 19, 2017, 06:18:17 am »
@Josh
Quote
I am using latest trunk to test some old units out and have come across a problem, with my routine that get user's Documents Folder.
The routine is working fine, when user has standard characters; but when user has character like áé etc, then it fails and raises the exception in the code.

Try something like this:
Code: Pascal  [Select][+][-]
  1. uses shlobj;
  2.  
  3. function GetSpecialFolder(const CSIDL: Integer): WideString;
  4. var
  5.   SpecialPath: PWideChar;
  6. begin
  7.   Result := '';
  8.   SpecialPath := WideStrAlloc(MAX_PATH);
  9.   try
  10.     FillChar(SpecialPath^, MAX_PATH, 0);
  11.     if SHGetSpecialFolderPathW(0, SpecialPath, CSIDL, False) then
  12.       Result := SpecialPath;
  13.   finally
  14.     StrDispose(SpecialPath);
  15.   end;
  16. end;
  17.  
  18. procedure TForm1.Button1Click(Sender: TObject);
  19. begin
  20.   ShowMessage(String(GetSpecialFolder(CSIDL_PERSONAL)));
  21.   //ShowMessage(UTF16ToUTF8(GetSpecialFolder(CSIDL_PERSONAL))); //for FPC 2.6.4 --> unit LazUTF8
  22. end;

Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: Trying to get My Documents folder
« Reply #4 on: July 19, 2017, 08:22:53 am »
Hi Getmem,

Thanks for your code, if I use this on my old lazarus installs it indeed works, as does my code.

If I use this code on Lazarus Trunk, It does not work and produces no result, I have modified the code to show an 'Oops' message which is triggered if the call to SHGetSpecialFolderPathW fails. Which it does on Trunk.

Is it possible I have found a bug in Lazarus Trunk?

Code: Pascal  [Select][+][-]
  1. function GetSpecialFolder(const CSIDL: Integer): WideString;
  2. var
  3.   SpecialPath: PWideChar;
  4. begin
  5.   Result := '';
  6.   SpecialPath := WideStrAlloc(MAX_PATH);
  7.   try
  8.     FillChar(SpecialPath^, MAX_PATH, 0);
  9.     if SHGetSpecialFolderPathW(0, SpecialPath, CSIDL, False) then
  10.       Result := SysToUTF8(SpecialPath)
  11.     else showmessage('Oops');  
  12.   finally
  13.     StrDispose(SpecialPath);
  14.   end;
  15. end;          
  16.  
« Last Edit: July 19, 2017, 08:24:48 am by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

balazsszekely

  • Guest
Re: Trying to get My Documents folder
« Reply #5 on: July 19, 2017, 08:31:31 am »
Quote
If I use this code on Lazarus Trunk, It does not work and produces no result, I have modified the code to show an 'Oops' message which is triggered if the call to SHGetSpecialFolderPathW fails. Which it does on Trunk.
Is it possible I have found a bug in Lazarus Trunk?
I also use Lazarus trunk and the code works fine. It's more likely a windows related issue(privilege, elevation, etc...). Please try:
 
Code: Pascal  [Select][+][-]
  1. ShowMessage(SysErrorMessage(GetLastOSError))

instead of:
 
Code: Pascal  [Select][+][-]
  1. ShowMessage('Oops');

Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: Trying to get My Documents folder
« Reply #6 on: July 19, 2017, 08:36:38 am »
Hi the error displayed adding that in produces the following error on trunk only.
The System Cannot find the path specified

Please note this is on the same computer, running both lazarus versions at the same time.
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

balazsszekely

  • Guest
Re: Trying to get My Documents folder
« Reply #7 on: July 19, 2017, 08:51:15 am »
I have no idea why SHGetSpecialFolderPathW api fails on your computer. You can also try:
Code: Pascal  [Select][+][-]
  1. uses windirs;
  2. begin
  3.   ShowMessage(GetWindowsSpecialDir(CSIDL_PERSONAL));
  4. end;
  or
Code: Pascal  [Select][+][-]
  1. uses LazFileUtils;
  2. begin
  3.   ShowMessage(AppendPathDelim(GetUserDir + 'Documents'));
  4. end;


Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: Trying to get My Documents folder
« Reply #8 on: July 19, 2017, 09:03:23 am »
Hi

ShowMessage(GetWindowsSpecialDir(CSIDL_PERSONAL));

also on trunk is displaying a blank, but on old old lazarus is displaying ok.

I do not think it is related to this machine, as I have tried on other machines and windows versions that I have , as soon as the user name has a utf8 character in it then it is unable to find the Documents folder. 
I have also sent both compiled binaries with same code to a friend in Finland, and he has just reported back that only the old version is showing the correct location and the second version is displaying the error.

My test SVN version is 55492, I will download the very latest version, in case something has changed in the last couple of days?

Addition
GetUserDir  is displaying the correct value. Unfortunately this is not usable as 'Documents' is not guaranteed to be correct across windows os's etc.
« Last Edit: July 19, 2017, 09:07:40 am by josh »
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4460
  • I like bugs.
Re: Trying to get My Documents folder
« Reply #9 on: July 19, 2017, 09:55:21 am »
Is it possible I have found a bug in Lazarus Trunk?
Code: Pascal  [Select][+][-]
  1. function GetSpecialFolder(const CSIDL: Integer): WideString;
  2. ...
  3.       Result := SysToUTF8(SpecialPath)
  4.  
No, you added the bug all by yourself. Instead of using the working code provided by GetMem, you added a conversion that is doomed to fail. Why?
You should know the W(ide) versions of WinAPI, like SHGetSpecialFolderPathW, work with Unicode, UTF-16.
That is why the parameter SpecialPath is PWideChar.
GetMem and everybody: I think the return type of your function should be UnicodeString instead of WideString. WideString should be used only with OLE programming.

The problem you are having josh, is that most versions of Windows DO NOT "speak" UTF8/16, or even necessarily Unicode correctly. You will need to ensure that any strings passed to Windows API functions are converted to old-fashioned ASCII code strings.
That is not correct. The WinAPI functions that work with old-fashioned ANSI strings do not work with Lazarus Unicode system. You must use the W versions that work with Unicode.
Please don't spread false information.
Unicode should be used everywhere in any case because ANSI <-> Unicode string conversions are lossy. The old ANSI string support can be seen as a historical remain, retained only for backwards compatibility.
Unicode in Lazarus + WinAPI are explained here:
 http://wiki.freepascal.org/Unicode_Support_in_Lazarus#Calling_Windows_API
« Last Edit: July 19, 2017, 10:11:30 am by JuhaManninen »
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

wp

  • Hero Member
  • *****
  • Posts: 11858
Re: Trying to get My Documents folder
« Reply #10 on: July 19, 2017, 10:09:19 am »
The original function of the first post is working perfectly on my Win7 VM with a special user "UmlautUser-äöüÄ". The only thing that needs to be changed is that SysToUTF8 must be replaced by WinCPToUTF8 on fpc 3+.

So, I guess the issue has something to do with missing rights or similar, but not with utf8/unicode.

JuhaManninen

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 4460
  • I like bugs.
Re: Trying to get My Documents folder
« Reply #11 on: July 19, 2017, 10:16:01 am »
The original function of the first post is working perfectly on my Win7 VM with a special user "UmlautUser-äöüÄ". The only thing that needs to be changed is that SysToUTF8 must be replaced by WinCPToUTF8 on fpc 3+.
It works if all the characters belong to the current codepage. Isn't it so?
There is no reason to gamble with codepages any more. Just use Unicode and you are good.
Mostly Lazarus trunk and FPC 3.2 on Manjaro Linux 64-bit.

balazsszekely

  • Guest
Re: Trying to get My Documents folder
« Reply #12 on: July 19, 2017, 10:16:23 am »
Quote
I think the return type of your function should be UnicodeString instead of WideString. WideString should be used only with OLE programming.
You can use both. UnicodeString is reference counted on the other hand WideString basically is the same good old COM BSTR type windows always used, without ref. count. WideString is slower though, since windows makes a copy on winapi calls. Unless you don't call a winapi 1000x times, you should be fine. The funny thing is UnicodeChar points back to WideChar.  :)
« Last Edit: July 19, 2017, 10:19:09 am by GetMem »

Josh

  • Hero Member
  • *****
  • Posts: 1271
Re: Trying to get My Documents folder
« Reply #13 on: July 19, 2017, 10:20:16 am »
Hi

The problem is that it is not even getting to the systoutf8 function, the function SHGetSpecialFolderPathW(0, SpecialPath, CSIDL, False) is returning FALSE, and the error being reported is 'The System Cannot find the path specified'.

I have tried using WinCPToUTF8 but this has no change in that the error is prior to this.

I can compile the small test app on old laz and new laz, place both files in same directory and one will work and one will not displaying the message 'System cannot find the path'

code now using
Code: Pascal  [Select][+][-]
  1. function GetSpecialFolder(const CSIDL: Integer): UnicodeString ;
  2. var
  3.   SpecialPath: PWideChar;
  4. begin
  5.   Result := '';
  6.   SpecialPath := WideStrAlloc(MAX_PATH);
  7.   try
  8.     FillChar(SpecialPath^, MAX_PATH, 0);
  9.     if SHGetSpecialFolderPathW(0, SpecialPath, CSIDL, False) then
  10.       Result := WinCPToUTF8 (SpecialPath)
  11.     else ShowMessage(SysErrorMessage(GetLastOSError));
  12.   finally
  13.     StrDispose(SpecialPath);
  14.   end;
  15. end;      
  16.  
The best way to get accurate information on the forum is to post something wrong and wait for corrections.

rvk

  • Hero Member
  • *****
  • Posts: 6112
Re: Trying to get My Documents folder
« Reply #14 on: July 19, 2017, 10:20:37 am »
Should SHGetSpecialFolderPath actually still be used ??
https://msdn.microsoft.com/en-us/library/windows/desktop/bb762204(v=vs.85).aspx

Quote
SHGetSpecialFolderPath is not supported. Instead, use ShGetFolderPath

And ShGetFolderPath is deprecated in favor of SHGetKnownFolderPath.
https://msdn.microsoft.com/en-us/library/windows/desktop/bb762181(v=vs.85).aspx

FPC also uses that one in windirs.pp

 

TinyPortal © 2005-2018