Recent

Author Topic: Cmd annoyance and a solution  (Read 2204 times)

GetMem

  • Hero Member
  • *****
  • Posts: 4031
Cmd annoyance and a solution
« 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.
« Last Edit: February 09, 2023, 02:20:54 pm by GetMem »

dseligo

  • Hero Member
  • *****
  • Posts: 957
Re: Cmd annoyance and a solution
« Reply #1 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?

GetMem

  • Hero Member
  • *****
  • Posts: 4031
Re: Cmd annoyance and a solution
« Reply #2 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.

domasz

  • Full Member
  • ***
  • Posts: 206
Re: Cmd annoyance and a solution
« Reply #3 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.

dseligo

  • Hero Member
  • *****
  • Posts: 957
Re: Cmd annoyance and a solution
« Reply #4 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...

Roland57

  • Sr. Member
  • ****
  • Posts: 369
    • GitLab
Re: Cmd annoyance and a solution
« Reply #5 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%

GetMem

  • Hero Member
  • *****
  • Posts: 4031
Re: Cmd annoyance and a solution
« Reply #6 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.

GetMem

  • Hero Member
  • *****
  • Posts: 4031
Re: Cmd annoyance and a solution
« Reply #7 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.

PascalDragon

  • Hero Member
  • *****
  • Posts: 4970
  • Compiler Developer
Re: Cmd annoyance and a solution
« Reply #8 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.

GetMem

  • Hero Member
  • *****
  • Posts: 4031
Re: Cmd annoyance and a solution
« Reply #9 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.

jcmontherock

  • Full Member
  • ***
  • Posts: 171
Re: Cmd annoyance and a solution
« Reply #10 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:

PascalDragon

  • Hero Member
  • *****
  • Posts: 4970
  • Compiler Developer
Re: Cmd annoyance and a solution
« Reply #11 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. ;)

dseligo

  • Hero Member
  • *****
  • Posts: 957
Re: Cmd annoyance and a solution
« Reply #12 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/ 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\""

dseligo

  • Hero Member
  • *****
  • Posts: 957
Re: Cmd annoyance and a solution
« Reply #13 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).

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10584
  • FPC developer.
Re: Cmd annoyance and a solution
« Reply #14 on: February 10, 2023, 01:16:14 pm »

 

TinyPortal © 2005-2018