【问题标题】:Delphi VirtualKey to WideString/UNICODE using TNT controls on non-unicode Delphi 7Delphi VirtualKey to WideString/UNICODE 在非 unicode Delphi 7 上使用 TNT 控件
【发布时间】:2012-06-01 21:31:38
【问题描述】:

我正在使用此代码将虚拟键转换为 WideString:

function VKeytoWideString (Key : Word) : WideString; 
var 
 WBuff         : array [0..255] of WideChar; 
 KeyboardState : TKeyboardState; 
 UResult       : Integer;
begin 
 Result := '';
 GetKeyBoardState (KeyboardState); 
 ZeroMemory(@WBuff[0], SizeOf(WBuff));
 UResult := ToUnicode(key, MapVirtualKey(key, 0), KeyboardState, WBuff, Length(WBuff), 0); 
 if UResult > 0 then
  SetString(Result, WBuff, UResult)
 else if UResult = -1 then
  Result := WBuff;
end; 

它在我的电脑上运行良好,但在中国电脑上我得到了这个:

它将中文字符转换为汉语拼音。我认为该函数实际上返回了键盘的原始输入,而不是用户真正想要输入的内容。

我应该如何处理?

【问题讨论】:

  • Stack Overflow 上的大多数人不会阅读中文,因此无法识别您显示的输出的问题。 (对我来说它看起来像中文!有什么问题?)你能告诉你给你的函数什么输入,你得到什么输出,以及你期望或希望收到什么?提及数字字符代码可能会有所帮助。
  • 我也不能告诉你太多。一位朋友在中文 Windows XP 上测试了我的程序,我在 KeyDown 事件中使用了 Delphi7 的 TNTMemo(将密钥作为单词返回)。之后,我使用上面的功能转换密钥并将其放入备忘录中。我的朋友说备忘录中应该有汉字,就像他在windows中的记事本上一样。但相反,它向他显示了他输入的汉语拼音。
  • 一个 KeyDown 事件可以导致 0、1 或更多 KeyPress 事件。这种转换非常复杂,Windows 会为您执行。你为什么要复制它?是不是不能听 KeyPress 事件而不是 KeyDown 事件?
  • @BenjaminWeiss 不同的是,在 KeyPress 事件中,虚拟键码已经转换为字符,并且您只能获取与字符对应的键的事件。因此,如果在美国国际布局上按下',您将获得一个 KeyDown 事件、没有 KeyPress 事件和一个 KeyUp 事件。然后按下x,你会得到另一个KeyDown 事件,接着是两个KeyPress 事件('x),最后是x 的一个KeyUp 事件。使用相同的布局键入 'a 会导致 KeyDown(') KeyUp(') KeyDown(a) KeyPress(á) KeyUp(a)。 (这有点简化,但基本思想。)
  • @BenjaminWeiss:GetKeyboardLayout

标签: delphi unicode keyboard cjk


【解决方案1】:

根据 cmets,这是一个示例,说明如何通过处理 KeyPress 事件而不是手动转换 KeyDown 事件来避免该问题。 TNT 控件不提供WideChar KeyPress 事件,但添加起来相当容易。理想情况下,您不应该像我在这里所做的那样将 TTntMemoTTntForm 的扩展放在派生类中,而是修改 TNT 源代码。

表单包含两个TTntMemo 控件。在第一个中按下键将在第二个中记录事件。

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, TntForms, StdCtrls, TntStdCtrls;

type
  TKeyPressWEvent = procedure(Sender: TObject; var Key: WideChar) of object;

  TTntMemo = class(TntStdCtrls.TTntMemo)
  private
    FOnKeyPressW: TKeyPressWEvent;
    procedure WMChar(var Msg: TWMChar); message WM_CHAR;
  protected
    function DoKeyPressW(var Message: TWMKey): Boolean;
    procedure KeyPressW(var Key: WideChar);
  published
    property OnKeyPressW: TKeyPressWEvent read FOnKeyPressW write FOnKeyPressW;
  end;

  TTntForm = class(TntForms.TTntForm)
  private
    FOnKeyPressW: TKeyPressWEvent;
    procedure WMChar(var Msg: TWMChar); message WM_CHAR;
  protected
    function DoKeyPressW(var Message: TWMKey): Boolean;
    procedure KeyPressW(var Key: WideChar);
  published
    property OnKeyPressW: TKeyPressWEvent read FOnKeyPressW write FOnKeyPressW;
  end;

  TForm1 = class(TTntForm)
    TntMemo1: TTntMemo;
    TntMemo2: TTntMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormKeyPressW(Sender: TObject; var Key: WideChar);
    procedure TntMemo1KeyPressW(Sender: TObject; var Key: WideChar);
  end;

var
  Form1: TForm1;

implementation

uses
  TntControls;

{$R *.dfm}

type
  TWinControlAccess = class(TWinControl);
  TTntFormAccess = class(TTntForm);

function TntControl_DoKeyPressW(Self: TWinControl; var Message: TWMKey;
  KeyPressW: Pointer): Boolean;
type
  TKeyPressWProc = procedure(Self: TWinControl; var Key: WideChar);
var
  Form: TCustomForm;
  Ch: WideChar;
begin
  Result := True;
  Form := GetParentForm(Self);
  if (Form <> nil) and (Form <> Self) and Form.KeyPreview then
  begin
    if (Form is TTntForm) and TTntFormAccess(Form).DoKeyPressW(Message) then Exit;
    if TWinControlAccess(Form).DoKeyPress(Message) then Exit;
  end;
  if not (csNoStdEvents in Self.ControlStyle) then
  begin
    Ch := GetWideCharFromWMCharMsg(Message);
    TKeyPressWProc(KeyPressW)(Self, Ch);
    SetWideCharForWMCharMsg(Message, Ch);
    if Ch = #0 then Exit;
  end;
  Result := False;
end;

{ TTntMemo }

function TTntMemo.DoKeyPressW(var Message: TWMKey): Boolean;
begin
  Result := TntControl_DoKeyPressW(Self, Message, @TTntMemo.KeyPressW);
end;

procedure TTntMemo.KeyPressW(var Key: WideChar);
begin
  if Assigned(FOnKeyPressW) then FOnKeyPressW(Self, Key);
end;

procedure TTntMemo.WMChar(var Msg: TWMChar);
begin
  if not DoKeyPressW(Msg) then inherited;
end;

{ TTntForm }

function TTntForm.DoKeyPressW(var Message: TWMKey): Boolean;
begin
  Result := TntControl_DoKeyPressW(Self, Message, @TTntForm.KeyPressW);
end;

procedure TTntForm.KeyPressW(var Key: WideChar);
begin
  if Assigned(FOnKeyPressW) then FOnKeyPressW(Self, Key);
end;

procedure TTntForm.WMChar(var Msg: TWMChar);
begin
  if not DoKeyPressW(Msg) then inherited;
end;

{ TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.OnKeyPressW := FormKeyPressW;
  TntMemo1.OnKeyPressW := TntMemo1KeyPressW;
end;

procedure TForm1.FormKeyPressW(Sender: TObject; var Key: WideChar);
begin
  TntMemo2.Lines.Add(WideString('FormKeyPress: ') + Key);
end;

procedure TForm1.TntMemo1KeyPressW(Sender: TObject; var Key: WideChar);
begin
  TntMemo2.Lines.Add(WideString('TntMemo1KeyPress: ') + Key);
end;

end.

【讨论】:

    【解决方案2】:

    我也没有太多输入中文的经验,但我怀疑这是IME(输入法编辑器)在起作用。这就是允许中国用户输入拼音的原因,然后将其翻译成表意字符(否则,您需要一个带有 1000 多个键的键盘......)

    虚拟键码与键盘直接相关,因此必然只对应于输入的键。所以你的函数工作正常:它将 VKEY 代码转换为 WideChar。为了做你想做的事,你必须编写第二个函数,它将拼音转换为字符。

    如果你想专门为中文做这个,我敢打赌那里有做这个的函数。如果你想让它更通用,并且独立于语言环境和语言,那么也许可以为相关的 TMemo 与 IME 交互,但如果是这样,我不知道。我最好的猜测是search MSDN for IME

    但是,回应hvd’s comment:你想在这里完成什么?

    直接复制 TMemo 的文字不是更容易吗?

    【讨论】:

    • 我不能像这样将它复制到 TMemo 中。它必须是一个 UNICODE 字符。我只需要一个字符的“副本”。
    • @BenjaminWeiss:您使用的是什么版本的 Delphi?如果 2009 或更高版本,Char = WideChar。在早期版本中,您可以使用WideCharToMultiByte 将 WideChar 转换为 TMemo 的活动代码页中的相应字符。
    • @Martijn Delphi 7 带有 TNT 控件,根据问题上的 cmets,所以 Char = AnsiChar,但 TTntMemo.Lines.Text 是 WideString :)
    • @hvd:确实如此,那么本杰明为什么不复制 TTntMemo 的文本呢?
    • @BenjaminWeiss:这不能回答我的问题。如果您复制 TTntMemo 的文本,您仍然拥有它的“副本”。问题出在哪里?或者您是否尝试实现过滤器,以便阻止显示特定字符​​?
    猜你喜欢
    • 2012-06-06
    • 2012-04-20
    • 1970-01-01
    • 2011-10-25
    • 2011-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多