2009年3月22日 星期日

動態產生對話框

因為工作上的需要,所以我需要一個動態產生對話框的程式,

經過survey之後,發現CDialog::InitModalIndirected可以滿足我的需求。

// 這些程式碼僅供概念驗證用,不表示這是沒有問題的程式碼。
// 本人亦不對這些程式碼所產生的損害負責。

wchar_t szCaption[] = L"hi";
wchar_t szFontname[] = L"Tahoma";
wchar_t szItemCaption[] = L"Hello";

DWORD dwSize =
((sizeof(DLGTEMPLATE) + sizeof(WORD) * 2 + sizeof(szCaption) + sizeof(WORD) + sizeof(szFontname) + 3) & ~3) +
3 * ((sizeof(DLGITEMTEMPLATE) + sizeof(WORD) + sizeof(WORD) + sizeof(szItemCaption) + sizeof(WORD) + 3) & ~3);

LPBYTE pBuf = new BYTE[dwSize];

memset(pBuf, 0, dwSize);

LPBYTE ptr = pBuf;
LPDLGTEMPLATE pDlgTmpl = (LPDLGTEMPLATE) pBuf;
LPDLGITEMTEMPLATE pItemTmpl = NULL;

pDlgTmpl->cdit = 2;
pDlgTmpl->cx = 200;
pDlgTmpl->cy = 300;
pDlgTmpl->dwExtendedStyle = WS_EX_APPWINDOW;
pDlgTmpl->style = WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

pDlgTmpl->x = 100;
pDlgTmpl->y = 100;

ptr += sizeof(DLGTEMPLATE);
memset(ptr, 0, sizeof(WORD) * 2);
ptr += sizeof(WORD) * 2;
memcpy_s(ptr, pBuf + dwSize - ptr, szCaption, sizeof(szCaption));
ptr += sizeof(szCaption);

*(WORD *)ptr = 9;
ptr += sizeof(WORD);
memcpy_s(ptr, pBuf + dwSize - ptr, szFontname, sizeof(szFontname));
ptr += sizeof(szFontname);

for (int i=0;i<2;i++) ptr =" (BYTE" pitemtmpl =" (LPDLGITEMTEMPLATE)">cx = 50;
pItemTmpl->cy = 30;
pItemTmpl->dwExtendedStyle = 0;
pItemTmpl->id = 0x1000 + i;
pItemTmpl->style = WS_CHILD | WS_VISIBLE;
pItemTmpl->x = 10;
pItemTmpl->y = 10 + i * 100;

ptr += sizeof(DLGITEMTEMPLATE);
*(WORD *)ptr = 0xFFFF;
ptr += sizeof(WORD);
*(WORD *)ptr = 0x0080;
ptr += sizeof(WORD);

memcpy_s(ptr, pBuf + dwSize - ptr, szItemCaption, sizeof(szItemCaption));
ptr += sizeof(szItemCaption);
*(WORD *)ptr = 0;
ptr += sizeof(WORD);
}

CDialog dlg;
dlg.InitModalIndirect(pDlgTmpl);
dlg.DoModal();

但是透過昨天一整天的打拼之後,發現我的程式一整個是徒勞無功,

因為我只能開對話框出來,一旦我的對話框上面有任何控制項的話,

我的對話框就建不出來,於是我把程式reduce到Win32 SDK上面處理,

把我的所有參數餵給CreateDialogIndirect,然後自己再隨便弄一個 DLGPROC。

登登,結果還是失敗的,如果我的對話框沒有任何控制項在上面的話,程式一切正常,

而且我的DLGPROC還會收到許許多多的對話框訊息,看起來一切正常;

但是我的對話框只要有任何一個控制項在裡面的話,就會發生錯誤,

CreateDialogIndirected傳回(HWND) NULL,可是GetLastError()又只會傳回 (DWORD) 0,

比較特別的是DLGPROC會收到一個WM_DESTROY跟一個WM_NCDESTROY,

這明確的表示出我的程式的對話框是有開出來的,但是上面的控制項可能開不出來,

或是有其他原因,所以我的對話框會直接結束…

結果今天下午繼續弄,發現我的DLGTEMPLATE的dwStyle少了一個屬性,DS_SETFONT,

我簡直快瘋了,為了這個小小的參數讓我搞了昨天一整天 :s

所以可以正常跑的程式如下:

// 這些程式碼僅供概念驗證用,不表示這是沒有問題的程式碼。
// 本人亦不對這些程式碼所產生的損害負責。

wchar_t szCaption[] = L"hi";
wchar_t szFontname[] = L"Tahoma";
wchar_t szItemCaption[] = L"Hello";

DWORD dwSize =
((sizeof(DLGTEMPLATE) + sizeof(WORD) * 2 + sizeof(szCaption) + sizeof(WORD) + sizeof(szFontname) + 3) & ~3) +
3 * ((sizeof(DLGITEMTEMPLATE) + sizeof(WORD) + sizeof(WORD) + sizeof(szItemCaption) + sizeof(WORD) + 3) & ~3);

LPBYTE pBuf = new BYTE[dwSize];

memset(pBuf, 0, dwSize);

LPBYTE ptr = pBuf;
LPDLGTEMPLATE pDlgTmpl = (LPDLGTEMPLATE) pBuf;
LPDLGITEMTEMPLATE pItemTmpl = NULL;

pDlgTmpl->cdit = 2;
pDlgTmpl->cx = 200;
pDlgTmpl->cy = 300;
pDlgTmpl->dwExtendedStyle = WS_EX_APPWINDOW;
pDlgTmpl->style = WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | DS_SETFONT;

pDlgTmpl->x = 100;
pDlgTmpl->y = 100;

ptr += sizeof(DLGTEMPLATE);
memset(ptr, 0, sizeof(WORD) * 2);
ptr += sizeof(WORD) * 2;
memcpy_s(ptr, pBuf + dwSize - ptr, szCaption, sizeof(szCaption));
ptr += sizeof(szCaption);

*(WORD *)ptr = 9;
ptr += sizeof(WORD);
memcpy_s(ptr, pBuf + dwSize - ptr, szFontname, sizeof(szFontname));
ptr += sizeof(szFontname);

for (int i=0;i<2;i++) ptr =" (BYTE" pitemtmpl =" (LPDLGITEMTEMPLATE)">cx = 50;
pItemTmpl->cy = 30;
pItemTmpl->dwExtendedStyle = 0;
pItemTmpl->id = 0x1000 + i;
pItemTmpl->style = WS_CHILD | WS_VISIBLE;
pItemTmpl->x = 10;
pItemTmpl->y = 10 + i * 100;

ptr += sizeof(DLGITEMTEMPLATE);
*(WORD *)ptr = 0xFFFF;
ptr += sizeof(WORD);
*(WORD *)ptr = 0x0080;
ptr += sizeof(WORD);

memcpy_s(ptr, pBuf + dwSize - ptr, szItemCaption, sizeof(szItemCaption));
ptr += sizeof(szItemCaption);
*(WORD *)ptr = 0;
ptr += sizeof(WORD);
}

CDialog dlg;
dlg.InitModalIndirect(pDlgTmpl);
dlg.DoModal();