スキンケア、サブカテゴリ化の方法について


スキンケア、サブカテゴリ化の方法について
 
アプリケーションの肌交換やサブクラス化について.次に、CAboutDlgでのButtonのクラス化を例に、既存のクラス1を直接使用し、クラスclass CButtonXPを自分で作成します.public CButton{/*/}興味のあるメッセージをMessageMapで処理します.2、CButtonの代わりにCButtonXPで変数m_を宣言するbtn; 3、void CAboutDlg:DoDataExchange(CDataExchange*pDX)に一言を加える.
DDX_Control(pDX, IDB_BUTTON1, m_edit);

あるいはInitDialog()に
m_btn.SubclassDlgItem(IDB_BUTTON1, this);

この2つの効果はあまり悪くない.2つ目は、Hookで既存のクラス1を使用し、クラスclass CButtonXP:public CButton{/*......*/}を自分で書いて、興味のあるメッセージをMessageMapで処理することです.2、SetWindowsHookExを使用してフックを取り付ける:
g_hWndProcHook = ::SetWindowsHookEx(WH_CALLWNDPROC,WndProcHook,NULL,::GetCurrentThreadId());

3、WndProcHookでウィンドウの作成と破棄のメッセージを処理する:
LRESULT CALLBACK WndProcHook(int code, WPARAM wParam, LPARAM lParam)
{
if (code == HC_ACTION)
{
switch (((CWPSTRUCT*) lParam)->message)
{
case WM_CREATE:
BeginSubclassing(((CWPSTRUCT*) lParam)->hwnd);
break;
case WM_NCDESTROY:
// TODO: clear subclass info.
EndSubclassing(((CWPSTRUCT*) lParam)->hwnd);
break;
default:
break;
}
}
return CallNextHookEx(g_hWndProcHook, code, wParam, lParam);
}

4、BeginSubclassingでGetClassNameで「Button」などのクラス名を取得し、CButtonXPクラスでサブクラス化する.
CButtonXP pButton = new CButtonXP;
VERIFY(pButton ->SubclassWindow(hWnd));

第三に、Hookでウィンドウプロシージャ1を使用し、ボタンを自分で書くウィンドウプロシージャ
WNDPROC oldProc;

LRESULT CALLBACK ProcButton(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
ASSERT(oldProc != 0);
if (oldProc == 0) return TRUE;

switch (uMsg)
{
case WM_ERASEBKGND:
break;
//......
default:
break;
}

return CallWindowProc(oldProc, hWnd, uMsg, wParam, lParam);
}

2、同第2種3、同第2種4、BeginSubclassingでクラス名を得た後、SetWindowLongの方式でサブクラス化する:
oldProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);
SetWindowLong(hWnd, GWL_WNDPROC, (LONG) ProcButton);

4つ目は、HookがダイアログボックスのOnInitDialogですべてのサブフォームを列挙する必要はありません.たとえば、次の2つの文で実現します.
hWnd=GetWindow(hDlg,GW_CHILD); 
hWnd=GetWindow(hWnd,GW_HWNDNEXT);

各サブフォームに対してサブクラス化処理を行い、処理プロセスは2番目と3番目と同じである.
5つ目:XPで実行する場合はmanifest、すなわち次のXMLファイルを使用できます.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="Microsoft.Windows.XXXX"
processorArchitecture="x86"
version="5.1.0.0"
type="win32"/>

<description>Windows Shell</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="x86"
publicKeyToken="6595b64144ccf1df"
language="*"/>

</dependentAssembly>
</dependency>
</assembly>

