Lazarus

Programming => General => Topic started by: evoshroom on January 11, 2013, 05:57:52 pm

Title: Windows Folder Shortcuts
Post by: evoshroom on January 11, 2013, 05:57:52 pm
Apparently, as I discovered today, there are multiple types of shortcuts on Windows.  There was some great code up on the Lazarus Windows Wiki for creating normal shortcuts:

Code: [Select]
uses
...
{$IFDEF MSWINDOWS}windows, shlobj {for special folders}, ActiveX, ComObj,{$ENDIF} 
...
procedure TMForm.CreateDesktopShortCut(Target, TargetArguments, ShortcutName: string);
var
  IObject: IUnknown;
  ISLink: IShellLink;
  IPFile: IPersistFile;
  PIDL: PItemIDList;
  InFolder: array[0..MAX_PATH] of Char;
  TargetName: String;
  LinkName: WideString;
begin
  { Creates an instance of IShellLink }
  IObject := CreateComObject(CLSID_ShellLink);
  ISLink := IObject as IShellLink;
  IPFile := IObject as IPersistFile;

  ISLink.SetPath(pChar(Target));
  ISLink.SetArguments(pChar(TargetArguments));
  ISLink.SetWorkingDirectory(pChar(ExtractFilePath(Target)));

  { Get the desktop location }
  SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, PIDL);
  SHGetPathFromIDList(PIDL, InFolder);
  LinkName := InFolder + PathDelim + ShortcutName+'.lnk';

  { Create the link }
  IPFile.Save(PWChar(LinkName), false);
end;

However, I couldn't find any code for creating Folder Shortcuts (see: http://en.wikipedia.org/wiki/Symbolic_link#Folder_Shortcuts.5B12.5D (http://en.wikipedia.org/wiki/Symbolic_link#Folder_Shortcuts.5B12.5D)).  While the normal shortcuts use CLSID_ShellLink folder shortcuts use CLSID_FolderShortcut.  So I tried just subbing that in the code.  Microsoft's C++ examples seem to hint that that would be okay (see: http://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx#Shellink_Creating_Shortcut (http://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx#Shellink_Creating_Shortcut)).   When I did that at first it said CLSID_FolderShortcut wasn't a known constant.  So then I dug up the constant from some old Delphi code adding:

Code: [Select]
const
CLSID_FolderShortcut: TGUID = (D1:$0AFACED1; D2:$E828; D3:$11D1; D4:($91, $87, $B5, $32, $F1, $E9, $57, $5D));

That code compiled.  However, when run I didn't end up with a Folder Shortcut.  Now in the meantime, I've done some digging into folder shortcuts and they seem to be normal shortcuts in a folder marked slightly specially, with a hidden desktop.ini, so I could probably hack my way into working ones if needed, but I was wondering if there was a nice way to do it more like the CreateDesktopShortCut() procedure above?
Title: Re: Windows Folder Shortcuts
Post by: J-G on January 09, 2018, 09:17:56 pm
Whilst I know this post was 5 years ago, it seems that no-one responded and now I need just such a solution.

I have successfully created a shortcut to a program on the desktop but, as noted by evoshroom, that isn't the same as creating a shortcut to a FOLDER.

The procedure CreateDesktopShortCut takes three arguments two of which are quite obvious but I have failed to find any information regarding 'TargetArguments' and suspect (hope) that this may be the key to a solution.

Apparently, as I discovered today, there are multiple types of shortcuts on Windows.  There was some great code up on the Lazarus Windows Wiki for creating normal shortcuts:

Code: [Select]
uses
...
{$IFDEF MSWINDOWS}windows, shlobj {for special folders}, ActiveX, ComObj,{$ENDIF} 
...
procedure TMForm.CreateDesktopShortCut(Target, TargetArguments, ShortcutName: string);
var
  IObject: IUnknown;
  ISLink: IShellLink;
  IPFile: IPersistFile;
  PIDL: PItemIDList;
  InFolder: array[0..MAX_PATH] of Char;
  TargetName: String;
  LinkName: WideString;
begin
  { Creates an instance of IShellLink }
  IObject := CreateComObject(CLSID_ShellLink);
  ISLink := IObject as IShellLink;
  IPFile := IObject as IPersistFile;

  ISLink.SetPath(pChar(Target));
  ISLink.SetArguments(pChar(TargetArguments));
  ISLink.SetWorkingDirectory(pChar(ExtractFilePath(Target)));

  { Get the desktop location }
  SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, PIDL);
  SHGetPathFromIDList(PIDL, InFolder);
  LinkName := InFolder + PathDelim + ShortcutName+'.lnk';

  { Create the link }
  IPFile.Save(PWChar(LinkName), false);
