2016-08-22 24 views
7

SetWindowSubClass()は、ANSIウィンドウをUNICODEウィンドウに変更するはずですか?この動作については、ドキュメントやWeb上で何も見つかりませんでした。SetWindowSubclassがANSIウィンドウをUNICODEに変更する

私はちょうどそれがいけないとして、ANSIからUNICODEに影響を受けたウィンドウの種類をどのようにSetWindowSubclassを(私は信じて)変化し説明するために、テストアプリケーション(full source)を作成しました! IsWindowUnicode()が変更を確定します。

program TwoWaySubclassing; 

{$apptype gui} 
{$R Generic.res}       

{ 
{ I created this test application just to illustrate how SetWindowSubclass() 
{ changes -- I believe -- the type of the affected window from ANSI to UNICODE, 
{ as it shouldn't! IsWindowUnicode() confirms that. 
{ 
{ The Delphi 7 (all ANSI) application has 2 edit controls: 
{ 1. The smaller, which is subclassed in 2 switchable ways (called Modes). 
{ 2. The bigger, like a memo, not subclassed. Just for dumping info. 
{ 3. A button for switching between modes, on-the-fly. 
{ 
{ The default subclassing Mode uses SetWindowLong (the classic way). 
{ When pressing the button, the edit control is subclassed via SetWindowSubclass. 
{ Pressing it again brings the edit control back to the default SetWindowLong mode. 
{ 
{ The main window (and all child controls) are created using the ANSI version 
{ of the API procedure, so the message handler should receive, in "lParam", 
{ a pointer to an ANSI text (along with the wm_SetText message), always! 
{ 
{ The problem is that's not happening when the edit control is subclassed using 
{ the SetWindowSubclass mode! SetWindowSubclass() simply changes the window 
{ from ANSI to UNICODE and starts sending a PWideChar(lParam) rather than the 
{ expected PAnsiChar(lParam). 
{ 
{ Once back to the default SetWindowLong mode, the window becomes ANSI again! 
{ Just run the application and try switching between modes. Look carefully at the 
{ detailed info shown in the bigger edit control. 
{ 
{ Screenshots: 
{ 1. http://imgh.us/mode1.png 
{ 2. http://imgh.us/mode2.png 
{ 
{ Environment: 
{ Windows 7 32-bit 
{ Delphi 7 (all-ANSI) 
{ 
{ Regards, 
{ Paulo França Lacerda 
} 

uses 
    Windows, 
    Messages, 
    SysUtils; 

type 
    UINT_PTR = Cardinal; 
    DWORD_PTR = Cardinal; 

    TSubClassProc = function (hWnd:HWND; uMsg:UINT; wParam:WPARAM; lParam:LPARAM; uIdSubclass:UINT_PTR; dwRefData:DWORD_PTR) :LRESULT; stdcall; 

    TSubMode = (
    subSetWindowLong, 
    subSetWindowSubclass); 

const 
    LtBool :Array[Boolean] of String = ('False', 'True'); 
    LtSubMode :Array[TSubMode] of String = ('SetWindowLong', 'SetWindowSubclass'); 

    strTextUsingPAnsiChar = 'ANSI Text in PAnsiChar(lParam)'; 
    strTextUsingPWideChar = 'UNICODE Text in PWideChar(lParam)'; 

const 
    cctrl = Windows.comctl32; 

function SetWindowSubclass (hWnd:Windows.HWND; pfnSubclass:TSubClassProc; uIdSubclass:UINT_PTR; dwRefData:DWORD_PTR) :BOOL; stdcall; external cctrl name 'SetWindowSubclass'; 
function RemoveWindowSubclass (hWnd:Windows.HWND; pfnSubclass:TSubClassProc; uIdSubclass:UINT_PTR) :BOOL;      stdcall; external cctrl name 'RemoveWindowSubclass'; 
function DefSubclassProc  (hWnd:HWND; uMsg:UINT; wParam:WPARAM; lParam:LPARAM) :LRESULT;         stdcall; external cctrl name 'DefSubclassProc'; 

var 
    wc :TWndClass; 
    Msg :TMsg; 

    hButton :HWnd; 
    hEdit :HWnd; 
    hEdit2 :HWnd; 
    hFont :HWnd; 
    hFont2 :HWnd; 

    hMainHandle :HWnd; 
    swl_OldProc :Pointer; // Default Procedure for Subclassing #1 (via SetWindowLong) 
    SubMode :TSubMode; 


procedure Release_Resources; 
begin 
    DestroyWindow (hButton); hButton := 0; 
    DestroyWindow (hEdit); hEdit := 0; 
    DestroyWindow (hEdit2); hEdit2 := 0; 
    DeleteObject (hFont); hFont := 0; 
    DeleteObject (hFont2); hFont2 := 0; 
end; 

procedure MsgBox (S:String); 
begin 
    MessageBox (hMainHandle, PChar(S), 'Information', mb_Ok or mb_IconInformation); 
end; 

procedure Reveal_Text (lParam:LPARAM); 
const 
    lf = #13#10; 
    lf2 = lf+lf; 
var 
    S :String; 
    AnsiTxt :String; 
    UnicTxt :String; 
    Remarks :Array[1..3] of String; 
begin 
    if IsWindowUnicode(hEdit) 
    then Remarks[1] := ' (Man! SetWindowSubclass changed it to Unicode!!)' 
    else Remarks[1] := ' (great! as designed)'; 

    AnsiTxt := PAnsiChar(lParam); 

    if (Length(AnsiTxt) = 1) 
    then Remarks[2] := ' (text is obviously truncated)' 
    else Remarks[2] := ' (text is healthy and is ANSI, as it should)'; 

    UnicTxt := PWideChar(lParam); 

    if (Pos('?',UnicTxt) > 0) 
    then Remarks[3] := ' (text is obviously garbaged)' 
    else Remarks[3] := ' (text is healthy, but I want it to be ANSI)'; 

    S := 
      'Subclassed using: ' 
    +lf +' '+LtSubMode[SubMode]+'()' 
    +lf2+ 'IsUnicodeWindow(hEdit)? ' 
    +lf +' '+LtBool[IsWindowUnicode(hEdit)] 
    +lf +  Remarks[1] 
    +lf2+'PAnsiChar(lParam):' 
    +lf +' "'+PAnsiChar(lParam)+'"' 
    +lf +  Remarks[2] 
    +lf2+ 'PWideChar(lParam):' 
    +lf +' "'+PWideChar(lParam)+'"' 
    +lf +  Remarks[3]; 

    SetWindowText (hEdit2, PChar(S)); 
end; 

function swl_EditWndProc (hWnd:HWnd; uMsg:UInt; wParam:WParam; lParam:LParam) :LResult; stdcall; 
begin 
    Result := CallWindowProc (swl_OldProc, hWnd, uMsg, wParam, lParam); 
    if (uMsg = wm_SetText) then Reveal_Text(lParam); 
end; 

function sws_EditWndProc (hWnd:HWND; uMsg:UINT; wParam:WPARAM; lParam:LPARAM; uIdSubclass:UINT_PTR; dwRefData:DWORD_PTR) :LRESULT; stdcall; 
begin 
    Result := DefSubclassProc (hWnd, uMsg, wParam, lParam); 
    if (uMsg = wm_SetText) then Reveal_Text(lParam); 
end; 

procedure do_SetWindowSubclass; 
begin 
    if not SetWindowSubclass (hEdit, @sws_EditWndProc, 1, dword_ptr($1234{whatever})) 
    then RaiseLastOSError; 

    SubMode := subSetWindowSubclass; 
end; 

procedure undo_SetWindowSubclass; 
begin 
    if not RemoveWindowSubclass (hEdit, @sws_EditWndProc, 1) 
    then RaiseLastOSError; 

    SubMode := subSetWindowLong; // restored 
end; 

function AppWindowProc (hWnd:HWnd; uMsg:UInt; wParam:WParam; lParam:LParam) :LResult; stdcall; 
begin 
    case uMsg of 
    wm_Command: 
    begin 
     if (lParam = hButton) then 
     case SubMode of 
     subSetWindowLong: 
     begin 
      do_SetWindowSubclass; // now using SetWindowSubclass() 
      SetWindowText (hEdit, PChar(strTextUsingPWideChar)); 
      SetWindowText (hButton, PChar('Switch back to SetWindowLong mode')); 
     end; 

     subSetWindowSubclass: 
     begin 
      undo_SetWindowSubclass; // back to SetWindowLong() 
      SetWindowText (hEdit, PChar(strTextUsingPAnsiChar)); 
      SetWindowText (hButton, PChar('Switch to SetWindowSubclass mode')); 
     end; 
     end; 
    end; 

    wm_Destroy: 
    begin 
     Release_Resources; 
     PostQuitMessage (0); 
     Exit; 
    end; 
    end; 

    Result := DefWindowProc (hWnd, uMsg, wParam, lParam); 
end; 

var 
    W,H :Integer; 

begin 
    wc.hInstance  := hInstance; 
    wc.lpszClassName := 'ANSI_Wnd'; 
    wc.Style   := cs_ParentDC; 
    wc.hIcon   := LoadIcon(hInstance,'MAINICON'); 
    wc.lpfnWndProc := @AppWindowProc; 
    wc.hbrBackground := GetStockObject(white_brush); 
    wc.hCursor  := LoadCursor(0,IDC_ARROW); 

    RegisterClass(wc); // ANSI (using Delphi 7, so all Windows API is mapped to ANSI). 

    W := 500; 
    H := 480; 

    hMainHandle := CreateWindow ( // ANSI (using Delphi 7, so all Windows API is mapped to ANSI). 
    wc.lpszClassName,'2-Way Subclassing App', 
    ws_OverlappedWindow or ws_Caption or ws_MinimizeBox or ws_SysMenu or ws_Visible, 
    ((GetSystemMetrics(SM_CXSCREEN)-W) div 2), // vertically centered in screen 
    ((GetSystemMetrics(SM_CYSCREEN)-H) div 2), // horizontally centered in screen 
    W,H,0,0,hInstance,nil); 

    // create the fonts 
    hFont := CreateFont (-14,0,0,0,0,0,0,0, default_charset, out_default_precis, clip_default_precis, default_quality, variable_pitch or ff_swiss, 'Tahoma'); 
    hFont2:= CreateFont (-14,0,0,0,0,0,0,0, default_charset, out_default_precis, clip_default_precis, default_quality, variable_pitch or ff_swiss, 'Courier New'); 

    // create the edits 
    hEdit :=CreateWindowEx (WS_EX_CLIENTEDGE,'EDIT','some text', WS_VISIBLE or WS_CHILD or ES_LEFT or ES_AUTOHSCROLL,    10,35,W-40, 23,hMainHandle,0,hInstance,nil); 
    hEdit2:=CreateWindowEx (WS_EX_CLIENTEDGE,'EDIT','details', WS_VISIBLE or WS_CHILD or ES_LEFT or ES_AUTOHSCROLL or ES_MULTILINE,10,72,W-40,300,hMainHandle,0,hInstance,nil); 
    SendMessage(hEdit, WM_SETFONT,hFont, 0); 
    SendMessage(hEdit2,WM_SETFONT,hFont2,0); 

    // create the button 
    hButton:=CreateWindow ('Button','Switch to SetWindowSubclass mode', WS_VISIBLE or WS_CHILD or BS_PUSHBUTTON or BS_TEXT, 90,H-95,290,32,hMainHandle,0,hInstance,nil); 
    SendMessage(hButton,WM_SETFONT,hFont,0); 

    // subclass the Edit using the default method. 
    swl_OldProc := Pointer(GetWindowLong(hEdit,GWL_WNDPROC)); 
    SetWindowLong (hEdit,GWL_WNDPROC,Longint(@swl_EditWndProc)); 

    SubMode := subSetWindowLong; 
    SetWindowText (hEdit, PChar(strTextUsingPAnsiChar)); 

    // message loop 
    while GetMessage(Msg,0,0,0) do 
    begin 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
    end; 
end. 

アプリケーションは2つのエディットコントロールを有する:

  1. 2切替可能な方法でサブクラス化された小さい方(ここでモードと呼ばれます)。
  2. メモのように大きなものは、サブクラス化されていません。ちょうど情報を投棄するために。

モードを切り替えるボタンもあります。

デフォルトのサブクラス化モードはSetWindowLong()を使用しています(古典的な方法):

デルファイ2007年

SetWindowLong mode

以前では、メインウィンドウ(およびすべての子コントロールが)のWin32 APIのANSIバージョンを使用して作成されていますしたがって、(サブクラス化されたコントロールの)メッセージハンドラは、常にANSIテキスト(WM_SETTEXTメッセージとともに)を受け取る必要があります。

問題は、編集コントロールがSetWindowSubclass()を使用してサブクラス化されているときには起こりません。 SetWindowSubClass()は、ANSIからUNICODEにウィンドウを変更し、予想されるANSIテキストではなくUnicodeテキストの受信を開始します。ボタンを押すと

SetWindowSubclass()を経由して、エディットコントロールをサブクラス:

SetWindowSubclass mode

もう一度ボタンを押すとSetWindowLong()を経由して編集コントロールをサブクラス。

戻ったらSetWindowLong()モードに、エディットコントロールは自動的に再ANSIテキストを受信します。

SetWindowLong mode

だけでアプリケーションを実行して、モードを切り替えるしてみてください。大きな編集コントロールに表示される詳細情報を注意深く見てください。

これはMicrosoftのバグだと思う。しかし、それが「機能」の場合、誰かがそれぞれの文書に私を導くことができますか?私はどこでもそれを見つけることができませんでした。 MSDNによる

+1

あなたは 'SetWindowSubClassA'または' SetWindowSubClassW'を呼び出していますか? –

+0

@Jonathan Potter:私が触れたように、Delphi 7は常にANSIバージョンのAPIに対応しています。また、A/Wのフレーバーに付属しているすべてのAPIへの明示的な呼び出しをテストしました。 'SetWindowSubClassA'私はリンクを管理しませんでした。それは存在しますか?私はそれにリンクしようとするとコンパイルエラーが発生します。 –

+2

@JonathanPotter:私はAとWのバージョンがあるとは思わない。ドキュメンテーションにはそれらは言及されておらず、関数は文字列をとらないので、明白な理由はありません。 –

答えて

11

Subclassing Controls Using ComCtl32.dll version 6

のComctl32.dllバージョン6のみユニコードです。ComCtl32.dllバージョン6でサポートされている共通コントロール は、ANSIウィンドウプロシージャでサブクラス化(または スーパークラス)しないでください。

...

は、プロシージャに渡されるすべての文字列は、Unicodeがプリプロセッサ定義として指定されていない場合でも、Unicode文字列です。

だから、と思われます。

comctl32.dll私のc:\windows\syswow64フォルダはバージョン6.1です。

+0

男!あなたはそれを持っています。非常に明確な答え!私はあなたが指摘したものを見逃してしまったので、そこにたくさんの記事を読んだ。私は、私のANSIウィンドウをサブクラス化する古典的なSetWindowLongの方法に固執する必要があると思います。 :) –

+0

私は恐れています。私はどちらの選択肢も見つかりませんでした。 –

+0

これは良い情報を知っている、ありがとう。しかし、サブクラス化されたAnsiウィンドウがUnicodeメッセージを受け取るかどうかは本当に重要ですか?彼らはUnicodeであることを知っているので、Ansiとしてそれらを扱う必要がある場合は、UnicodeからAnsiに変換できます。しかし、なぜそれらをUnicodeとして扱わないのでしょうか?そしてBTWでは、あなたのアプリケーションは明示的に明示されない限り(例えばビジュアルスタイルを有効にするため)、ComCtrl v6を使用しません。 –

関連する問題