記事ディレクトリ
1. デュリブの紹介
中国初のオープンソースのdirectuiインターフェイス ライブラリで、WYSIWYG 開発ツールであるUIDesignerを提供します。メイン フレーム ウィンドウのみを持ち、残りのスペースは描画によって実現されるため、コントロールのハンドルやウィンドウ クラスはありません。UIDesigner ツールを通じてユーザー定義のウィンドウを xml ファイルに保存し、ウィンドウの作成時に xml ファイルの内容を読み取り、対応するコントロールを描画します。現在、duilibで書かれたインターフェースが数多く存在しており、インターネットにアクセスして関連情報を収集することができます。
2. 基本フレームウィンドウ
まず、 Win32系のプロジェクトを新規作成し、main関数を追加します。次に、新しいクラスを作成します。これをCDuiFrameWndと呼びます。クラスのソース コードは次のとおりです。
//头文件
#include <DuiLib\UIlib.h>
using namespace DuiLib;
class CDuiFrameWnd :public CWindowWnd{
public:
CDuiFrameWnd();
~CDuiFrameWnd();
virtual LPCTSTR GetWindowClassName() const;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
protected:
CPaintManagerUI m_PaintManager;
};
//cpp文件
LPCTSTR CDuiFrameWnd::GetWindowClassName() const
{
return _T("DuiFrameWnd");
}
LRESULT CDuiFrameWnd::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
if (WM_CLOSE == uMsg)
{
::CloseWindow(m_hWnd);
::DestroyWindow(m_hWnd);
}
if (WM_DESTROY == uMsg)
{
::PostQuitMessage(0);
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
対応する dll ファイルを使用できるようにするには、対応する lib ファイルをインポートする必要もあります。パブリック ヘッダー ファイルに次のコードを追加します。
#ifdef _DEBUG
# ifdef _UNICODE
# pragma comment(lib, "Duilib_ud.lib")
# else
# pragma comment(lib, "Duilib_d.lib")
# endif
#else
# ifdef _UNICODE
# pragma comment(lib, "Duilib_u.lib")
# else
# pragma comment(lib, "Duilib.lib")
# endif
#endif
main関数のコードは次のとおりです。
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
CPaintManagerUI::SetInstance(hInstance);
CDuiFrameWnd duiFrame;
//#define UI_WNDSTYLE_FRAME (WS_VISIBLE | WS_OVERLAPPEDWINDOW)
duiFrame.Create(NULL, _T("测试"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
duiFrame.ShowWindow();
CPaintManagerUI::MessageLoop();
return 0;
}
これらのコードは、基本的なフレーム ウィンドウの生成に役立ちます。また、duilibはwin32 APIのパッケージであるため、 win32プログラミング メソッドを直接使用できることを常に覚えておく必要があります。将来使用されないものがある場合は、win32 APIを使用して関連関数の記述を完了できます。
3. フレームワークの分析
単一ドキュメントのフレーム ウィンドウを生成できるため、コード内の手順は基本的に純粋なwin32 APIを使用する場合と同じです。そのため、この考え方に従ってフレームの簡単な分析を実行します。
main 関数の最初の部分は、CPaintManagerUI::SetInstance(hInstance);というコードです CPaintManagerUI クラスの関数については、よくわかりません。このクラスの関連コードをよく見ていませんが、この文は主に プロセス のインスタンス ハンドルを取得します。今のところは心配しないでください。次の手順は主に、クラスCDuiFrameWndまたはその基本クラスCWindowWndで完了します。
3.1. ウィンドウクラスの作成
main 関数の 2 番目のコードは、主にクラスCDuiFrameWndオブジェクトの作成を完了します。対応するコンストラクターをたどったところ、冗長な操作が行われていないことがわかりました。構築方法に関係なく、次はクラスの Create 関数を呼び出してウィンドウを作成します。この関数のコードは次のとおりです。
HWND CWindowWnd::Create(HWND hwndParent, LPCTSTR pstrName, DWORD dwStyle, DWORD dwExStyle, int x, int y, int cx, int cy, HMENU hMenu)
{
if( GetSuperClassName() != NULL && !RegisterSuperclass() ) return NULL;
if( GetSuperClassName() == NULL && !RegisterWindowClass() ) return NULL;
m_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this);
ASSERT(m_hWnd!=NULL);
return m_hWnd;
}
主に 2 番目の if のコードを見てみましょう。まず、親ウィンドウの文字列が NULL で、次にRegisterWindowClass が実行されます。さらにRegisterWindowClassをたどると、そのコードは次のようになります。
bool CWindowWnd::RegisterWindowClass()
{
WNDCLASS wc = {
0 };
wc.style = GetClassStyle();
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hIcon = NULL;
wc.lpfnWndProc = CWindowWnd::__WndProc;
wc.hInstance = CPaintManagerUI::GetInstance(); //之前设置的实例句柄在这个地方使用
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = GetWindowClassName();
ATOM ret = ::RegisterClass(&wc);
ASSERT(ret!=NULL || ::GetLastError()==ERROR_CLASS_ALREADY_EXISTS);
return ret != NULL || ::GetLastError() == ERROR_CLASS_ALREADY_EXISTS;
}
最初に行うべきことはウィンドウ クラスを作成することであり、ウィンドウ クラスを作成する際の主な関心事は、ウィンドウクラスのlpfnWndProcメンバーとlpszClassNameであることがわかりました。lpszClassName は、派生クラスで書き直した関数GetWindowClassNameを呼び出します。そのため、ポリモーフィズムに従って派生クラスのGetWindowClassName関数を呼び出し、指定した文字列をウィンドウ クラスのクラス名として使用します。
3.2. ウィンドウクラスの登録
上記のコードから、登録されたコードもRegisterWindowClassに配置されていることがわかります。最後に、RegisterClass 関数が呼び出されて登録が完了します。
3.3. ウィンドウの作成
RegisterWindowClassが実行されると、次のコード、つまりm_hWnd = ::CreateWindowEx(dwExStyle, GetWindowClassName(), pstrName, dwStyle, x, y, cx, cy, hwndParent, hMenu, CPaintManagerUI::GetInstance(), this); が実行され、ウィンドウの作成タスクが完了します。
3.4. 表示ウィンドウ
Create関数が実行された後、次のduiFrame.ShowWindow()が実行されます。この関数に従い、関数コードは次のとおりです。
void CWindowWnd::ShowWindow(bool bShow /*= true*/, bool bTakeFocus /*= false*/)
{
ASSERT(::IsWindow(m_hWnd));
if( !::IsWindow(m_hWnd) ) return;
::ShowWindow(m_hWnd, bShow ? (bTakeFocus ? SW_SHOWNORMAL : SW_SHOWNOACTIVATE) : SW_HIDE);
}
ShowWindow関数のデフォルトの受信パラメータは bShow = true、bTakeFocus = false です。ShowWindow関数が最後に呼び出されるとき、 bShowおよびbTakeFocusに従って渡す価値があります。コードによると、パラメータが渡されない場合、コードShowWindow(m_hWnd, SW_SHOWNOACTIVATE);が必要であることがわかりました。
3.5、メッセージループ
メッセージ ループは実際にはコードCPaintManagerUI::MessageLoop();によって完了します。MessageLoop関数に従って確認します。
MSG msg = {
0 };
while( ::GetMessage(&msg, NULL, 0, 0) ) {
if( !CPaintManagerUI::TranslateMessage(&msg) ) {
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
}
この関数では、メッセージ ループが実行されます。
3.6. コールバック関数
上では、lpfnWndProc関数ポインターについては言及せずにそのままにしました。次に、この部分を説明し、対応するコンストラクターをたどって、クラス自体は操作を行わないが、親クラスのコンストラクターが関連する初期化操作を実行することを確認しましょう。対応するコードは次のとおりです:
CWindowWnd::CWindowWnd() : m_hWnd(NULL), m_OldWndProc(::DefWindowProc), m_bSubclassed(false)
{
}
これは、デフォルトのメッセージを処理するために、 lpfnWndProc が__WndProc を指すようにします。これは静的ハンドラー関数であり、そのコードは次のとおりです。
LRESULT CALLBACK CWindowWnd::__WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CWindowWnd* pThis = NULL;
if( uMsg == WM_NCCREATE ) {
LPCREATESTRUCT lpcs = reinterpret_cast<LPCREATESTRUCT>(lParam);
pThis = static_cast<CWindowWnd*>(lpcs->lpCreateParams);
pThis->m_hWnd = hWnd;
//当开始创建窗口将窗口类对象的指针放入到对应的GWLP_USERDATA字段中
::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LPARAM>(pThis));
}
else {
//取出窗口类对象的指针
pThis = reinterpret_cast<CWindowWnd*>(::GetWindowLongPtr(hWnd, GWLP_USERDATA));
if( uMsg == WM_NCDESTROY && pThis != NULL ) {
LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, hWnd, uMsg, wParam, lParam);
::SetWindowLongPtr(pThis->m_hWnd, GWLP_USERDATA, 0L);
if( pThis->m_bSubclassed ) pThis->Unsubclass();
pThis->m_hWnd = NULL;
pThis->OnFinalMessage(hWnd);
return lRes;
}
}
if( pThis != NULL ) {
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else {
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
上記のコードでは、ウィンドウの作成時にウィンドウ クラス オブジェクト ポインタが対応する場所に保存されるため、他の場所で取得して使用することができます。return pThis->HandleMessage(uMsg, wParam, lParam);この文によって呼び出される特定のオブジェクトのHandleMessageにより、対応する派生クラスで対応する仮想関数を定義しているため、ポリモーフィズムに従って、特定のメッセージを処理するために書き換えた仮想関数を呼び出します。関心のないメッセージについては、LRESULT lRes = ::CallWindowProc(pThis->m_OldWndProc, h Wnd) を呼び出します。 、uMsg、wParam、lParam); または DefWindowProc の基本クラスのコンストラクターを確認すると、 m_OldWndProc が実際には DefWindowProc であることがわかりました。
CWindowWnd::CWindowWnd() : m_hWnd(NULL), m_OldWndProc(::DefWindowProc), m_bSubclassed(false)
{
}
4. まとめ
上記では duilib の基本的なフレームワークを説明しましたが、以下にそれを要約しましょう。
- CPaintManagerUI::SetInstance(hInstance) ;プロセスのインスタンス ハンドルを設定します。この値はウィンドウ クラスを登録するときに使用されます。最初に .exe ファイルのパスを設定し、その後で XML のパスを設定します。
- CWindowWndクラスでは、Create関数によってウィンドウ クラスの作成と登録、およびウィンドウの作成が完了します。
- ウィンドウを表示するには、CWindowWndクラスのShowWindow関数を使用します。
- メッセージ ループはCPaintManagerUI::MessageLoop() ; コードによって実行されます。
- 最後に、関心のあるメッセージを処理するためにHandleMessage()関数を書き直す必要があります。最後に、基本クラスのHandleMessage()関数を呼び出す必要があります。主に、関心のないメッセージを処理するためにDefWindowProcを呼び出します。