2011年11月20日 星期日

Keep Menu Expand - MFC

在VS 2008 Feature Pack後MFC的menu有了expand的style,VS 2010進一步整合feature pack的GUI行為在建立專案時就可以選擇新的menu style。
不過新功能有一個很不好用的地方,不管你應用程式中的選單數量是多還是少,都會預設的收折(collapse)。
選單收折的新功能是由下面這段程式碼所引發

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ...

    CList< UINT, UINT> lstBasicCommands;

    lstBasicCommands.AddTail(ID_FILE_NEW);
    lstBasicCommands.AddTail(ID_FILE_OPEN);
    lstBasicCommands.AddTail(ID_FILE_SAVE);
    lstBasicCommands.AddTail(ID_FILE_PRINT);
    lstBasicCommands.AddTail(ID_APP_EXIT);
    lstBasicCommands.AddTail(ID_EDIT_CUT);
    lstBasicCommands.AddTail(ID_EDIT_PASTE);
    lstBasicCommands.AddTail(ID_EDIT_UNDO);
    lstBasicCommands.AddTail(ID_APP_ABOUT);
    lstBasicCommands.AddTail(ID_VIEW_STATUS_BAR);
    lstBasicCommands.AddTail(ID_VIEW_TOOLBAR);

    CMFCToolBar::SetBasicCommands(lstBasicCommands);
}

CMFCToolBar::SetBasicCommands是採正面列表的方式,指定基本選單。這些基本選單在使用者點選選單時,會保持可視狀態而不會被隱藏,而不在清單中的選單則自動收藏起來。因此如果你的選單是不想被收折起來,只要把選單的ID加入到lstBasicCommands中即可。 如果應用程式不希望有收折的功能,則只要把這段程式刪除即可。

2011年10月31日 星期一

註冊自已的dialog base類別

有時候我們會設計一些跨行程的應用程式,當我們需要取得另一行程的視窗Handle對其做操作時通常會利用FindWindow來找到我們想要的視窗。

FindWindow可利用title name或是windows class二種方式來找到指定的視窗。為避免命名上的衝突或是多語系的支援,通常我們會重新註冊我們的dialog類別為特殊的名稱。註冊的方式分為2部份。

Step 1:首先找到你的RC檔,在dialog的rc區加入以下的程式碼

IDD_MYCLASSDLG_DIALOG DIALOGEX 0, 0, 200, 200
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
CAPTION "MyClass Dialog"
CLASS "MyClassDlg"
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
...


也可透過Visual Studio的GUI來修改,先將RC設定為非MFC Mode


如此一來,Dialog的屬性Class Name變為可修改狀態,再輸入指定的Class Name即可



Step 2:設定好RC屬性後,在App的InitInstance中呼叫main dialog之前註冊MyClassDlg即可完成註冊

WNDCLASS wndcls = { 0 };
::GetClassInfo(AfxGetInstanceHandle(),MAKEINTRESOURCE(32770),&wndcls);

wndcls.lpszClassName = _T("MyClassDlg");

AfxRegisterClass(&wndcls);

CMyClassDlg dlg;
m_pMainWnd = &dlg;
INT_PTR nResponse = dlg.DoModal();
...



2011年8月16日 星期二

shared_ptr / unique_ptr vs auto_ptr

shared_ptr與unique_ptr是C++0x中用來取代過去auto_ptr以避免定去的定義曖昧不明所造成的誤用

下面這段code是從The C++ Standard Library的autoptr1.cpp中節錄出來的

int main(void)
{
 auto_ptr p(new int(42));
 auto_ptr q;

 cout << "after initialization: " << endl;
 cout << " p: " << p << endl;
 cout << " q: " << q << endl;

 q = p;
 cout << "after assigning auto pointers: " << endl;
 cout << " p: " << p << endl;
 cout << " q: " << q << endl;

 *q += 13;
 p = q;
 cout << "after change and reassignment: " << endl;
 cout << " p: " << p << endl;
 cout << " q: " << q << endl;

 return 0;
}

執行結果:
after initialization: 
 p: 42
 q: NULL
after assigning auto pointers:
 p: NULL
 q: 42
after change and reassignment:
 p: 55
 q: NULL

 如果改用shared_ptr取代auto_ptr
shared_ptr p(new int(42));
shared_ptr q;
編譯時無錯誤或警告,而執行果結則變成了

after initialization:
 p: 42
 q: NULL
after assigning auto pointers:
 p: 42
 q: 42
after change and reassignment:
 p: 55
 q: 55

使用shared_ptr的物件,當與其他物件共享時,其內部會將其參用計數+1,而auto_ptr則不允許共享,會進行所有權的轉移。 如果改用unique_ptr取代auto_ptr
unique_ptr p(new int(42));
unique_ptr q;
則在編譯時
q = p;  // error C2248: 'std::unique_ptr<_Ty>::operator =' : ...
...
q = p;  // error C2248: 'std::unique_ptr<_Ty>::operator =' : ...
都產生了error C2248的錯誤,使用unique_ptr的物件是不允許物件共享,而為了明確表示物件所有權的轉移,必需使用std::move來轉移所有權。程式需改為:
q = move(p); // q = p;
...
p = move(q); // q = p;

執行結果與auto_ptr相同。

2011年4月19日 星期二

Building DMODemo

今天試著要build一個directshow的範例DMODemo結果卻跑出下面的error

C:\Program Files\Microsoft SDKs\Windows\v7.0A\include\objidl.h(11280): error C2061: syntax error : identifier '__RPC__out_xcount_part'

