Recent

Author Topic: Create shortcuts on Desktop and start Windows with usernames in Latin characters  (Read 279 times)

Marc_rp

  • Guest
After several attempts, this code worked.

This application works well on Windows 7, 10, and 11. It also correctly recognizes Latin characters in PlayOnLinux (on Linux Mint).

To test the Latin characters: Create a Windows user named  Jôsé Márìõñ  and run the application under it.

Right-click the Desktop shortcut and you will see that the "Target" and "Start in" text boxes have been correctly filled.

I hope this helps.

Code: Pascal  [Select][+][-]
  1.  
  2. //
  3. // Develeped on Lazarus v 4.6  - May 2026
  4. //
  5. unit UDesktopShortcut;
  6.  
  7. {$mode objfpc}{$H+}
  8.  
  9. interface
  10.  
  11. uses
  12.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  13.  
  14.   // User's start and desktop directories for shortcuts.
  15.   Windows, windirs, Dos,
  16.  
  17.   // For Latin Characters
  18.   lazutf8,
  19.  
  20.   // to create shortcuts
  21.   {windows,} shlobj {for special folders}, ActiveX, ComObj,
  22.   LazFileUtils
  23.  
  24. ;
  25.  
  26.  
  27. type
  28.  
  29.   { TForm1 }
  30.  
  31.   TForm1 = class(TForm)
  32.     Button1: TButton;
  33.     chkShortcutDesktop: TCheckBox;
  34.     chkShortcutStartup: TCheckBox;
  35.     Label1: TLabel;
  36.     lblHasShortcutDesktop: TLabel;
  37.     lblHasShortcutStartUp: TLabel;
  38.     procedure Button1Click(Sender: TObject);
  39.     procedure FormCreate(Sender: TObject);
  40.     procedure FormShow(Sender: TObject);
  41.   private
  42.     procedure GetUserWindowsDirectories();
  43.     procedure CreateDesktopShortCut(Target, TargetArguments, ShortcutName: string);
  44.     procedure CreateStartupShortCut(Target, TargetArguments, ShortcutName: string);
  45.  
  46.   public
  47.  
  48.   end;
  49.  
  50. var
  51.   Form1: TForm1;
  52.   UserDesktop: rawbytestring;     //  I retrieve the User's Desktop folder.
  53.   UserStartUp: rawbytestring;     //  I retrieve the Windows User's Startup folder
  54.   ShortcutInDesktop: string;      //  shortcut file on the Desktop
  55.   ShortcutInStartUp: string;      //  shortcut file in Windows Startup
  56.  
  57. implementation
  58.  
  59. {$R *.lfm}
  60.  
  61. { TForm1 }
  62.  
  63. procedure TForm1.Button1Click(Sender: TObject);
  64. var
  65.   ShortcutToBeCreated: RawByteString;
  66. begin
  67.   // I need to create the shortcut on the desktop.
  68.      if (chkShortcutDesktop.Checked = true) And (lblHasShortcutDesktop.Caption = 'lblHasShortcutDesktop=false') then
  69.       begin
  70.          if fileexists(PChar(ShortcutInDesktop)) then
  71.            begin
  72.               ShortcutInDesktop := AnsiToUtf8(ShortcutInDesktop);
  73.               FileSetAttr(ShortcutInDesktop,0);
  74.            end;
  75.          // Call the procedure create on the desktop.
  76.         // Showmessage ('I'll create the shortcut on the Desktop');
  77.         ShortcutToBeCreated:= AnsiToUtf8(Application.ExeName);
  78.         CreateDesktopShortCut(Pchar(ShortcutToBeCreated), '', 'DesktopShortcut.lnk');
  79.        end;
  80.  
  81.    // I need to delete the shortcut on the desktop, BUT I have to check if it exists and FIRST it's better to filesetatrrib
  82.     if (chkShortcutDesktop.Checked = false) And (lblHasShortcutDesktop.Caption = 'lblHasShortcutDesktop=true') then
  83.       begin
  84.         if fileexists(PChar(ShortcutInDesktop)) then
  85.           begin
  86.             ShortcutInDesktop := AnsiToUtf8(ShortcutInDesktop);
  87.             FileSetAttr(ShortcutInDesktop,0);
  88.             DeleteFile(PChar(UTF8ToWinCP(ShortcutInDesktop)))
  89.             // The command will only be the one above; the one below is just for testing.
  90.             // if DeleteFile(PChar(UTF8ToWinCP(ShortcutInDesktop)))
  91.             // then ShowMessage(Pchar(ShortcutInDesktop)+' = deleted')
  92.             // else ShowMessage(Pchar(ShortcutInDesktop)+' = did not delete, error = '+
  93.             // IntToStr(GetLastError));
  94.           end;
  95.       end;
  96.  
  97.    // I need to create the shortcut in the Start menu.
  98.        if (chkShortcutStartup.Checked = true) And (lblHasShortcutStartUp.Caption = 'lblHasShortcutStartUp=false') then
  99.          begin
  100.            if fileexists(PChar(ShortcutInStartUp)) then
  101.              begin
  102.                 ShortcutInStartUp := AnsiToUtf8(ShortcutInStartUp);
  103.                 FileSetAttr(ShortcutInStartUp,0);
  104.              end;
  105.           // Call the Create shortcut in the Start menu
  106.           // Showmessage ('I'll create the shortcut.');
  107.           ShortcutToBeCreated:= AnsiToUtf8(Application.ExeName);
  108.           CreateStartupShortCut(Pchar(ShortcutToBeCreated), '', 'DesktopShortcut.lnk');
  109.          end;
  110.  
  111.   // I need to delete the shortcut on the startmenu, BUT I have to check if it exists and FIRST it's better to filesetatrrib
  112.      if (chkShortcutStartup.Checked = false) And (lblHasShortcutStartUp.Caption = 'lblHasShortcutStartUp=true') then
  113.       begin
  114.         if fileexists(PChar(ShortcutInStartUp)) then
  115.           begin
  116.             ShortcutInStartUp := AnsiToUtf8(ShortcutInStartUp);
  117.             FileSetAttr(ShortcutInStartUp,0);
  118.             DeleteFile(PChar(UTF8ToWinCP(ShortcutInStartUp)))
  119.            // The command will only be the one above; the one below is just for testing.
  120.            // if DeleteFile(PChar(UTF8ToWinCP(ShortcutInStartUp)))
  121.            // then ShowMessage(Pchar(ShortcutInStartUp)+' =  deleted')
  122.            // else ShowMessage(Pchar(ShortcutInStartUp)+' = did not delete, error = '+
  123.            // IntToStr(GetLastError));
  124.           end;
  125.       end;
  126.  
  127.   Close;
  128.  
  129. end;
  130.  
  131. procedure TForm1.FormCreate(Sender: TObject);
  132. begin
  133.   chkShortcutDesktop.Caption := 'Desktop Shortcut';
  134.   chkShortcutStartup.Caption := 'Start Windows Shortcut';
  135.   lblHasShortcutDesktop.Caption := 'lblHasShortcutDesktop=false';
  136.   lblHasShortcutStartUp.Caption := 'lblHasShortcutStartUp=false';
  137.   // lblHasShortcutDesktop.Visible := false;
  138.   // lblHasShortcutStartUp.Visible := false;
  139.   GetUserWindowsDirectories;
  140.   //
  141.   Label1.Caption := 'This application works well on Windows 7, 10 and 11.' +#13+#10+
  142.                     'It also correctly recognizes Latin characters in PlayOnLinux (on Linux Mint).' +#13+#10+ #13+#10+
  143.                     'To test the Latin Characters: Create a "Windows User" named:  Jôsé Márìõñ  and runs app in there' +#13+#10+ #13+#10+
  144.                     'Right-click the Desktop shortcut and you will see that the text boxes: Target and Start in, have been filled in correctly.' +#13+#10+ #13+#10+
  145.                     'checkbox checked (true) = Create Shortcuts' +#13+#10+
  146.                     'unchecked = Delete Shortcuts';
  147.  
  148.   // TO SEE THE SHORTCUTS:
  149.   // On Windows Execute box type:
  150.   // shell:startup  or  shell:desktop
  151.  
  152. end;
  153.  
  154. procedure TForm1.FormShow(Sender: TObject);
  155. begin
  156.   // GET THE SHORTCUTS HERE
  157.   ShortcutInDesktop := UserDesktop + 'DesktopShortcut.lnk';
  158.   ShortcutInDesktop := AnsiToUtf8(ShortcutInDesktop);
  159.   // showmessage ('PChar(ShortcutInDesktop)   ' + PChar(ShortcutInDesktop));
  160.   if fileexists(PChar(ShortcutInDesktop)) then
  161.   begin
  162.     // showmessage ('There is a shortcut in the Desktop ' + Pchar(ShortcutInDesktop));
  163.     chkShortcutDesktop.Checked := True;
  164.     lblHasShortcutDesktop.Caption := 'lblHasShortcutDesktop=true';
  165.   end;
  166.   ShortcutInStartUp := UserStartUp + 'DesktopShortcut.lnk';
  167.   ShortcutInStartUp := AnsiToUtf8(ShortcutInStartUp);
  168.   // showmessage ('PChar(ShortcutInStartUp)   ' + PChar(ShortcutInStartUp));
  169.   if fileexists(PChar(ShortcutInStartUp)) then
  170.   begin
  171.     // showmessage ('There is a shortcut in the start menu.' + Pchar(ShortcutInStartUp));
  172.     chkShortcutStartup.Checked := True;
  173.     lblHasShortcutStartUp.Caption := 'lblHasShortcutStartUp=true';
  174.   end;
  175. end;
  176.  
  177. // The call is in the Form Create
  178. procedure TForm1.GetUserWindowsDirectories();
  179. begin
  180.   UserDesktop := AnsiToUtf8(GetWindowsSpecialDir(CSIDL_DESKTOPDIRECTORY));
  181.   if RightStr(UserDesktop, 1) <> '\' then UserDesktop := UserDesktop + '\';
  182.   //ShowMessage ('UserDesktop  =  ' + UserDesktop);
  183.   UserStartUp := AnsiToUtf8(GetWindowsSpecialDir(CSIDL_STARTUP));
  184.   if RightStr(UserStartUp, 1) <> '\' then UserStartUp := UserStartUp + '\';
  185.   //ShowMessage ('Start User  ' + UserStartUp + #13 + #13 + 'This is from the Windows 7 Start Menu. =  C:\Users\Marck-7\AppData\Roaming\Microsoft\Windows\Start Menu\Programs');
  186.   //
  187.   // On Windows Execute box type:
  188.   // shell:startup  or  shell:desktop
  189.  
  190. end;
  191.  
  192. // from lazarus wiki
  193. procedure TForm1.CreateDesktopShortCut(Target, TargetArguments,
  194.   ShortcutName: string);
  195. // usage:   CreateShortcut('c:\windows\notepad.exe','c:\MyNotePad.lnk','This is Notepad','');
  196. var
  197.   IObject: IUnknown;
  198.   ISLink: IShellLink;
  199.   IPFile: IPersistFile;
  200.   PIDL: PItemIDList;
  201.   InFolder: array[0..MAX_PATH] of char;
  202.   TargetName: string;
  203.   LinkName: widestring;
  204. begin
  205.   { Creates an instance of IShellLink }
  206.   IObject := CreateComObject(CLSID_ShellLink);
  207.   ISLink := IObject as IShellLink;
  208.   IPFile := IObject as IPersistFile;
  209.   //
  210.   // ***** After MANY attempts, this one WORKED.
  211.   // ***** HERE MUST BE ---> UTF8ToWinCP  *****
  212.   //
  213.   ISLink.SetPath(PChar(UTF8ToWinCP(Target)));
  214.   ISLink.SetArguments(PChar(UTF8ToWinCP(TargetArguments)));
  215.   ISLink.SetWorkingDirectory(PChar(ExtractFilePath(UTF8ToWinCP(Target))));
  216.  
  217.   { Get the desktop location }
  218.   SHGetSpecialFolderLocation(0, CSIDL_DESKTOPDIRECTORY, PIDL);
  219.   SHGetPathFromIDList(PIDL, InFolder);
  220.   // This one worked on shortcut 1.
  221.   LinkName := WinCPToUTF8(InFolder) + PathDelim + ShortcutName; // + '.lnk';
  222.  
  223.   // **** Below are just a few tests.
  224.   //
  225.   // showmessage ('InFolder ' + InFolder);
  226.   // showmessage ('InFolder AnsiToUtf8(Pchar( ---- ' + AnsiToUtf8(Pchar(InFolder)));
  227.   // showmessage ('WinCPToUTF8(Pchar(InFolder --- ' + WinCPToUTF8(Pchar(InFolder)));
  228.   // showmessage ('UTF8toWinCP(Pchar(InFolder --- ' + UTF8toWinCP(Pchar(InFolder)));
  229.   //
  230.   //  showmessage ('PathDelim ' + PathDelim);
  231.   //  showmessage ('ShortcutName ' + ShortcutName);
  232.   //  showmessage('1 Pchar(UserDesktop) ----- ' + Pchar(UserDesktop));
  233.   //  showmessage('2 AnsiToUtf8(Pchar(UserDesktop)) -----  ' + AnsiToUtf8(Pchar(UserDesktop)));
  234.   //  showmessage('3 UTF8ToWinCP(AnsiToUtf8(Pchar(UserDesktop)) ----------  ' + UTF8ToWinCP(AnsiToUtf8(Pchar(UserDesktop))));
  235.   //  WinCPToUTF8
  236.   //showmessage (PChar(linkName));
  237.   {LinkName := InFolder + AnsiToUtf8(PathDelim) + ShortcutName; // + '.lnk';
  238.   LinkName := InFolder + UTF8ToWinCP(PathDelim) + ShortcutName; // + '.lnk';
  239.   linkName := AnsiToUtf8(linkName);
  240.   linkName := UTF8ToWinCP(linkName);
  241.   showmessage (PChar(linkName));
  242.   Here it only shows C.}
  243.  
  244.   { Create the link }
  245.   IPFile.Save(PWChar(LinkName), False);
  246. end;
  247.  
  248. procedure TForm1.CreateStartupShortCut(Target, TargetArguments,
  249.   ShortcutName: string);
  250. // usage:  CreateShortcut('c:\windows\notepad.exe','c:\MyNotePad.lnk','This is Notepad','');
  251. var
  252.   IObject: IUnknown;
  253.   ISLink: IShellLink;
  254.   IPFile: IPersistFile;
  255.   PIDL: PItemIDList;
  256.   InFolder: array[0..MAX_PATH] of char;
  257.   TargetName: string;
  258.   LinkName: widestring;
  259. begin
  260.   { Creates an instance of IShellLink }
  261.   IObject := CreateComObject(CLSID_ShellLink);
  262.   ISLink := IObject as IShellLink;
  263.   IPFile := IObject as IPersistFile;
  264.   //
  265.   // ***** After MANY attempts, this one WORKED.
  266.   // ***** HERE MUST BE ---> UTF8ToWinCP  *****
  267.   //
  268.   ISLink.SetPath(PChar(UTF8ToWinCP(Target)));
  269.   ISLink.SetArguments(PChar(UTF8ToWinCP(TargetArguments)));
  270.   ISLink.SetWorkingDirectory(PChar(ExtractFilePath(UTF8ToWinCP(Target))));
  271.  
  272.   { Get the desktop location }
  273.   SHGetSpecialFolderLocation(0, CSIDL_STARTUP, PIDL);
  274.   SHGetPathFromIDList(PIDL, InFolder);
  275.   // This one worked on shortcut 1.
  276.   LinkName := WinCPToUTF8(InFolder) + PathDelim + ShortcutName; // + '.lnk';
  277.   { Create the link }
  278.   IPFile.Save(PWChar(LinkName), False);
  279. end;
  280.  
  281.  
  282. end.
  283.  
  284.  
  285.  
« Last Edit: May 28, 2026, 02:50:56 pm by marcov »

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 12901
  • FPC developer.
(but what if I run Windows 8? :))

Seriously, I use similar code, but enable the win32 UTF8 manifest in the project's application tab. Then you don't need all the conversions.

In upcoming (but not yet in RC1) 3.2.4 and 3.3.1 you can also do this easily on the commandline (without lazarus) by adding winmanutf8lfn  to the USES line of the main program, at the expense of Windows 10 minimal requirements.


Marc_rp

  • Guest
Hi there, I don't know, as I don't use and don't have a Windows 8 license.
I enabled the win32 UTF8 manifest, but on some machines, for some reason, the problem persisted.
The conversions were necessary because, if the shortcut in the "Start in" text box is not filled in correctly and the application does not contain the complete path (app.path) of the database (dbf), only: DftPath=: 'mydatabase.dbf', the program will not find the dbf file.
Since it took me a while to solve this problem, I decided to publish the post to help others who are facing the same problem. But please feel free to delete it.
This is good news: in future versions, only one command line will be needed.
Thank you for your comment.
Sincerely.

 

TinyPortal © 2005-2018