アプリケーション名として保存します.manifestは、アプリケーションに対応するディレクトリの下に置くか、リソースタイプ24のリソースとしてアプリケーションにコンパイルします.このようにプログラムはXPの下で自動的にXPの風格を持っています.6つ目:サードパーティのライブラリSkin+(www.uipower.com)を使用して肌を変える7つ目:サードパーティのアプリケーションでwindows全体に肌を変える(windowblinds)以上の7つの方法にはそれぞれメリットとデメリットがあります.私は使用中にも多くの問題に直面しています.今、一つ一つ来て、みんなと一緒に問題を解決したいと思っています.まず、いくつかの深く検討する準備ができていない方法を排除します.5つ目は、manifest方式が最も迅速で簡潔ですが、機能が限られており、深刻なプラットフォームの制限がありますが、メリットはアプリケーションがwindowsと同じスタイルを共有できることです.6つ目は、サードパーティ製のライブラリSkin++(www.uipower.com)を使用して肌を変える方法が簡単で、カスタマイズ性もよく、選択可能な肌の種類が非常に多く、サポートされている言語が非常に広く、肌を変える機能のターミネーターと言える.例えばBCGを肌に変えることができるなど、いくつかの技術点では、ComboBoxのスクロールバー、システムダイアログボックス(open or close Dialog)のメニューなど、多くの同類製品ができていません.7つ目は、娯楽的な性質に属しているので、あまり言わないでください.第1種、直接既成の類を使って、とてもよくある1種の用法に属して、一般的に使う上でどんな問題が発生しないで、欠点は言わないで、もしこのような方式が私を満足させるならば、私はこの招待状を出す必要はありません.次の2、3、4種類を見てみましょう.2つ目はHOOK+ウィンドウクラスで、実現が便利で、自画コントロールを作る作業量と同じです.3つ目はHOOK+ウィンドウプロセスで、実現するのが面倒で、自分でswitch caseを処理し、自分でメッセージパラメータを変換し、自分で場所を探して状態変数を維持し、仕事量が大きい.4つ目はHOOKを使わない方法で、肌を入れ替えるプログラムのソースコードの修正が多いという欠点があります.もちろん、直接プロセスの中でウィンドウハンドルを探して、それからサブクラス化してソースコードを使わないで、しかしこのようにするならばHOOKを使うほうがいいです.実際,HOOKメカニズムと列挙フォームはプロセスが異なるが,最終的な目的は同じであり,いずれもサブクラス化ウィンドウのためである.だからここではどちらが優れているのか、どちらが劣っているのかを検討しません.次に本題に入り、サブクラス化の過程で出会った問題について話します.1つはsubclassを繰り返す問題で、サブクラス化の2つの方法:ウィンドウクラスまたはウィンドウプロセスを使用することです.ウィンドウクラスを使用するのは、CWndから派生したクラスであり、CWndのprotected関数SubclassWindowを呼び出す.しかし、正常にウィンドウクラス(メンバー変数を宣言し、DDX_Controlを追加)を使用すると、実際にはDDX_ControlでもSubclassWindowを使っています.1つのコントロールに変数を宣言し、Hookでサブクラス化した場合、結果はどうなりますか?答えは、プログラムのクラッシュまたはポップアップ・メッセージ・ボックス「サポートされていない操作」です.SubclassWindow関数が呼び出される前にAttachからHWNDに移動するからです.繰り返されるアタックは許されないようだ.プログラムのクラッシュを避ける方法もあります:1、コントロールのために1つのポインタ変数だけを宣言して、動的にCWnd類のインスタンスを取得して、しかしこのように皮膚を交換する目的に達しません.2、もう一つの方法は、私の試験を経て、2つのSubclassWindowの呼び出しが異なるモジュールにある場合、例えばexeにある場合、dllにある場合(私はexeでdllの関数を呼び出してdllのダイアログボックスを表示してテストした)、問題は発生しません.もっと良い方法が見つからないうちに、これも一応解決策でしょう.しかし,ウィンドウプロシージャを用いてサブクラス化するとsubclassを繰り返す問題はなく,注意深く処理すればサブクラス化は数回も問題ないが,複雑な自己描画イベントに対しては1つのウィンドウプロシージャでswitch文を書くのは面倒なようである.私は自分で新しいSubclassWindow関数を書いてCWndのウィンドウプロセスを借りることを試みたことがあります.これにより、MFC方式でメッセージ応答関数を書くことができます.残念なことに、SubclassWindowは虚関数ではなく、CWndのウィンドウプロシージャはprotectedメンバーとして存在するため、最終的には無効です.したがって,MFCのメッセージメカニズムを外部から借りることはできない.だから、自分でコードを書いてwParamとlParamを処理するのは避けられないようだ.ゼロはサブクラス化されたシステムダイアログの問題であり、システムのダイアログと自分のダイアログの表現は常に異なる.現在、すべてのシステムダイアログボックスをテストしていません.MessageBoxでポップアップされたダイアログボックスで発生した問題については、私の投稿を参照してください.
http://community.csdn.net/Expert/To....asp?id=3103399