一開始以為是用錯SDK的library,因此我把Platform設定改為Windows7.1SDK。不幸的是錯誤依舊。


__RPC__out_xcount_part 是定義在 rpcsal.h 之中,我試著檢查SDK v7.0A與v7.1目錄下的rpcsal.h,確定這2個.h檔都有定義__RPC__out_xcount_part,很明顯的是include到錯誤的目錄。當一個人的電腦灌了好幾種編譯器與SDK這個問題是常常發生的。

進一步磁碟搜尋後在另外2個目錄下發現rpcsal.h檔,分別是在 C:\Program Files\Microsoft DirectX SDK (June 2010)\Include 與 C:\Program Files\Microsoft DirectX SDK (November 2007)\Include 之中。


答案已呼之欲出,DirectX SDK (November 2007)下的rpcsal.h未定義__RPC__out_xcount_part,罪人就是這個檔案。

修改的方式有2種,第一種比較簡單:把專案中[Property Pages] → [Configuration Properties] → [C/C++] → [General] → [Additional Include Directory]原本的設定$(DXSDK_DIR)Include刪除。

或是修改系統的DXSDK_DIR路徑為
C:\Program Files\Microsoft DirectX SDK (June 2010)\Include\

第2種作法比較一勞永役,但系統必需重開才能生效,不過如果你希望預設的DX版本為2007 Nov版的話,就只能採用第一種改法。

2011年4月13日 星期三

iota 不是 itoa

今天在看C9的 Standard Template Library (STL), 7 of n 時看到一個蠻有趣的東西 ─ iota(唸i.o.ta,我可沒拼錯字喔)。這個STL algorithm連VS 2010的Help也查不出來,但實際上是有功能的。此函式可以產生遞增的連續資料。

如:

iota(v.begin(),v.end(),0);

結果會產生出,0, 1, 2, 3, ...的數字。

如果是改為

iota(v.begin(),v.end(),7);


結果會產生出,7, 8, 9, 10, ...的數字。

這功能還蠻有用的。特別是對於初始化如調色盤這類型的東西。

2011年3月23日 星期三

warning C4996: _Is_checked? No I don't.

如果我們在VS2010用非STL的container來使用某些STL algorithm時很容易得到傳說中的warning C4996。

warning C4996: 'std::_Merge1': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators'

除了關掉4996的warning訊息外。可對cl編譯器指定/D_SCL_SECURE_NO_WARNINGS,煩人的警告訊息OUT

2011年3月16日 星期三

知識更新 MFC Modeless Dialog 生成與結束

Windows的dialog有二種模式,model與modeless。所謂的model模式指的是當dialog產生時,該應用程式的訊息會被鎖住,直到dialog結束。而modeless dialog則較像是程式中的floating panel一樣可和應用程式一同運作,能做出較複雜的功能。

如果要產生一個modeless dialog

我所會的舊知識(這是MS VC 1.5時代學到的)

1. 建立一個新的CModeless::Create(CWnd* pParent)函式。
2. 呼叫端中必需記錄該dialog object。
3. 呼叫端必需有closedialog函式。
4. 要產生modeless dialog時必需透過new、Create、與ShowWindow三個步驟。
5. 當對話盒要關閉時,需使用呼叫端的closedialog函式而非自行解構。
6. 當程式要結束時,必需檢查dialog object是否已解構,否則必需呼叫closedialog函式以避免memory leakage的發生。

今天的新知識(可參考VC Sample : Modeless)

1. 建立一個新的CModeless::Create(CWnd* pParent)函式。
2. 呼叫端中不必有記錄該dialog object。
3. 呼叫端不必有closedialog函式。
4. 要產生modeless dialog時必需透過new與Create ShowWindow 個步驟。
5. 當對話盒要關閉時,透過WM_NCDESTROY訊息自行解構。
6. 當程式要結束時,必需檢查dialog object是否已解構,否則必需呼叫closedialog函式以避免memory leakage的發生。

以前在呼叫Create之後必需再呼叫ShowWindow(SW_SHOW)對話盒才可視,但可在resouce中將對話盒Visible屬性設為True即可在Create後直接出現,省去了呼叫ShowWindow的動作。

以前要在呼叫端記錄dialog object是為了在程式結束時能順利解構,但透過接收WM_NCDESTROY訊息可正確得知視窗需解構的時間點,是否要儲存dialog object就不再是必需。

VC的Modeless sample在收到WM_NCDESTROY後馬上就執行delete this,對自己進行解構。
void CModeless::OnNcDestroy()
{
    delete this;
}

但CDialog::OnNcDestroy會釋放dialog中的controller元件的資源,若未先呼叫可能會有memory leakage的疑慮。我加入了這段
void CModeless::OnNcDestroy()
{
    CDialog::OnNcDestroy();
    delete this;
}

2011年1月19日 星期三

max不一定真的是max


今天犯了一個蠻笨的錯誤

我在做一個功能的測試程式,透過rand( )亂數產生一些數值,數值必需介於5100之間,於是我很天才的這樣寫:

a = max(5,rand()%100);

結果a有可能會低於5,這不是靈異現象,也不是compiler出了問題,是我誤會了maxmaxVS2008中並非函式,而僅是一個巨集,定義在WinDef.h

#define max(a,b)            (((a) > (b)) ? (a) : (b))

看出問題了嗎,如果把巨集展開

a = (((5) > (rand()%100)) ? (5) : (rand()%100));

rand()在過程中呼叫了2次,使用max(或min)要小心,勿使用到會改變自身狀態的函式或物件。

這個例子比較好的寫法是    a = 5 + (rand() % 95); 省去了max的運算