[Container] 存有pointer的vector使用iterator取內容

使用方式如下

vector<CObj*>::iterator iterEnd = cObj.end();
vector<CObj*>::iterator iter = --iterEnd;

string strData = (*iter)->GetData();

[Reg]登入後自動啟動程式

將需執行的程式之路徑寫到下列Registry Key底下即可
Current User:
\\HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run

Local Machine:
\\HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run

一樣可以加入command line來使用。

KeyValue的Type為String,KeyValue名稱可自定

已知所支援之平台:
XP、Vista、Server2008

[Win]Mapping Disk的紀錄位置

XP、Server2008、Vista、Win7:
Key:HKEY_CURRENT_USER\Network
ValueType:String
ValueName:Remote Path

[Win]在SystemTray Icon上跳出Pop-up Menu

一開始先加入Menu的resource,然後輸入所要顯示的項目。

之後將對應的ID加入到MessageMap裡面去,並且也需要建立對應的操作行為。
這邊所需要用到的Function為ON_COMMAND()
例如:
Message Map
BEGIN_MESSAGE_MAP(CMainDlg, CDialog)
ON_COMMAND(ID_TRAYMENU_MAXIMIZE, OnTraymenuMax)
ON_COMMAND(ID_TRAYMENU_MINIMIZE, OnTraymenuMin)
ON_COMMAND(ID_TRAYMENU_CLOSE, OnTraymenuClose)
ON_COMMAND(ID_TRAYMENU_SHOWBALLOON, &CMDXMainDlg::OnTraymenuShowballoon)
END_MESSAGE_MAP()

之後再宣告CMenu物件來使用。宣告位置在function或是header內都可以。

之後需要彈出menu時,僅需做下列步驟:
     this->m_pRClkMenu->LoadMenu(nMenuID);  
     this->m_pRClkMenu->GetSubMenu(0)->TrackPopupMenu(TPM_LEFTALIGN|TPM_RIGHTBUTTON, rect.left, rect.top, this, &rect);  
     this->m_pRClkMenu->Detach();    //這行也會讓menu消失

Maximum function
In Head file
afx_msg void OnTraymenuMax();


In Cpp
void CMainDlg::OnTraymenuMax()
{
this->ShowMainForm();
}


不過目前跳出來的Pop-up menu在focus離開後並不會自行消失,且無法使用HotKey,所以這邊之後有時間再補上。

2009/12/08 - 註記
Pop-up menu不會消失的問題,只需在顯示pop-up menu之前多加個main form的SetForegroundWindow()就可以解決了。

[MFC]比較安全的殺Modeless Dialog方法

在結束dialog之前,先在PostNcDestroy()內下PostMessage()或是SendMessage給()Parent Dialog來將pointer設成NULL。
之後再刪除this,這樣也比較不會發生modeless dialog內的destructor動作沒做完前,因Parent Dialog刪除modeless dialog而造成不定時的crash事件。

PostNcDestroy可以在IDDProperties裡面設定就可用了

Sample code
PostNcDestory
void CMDXBatchJobManagerDlg::PostNcDestroy()
{
// TODO: Add your specialized code here and/or call the base class

CDialog::PostNcDestroy();

::AfxGetMainWnd()->PostMessage(WM_MDLESSDLG_CLOSE);
//::AfxGetMainWnd()->SendMessage(WM_MDLESSDLG_CLOSE);

delete this;
}

OnPostMessage
LRESULT CMDXBatchJobManagerDlg::OnPostMessage(WPARAM wParam, LPARAM lParam)
{
m_pModelessDlg = NULL; //Set null. It will be delete by itself
return TRUE;
}


message map
in header file
#define WM_MDLESSDLG_CLOSE 10058 //Any unique value


in cpp
BEGIN_MESSAGE_MAP(CMDXBatchJobManagerDlg, CDialog)
ON_MESSAGE(WM_MDLESSDLG_CLOSE, PostNcDestroy)
END_MESSAGE_MAP()

Dialog隱藏後不在task tray顯示

程式最小化至系統列的測試中發現即使程式最小化之後,還是會存在task tray內,這樣看起來是有點怪。因此需要在最小化時,另外去處理。

所需用到的Api為SetWindowLongPtr(MSDN上說明這樣在32與64平台間比較有相容性)
LONG_PTR SetWindowLongPtr(  
HWND hWnd,
int nIndex,
LONG_PTR dwNewLong
);

以及GetWindowLongPtr
LONG_PTR GetWindowLongPtr(  
HWND hWnd,
int nIndex
);

要不顯示於task tray的時候需用
SetWindowLongPtr(hWnd, GWL_EXSTYLE,GetWindowLong(hWnd, GWL_EXSTYLE) & ~WS_EX_TOOLWINDOW);

