📄 frxunicodectrls.pas
字号:
{*******************************************************}
{ The Delphi Unicode Controls Project }
{ }
{ http://home.ccci.org/wolbrink }
{ }
{ Copyright (c) 2002, Troy Wolbrink (wolbrink@ccci.org) }
{ }
{*******************************************************}
unit frxUnicodeCtrls;
interface
{$I frx.inc}
uses Windows, Messages, Classes, Controls, Forms, StdCtrls, frxRichEdit;
type
TUnicodeEdit = class(TEdit)
private
procedure SetSelText(const Value: WideString);
function GetText: WideString;
procedure SetText(const Value: WideString);
protected
procedure CreateWindowHandle(const Params: TCreateParams); override;
function GetSelText: WideString; reintroduce;
public
property SelText: WideString read GetSelText write SetSelText;
property Text: WideString read GetText write SetText;
end;
TUnicodeMemo = class(TMemo)
private
procedure SetSelText(const Value: WideString);
function GetText: WideString;
procedure SetText(const Value: WideString);
protected
procedure CreateWindowHandle(const Params: TCreateParams); override;
function GetSelText: WideString; reintroduce;
public
property SelText: WideString read GetSelText write SetSelText;
property Text: WideString read GetText write SetText;
end;
TRxUnicodeRichEdit = class(TRxRichEdit)
{$IFDEF Delphi12};
{$ELSE}
protected
procedure CreateWindowHandle(const Params: TCreateParams); override;
end;
{$ENDIF}
implementation
uses SysUtils, Graphics, Imm, RichEdit;
const
UNICODE_CLASS_EXT = '.UnicodeClass';
ANSI_UNICODE_HOLDER = $FF;
var
UnicodeCreationControl: TWinControl = nil;
Win32PlatformIsUnicode: Boolean;
Win32PlatformIsXP: Boolean;
{$IFDEF Delphi6}
function MakeObjectInstance(Method: TWndMethod): Pointer;
begin
Result := Classes.MakeObjectInstance(Method);
end;
procedure FreeObjectInstance(ObjectInstance: Pointer);
begin
Classes.FreeObjectInstance(ObjectInstance);
end;
{$ENDIF}
function IsUnicodeCreationControl(Handle: HWND): Boolean;
begin
Result := (UnicodeCreationControl <> nil)
and (UnicodeCreationControl.HandleAllocated)
and (UnicodeCreationControl.Handle = Handle);
end;
function WMNotifyFormatResult(FromHandle: HWND): Integer;
begin
if Win32PlatformIsUnicode
and (IsWindowUnicode(FromHandle) or IsUnicodeCreationControl(FromHandle)) then
Result := NFR_UNICODE
else
Result := NFR_ANSI;
end;
function IsTextMessage(Msg: UINT): Boolean;
begin
// WM_CHAR is omitted because of the special handling it receives
Result := (Msg = WM_SETTEXT)
or (Msg = WM_GETTEXT)
or (Msg = WM_GETTEXTLENGTH);
end;
procedure MakeWMCharMsgSafeForAnsi(var Message: TMessage);
begin
with TWMChar(Message) do begin
Assert(Msg = WM_CHAR);
Assert(Unused = 0);
if (CharCode > Word(High(AnsiChar))) then begin
Unused := CharCode;
CharCode := ANSI_UNICODE_HOLDER;
end;
end;
end;
procedure RestoreWMCharMsg(var Message: TMessage);
begin
with TWMChar(Message) do begin
Assert(Message.Msg = WM_CHAR);
if (Unused > 0)
and (CharCode = ANSI_UNICODE_HOLDER) then
CharCode := Unused;
Unused := 0;
end;
end;
//-----------------------------------------------------------------------------------
type
TAccessControl = class(TControl);
TAccessWinControl = class(TWinControl);
TWinControlTrap = class(TComponent)
private
WinControl_ObjectInstance: Pointer;
ObjectInstance: Pointer;
DefObjectInstance: Pointer;
function IsInSubclassChain(Control: TWinControl): Boolean;
procedure SubClassWindowProc;
private
FControl: TAccessWinControl;
Handle: THandle;
PrevWin32Proc: Pointer;
PrevDefWin32Proc: Pointer;
PrevWindowProc: TWndMethod;
private
LastWin32Msg: UINT;
Win32ProcLevel: Integer;
IDEWindow: Boolean;
DestroyTrap: Boolean;
TestForNull: Boolean;
FoundNull: Boolean;
// {$IFDEF TNT_VERIFY_WINDOWPROC}
LastVerifiedWindowProc: TWndMethod;
/// {$ENDIF}
procedure Win32Proc(var Message: TMessage);
procedure DefWin32Proc(var Message: TMessage);
procedure WindowProc(var Message: TMessage);
private
{$IFDEF Delphi12}
procedure SubClassControl(Params_Caption: PWideChar);
{$ELSE}
procedure SubClassControl(Params_Caption: PAnsiChar);
{$ENDIF}
procedure UnSubClassUnicodeControl;
protected
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
constructor TWinControlTrap.Create(AOwner: TComponent);
begin
FControl := TAccessWinControl(AOwner as TWinControl);
inherited Create(nil);
FControl.FreeNotification(Self);
WinControl_ObjectInstance := MakeObjectInstance(FControl.MainWndProc);
ObjectInstance := MakeObjectInstance(Win32Proc);
DefObjectInstance := MakeObjectInstance(DefWin32Proc);
end;
destructor TWinControlTrap.Destroy;
begin
FreeObjectInstance(ObjectInstance);
FreeObjectInstance(DefObjectInstance);
FreeObjectInstance(WinControl_ObjectInstance);
inherited;
end;
procedure TWinControlTrap.Notification(AComponent: TComponent; Operation: TOperation);
begin
inherited;
if (AComponent = FControl) and (Operation = opRemove) then begin
FControl := nil;
if Win32ProcLevel = 0 then
Free
else
DestroyTrap := True;
end;
end;
procedure TWinControlTrap.SubClassWindowProc;
begin
if not IsInSubclassChain(FControl) then begin
PrevWindowProc := FControl.WindowProc;
FControl.WindowProc := Self.WindowProc;
end;
// {$IFDEF TNT_VERIFY_WINDOWPROC}
LastVerifiedWindowProc := FControl.WindowProc;
// {$ENDIF}
end;
{$IFDEF Delphi12}
procedure TWinControlTrap.SubClassControl(Params_Caption: PWideChar);
{$ELSE}
procedure TWinControlTrap.SubClassControl(Params_Caption: PAnsiChar);
{$ENDIF}
begin
// initialize trap object
Handle := FControl.Handle;
PrevWin32Proc := Pointer(GetWindowLongW(FControl.Handle, GWL_WNDPROC));
PrevDefWin32Proc := FControl.DefWndProc;
// subclass Window Procedures
SetWindowLongW(FControl.Handle, GWL_WNDPROC, Integer(ObjectInstance));
FControl.DefWndProc := DefObjectInstance;
SubClassWindowProc;
end;
function SameWndMethod(A, B: TWndMethod): Boolean;
begin
Result := @A = @B;
end;
var
PendingRecreateWndTrapList: TList = nil;
procedure TWinControlTrap.UnSubClassUnicodeControl;
begin
// restore window procs (restore WindowProc only if we are still the direct subclass)
if SameWndMethod(FControl.WindowProc, Self.WindowProc) then
FControl.WindowProc := PrevWindowProc;
TAccessWinControl(FControl).DefWndProc := PrevDefWin32Proc;
SetWindowLongW(FControl.Handle, GWL_WNDPROC, Integer(PrevWin32Proc));
if IDEWindow then
DestroyTrap := True
else if not (csDestroying in FControl.ComponentState) then
// control not being destroyed, probably recreating window
PendingRecreateWndTrapList.Add(Self);
end;
var
Finalized: Boolean; { If any tnt controls are still around after finalization it must be due to a memory leak.
Windows will still try to send a WM_DESTROY, but we will just ignore it if we're finalized. }
procedure TWinControlTrap.Win32Proc(var Message: TMessage);
begin
if (not Finalized) then begin
Inc(Win32ProcLevel);
try
with Message do begin
// {$IFDEF TNT_VERIFY_WINDOWPROC}
if not SameWndMethod(FControl.WindowProc, LastVerifiedWindowProc) then begin
SubClassWindowProc;
LastVerifiedWindowProc := FControl.WindowProc;
end;
// {$ENDIF}
LastWin32Msg := Msg;
Result := CallWindowProcW(PrevWin32Proc, Handle, Msg, wParam, lParam);
end;
finally
Dec(Win32ProcLevel);
end;
if (Win32ProcLevel = 0) and (DestroyTrap) then
Free;
end else if (Message.Msg = WM_DESTROY) then
FControl.WindowHandle := 0
end;
procedure TWinControlTrap.DefWin32Proc(var Message: TMessage);
begin
with Message do begin
if Msg = WM_NOTIFYFORMAT then
Result := WMNotifyFormatResult(Message.wParam)
else begin
if (Msg = WM_CHAR) then begin
RestoreWMCharMsg(Message)
end;
if (Msg = WM_IME_CHAR) and (not Win32PlatformIsXP) then
begin
{ In Windows XP, DefWindowProc handles WM_IME_CHAR fine for VCL windows. }
{ Before XP, DefWindowProc will sometimes produce incorrect, non-Unicode WM_CHAR. }
{ Also, using PostMessageW on Windows 2000 didn't always produce the correct results. }
Message.Result := SendMessageW(Handle, WM_CHAR, wParam, lParam)
end else begin
if (Msg = WM_DESTROY) then begin
UnSubClassUnicodeControl; {The reason for doing this in DefWin32Proc is because in D9, TWinControl.WMDestroy() does a perform(WM_TEXT) operation. }
end;
{ Normal DefWindowProc }
Result := CallWindowProcW(PrevDefWin32Proc, Handle, Msg, wParam, lParam);
end;
end;
end;
end;
function TWinControlTrap.IsInSubclassChain(Control: TWinControl): Boolean;
var
Message: TMessage;
begin
if SameWndMethod(Control.WindowProc, TAccessWinControl(Control).WndProc) then
Result := False { no subclassing }
else if SameWndMethod(Control.WindowProc, Self.WindowProc) then
Result := True { directly subclassed }
else begin
TestForNull := True;
FoundNull := False;
ZeroMemory(@Message, SizeOf(Message));
Message.Msg := WM_NULL;
Control.WindowProc(Message);
Result := FoundNull; { indirectly subclassed }
end;
end;
procedure TWinControlTrap.WindowProc(var Message: TMessage);
var
CameFromWindows: Boolean;
begin
if TestForNull and (Message.Msg = WM_NULL) then
FoundNull := True;
if (not FControl.HandleAllocated) then
FControl.WndProc(Message)
else begin
CameFromWindows := LastWin32Msg <> WM_NULL;
LastWin32Msg := WM_NULL;
with Message do begin
if (not CameFromWindows)
and (IsTextMessage(Msg)) then
Result := SendMessageA(Handle, Msg, wParam, lParam)
else begin
if (Msg = WM_CHAR) then begin
MakeWMCharMsgSafeForAnsi(Message);
end;
PrevWindowProc(Message)
end;
end;
end;
end;
//----------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -