UPD: Better Total Commander plugins compatibility

This commit is contained in:
Alexander Koblov 2019-08-10 08:51:14 +00:00
commit a51b586239
4 changed files with 134 additions and 142 deletions

View file

@ -129,7 +129,7 @@ uses
, uTotalCommander, FileUtil, Windows, ShlObj, uShlObjAdditional
, uWinNetFileSource, uVfsModule, uLng, uMyWindows, DCStrUtils
, uListGetPreviewBitmap, uThumbnailProvider, uDCReadSVG, uFileSourceUtil
, Dialogs, Clipbrd, uShowMsg
, Dialogs, Clipbrd, uShowMsg, uDebug, JwaDbt
{$ENDIF}
{$IFDEF UNIX}
, BaseUnix, fFileProperties, uJpegThumb
@ -374,6 +374,27 @@ var
ShellContextMenu : TShellContextMenu = nil;
{$IFDEF MSWINDOWS}
var
OldWProc: WNDPROC;
function MyWndProc(hWnd: HWND; uiMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
if (uiMsg = WM_SETTINGCHANGE) and (lParam <> 0) and (StrComp('Environment', {%H-}PAnsiChar(lParam)) = 0) then
begin
UpdateEnvironment;
DCDebug('WM_SETTINGCHANGE:Environment');
end;
if (uiMsg = WM_DEVICECHANGE) and (wParam = DBT_DEVNODES_CHANGED) and (lParam = 0) then
begin
Screen.UpdateMonitors; // Refresh monitor list
DCDebug('WM_DEVICECHANGE:DBT_DEVNODES_CHANGED');
end;
Result := CallWindowProc(OldWProc, hWnd, uiMsg, wParam, lParam);
end;
procedure ActivateHandler(Self, Sender: TObject);
var
I: Integer = 0;
@ -534,13 +555,16 @@ begin
// Disable application button on taskbar
with Widgetset do
SetWindowLong(AppHandle, GWL_EXSTYLE, GetWindowLong(AppHandle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
// Emulate Total Commander window
CreateTotalCommanderWindow(MainForm.Handle);
// Register network file source
RegisterVirtualFileSource(rsVfsNetwork, TWinNetFileSource);
if (Win32MajorVersion > 5) and IsUserAdmin then // if run under administrator
MainForm.Caption:= MainForm.Caption + ' - Administrator';
// Add main window message handler
{$PUSH}{$HINTS OFF}
OldWProc := WNDPROC(SetWindowLongPtr(Application.MainForm.Handle, GWL_WNDPROC, LONG_PTR(@MyWndProc)));
{$POP}
with frmMain do
begin
Handler.Code:= @MenuHandler;

View file

@ -1,7 +1,7 @@
{
Double Commander
-------------------------------------------------------------------------
Creates Total Commander fake window (some plugins don't work without it)
Total Commander integration functions
Copyright (C) 2009-2019 Alexander Koblov (alexx2000@mail.ru)
@ -30,8 +30,6 @@ unit uTotalCommander;
{$MODE DELPHI}
{.$DEFINE DEBUG}
interface
uses
@ -59,7 +57,6 @@ const
var
sTotalCommanderMainbarFilename: string = TCCONFIG_MAINBAR_NOTPRESENT;
procedure CreateTotalCommanderWindow({%H-}hWindow: HWND);
function ConvertTCStringToString(TCString: ansistring): string;
function ConvertStringToTCString(sString: string): ansistring;
function ReplaceDCEnvVars(const sText: string): string;
@ -75,11 +72,11 @@ implementation
uses
//Lazarus, Free-Pascal, etc.
Graphics, LCLVersion, Forms, JwaDbt, SysUtils, LCLProc, LazUTF8,
Graphics, LCLVersion, Forms, SysUtils, LCLProc, LazUTF8,
//DC
fOptionsMisc, uKASToolItemsExtended,
DCClassesUtf8, DCOSUtils, uDebug, DCStrUtils, uPixMapManager, uShowMsg,
DCClassesUtf8, DCOSUtils, DCStrUtils, uPixMapManager, uShowMsg,
uDCUtils, uLng, uGlobs, uGlobsPaths, DCConvertEncoding, uMyWindows;
type
@ -605,108 +602,10 @@ const
//cm_WorkWithDirectoryHotlist
var
wcFakeWndClass: TWndClassEx;
//hMainWindow,
{$IFDEF DEBUG}
hFakeWindow: HWND;
{$ENDIF}
TCIconSize: integer = 32;
TCNumberOfInstance: integer;
TCListOfCreatedTCIconFilename: TStringList;
procedure UpdateEnvironment;
var
dwSize: DWORD;
ASysPath: UnicodeString;
AUserPath: UnicodeString;
APath: UnicodeString = '';
begin
// System environment
if RegReadKey(HKEY_LOCAL_MACHINE, 'System\CurrentControlSet\Control\Session Manager\Environment', 'Path', ASysPath) then
begin
APath := ASysPath;
if (Length(APath) > 0) and (APath[Length(APath)] <> PathSeparator) then APath += PathSeparator;
end;
// User environment
if RegReadKey(HKEY_CURRENT_USER, 'Environment', 'Path', AUserPath) then
begin
APath := APath + AUserPath;
if (Length(APath) > 0) and (APath[Length(APath)] <> PathSeparator) then APath += PathSeparator;
end;
// Update path environment variable
if Length(APath) > 0 then
begin
SetLength(ASysPath, MaxSmallInt + 1);
dwSize:= ExpandEnvironmentStringsW(PWideChar(APath), PWideChar(ASysPath), MaxSmallInt);
if (dwSize = 0) or (dwSize > MaxSmallInt) then
ASysPath:= APath
else begin
SetLength(ASysPath, dwSize - 1);
end;
SetEnvironmentVariableW('Path', PWideChar(ASysPath));
end;
end;
{ WindowProc }
function WindowProc(hWnd: HWND; uiMsg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
begin
{
Resend message to DoubleCommander main window.
Disabled currently, because it may interfere with LCL, especially since the fake
TotalCmd window is also a main app window (WS_OVERLAPPEDWINDOW). May be enabled
in future if any plugins need this, but following messages should be skipped
because they are known to cause conflict:
- WM_ACTIVATEAPP
Confuses LCL about which main form (window) is currently active and
it stops calling OnExit events for controls (see TWinControl.WMKillFocus).
}
//SendMessage(hMainWindow, uiMsg, wParam, lParam);
if (uiMsg = WM_SETTINGCHANGE) and (lParam <> 0) and (StrComp('Environment', {%H-}PAnsiChar(lParam)) = 0) then
begin
UpdateEnvironment;
DCDebug('WM_SETTINGCHANGE:Environment');
end;
{$IF (lcl_fullversion >= 1020000)}
if (uiMsg = WM_DEVICECHANGE) and (wParam = DBT_DEVNODES_CHANGED) and (lParam = 0) then
begin
Screen.UpdateMonitors; // Refresh monitor list
DCDebug('WM_DEVICECHANGE:DBT_DEVNODES_CHANGED');
end;
{$ENDIF}
{$IFDEF DEBUG}
WriteLn(uiMsg);
{$ENDIF}
Result := DefWindowProc(hWnd, uiMsg, wParam, lParam);
end;
{ CreateTotalCommanderWindow }
procedure CreateTotalCommanderWindow(hWindow: HWND);
begin
// hMainWindow:= hWindow;
FillByte(wcFakeWndClass, SizeOf(wcFakeWndClass), 0);
wcFakeWndClass.cbSize := SizeOf(wcFakeWndClass);
wcFakeWndClass.Style := CS_HREDRAW or CS_VREDRAW;
wcFakeWndClass.lpfnWndProc := @WindowProc;
wcFakeWndClass.hInstance := hInstance;
wcFakeWndClass.hbrBackground := Color_BtnFace + 12;
wcFakeWndClass.lpszMenuName := nil;
wcFakeWndClass.lpszClassName := 'TTOTAL_CMD';
RegisterClassEx(wcFakeWndClass);
// Create Total Commander fake window
{$IFDEF DEBUG}
hFakeWindow :=
{$ENDIF}
CreateWindowEx(0, 'TTOTAL_CMD', 'Double Commander', WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, 0, 0, hInstance, nil);
{$IFDEF DEBUG}
// Show window (for debugging only)
ShowWindow(hFakeWindow, SW_SHOW);
{$ENDIF}
end;
// Test have been made with string from site http://stackoverflow.com/questions/478201/how-to-test-an-application-for-correct-encoding-e-g-utf-8
// Note: If you ever "think" to change or modify this routine, make sure to test the following:
// 1o) Make a directory with utf-8 special characters, a path like this: "Card-♠♣♥♦"

View file

@ -30,38 +30,10 @@ implementation
uses
Classes, SysUtils, Win32Int, WSLCLClasses, Forms, Windows, Win32Proc,
Controls, WSForms, Win32WSForms, LCLType, fMain;
Controls, LCLType, fMain, Win32WSControls, uImport;
const
ClassNameW: UnicodeString = 'DClass'#0;
type
{ TWin32WSCustomFormEx }
TWin32WSCustomFormEx = class(TWin32WSCustomForm)
published
class function CreateHandle(const AWinControl: TWinControl;
const AParams: TCreateParams): HWND; override;
end;
{ TWin32WSCustomFormEx }
class function TWin32WSCustomFormEx.CreateHandle(const AWinControl: TWinControl;
const AParams: TCreateParams): HWND;
var
AClass: String;
AMainForm: Boolean;
begin
AMainForm := AWinControl is TfrmMain;
if AMainForm then
begin
AClass := ClsName;
ClsName := String(ClassNameW);
end;
Result := inherited CreateHandle(AWinControl, AParams);
if AMainForm then ClsName := AClass;
end;
ClassNameW: PWideChar = 'TTOTAL_CMD'; // for compatibility with plugins
function WinRegister: Boolean;
var
@ -77,17 +49,79 @@ begin
if hIcon = 0 then
hIcon := Windows.LoadIcon(0, IDI_APPLICATION);
hCursor := Windows.LoadCursor(0, IDC_ARROW);
LPSzClassName := PWideChar(ClassNameW);
LPSzClassName := ClassNameW;
end;
Result := Windows.RegisterClassW(@WindowClassW) <> 0;
end;
var
__GetProp: function(hWnd: HWND; lpString: LPCSTR): HANDLE; stdcall;
__SetProp: function(hWnd: HWND; lpString: LPCSTR; hData: HANDLE): WINBOOL; stdcall;
__CreateWindowExW: function(dwExStyle: DWORD; lpClassName: LPCWSTR; lpWindowName: LPCWSTR; dwStyle: DWORD; X: longint; Y: longint; nWidth: longint; nHeight: longint; hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: LPVOID): HWND; stdcall;
function _GetProp(hWnd: HWND; lpString: LPCSTR): HANDLE; stdcall;
var
Atom: UIntPtr absolute lpString;
begin
if (Atom > MAXWORD) and (lpString = 'WinControl') then
Result:= __GetProp(hWnd, 'WinControlDC')
else
Result:= __GetProp(hWnd, lpString);
end;
function _SetProp(hWnd: HWND; lpString: LPCSTR; hData: HANDLE): WINBOOL; stdcall;
var
Atom: UIntPtr absolute lpString;
begin
if (Atom > MAXWORD) and (lpString = 'WinControl') then
Result:= __SetProp(hWnd, 'WinControlDC', hData)
else
Result:= __SetProp(hWnd, lpString, hData);
end;
function _CreateWindowExW(dwExStyle: DWORD; lpClassName: LPCWSTR; lpWindowName: LPCWSTR; dwStyle: DWORD; X: longint; Y: longint; nWidth: longint; nHeight: longint; hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: LPVOID): HWND; stdcall;
var
AParams: PNCCreateParams absolute lpParam;
begin
if (hWndParent = 0) and Assigned(AParams) and (AParams^.WinControl is TfrmMain) then lpClassName:= ClassNameW;
Result:= __CreateWindowExW(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
end;
procedure Initialize;
var
hModule: THandle;
pLibrary, pFunction: PPointer;
begin
WinRegister;
// Replace TCustomForm widgetset class
RegisterCustomForm;
RegisterWSComponent(TCustomForm, TWin32WSCustomFormEx);
pLibrary:= FindImportLibrary(MainInstance, user32);
if Assigned(pLibrary) then
begin
hModule:= GetModuleHandle(user32);
pFunction:= FindImportFunction(pLibrary, GetProcAddress(hModule, 'CreateWindowExW'));
if Assigned(pFunction) then
begin
Pointer(__CreateWindowExW):= ReplaceImportFunction(pFunction, @_CreateWindowExW);
end;
// Prevent plugins written in Lazarus from crashing by changing the name for
// GetProp/SetProp to store control data from 'WinControl' to 'WinControlDC'
pFunction:= FindImportFunction(pLibrary, GetProcAddress(hModule, 'GetPropA'));
if Assigned(pFunction) then
begin
Pointer(__GetProp):= ReplaceImportFunction(pFunction, @_GetProp);
end;
pFunction:= FindImportFunction(pLibrary, GetProcAddress(hModule, 'SetPropA'));
if Assigned(pFunction) then
begin
Pointer(__SetProp):= ReplaceImportFunction(pFunction, @_SetProp);
end;
end;
Windows.GlobalDeleteAtom(WindowInfoAtom);
WindowInfoAtom := Windows.GlobalAddAtom('WindowInfoDC');
end;
initialization

View file

@ -3,7 +3,7 @@
-------------------------------------------------------------------------
This unit contains specific WINDOWS functions.
Copyright (C) 2006-2018 Alexander Koblov (alexx2000@mail.ru)
Copyright (C) 2006-2019 Alexander Koblov (alexx2000@mail.ru)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -153,6 +153,8 @@ function ExtractFileAttributes(const FindData: TWin32FindDataW): DWORD;
procedure InitErrorMode;
procedure UpdateEnvironment;
procedure FixCommandLineToUTF8;
implementation
@ -940,6 +942,39 @@ begin
SetErrorMode(SEM_FAILCRITICALERRORS or SEM_NOOPENFILEERRORBOX);
end;
procedure UpdateEnvironment;
var
dwSize: DWORD;
ASysPath: UnicodeString;
AUserPath: UnicodeString;
APath: UnicodeString = '';
begin
// System environment
if RegReadKey(HKEY_LOCAL_MACHINE, 'System\CurrentControlSet\Control\Session Manager\Environment', 'Path', ASysPath) then
begin
APath := ASysPath;
if (Length(APath) > 0) and (APath[Length(APath)] <> PathSeparator) then APath += PathSeparator;
end;
// User environment
if RegReadKey(HKEY_CURRENT_USER, 'Environment', 'Path', AUserPath) then
begin
APath := APath + AUserPath;
if (Length(APath) > 0) and (APath[Length(APath)] <> PathSeparator) then APath += PathSeparator;
end;
// Update path environment variable
if Length(APath) > 0 then
begin
SetLength(ASysPath, MaxSmallInt + 1);
dwSize:= ExpandEnvironmentStringsW(PWideChar(APath), PWideChar(ASysPath), MaxSmallInt);
if (dwSize = 0) or (dwSize > MaxSmallInt) then
ASysPath:= APath
else begin
SetLength(ASysPath, dwSize - 1);
end;
SetEnvironmentVariableW('Path', PWideChar(ASysPath));
end;
end;
procedure FixCommandLineToUTF8;
var
I, nArgs: Integer;