Lazarus

Programming => Operating Systems => Windows => Topic started by: balazsszekely on February 09, 2023, 11:58:35 am

Title: Cmd annoyance and a solution
Post by: balazsszekely on February 09, 2023, 11:58:35 am
When I open cmd(command promt) it's defaulting to the user(or system32) folder, which is fine, but then I have to use the cd command to go to a particular folder. Typing a folder path or even copy pasting takes time and it's annoying. Some linux distroes solved this issue by adding the "Open in terminal" item to the context menu, which opens the current folder from the file explorer. Very nicely done penguin!
The attached project, a shell extension dll, will do the same for windows:
  - download, extract attachment
  - build the dll, make sure Lazarus/FPC bitness is the same as your windows bitness
  - run cmd "As admin", cd to the dll folder, then type: "Regsvr32 OpenFolderInCmdShell.dll" without quotes
  - if the registration was successfull, go to a random folder with file explorer, right click the empty space or a subfolder. The following menu item should be visible in the context menu: "Open folder in cmd"(see attached image)

Tested with Lazarus trunk/FPC 3.2.2(64 bit) on windows 10. To unregister the dll, type: "Regsvr32 /u OpenFolderInCmdShell.dll" in the command prompt, then press enter.
Code: Pascal  [Select][+][-]
  1. unit OpenFolderInCmd;
  2.  
  3. interface
  4.  
  5. uses
  6.   Windows, ActiveX, ComObj, ShlObj, ShellApi;
  7.  
  8. type
  9.   TOpenFolderInCmd = class(TComObject, IUnknown, IContextMenu, IShellExtInit)
  10.   private
  11.     FPath: WideString;
  12.     function ShellGetPathFromIdListW(APItemIdList: PItemIdList): WideString;
  13.   protected
  14.     function QueryContextMenu(Menu: HMENU; indexMenu, idCmdFirst, idCmdLast,
  15.       uFlags: UINT): HResult; stdcall;
  16.     function InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
  17.     function GetCommandString(idCmd: UINT_PTR; uFlags: UINT; pwReserved: PUINT;
  18.       pszName: LPSTR; cchMax: UINT): HResult; stdcall;
  19.     function IShellExtInit.Initialize = InitShellExt;
  20.     function InitShellExt (pidlFolder: PItemIDList; lpdobj: IDataObject;
  21.       hKeyProgID: HKEY): HResult; stdcall;
  22.   end;
  23.  
  24.   TOpenFolderInCmdFactory = class(TComObjectFactory)
  25.   public
  26.     procedure UpdateRegistry (Register: Boolean); override;
  27.   end;
  28.  
  29. const
  30.   cName = 'OpenFolderInCmd';
  31.   cMenuCaption = 'Open folder in cmd';
  32.   cMenuHelp = 'Open current folder in command prompt';
  33.   cGUID: TGUID = '{55639E10-A032-4E22-B039-882E3BCCA67C}';
  34.  
  35. implementation
  36.  
  37. uses
  38.   ComServ, SysUtils, Registry;
  39.  
  40. function TOpenFolderInCmd.InitShellExt(pidlFolder: PItemIDList;
  41.   lpdobj: IDataObject; hKeyProgID: HKEY): HResult; stdcall;
  42. var
  43.   medium: TStgMedium;
  44.   fe: TFormatEtc;
  45. begin
  46.   Result := NOERROR;
  47.   if lpdobj <> nil then
  48.   begin
  49.     with fe do
  50.     begin
  51.       cfFormat := CF_HDROP;
  52.       ptd := nil;
  53.       dwAspect := DVASPECT_CONTENT;
  54.       lindex := -1;
  55.       tymed := TYMED_HGLOBAL;
  56.     end;
  57.     Result := lpdobj.GetData(fe, medium);
  58.     if not Failed (Result) then
  59.     begin
  60.     if DragQueryFileW(medium.hGlobal, $FFFFFFFF, nil, 0) = 1 then
  61.       begin
  62.         SetLength(FPath, 1000);
  63.         DragQueryFileW(medium.hGlobal, 0, PWideChar(FPath), 1000);
  64.         FPath := PWideChar(FPath);
  65.         if not DirectoryExists(FPath) then
  66.           Result := E_FAIL
  67.       end
  68.       else
  69.         Result := E_FAIL;
  70.     end;
  71.     ReleaseStgMedium(medium);
  72.   end
  73.   else
  74.     FPath := ShellGetPathFromIdListW(pidlFolder);
  75. end;
  76.  
  77. function TOpenFolderInCmd.QueryContextMenu(Menu: HMENU;
  78.   indexMenu, idCmdFirst, idCmdLast, uFlags: UINT): HResult; stdcall;
  79. begin
  80.   InsertMenu(Menu, indexMenu, MF_STRING or MF_BYPOSITION, idCmdFirst, cMenuCaption);
  81.   Result := 1;
  82. end;
  83.  
  84. function TOpenFolderInCmd.InvokeCommand(var lpici: TCMInvokeCommandInfo): HResult; stdcall;
  85. var
  86.   Drv: WideString;
  87.   Cmd: WideString;
  88. begin
  89.   Result := NOERROR;
  90.  
  91.   if HiWord(LongInt(lpici.lpVerb)) <> 0 then
  92.   begin
  93.     Result := E_FAIL;
  94.     Exit;
  95.   end;
  96.  
  97.   if LoWord(LongInt(lpici.lpVerb)) > 0 then
  98.   begin
  99.     Result := E_INVALIDARG;
  100.     Exit;
  101.   end;
  102.  
  103.   if LoWord(LongInt(lpici.lpVerb)) = 0 then
  104.   begin
  105.     FPath := IncludeTrailingBackslash(FPath);
  106.     Drv := ExtractFileDrive(FPath);
  107.     FPath := '"' + FPath + '"';
  108.     Cmd := '/K ' + Drv + ' && CD ' + FPath;
  109.     ShellExecuteW(0, 'runas', 'cmd.exe', PWideChar(Cmd), PWideChar(FPath), SW_SHOWNORMAL);
  110.    end;
  111. end;
  112.  
  113. function TOpenFolderInCmd.GetCommandString(idCmd: UINT_PTR; uFlags: UINT;
  114.   pwReserved: PUINT; pszName: LPSTR; cchMax: UINT): HResult; stdcall;
  115. begin
  116.   if (idCmd = 0) and (uFlags = GCS_HELPTEXT) then
  117.   begin
  118.     StrLCopy(pszName, cMenuHelp, cchMax);
  119.     Result := NOERROR;
  120.   end
  121.   else
  122.     Result := E_INVALIDARG;
  123. end;
  124.  
  125. function TOpenFolderInCmd.ShellGetPathFromIdListW(APItemIdList: PItemIdList): WideString;
  126. var
  127.   sz: array[ 0..MAX_PATH ] of WideChar;
  128.   w: Word;
  129. begin
  130.   if (APItemIdList = nil) then
  131.   begin
  132.     w := 0;
  133.     APItemIdList := Pointer(@w);
  134.   end;
  135.  
  136.   if SHGetPathFromIdListW(APItemIdList, @sz[0]) then
  137.     SetString(Result, sz, Strlen(sz))
  138.   else
  139.     Result := '';
  140. end;
  141.  
  142. procedure TOpenFolderInCmdFactory.UpdateRegistry(Register: Boolean);
  143. var
  144.   Reg: TRegistry;
  145. begin
  146.   inherited UpdateRegistry (Register);
  147.  
  148.   Reg := TRegistry.Create;
  149.   Reg.RootKey := HKEY_CLASSES_ROOT;
  150.   try
  151.     if Register then
  152.     begin
  153.       if Reg.OpenKey('\*\ShellEx\ContextMenuHandlers\' + cName, True) then
  154.         Reg.WriteString('', GUIDToString(cGUID));
  155.       if Reg.OpenKey('\Directory\Background\ShellEx\ContextMenuHandlers\' + cName, True) then
  156.         Reg.WriteString('', GUIDToString(cGUID));
  157.       if Reg.OpenKey('\Directory\shellex\ContextMenuHandlers\' + cName, True) then
  158.         Reg.WriteString('', GUIDToString(cGUID));
  159.       if Reg.OpenKey('\Drive\shellex\ContextMenuHandlers\' + cName, True) then
  160.         Reg.WriteString('', GUIDToString(cGUID));
  161.     end
  162.     else
  163.     begin
  164.       if Reg.OpenKey('\*\ShellEx\ContextMenuHandlers\' + cName, False) then
  165.         Reg.DeleteKey ('\*\ShellEx\ContextMenuHandlers\' + cName);
  166.       if Reg.OpenKey('\Directory\Background\shellex\ContextMenuHandlers\' + cName, False) then
  167.         Reg.DeleteKey ('\Directory\Background\shellex\ContextMenuHandlers\' + cName);
  168.       if Reg.OpenKey('\Directory\shellex\ContextMenuHandlers\' + cName, False) then
  169.         Reg.DeleteKey ('\Directory\shellex\ContextMenuHandlers\' + cName);
  170.       if Reg.OpenKey('\Drive\shellex\ContextMenuHandlers\' + cName, False) then
  171.         Reg.DeleteKey ('\Drive\shellex\ContextMenuHandlers\' + cName);
  172.     end;
  173.   finally
  174.     Reg.CloseKey;
  175.     Reg.Free;
  176.   end;
  177. end;
  178.  
  179. initialization
  180.   TOpenFolderInCmdFactory.Create (ComServer, TOpenFolderInCmd, cGUID, cName,
  181.                                   cMenuCaption, ciMultiInstance, tmApartment);
  182.  
  183. end.
Title: Re: Cmd annoyance and a solution
Post by: dseligo on February 09, 2023, 12:18:49 pm
Nice example, I have to try it.

But: don't you already have 'Open in terminal' when you right click (or Shift + Right click) in empty space in Windows explorer?
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 09, 2023, 01:28:16 pm
@dseligo
Quote
But: don't you already have 'Open in terminal' when you right click (or Shift + Right click) in empty space in Windows explorer?
I don't have any command prompt related item(s) in the context menu, only "Open PowerShell window here" (Shift + Right click), but PowerShell it's a different beast.
Title: Re: Cmd annoyance and a solution
Post by: domasz on February 09, 2023, 02:12:21 pm
Nice example, I have to try it.

But: don't you already have 'Open in terminal' when you right click (or Shift + Right click) in empty space in Windows explorer?

You must have installed something. Win 7 and Win 10 do not come with such an option out of the box.
Title: Re: Cmd annoyance and a solution
Post by: dseligo on February 09, 2023, 02:33:50 pm
@dseligo
Quote
But: don't you already have 'Open in terminal' when you right click (or Shift + Right click) in empty space in Windows explorer?
I don't have any command prompt related item(s) in the context menu, only "Open PowerShell window here" (Shift + Right click), but PowerShell it's a different beast.

I think you can change somewhere (maybe in registry) to open cmd instead of Powershell (I have both on right click in one W10 installation).
In W11 this got improved. It is very customizable, you can have more sessions in one tabbed window, more profiles in context menu...
Title: Re: Cmd annoyance and a solution
Post by: Roland57 on February 09, 2023, 02:34:45 pm
@GetMem

Thanks, I will try it.

Until now, I paste in each new project folder a file named prompt.cmd with this content:

Code: Text  [Select][+][-]
  1. @%comspec%
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 09, 2023, 02:39:12 pm
Windows 11 does have an "Open in Terminal" item in the context menu(just checked), it will open a PowerShell window, which is somewhat confusing. Why do they refer to PowerShell as terminal? Anyways the shell extension works with win11 too.
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 09, 2023, 02:49:44 pm
@dseligo
Quote
I think you can change somewhere (maybe in registry) to open cmd instead of Powershell (I have both on right click in one W10 installation).
If you find that option please let me know. Most likely though, some other application registered that menu item under win7, win10.
I'm also curios if it's possible to change the "Open in terminal" behaviour under win11. I would like to open cmd instead of PowerShell.
 

@Roland57
Quote
Until now, I paste in each new project folder a file named prompt.cmd with this content:
@%comspec%
Nice trick and it works fine. It seams that I'm not the only one bothered by the cmd limitation.
Title: Re: Cmd annoyance and a solution
Post by: PascalDragon on February 09, 2023, 09:45:55 pm
Navigate to the folder you want in Windows Explorer, go into the address bar, enter “cmd” and press enter. That will open the Command Shell in the currently open directory.
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 09, 2023, 09:58:53 pm
Navigate to the folder you want in Windows Explorer, go into the address bar, enter “cmd” and press enter. That will open the Command Shell in the currently open directory.
This is really nice! I wasn't aware of this feature. Thanks!
I do prefer the shell integration a little bit more though.
Title: Re: Cmd annoyance and a solution
Post by: jcmontherock on February 09, 2023, 10:06:57 pm
Use a Windows shortcut:

%windir%\SysWOW64\cmd.exe /k MyDir:
or
%windir%\System32\cmd.exe /k MyDir:
Title: Re: Cmd annoyance and a solution
Post by: PascalDragon on February 09, 2023, 10:17:43 pm
Navigate to the folder you want in Windows Explorer, go into the address bar, enter “cmd” and press enter. That will open the Command Shell in the currently open directory.
This is really nice! I wasn't aware of this feature. Thanks!
I do prefer the shell integration a little bit more though.

I prefer the one with the address bar, cause it's faster: you can reach the address bar using Ctrl + L and cmd is entered rather quickly. ;)
Title: Re: Cmd annoyance and a solution
Post by: dseligo on February 10, 2023, 01:06:12 pm
@dseligo
Quote
I think you can change somewhere (maybe in registry) to open cmd instead of Powershell (I have both on right click in one W10 installation).
If you find that option please let me know. Most likely though, some other application registered that menu item under win7, win10.
I'm also curios if it's possible to change the "Open in terminal" behaviour under win11. I would like to open cmd instead of PowerShell.

I don't remember how I changed it originally, but I just tried method #1 from https://www.winhelponline.com/blog/cmd-here-windows-10-context-menu-add/ (https://www.winhelponline.com/blog/cmd-here-windows-10-context-menu-add/) and it works.

Basically, this goes into registry:
Code: Text  [Select][+][-]
  1. Windows Registry Editor Version 5.00
  2.  
  3. [HKEY_CLASSES_ROOT\Directory\shell\cmdprompt]
  4. @="@shell32.dll,-8506"
  5. "NoWorkingDirectory"=""
  6.  
  7. [HKEY_CLASSES_ROOT\Directory\shell\cmdprompt\command]
  8. @="cmd.exe /s /k pushd \"%V\""
  9.  
  10. [HKEY_CLASSES_ROOT\Directory\Background\shell\cmdprompt]
  11. @="@shell32.dll,-8506"
  12. "NoWorkingDirectory"=""
  13.  
  14. [HKEY_CLASSES_ROOT\Directory\Background\shell\cmdprompt\command]
  15. @="cmd.exe /s /k pushd \"%V\""
  16.  
  17. [HKEY_CLASSES_ROOT\Drive\shell\cmdprompt]
  18. @="@shell32.dll,-8506"
  19. "NoWorkingDirectory"=""
  20.  
  21. [HKEY_CLASSES_ROOT\Drive\shell\cmdprompt\command]
  22. @="cmd.exe /s /k pushd \"%V\""
Title: Re: Cmd annoyance and a solution
Post by: dseligo on February 10, 2023, 01:12:41 pm
Navigate to the folder you want in Windows Explorer, go into the address bar, enter “cmd” and press enter. That will open the Command Shell in the currently open directory.

Nice. And you can also type name of the file in that directory and it's get open too.

P.S.: This also works with Double commander (Shift + F2), although it has dedicated key for opening terminal too (F9).
Title: Re: Cmd annoyance and a solution
Post by: marcov on February 10, 2023, 01:16:14 pm
For me it works, but I followed the procedure in this URL: https://www.linkedin.com/pulse/replace-powershell-command-prompt-windows-10-buddhi-sampath
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 10, 2023, 06:10:26 pm
@all
Thank you for the feedback. Apparently I was wrong, there are several good and much easier solution then the shell integration.  :)
Title: Re: Cmd annoyance and a solution
Post by: KodeZwerg on February 10, 2023, 09:12:18 pm
@getmem, i like your try best but its not what i need since it uses wrong settings for my usecase (like all other methods mentioned in here)
if allowed, i modify it to my needs and show you the "kodezwergs" needs :D
i might switch away from handler to a simple exe anyway, like i did in past but killed source  :-X

disadvantage and what i really do need is, that above methods just act within the file explorer while my approach would rely on context menu itself do either use folder or file as input, independent from file explorer.
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 11, 2023, 07:41:25 am
@KodeZwerg
Quote
if allowed, i modify it to my needs and show you the "kodezwergs" needs :D 
Allowed? Please modify it in any way you like.  :)
Title: Re: Cmd annoyance and a solution
Post by: dbannon on February 11, 2023, 07:59:26 am
When I open cmd(command promt) it's defaulting to the user(or system32) folder, .....

Do you mean from within the Lazarus IDE ?

https://wiki.freepascal.org/IDE_tricks#Opening_a_Terminal_Windows_from_the_IDE

Davo
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 11, 2023, 10:29:02 am
@dbannon
Quote
Do you mean from within the Lazarus IDE ?
No, from the file explorer, this issue has nothing to do with Lazarus, other then compiling the shell extension with it.

PS: Thanks for the link.
Title: Re: Cmd annoyance and a solution
Post by: KodeZwerg on February 14, 2023, 01:59:21 pm
Here is what I have done, build from scratch. Maybe someone likes.
Title: Re: Cmd annoyance and a solution
Post by: balazsszekely on February 14, 2023, 06:50:17 pm
Here is what I have done, build from scratch. Maybe someone likes.
Very nicely done! I like it. The install/uninstall feature is a a big bonus, plus creating the window/buttons with pure api is a nice touch too.
Title: Re: Cmd annoyance and a solution
Post by: KodeZwerg on February 14, 2023, 10:58:18 pm
I am glad you like it. Yeah I tried to get out a small file, but my original goal to have a 50kb I did not reached, used too many units :D
Anyway, for me it was more important to having this feature directly in context, always available and not just within file explorer :-*
That way I also found out a difference between Delphis Registry unit and Lazarus, on Delphi I do not need to delete subkey first, it just delete :-[
When I would spend more time to make it full api only, I guess my wanted 50kb I could reach :P
Title: Re: Cmd annoyance and a solution
Post by: lainz on February 14, 2023, 11:30:13 pm
On Windows 11 there's already a menu that opens the terminal. By default is configured as PowerShell but if you right click the PowerShell window title and go to settings, there's the 'settings' panel where you can choose CMD.
Title: Re: Cmd annoyance and a solution
Post by: BobDog on February 15, 2023, 11:49:56 am
@dseligo
Quote
But: don't you already have 'Open in terminal' when you right click (or Shift + Right click) in empty space in Windows explorer?
I don't have any command prompt related item(s) in the context menu, only "Open PowerShell window here" (Shift + Right click), but PowerShell it's a different beast.
You can open a window in powershell and type cmd.exe.
Title: Re: Cmd annoyance and a solution
Post by: KodeZwerg on February 15, 2023, 10:56:11 pm
If someone is interested, here is a ~65kb build of my above code (not compressed, 64bit build)
Title: Re: Cmd annoyance and a solution
Post by: Graham1 on February 23, 2023, 10:18:23 pm
Why not just use Winaero Tweaker: https://winaero.com/winaero-tweaker/ (https://winaero.com/winaero-tweaker/)?

It has the Command Here option and dozens of other tools to amend (fix) Windows.
Title: Re: Cmd annoyance and a solution
Post by: lazpas on April 16, 2023, 01:41:43 pm
Hi,@GetMem

After unregister,how to delete this com file?

Thanks.
TinyPortal © 2005-2018