end;

However, I couldn't find any code for creating Folder Shortcuts (see: http://en.wikipedia.org/wiki/Symbolic_link#Folder_Shortcuts.5B12.5D (http://en.wikipedia.org/wiki/Symbolic_link#Folder_Shortcuts.5B12.5D)).  While the normal shortcuts use CLSID_ShellLink folder shortcuts use CLSID_FolderShortcut.  So I tried just subbing that in the code.  Microsoft's C++ examples seem to hint that that would be okay (see: http://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx#Shellink_Creating_Shortcut (http://msdn.microsoft.com/en-us/library/windows/desktop/bb776891(v=vs.85).aspx#Shellink_Creating_Shortcut)).   When I did that at first it said CLSID_FolderShortcut wasn't a known constant.  So then I dug up the constant from some old Delphi code adding:

Code: [Select]
const
CLSID_FolderShortcut: TGUID = (D1:$0AFACED1; D2:$E828; D3:$11D1; D4:($91, $87, $B5, $32, $F1, $E9, $57, $5D));

That code compiled.  However, when run I didn't end up with a Folder Shortcut.  Now in the meantime, I've done some digging into folder shortcuts and they seem to be normal shortcuts in a folder marked slightly specially, with a hidden desktop.ini, so I could probably hack my way into working ones if needed, but I was wondering if there was a nice way to do it more like the CreateDesktopShortCut() procedure above?
Title: Re: Windows Folder Shortcuts
Post by: lainz on January 09, 2018, 09:34:55 pm
The procedure CreateDesktopShortCut takes three arguments two of which are quite obvious but I have failed to find any information regarding 'TargetArguments' and suspect (hope) that this may be the key to a solution.

That argument is to send parameters to the program like:

Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('C:\Windows\Notepad.exe', 'C:\Windows\win.ini', 'Win.ini in Notepad');

And can be used to create a shortcut to a folder like this:

Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('C:\Windows\explorer.exe', 'C:\Windows\', 'Windows folder');

Edit: Better use this
Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', 'C:\Windows\', 'Windows folder');

The second parameter is the folder.
Title: Re: Windows Folder Shortcuts
Post by: J-G on January 09, 2018, 10:42:28 pm
The procedure CreateDesktopShortCut takes three arguments two of which are quite obvious but I have failed to find any information regarding 'TargetArguments' and suspect (hope) that this may be the key to a solution.

That argument is to send parameters to the program like:

Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('C:\Windows\Notepad.exe', 'C:\Windows\win.ini', 'Win.ini in Notepad');
Thanks lainz  -  I ought to have realized that  :-[
Quote from: lainz
And can be used to create a shortcut to a folder like this:
Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', 'C:\Windows\', 'Windows folder');

The second parameter is the folder.

I had already tried that - without success - it does create a shortcut but when that is 'clicked' it puts up a dialogue asking which program I want to use to open the file. Thus it isn't a 'Folder' shortcut - and of course the icon is not what a folder shortcut icon would be, just a 'document' icon.



Title: Re: Windows Folder Shortcuts
Post by: lainz on January 09, 2018, 10:43:41 pm
I'm using Windows 10 and works fine.

The icon is not a folder icon, but the explorer.exe icon.
Title: Re: Windows Folder Shortcuts
Post by: lainz on January 09, 2018, 10:54:52 pm
I had already tried that - without success - it does create a shortcut but when that is 'clicked' it puts up a dialogue asking which program I want to use to open the file. Thus it isn't a 'Folder' shortcut - and of course the icon is not what a folder shortcut icon would be, just a 'document' icon.

Your folder contains spaces? If is that quote with double quotes.

Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', '"C:\Program Files"', 'PF folder');

Or try this:
Replace %WINDIR%\explorer.exe with C:\Windows\explorer.exe C:\ or the letter of your hard drive..

You may find where is in the user PC with some function I don't have...

Full path required, not just explorer.exe
Title: Re: Windows Folder Shortcuts
Post by: lainz on January 09, 2018, 11:11:06 pm
Code: Pascal  [Select][+][-]
  1. const
  2. CLSID_FolderShortcut: TGUID = (D1:$0AFACED1; D2:$E828; D3:$11D1; D4:($91, $87, $B5, $32, $F1, $E9, $57, $5D));
  3.                    
  4. procedure CreateDesktopShortCut(Target, ShortcutName: string);
  5. var
  6.   IObject: IUnknown;
  7.   ISLink: IShellLink;
  8.   IPFile: IPersistFile;
  9.   PIDL: PItemIDList;
  10.   InFolder: array[0..MAX_PATH] of Char;
  11.   TargetName: String;
  12.   LinkName: WideString;
  13. begin
  14.   { Creates an instance of IShellLink }
  15.   IObject := CreateComObject(CLSID_FolderShortcut);
  16.   ISLink := IObject as IShellLink;
  17.   IPFile := IObject as IPersistFile;
  18.  
  19.   ISLink.SetPath(pChar(Target));
  20.   //ISLink.SetArguments(pChar(TargetArguments));
  21.   ISLink.SetWorkingDirectory(pChar(ExtractFilePath(Target)));
  22.  
  23.   { Get the desktop location }
  24.   SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, PIDL);
  25.   SHGetPathFromIDList(PIDL, InFolder);
  26.   LinkName := InFolder + PathDelim + ShortcutName;
  27.  
  28.   { Create the link }
  29.   IPFile.Save(PWChar(LinkName), false);
  30. end;

I changed the code, it creates a folder that points to another, is not a folder shortcut but it works also!
Title: Re: Windows Folder Shortcuts
Post by: J-G on January 09, 2018, 11:33:25 pm
Lainz - sometimes I need the information knocking into my head from both sides!!

I'd used the folder name for both 1st and 2nd arguments  %)  -  your second reference to the Explorer Icon eventually got it into my thick skull.

However - now it does create a shortcut with the Explorer Icon but doesn't take me to the folder - always to [My Documents]. As it happens the folder doesn't contain spaces but adding the double quotes creates the target :-
   %WinDir%\explorer.exe "O:\FB\Music/"  (taken from the 'Properties')

That is the correct folder (for my tests) but is ignored by the shortcut - with or without the double quotes.

Using an existing 'Folder' shortcut, copying and then changing the target to O:\FB\Music  works - as you would expect - so there must be some other parameter that a 'Folder' shortcut has.

The folder shortcut I modified didn't have anything in 'Start in' but taking out the %WinDir% had no effect.

I've tried your latest modified code (received whist I was typing) but that returns me to the 'Document' icon and a request to select a program to open it with. :(
Title: Re: Windows Folder Shortcuts
Post by: lainz on January 09, 2018, 11:41:27 pm
Maybe things changed on Windows 10, do you have Windows 7?

According to this:
http://www.geoffchappell.com/studies/windows/shell/explorer/cmdline.htm

You need to specify /root,folderpath

Something like:
Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', '/root,"C:\Program Files"', 'PF folder');
Title: Re: Windows Folder Shortcuts
Post by: J-G on January 09, 2018, 11:49:05 pm
Maybe things changed on Windows 10, do you have Windows 7?
Yes.

I noticed a potential error in your code at line 26 - it was missing   +'.lnk'   but adding that didn't have any effect.
Title: Re: Windows Folder Shortcuts
Post by: lainz on January 09, 2018, 11:51:14 pm
Maybe things changed on Windows 10, do you have Windows 7?
Yes.

I noticed a potential error in your code at line 26 - it was missing   +'.lnk'   but adding that didn't have any effect.

I removed that since the thing is created is a folder not a shortcut, so that extension is pointless in the modified version.

Try this with the original code, try this exactly don't change it:
Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', '/root,"C:\Program Files"', 'PF folder');

Or
Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', '/root,%PROGRAMFILES%', 'PF folder');
Title: Re: Windows Folder Shortcuts
Post by: molly on January 10, 2018, 02:50:58 am
Try to surround the OLE function calls with OLECheck, e.g.

Code: [Select]
    OleCheck(SetPath(PChar(Target)));
    OleCheck(SetDescription(PChar(Description)));
   ...
  OleCheck(IPFile.Save(PWideChar(LinkName), false));
For me it failed on the last call because i forgot to declare it as a widestring  :-[

Although perhaps not very informative, it at least shows where things fail and a general direction as of why.

fwiw:
Target: D:\Applications\JG\Reader
linkname: Hoopla.lnk
Description: This is a description

creates a folder icon for me on the desktop. Not explicitly invoking explorer. If explorer is mandatory then i can check that for you as well.

PS: i've used CLSID_ShellLink instead of CLSID_FolderShortcut (as was used in latest code from lainz)

edit: got my ID's the wrong way around.
Title: Re: Windows Folder Shortcuts
Post by: molly on January 10, 2018, 03:57:33 am
Full example (FPC):

Code: Pascal  [Select][+][-]
  1. program Desktop_Shortcut;
  2.  
  3. {$MODE OBJFPC}{$H+}
  4.  
  5. uses
  6.   ShlObj, ActiveX, ComObj,  windows, Strings, SysUtils;
  7.  
  8.  
  9. procedure CreateDesktopShortCutEx(sTarget: string; sLink: string; sDescription: string = ''; sTargetArgs: string = ''; sStartInDir : string = '');
  10. var
  11.   IObject          : IUnknown;
  12.   ISLink           : IShellLink;
  13.   IPFile           : IPersistFile;
  14.   ShortCutLocation : array[0..MAX_PATH] of Char;
  15.   LinkName         : WideString;
  16. begin
  17.   IObject := CreateComObject(CLSID_ShellLink) ;
  18.   ISLink := IObject as IShellLink;
  19.   IPFile := IObject as IPersistFile;
  20.  
  21.   with ISLink do
  22.   begin
  23.     OleCheck(SetPath(PChar(sTarget)));
  24.     if (sTargetArgs  <> '') then OleCheck(SetArguments        (PChar(sTargetArgs)));
  25.     if (sStartInDir  <> '') then OleCheck(SetWorkingDirectory (PChar(sStartInDir)));
  26.     if (sDescription <> '') then OleCheck(SetDescription      (PChar(sDescription)));
  27.   end;
  28.  
  29.   if SHGetSpecialFolderPath(0, ShortCutLocation, CSIDL_DESKTOPDIRECTORY, false)
  30.   then WriteLn('special folder "DesktopDirectory" was found')
  31.   else WriteLn('ERROR: unable to locate special folder "DesktopDirectory"');
  32.  
  33.   LinkName := ShortCutLocation + PathDelim + sLink;
  34.   WriteLn('Filename of the link being saved = "', LinkName, '"');
  35.   OleCheck(IPFile.Save(PWideChar(LinkName), false));
  36. end;
  37.  
  38.  
  39. const
  40.   FolderToOpen = 'Drive:\Some Directory\Some Subdirectory\Another Subdirectory';
  41.   StartFolder  = 'Drive:\Some Directory';
  42. begin
  43.   // Example 1: create a folder icon link
  44.   CreateDesktopShortCutEx(FolderToOpen, 'My Folder Icon.lnk', 'This is a description');
  45.  
  46.   // Example 2: create an explorer link to open a folder, also adds "Start in" property
  47.   CreateDesktopShortCutEx('%SystemRoot%\Explorer.exe', 'My Explorer Icon.lnk', 'This is also a description', FolderToOpen, StartFolder);
  48.  
  49.   // Example 3: create an explorer link to open a folder, in expanded view
  50.   CreateDesktopShortCutEx('%SystemRoot%\Explorer.exe', 'My Expanded Explorer Icon.lnk', 'This is again a description', '/e,'+ FolderToOpen, StartFolder);
  51. end.
  52.  
Title: Re: Windows Folder Shortcuts
Post by: J-G on January 10, 2018, 04:05:48 am
Try this with the original code, try this exactly don't change it:
Code: Pascal  [Select][+][-]
  1. CreateDesktopShortCut('%WINDIR%\explorer.exe', '/root,"C:\Program Files"', 'PF folder');
FOUND IT !!

I tried all manner of combinations - your original 'Don't change anything' arguments worked. Changing args 2 and 3 to the folders I want - worked !!  Changing the 2nd arg to the variable which is the directory name  - failed !!  ?????

Use of double quotes seemed to be irrelevant and many adjustments to the form of the 2nd arg and the use of '/root' also made no difference.

Eventually - after one test I reviewed the string passed to the 'Target' in the shortcut and noticed that the one that worked had no trailing '/' (char 47 - solidus) whereas the one that always opened 'My Documents' did. Removal of this forward slash solved the issue.

I then did some further tests removing the '/root',  double quotes and finally adding a backslash instead.

Ultimately, just the change from solidus to backslash was all that was needed.

As I was about to post this, Molly's 1st post came in so I removed even the Explorer part - replacing that with the Folder variable (now including the backslash) and BINGO! A proper 'Folder Icon' Shortcut. - now past 3 a.m. so I can retire as a happy bunny  8-)

Thanks to both lainz & Molly.
Title: Re: Windows Folder Shortcuts
Post by: molly on January 10, 2018, 04:32:18 am
Glad that you were able to solve it J-G.

Thank you for the feedback and Sleep well !  :)

PS & FWIW: ExcludeTrailingPathDelimiter (https://www.freepascal.org/docs-html/rtl/sysutils/excludetrailingpathdelimiter.html) & SetDirSeparators (https://www.freepascal.org/docs-html/rtl/sysutils/setdirseparators.html)
TinyPortal © 2005-2018