如需還原時則可使用
SetWindowLongPtr(hWnd, GWL_EXSTYLE,GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW);

這邊所使用的參數WS_EX_TOOLWINDOW據MSDN的說法,可以讓ALT+TAB的時候也看不到程式,但測試的結果是失敗的,所以看來還需要其他方法才能達成吧。
(結果上面那段寫完之後,竟然成功的在ALT+TAB裡面看不到隱藏的程式,這可真是神奇阿XDD)
參數部分可參考CreateWindowEX裡的dwExStyle。

最後完成的code如下:
LRESULT CMainDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch ( message )
{
case WM_USER: //NOTE:滑鼠移到上面就會送這個進來了
if ( lParam == WM_LBUTTONDBLCLK )
{
SetWindowLongPtr(hWnd, GWL_EXSTYLE,this->ShowWindow(SW_SHOW);
}else if ( lParam == WM_RBUTTONDOWN ) {
::AfxMessageBox("OnMessage_Right button");
}
break;
case WM_SYSCOMMAND: //NOTE:OnCancel會進來到這邊
if ( wParam == SC_MINIMIZE || wParam == SC_CLOSE)
{
if ( wParam == SC_CLOSE )
{
SetWindowLongPtr(hWnd, GWL_EXSTYLE, this->ShowWindow(SW_HIDE);
this->GotoSystemTray();
}
}
break;
}

return CDialog::WindowProc(message, wParam, lParam);
}

Reference:http://delphi.ktop.com.tw/board.php?cid=168&fid=912&tid=74287

程式最小化至系統工具列

所需要用到的API為Shell_NotifyIcon
BOOL Shell_NotifyIcon(
DWORD dwMessage,
PNOTIFYICONDATA lpdata
);

傳入的Message有NIM_ADD、NIM_MODIFY、NIM_DELETE、NIM_SETFOCUS、NIM_SETVERSION
其所需傳入的結構為 NOTIFYICONDATA
typedef struct _NOTIFYICONDATAA{
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
#if (NTDDI_VERSION < NTDDI_WIN2K)
TCHAR szTip[64];
#endif
#if (NTDDI_VERSION >= NTDDI_WIN2K)
TCHAR szTip[128];
DWORD dwState;
DWORD dwStateMask;
TCHAR szInfo[256];
union{
UINT uTimeout;
UINT uVersion; // Used with Shell_NotifyIcon flag NIM_SETVERSION.
} DUMMYUNIONNAME;
TCHAR szInfoTitle[64];
DWORD dwInfoFlags;
#endif
#if (NTDDI_VERSION >= NTDDI_WINXP)
GUID guidItem;
#endif
#if (NTDDI_VERSION >= NTDDI_VISTA)
HICON hBalloonIcon;
#endif
}

如有需要針對滑鼠點擊System tray裡面的Icon反應的話,則需在uCallbackMessage給定回傳之訊息。

之後再於
LRESULT CDialog::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
裡面去接收所需要的資訊並作相對應的動作即可

Sample-縮到System Tray:
bool CMainDlg::GotoSystemTray()
{
NOTIFYICONDATA NotifyIconData;

NotifyIconData.cbSize = sizeof (NOTIFYICONDATA);
NotifyIconData.hWnd = this->m_hWnd;
NotifyIconData.uID = IDD_MAINDLG;
NotifyIconData.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;
NotifyIconData.hIcon = hIcon;
strcpy(NotifyIconData.szTip, "This is tip!!");
NotifyIconData.dwState = NIS_SHAREDICON;
strcpy(NotifyIconData.szInfo, "This is Info!!");
strcpy(NotifyIconData.szInfoTitle,"This is Info title!!");
NotifyIconData.dwInfoFlags = NIIF_INFO;
NotifyIconData.uCallbackMessage = WM_USER;


if ( !::Shell_NotifyIcon(NIM_ADD, &NotifyIconData) )
{
CString cstrDis;
cstrDis.Format("Err = %d", ::GetLastError());
::AfxMessageBox(cstrDis);
return false;
}

return true;
}

WinProc code:
LRESULT CMainDlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
// TODO: Add your specialized code here and/or call the base class
switch ( message )
{
case WM_USER: //NOTE:滑鼠移到上面就會送這個進來了
if ( lParam == WM_LBUTTONDBLCLK )
{
::AfxMessageBox("OnMessage_Left button");
}else if ( lParam == WM_RBUTTONDOWN ) {
::AfxMessageBox("OnMessage_Right button");
}
break;
case WM_SYSCOMMAND: //NOTE:OnCancel會進來到這邊
if ( wParam == SC_MINIMIZE || wParam == SC_CLOSE)
{
::AfxMessageBox("OnSystem bar");
}
break;
}

return CDialog::WindowProc(message, wParam, lParam);
}

