SetWindowLong(GWL_USERDATA)で起こる不具合

[戻る]
ひな 2004/01/22(木) 21:15:43
VC++ ver7.0.9486 SDKで使っております。

IPアドレスコントロールをサブクラス化しようと、
DefControlProc = (FARPROC)GetWindowLong(HWnd, GWL_WNDPROC);
SetWindowLong(HWnd, GWL_USERDATA, (LONG)this);
SetWindowLong(HWnd, GWL_WNDPROC, (LONG)PreControlProc);
としたのですが、コントロールが上手く動作せず、
コントロールの表示もおかしくなってしまいました。

実験で、エディットコントロールでは不具合が出ませんでした。
他にSetWindowLong(GWL_USERDATA)を使わずにポインタを渡した時は不具合が出ず、
SetWindowLong(GWL_USERDATA)だけを使い、
SetWindowLong(GWL_WNDPROC)とGetWindowLong(GWL_WNDPROC)を使わなかった時には、
不具合が起こったので、SetWindowLong(GWL_USERDATA)が原因だと思うのですが、
原因がなんなのか全く思いつきません。

長いので次にソースを書きます。下に書くクラスを派生して使っています。
派生してもコントロールによる違いはSetControl(int id, CDialog *dialog)の
id(リソースのID)の違いだけです。

なにとぞご教授お願いいたします。

ひな 2004/01/22(木) 21:17:42
このクラスを派生して使っています。
派生クラスでは値の収得、設定などしかおこなっておりません。

class CBaseControl
{
public:
    CBaseControl() : ControlID(0), Dialog(NULL), HWnd(0), DefControlProc
(NULL) {}
    virtual ~CBaseControl();

    Bool SetControl(int id, CDialog *dialog);

protected:
    virtual LRESULT ControlProc(HWND hwnd, UINT msg, WPARAM wparam, 
LPARAM lparam);

    int ControlID;
    CDialog *Dialog; //ダイアログのハンドルを持ってるクラス
    HWND HWnd;

private:
    static LRESULT CALLBACK PreControlProc(HWND msg, UINT id, WPARAM 
wparam, LPARAM lparam);
    FARPROC DefControlProc;
};

CBaseControl::~CBaseControl()
{
    if(DefControlProc)
        SetWindowLong(HWnd, GWL_WNDPROC, (LONG)DefControlProc);
}

LRESULT CALLBACK CBaseControl::PreControlProc(HWND hwnd, UINT msg, WPARAM 
wparam, LPARAM lparam)
{
    CBaseControl *control = (CBaseControl*)GetWindowLong(hwnd, 
GWL_USERDATA);
    if(!control)
        return 0;

    return control->ControlProc(hwnd, msg, wparam, lparam);
}

LRESULT CBaseControl::ControlProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM 
lparam)
{
    return CallWindowProc((WNDPROC)DefControlProc, hwnd, msg, wparam, 
lparam);
}

Bool CBaseControl::SetControl(int id, CDialog *dialog)
{
    if(!dialog) return False;
    ControlID = id; Dialog = dialog;
    HWnd = GetDlgItem(Dialog->GetDlgHandle(), ControlID);
    
    DefControlProc = (FARPROC)GetWindowLong(HWnd, GWL_WNDPROC);
    SetWindowLong(HWnd, GWL_USERDATA, (LONG)this);
    SetWindowLong(HWnd, GWL_WNDPROC, (LONG)PreControlProc);    

    return True;
}

よろしくお願いいたします。

ひな 2004/01/22(木) 21:25:07
使う時はこのようにつかっております。

CIPAddressControl : public CBaseControl として、

ダイアログボックスクラスでCIPAddressControl IPControlを定義
ダイアログのWM_INITDIALOGでIPControl.SetControl(id, this/*dialogクラス*/)
この後コントロールを操作しなくてもうまくいきません。

あと、CIPAddressControlのvirtual ControlProc()は定義せずにデフォルトを使用してま
す。うー・・・、なんか簡単なミスな気がしてきます。ミスだったらすいません。

ku 2004/01/23(金) 09:47:49
> 不具合が起こったので、SetWindowLong(GWL_USERDATA)が原因だと思うのですが、
> 原因がなんなのか全く思いつきません。
ためしにエディットとIPアドレスに最初からGWL_USERDATAに設定されている値を取得してみ
ました
LONG l1 = ::GetWindowLong(m_Edit.GetSafeHwnd(), GWL_USERDATA);
LONG l2 = ::GetWindowLong(m_Adr.GetSafeHwnd(), GWL_USERDATA);
エディットの方は0でしたが、IPアドレスの方は0以外でした

IPアドレスのコントロールは最初からこの領域を(何らかの理由があって)
すでに使用しているということではないでしょうか?

ひな 2004/01/23(金) 13:58:49
[[解決]]
kuさんお答えありがとうございます。

GWL_USERDATAの領域はこっちが自由に使える領域じゃなかったとは・・・。
前提の知識が間違ってました。すいませんでした。

これだと、ポインタは自分で管理するしかありませんね。
GWL_USERDATAの領域って使えない

お答え本当にありがとうございました。

古いスレッドには追加発言できません。