ファイルダイアログボックスで問題が発生しました.サブクラス化されたCStaticの背景は再描画されていないようですが、本来はCStaticの親フォームが背景を担当するはずです.私はCStaticNewクラスでOnPaintだけをリロードし、中では文字とアイコンの描画だけを処理し、背景の描画は親フォームに残して完成しました.このような処理はMessageBoxと自分のAboutDlgでは問題なく、Staticコントロールの背景は親ウィンドウの背景であるが、CFIleDlgでは背景は再描画されていない.
void CStaticNew::OnPaint() 
{
CPaintDC dc(this); // device context for painting

// TODO: Add your message handler code here
CRect rt;
GetWindowRect(rt);

//
dc.SetBkMode(TRANSPARENT);

//
CFont *pfont, * pOldFont;
pfont = GetFont();
if (pfont)
pOldFont = dc.SelectObject(pfont);

CString szTitle;
GetWindowText(szTitle);
dc.DrawText(szTitle, CRect(0, 0, rt.Width(), rt.Height()), DT_LEFT | DT_WORDBREAK );

if (pfont)
dc.SelectObject(pOldFont);

//
if ((GetStyle() & SS_ICON) != 0)
{
dc.DrawIcon(0, 0, GetIcon());
}

// Do not call CStatic::OnPaint() for painting messages
}

クラス名の識別問題は、これまで私が使用してきたサブクラス化手法はGetClassNameという関数に基づいてウィンドウクラス名を取得し、spy++で得られた知識に基づいて「#32770」でダイアログボックスを表し、「ToolbarWindow 32」でツールバーなどである.しかし、ウィンドウクラス名は作成時に任意に指定できますが、CMainFrameのようなクラス名は確定できません.例えば、手帳のメインフォームのクラス名は「Notepad」、タブレットのメインフォームのクラス名は「WordPadClass」です.そうなると、サブクラス化はどのように行われるのでしょうか.Windowsがどうやってやったのか、skinmagicがどうやってやったのか知りたいです.今は主にこの3つの問題です.皆さんが討論を展開して、肌を変える完璧な解決策を提供することを望んでいます.繰り返しサブクラス化の問題を解決し,ウィンドウプロセスを簡略化するために簡略化されたCWndクラスを書いたが,それは自分の繰り返しサブクラス化をサポートしていない(すなわち,布団でクラス化されていない,あるいはCWndでサブクラス化されたHWNDにしか使用できない).MessageMapのように複雑になりたくないので、WPARAMとLPARAMを手動で変換する必要があり、メッセージ処理が継承できず、マルチスレッドがサポートされていない機能も限られています.使用は簡単です.
CWndNew* pWnd = new CWndNew;
pWnd->SubclassWindow(hWnd);

使い終わったら、リリース処理を忘れないでください.
pWnd->UnsubclassWindow();
delete pWnd;

機能拡張(継承)を行う場合は、いくつかの虚関数を書き換えます.
class CWndNew 
{
public:
CWndNew();
virtual ~CWndNew();
bool SubclassWindow(HWND hWnd);
void UnsubclassWindow();

protected: // virtual
virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
virtual void PresubclassWindow(){};
virtual void PostunsubclassWindow(){};

protected:
LRESULT PrevWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND m_hWnd;

private:
WNDPROC m_oldProc;
static map m_map;
static LRESULT CALLBACK StaticWindowProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam);
};

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

map CWndNew::m_map;

CWndNew::CWndNew()
{
m_hWnd = NULL;
}

CWndNew::~CWndNew()
{
ASSERT(m_hWnd == NULL);
}

