Problem with GetShellLinkInfo lazarus / 34-Windows ---------------------------------------------------------------------- jojo86 · 2019-02-24 12:37 Hi, I have found this topic : http://forum.lazarus- ide.org/index.php?topic=1709.0 But I have a problem with GetShellLinkInfo. PathName return me "Programmes files (x86)" for a shortcut, but the real path is : C:\Program Files\Google\Google Earth Pro\client This procedure can't see the diference between the real Program Files (x86) and the Program Files. How can I solve my problem ? Thanks you ---------------------------------------------------------------------- Bart · 2019-02-24 13:36 Without code it is impossble to say. Note that when you run a 32-bit program %ProgramFiles% will return the (x86) variant. So, show us the code and tell if you use 32 or 64 bit compiler. Bart ---------------------------------------------------------------------- jojo86 · 2019-02-24 13:57 Quote from: Bart on February 24, 2019, 02:36:48 pmWithout code it is impossble to say. Note that when you run a 32-bit program %ProgramFiles% will return the (x86) variant. So, show us the code and tell if you use 32 or 64 bit compiler. Bart Hi did you looked on the link ? And this on too : http://www.informit.com/articles/article.aspx?p=26940&seqNum=4 But I can post the code here : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit WinShell; interface uses SysUtils, Windows, Registry, ActiveX, ShlObj; type EShellOleError = class(Exception);  TShellLinkInfo = record  PathName: string;  Arguments: string;  Description: string;  WorkingDirectory: string;  IconLocation: string;  IconIndex: integer;  ShowCmd: integer;  HotKey: word; end;  TSpecialFolderInfo = record  Name: string;  ID: Integer; end; const SpecialFolders: array[0..29] of TSpecialFolderInfo = (  (Name: 'Alt Startup'; ID: CSIDL_ALTSTARTUP),  (Name: 'Application Data'; ID: CSIDL_APPDATA),  (Name: 'Recycle Bin'; ID: CSIDL_BITBUCKET),  (Name: 'Common Alt Startup'; ID: CSIDL_COMMON_ALTSTARTUP),  (Name: 'Common Desktop'; ID: CSIDL_COMMON_DESKTOPDIRECTORY),  (Name: 'Common Favorites'; ID: CSIDL_COMMON_FAVORITES),  (Name: 'Common Programs'; ID: CSIDL_COMMON_PROGRAMS),  (Name: 'Common Start Menu'; ID: CSIDL_COMMON_STARTMENU),  (Name: 'Common Startup'; ID: CSIDL_COMMON_STARTUP),  (Name: 'Controls'; ID: CSIDL_CONTROLS),  (Name: 'Cookies'; ID: CSIDL_COOKIES),  (Name: 'Desktop'; ID: CSIDL_DESKTOP),  (Name: 'Desktop Directory'; ID: CSIDL_DESKTOPDIRECTORY),  (Name: 'Drives'; ID: CSIDL_DRIVES),  (Name: 'Favorites'; ID: CSIDL_FAVORITES),  (Name: 'Fonts'; ID: CSIDL_FONTS),  (Name: 'History'; ID: CSIDL_HISTORY),  (Name: 'Internet'; ID: CSIDL_INTERNET),  (Name: 'Internet Cache'; ID: CSIDL_INTERNET_CACHE),  (Name: 'Network Neighborhood'; ID: CSIDL_NETHOOD),  (Name: 'Network Top'; ID: CSIDL_NETWORK),  (Name: 'Personal'; ID: CSIDL_PERSONAL),  (Name: 'Printers'; ID: CSIDL_PRINTERS),  (Name: 'Printer Links'; ID: CSIDL_PRINTHOOD),  (Name: 'Programs'; ID: CSIDL_PROGRAMS),  (Name: 'Recent Documents'; ID: CSIDL_RECENT),  (Name: 'Send To'; ID: CSIDL_SENDTO),  (Name: 'Start Menu'; ID: CSIDL_STARTMENU),  (Name: 'Startup'; ID: CSIDL_STARTUP),  (Name: 'Templates'; ID: CSIDL_TEMPLATES)); function CreateShellLink(const AppName, Desc: string; Dest: Integer): string;function GetSpecialFolderPath(Folder: Integer; CanCreate: Boolean): string;procedure GetShellLinkInfo(const LinkFile: WideString; var SLI: TShellLinkInfo);procedure SetShellLinkInfo(const LinkFile: WideString; const SLI: TShellLinkInfo); implementation uses ComObj; function GetSpecialFolderPath(Folder: Integer; CanCreate: Boolean): string;var FilePath: array[0..MAX_PATH] of char;begin { Get path of selected location } SHGetSpecialFolderPathW(0, FilePath, Folder, CanCreate); Result := FilePath;end; function CreateShellLink(const AppName, Desc: string; Dest: Integer): string;{ Creates a shell link for application or document specified in }{ AppName with description Desc. Link will be located in folder }{ specified by Dest, which is one of the string constants shown }{ at the top of this unit. Returns the full path name of the  }{ link file. }var SL: IShellLink; PF: IPersistFile; LnkName: WideString;begin OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,  IShellLink, SL)); { The IShellLink implementer must also support the IPersistFile } { interface. Get an interface pointer to it. } PF := SL as IPersistFile; OleCheck(SL.SetPath(PChar(AppName))); // set link path to proper file if Desc <> '' then  OleCheck(SL.SetDescription(PChar(Desc))); // set description { create a path location and filename for link file } LnkName := GetSpecialFolderPath(Dest, True) + '\' +       ChangeFileExt(AppName, 'lnk'); PF.Save(PWideChar(LnkName), True);     // save link file Result := LnkName;end; procedure GetShellLinkInfo(const LinkFile: WideString; var SLI: TShellLinkInfo);{ Retrieves information on an existing shell link }var SL: IShellLink; PF: IPersistFile; FindData: TWin32FindData; AStr: array[0..MAX_PATH] of char;begin OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,  IShellLink, SL)); { The IShellLink implementer must also support the IPersistFile } { interface. Get an interface pointer to it. } PF := SL as IPersistFile; { Load file into IPersistFile object } OleCheck(PF.Load(PWideChar(LinkFile), STGM_READ)); { Resolve the link by calling the Resolve interface function. } OleCheck(SL.Resolve(0, SLR_ANY_MATCH or SLR_NO_UI)); { Get all the info! } with SLI do begin  OleCheck(SL.GetPath(AStr, MAX_PATH, FindData, SLGP_SHORTPATH));  PathName := AStr;  OleCheck(SL.GetArguments(AStr, MAX_PATH));  Arguments := AStr;  OleCheck(SL.GetDescription(AStr, MAX_PATH));  Description := AStr;  OleCheck(SL.GetWorkingDirectory(AStr, MAX_PATH));  WorkingDirectory := AStr;  OleCheck(SL.GetIconLocation(AStr, MAX_PATH, IconIndex));  IconLocation := AStr;  OleCheck(SL.GetShowCmd(ShowCmd));  OleCheck(SL.GetHotKey(HotKey)); end;end; procedure SetShellLinkInfo(const LinkFile: WideString; const SLI: TShellLinkInfo);{ Sets information for an existing shell link }var SL: IShellLink; PF: IPersistFile;begin OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER,  IShellLink, SL)); { The IShellLink implementer must also support the IPersistFile } { interface. Get an interface pointer to it. } PF := SL as IPersistFile; { Load file into IPersistFile object } OleCheck(PF.Load(PWideChar(LinkFile), STGM_SHARE_DENY_WRITE)); { Resolve the link by calling the Resolve interface function. } OleCheck(SL.Resolve(0, SLR_ANY_MATCH or SLR_UPDATE or SLR_NO_UI)); { Set all the info! } with SLI, SL do begin  OleCheck(SetPath(PChar(PathName)));  OleCheck(SetArguments(PChar(Arguments)));  OleCheck(SetDescription(PChar(Description)));  OleCheck(SetWorkingDirectory(PChar(WorkingDirectory)));  OleCheck(SetIconLocation(PChar(IconLocation), IconIndex));  OleCheck(SetShowCmd(ShowCmd));  OleCheck(SetHotKey(HotKey)); end; PF.Save(PWideChar(LinkFile), True);  // save fileend; end. To use it, just add WinShell to the uses of my main form and : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Test;var MyVar:TShellLinkInfo;begin  WinShell.GetShellLinkInfo( FileNames[i],MyVar);Showmessage(MyVar.PathName);end;  Exemple of my problem : "" Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Test;var MyVar:TShellLinkInfo;begin  WinShell.GetShellLinkInfo('C:\Program Files\Google\Google Earth Pro\cl ient\googleearth.exe',MyVar);Showmessage(MyVar.PathName);//Return : C:\Program Files (x86)\Google\Google Earth Pro\client\googleearth.exeend;  I have found this subject on microsoft forums, but no issues for lazarus : https://social.msdn.microsoft.com/Forums/windowsdesktop/en- US/6f2e7920-50a9-459d-bfdd-316e459e87c0/ishelllink-getpath-returns- wrong-folder-for-64-bit-application-when-called-from-32-bit- application?forum=windowsgeneraldevelopmentissues ---------------------------------------------------------------------- ASerge · 2019-02-24 18:58 If I understand correctly, the 32-bit program runs in 64-bit Windows, and the link is made to 64-bit program. First, the file name in your example does not look like a link filename. Second, maybe the path in the link contains environment variables? Example (on my computer, 32bit FPC on Win 64x)Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$APPTYPE CONSOLE}{$MODE OBJFPC} uses ActiveX, ShlObj, ComObj; function ShowLinkPath(const LinkName: string): string;var  Common: IUnknown;  Link: IShellLinkW;  Persist: IPersistFile;  Buffer: array[0..260] of WideChar;begin  Result := '';  Common := CreateComObject(CLSID_ShellLink);  Link := Common as IShellLinkW;  Persist := Common as IPersistFile;  if (Persist.Load(PWideChar(UnicodeString(LinkName)), STGM_READ) = S_OK) and    (Link.GetPath(Buffer, Length(Buffer), nil, SLGP_RAWPATH) = S_OK) then  // SLGP_RAWPATH - not expand env!  begin    Result := string(UnicodeString(Buffer));  end;end; begin  Writeln(ShowLinkPath('c:\Program Files\Microsoft Games\Chess\ChessMCE.lnk'));  Readln;end.Show Quote%ProgramFiles%\Microsoft Games\chess\chess.exe ---------------------------------------------------------------------- jojo86 · 2019-03-01 14:10 Quote from: ASerge on February 24, 2019, 07:58:11 pmIf I understand correctly, the 32-bit program runs in 64-bit Windows, and the link is made to 64-bit program. First, the file name in your example does not look like a link filename. Second, maybe the path in the link contains environment variables? Example (on my computer, 32bit FPC on Win 64x)Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$APPTYPE CONSOLE}{$MODE OBJFPC} uses ActiveX, ShlObj, ComObj; function ShowLinkPath(const LinkName: string): string;var  Common: IUnknown;  Link: IShellLinkW;  Persist: IPersistFile;  Buffer: array[0..260] of WideChar;begin  Result := '';  Common := CreateComObject(CLSID_ShellLink);  Link := Common as IShellLinkW;  Persist := Common as IPersistFile;  if (Persist.Load(PWideChar(UnicodeString(LinkName)), STGM_READ) = S_OK) and    (Link.GetPath(Buffer, Length(Buffer), nil, SLGP_RAWPATH) = S_OK) then  // SLGP_RAWPATH - not expand env!  begin    Result := string(UnicodeString(Buffer));  end;end; begin  Writeln(ShowLinkPath('c:\Program Files\Microsoft Games\Chess\ChessMCE.lnk'));  Readln;end.Show Quote%ProgramFiles%\Microsoft Games\chess\chess.exe This work doesn't works too... Please look on my problem because I'm not sure that you undestood well... So : My Link properties : (https://nsa40.casimages.com/img/2019/03/01/190301031240312624.jpg) And the path returned by your code : (https://nsa40.casimages.com/img/2019/03/01/190301031510293709.jpg) ---------------------------------------------------------------------- marcov · 2019-03-01 14:52 Seems to be a common problem. Googling for "shortcut getpath gives wrong program files"  gives amongst others this this ---------------------------------------------------------------------- ASerge · 2019-03-01 18:27 Quote from: jojo86 on March 01, 2019, 03:10:12 pmThis work doesn't works too... Please look on my problem because I'm not sure that you undestood well... The graphical shell (which you give) does not get the answer how path is written in the .lnk file. It always expands environment variables. So try my code or even just "type PathtoLink.lnk" in the command line and in the resulting garbage you can try to see the contents of your shortcut. If used %ProgramFiles%, you will have to go the hard way - replace the variable with different values (C:\Program Files or C:\Program Files (x86)) and check the real existence of the file at the specified path. ---------------------------------------------------------------------- ASBzone · 2019-03-01 18:37 Quote from: marcov on March 01, 2019, 03:52:59 pmSeems to be a common problem. Googling for "shortcut getpath gives wrong program files"  gives amongst others this this Wow... That is a mind-boggling article. I'll have to test this on Windows 10 and see... ---------------------------------------------------------------------- jojo86 · 2019-03-02 14:41 Quote from: ASerge on March 01, 2019, 07:27:03 pmQuote from: jojo86 on March 01, 2019, 03:10:12 pmThis work doesn't works too... Please look on my problem because I'm not sure that you undestood well... The graphical shell (which you give) does not get the answer how path is written in the .lnk file. It always expands environment variables. So try my code or even just "type PathtoLink.lnk" in the command line and in the resulting garbage you can try to see the contents of your shortcut. If used %ProgramFiles%, you will have to go the hard way - replace the variable with different values (C:\Program Files or C:\Program Files (x86)) and check the real existence of the file at the specified path. The pics that I shared are from your code... So your code give me the same problem... ---------------------------------------------------------------------- jojo86 · 2019-03-02 14:42 Quote from: ASBzone on March 01, 2019, 07:37:02 pmQuote from: marcov on March 01, 2019, 03:52:59 pmSeems to be a common problem. Googling for "shortcut getpath gives wrong program files"  gives amongst others this this Wow... That is a mind-boggling article. I'll have to test this on Windows 10 and see... If you could try on win 10 why not, I would want to see if you have the same result as me... Thank for testing ! ---------------------------------------------------------------------- Remy Lebeau · 2019-03-05 23:19 I just found this: How can I get the original shortcut target path with environment variables unexpanded? QuoteIShellLink will automatically save c”:\program files\…” as “%ProgramFiles%\…” in this extra block but this causes WOW64 issues when a 64-bit app reads this information if it was written by a 32-bit app, %ProgramFiles% will expand to the wrong directory. The shortcut properties change icon feature has this issue for example. So, as the article explains, you could try retrieving the link's EXP_SZ_LINK data block, which will give you the unexpanded target path, and then you can replace %ProgramFiles% with %ProgramFiles(x86)% or %ProgramW6432% as needed, before then using ExpandEnvironmentStrings() to expand the actual path. ---------------------------------------------------------------------- ASBzone · 2019-03-06 02:50 Quote from: jojo86 on March 02, 2019, 03:42:46 pmIf you could try on win 10 why not, I would want to see if you have the same result as me... Thank for testing ! Okay, so I tested on Microsoft Windows [Version 10.0.17763.195] This is the x64 edition of Windows 10 Pro, version 1809. I ran the program looking for: Writeln(ShowLinkPath('C:\Program Files\Classic Shell\Start Menu Settings.lnk')); And I received: C:\Program Files\Classic Shell\ClassicStartMenu.exe So, on my system, the code worked properly. ---------------------------------------------------------------------- Remy Lebeau · 2019-03-06 20:23 Quote from: ASBzone on March 06, 2019, 03:50:25 amOkay, so I tested on Microsoft Windows [Version 10.0.17763.195] This is the x64 edition of Windows 10 Pro, version 1809. I ran the program looking for: Writeln(ShowLinkPath('C:\Program Files\Classic Shell\Start Menu Settings.lnk')); And I received: C:\Program Files\Classic Shell\ClassicStartMenu.exe So, on my system, the code worked properly. But, did you perform your test using a 32bit or 64bit app?