Reference:http://blog.chinaunix.net/u2/67530/showart_603037.html

FunctionPtr

在持有的class(即要給他人使用的class)的class內先宣告
class CClassName4PtrFunc
{
public:
typedef void (CClassName4PtrFunc::*PtrFuncName) (arg...);

public:
void FuncName(arg...);

};


之後有要用的人則先在自己的head file內加入
class CClassName4PtrFunc;



需要使用時則在cpp內做以下動作即可
#include "CClassName4PtrFunc.h"

void CIWantUsePtrFunc::UsePtrFunc(arg...)
{
CClassName4PtrFunc::PtrFuncName pPtrFunc;

(*pPtrFunc)(agr...);
}


上面內容目前還沒測過,僅用於memo用。
有誤還煩請指教。

[MFC]CListCtrl自動選擇一行

m_List.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
m_List.EnsureVisible(nIndex, FALSE);

不過使用上面方式後的選擇顯示是灰色的標記,並不是用滑鼠點選時的藍色。

如果需要選擇成藍色則如下:
m_List.SetItemState(nIndex, LVIS_SELECTED|LVIS_FOCUSED, LVIS_SELECTED|LVIS_FOCUSED);
m_List.EnsureVisible(nIndex, FALSE);
m_List.SetFocus();

但是方法僅能用在按下按鈕的同時。

如有需要在另外的Thread中來做SetFocus(),則需使用SendMessage的方式來達成。

未指定大小的vector取址問題

目前使用vector來當做資料儲存用的array(這邊的資料都是用copy的方式存入vector),剛好又需要在thread內要去更新vector內的資料。
所以是利用取址的方式來將資料寫回原本使用的vector內。

此時發生只要有新資料進來的時候,原本取到的位置的值會被變更。

這邊是懷疑因為vector自動重新分配大小時,會將原本的就資料搬到新的記憶體位置。
所以原本參照的位置就變成沒有使用或是被別的變數使用了,所以導致只要有新資料進來,值就錯誤的問題。

後來重新於一開始就設定vector的capacity,之後下去運作後,先前的問題就都不見了,所以有以上之推論。

不過在map上並沒有發生類似的狀況,所以這邊還需要在詳查一下吧

[WinAPI]FindWindow在Vista的奇異事件

話說最近嘗試透過PostMessage(HWND hwnd, LPARAM lpParam)要將一比資料從一個視窗程式傳到另一個視窗程式,這時候由於第一個程式的hWnd第二隻並不知道。所以這邊在第二個程式啟動的時候,就利用FindWindow來尋找第一個程式的hWnd。

在XP x32/x64以及Vista x64的平台上測試,都可以正正確確的找到第一個程式的hWnd。但是在"某台特定"的Vista x32上卻發生第二個程式永遠抓到一組固定的hWnd,即使是第一個程式有重開過。

這個現象就非常的不合理,因為每次視窗重新產生的時候,都會有不同的hWnd,所以這邊的FindWindow抓到了誰,那就不得而知了。所以這邊在做PostMessgae的時候,想當然,資料就永遠傳不到第一個程式了。

後來在網路上搜尋了一下,發現還有EnumWindow(WNDENUMPROC EnumProc, LPARAM lParam)可以用來取得所有視窗的hWnd。這個API還須透過一個Callback function來使用,就是EnumWindowProc(HWND hWnd, LPARAM lParam)。第二個參數看個人需求可選擇自行傳遞,這邊是因為要取得第一個程式的hWnd,所以這邊是傳入第一個程式的PID。

由於這邊是使用PID來比對,所以還需要透過GetWindowThreadProcessId(HWND hWnd,LPDWORD lpdwProcessId)來取得目前所列舉的視窗的PID。

之後就可以利用取得之hWnd來進行PostMessage了。

Callback的範例如下
BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
DWORD iPID = 0;

if ( GetWindowThreadProcessId(hwnd, &iPID) != NULL )
{
if ( iPID == (DWORD)lParam )
{
m_1sthWnd = hwnd;
return FALSE; //Find the PID and stop the callback function
}
}
return TRUE; //Continue the callback function
}


EnumWindow使用範例如下
::EnumWindows(&EnumWindowsProc, (LPARAM)m_1stPID);

2009/Sep/30-Add
不過這種方法在遇到會換hWnd的Dialog下就會出問題(目前我遇到的是切換combo會造成hWnd的變更),所以最保險的還是用FindWindow會比較好。

不過還是要找比較安全的作法才行。

Build docker image from multiple build contexts

Build docker image from multiple build contexts ...