bool CWndNew::SubclassWindow(HWND hWnd)
{
m_map[hWnd] = this;
ASSERT(m_hWnd == NULL);
m_hWnd = hWnd;

// .
PresubclassWindow();
m_oldProc = (WNDPROC) GetWindowLong(hWnd, GWL_WNDPROC);
ASSERT(m_oldProc != 0);
SetWindowLong(hWnd, GWL_WNDPROC, (LONG) StaticWindowProc);

return true;
}

void CWndNew::UnsubclassWindow()
{
SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_oldProc);
PostunsubclassWindow();
m_map.erase(m_hWnd);
m_hWnd = NULL;
}

LRESULT CALLBACK CWndNew::StaticWindowProc(HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CWndNew* pWnd = m_map[hWnd];
ASSERT(pWnd != NULL);
return pWnd->WindowProc(uMsg, wParam, lParam);
}

LRESULT CWndNew::PrevWindowProc(UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
return CallWindowProc(m_oldProc, m_hWnd, uMsg, wParam, lParam);
}

LRESULT CWndNew::WindowProc(UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
return PrevWindowProc(uMsg, wParam, lParam);
}

サブクラス化とその取り消しの順序問題については,自分のクラスあるいはプログラムサブクラス化ウィンドウを用いる場合には,MFCクラスサブクラス化との順序衝突をうまく処理する必要がある.我々自身のクラスをCWndNewと呼ぶと,CWndとCWndNewのどちらが先にウィンドウをサブクラス化しても,最終的に両者が協働した結果,そのウィンドウのウィンドウプロセスは非サブクラス化以前の状態に復元されるべきである.まず、HOOKプロセスでWM_を処理しないNCESTROYメッセージ.理由:CWndNewがCWndより先にサブクラス化されていれば、HOOKのせいで、あなたは依然としてWM_を先に処理します.NCESTROY、このときサブクラス化を取り消すと、CWndクラスはクリーンアップする機会が得られません.また、販売子を分類しないと、CWndは布団を分類したウィンドウを最初の状態に復元する能力がありません.HOOKプロセスでは、SendMessage関数を呼び出してCWndを先に処理し、自分で処理することはできません.SendMessage後、メッセージはHOOKにブロックされるからです.以上の理由により、CWndNewのメッセージ処理中にWM_が処理されるNCESTROYはいい選択ですが、MFCもそうします.以下のコードを参照して説明します.
case WM_NCDESTROY:
{
LRESULT lret;
WNDPROC wndproc;
wndproc = (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC);
if (wndproc == CWndNew::StaticWindowProc)
{
HWND hWnd = m_hWnd;
UnsubclassWindow();
lret = CallWindowProc(m_oldProc,
hWnd,
uMsg,
wParam,
lParam);
}
else
{
lret = CallWindowProc(m_oldProc,
m_hWnd,
uMsg,
wParam,
lParam);

if(wndproc == (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC))
UnsubclassWindow();
}
delete this;
return lret;
}

まず、このウィンドウのWNDPROCが変動したかどうかを判断し、なければ最良であり、急いでサブクラス化を取り消し、前のウィンドウにメッセージを伝える過程で、功成身退し、世事を問わない.変動が発生した場合,つまり別のクラスがCWndNewサブクラス化後にサブクラス化され,現在ではWM_NCESTROYはCWndNewに伝えられた.これはやりやすくて、法の砲制のようで、ニュースを引き続き前に伝えて、もしWNDPROCがまた変化が発生したら、前のあるウィンドウの過程がすでに処理したことを説明して、更にサブクラス化の操作を取り消す必要はありません.この点MFCのCWndクラスもそうしています.もう一つの問題は、Edit、ListBox、ListCtrlなどのコントロールの内蔵されたスクロールバーがどのように肌を変えたのかということです.ネット上で一般的に紹介されている方法は、元のものを隠して、自分で再実現することです.このようなSpy++では一見原形が現れるが、Skin++で肌を変えた後のスクロールバーはどうやって実現したのか分からない.私はcoolsbという文章を見たことがあります.彼はスクロールバーに肌を変える機能を実現することができますが、Comboxのサポートはよくありません.