初學ATL...從頭開始吧
<B>#include <shlobj.h>
#include <comdef.h></B>
class ATL_NO_VTABLE CDLLRegShlExt :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CDLLRegShlExt, &CLSID_DllRegShlExt>,
<STRIKE>public IDllRegShlExt,</STRIKE>
<B>public IShellExtInit</B>
{
BEGIN_COM_MAP(CDLLRegShlExt)
<STRIKE>COM_INTERFACE_ENTRY(IDllRegShlExt)</STRIKE>
<B>COM_INTERFACE_ENTRY(IShellExtInit)</B>
END_COM_MAP()
template<
class ThreadModel
>
class CComObjectRootEx : public CComObjectRootBase
這里CComObjectRootEx是一個ATL的類
我看來大概是管理Com對象的一個類,是搞計數的吧,一個實現COM的類必須要繼承它的
它還是個模板類,模板參數是ThreadModel:
CComSingleThreadModel, CComMultiThreadModel, or CComMultiThreadModelNoCS. You can accept the server's default thread model by setting ThreadModel to CComObjectThreadModel or CComGlobalsThreadModel
A class that implements a COM server must inherit from CComObjectRootEx or CComObjectRoot.
template<
class T,
const CLSID* pclsid = &CLSID_NULL
>
class CComCoClass
CComCoClass的類應該之前
繼續加一些新的東西 看MSDN Magzine 關于COM的東西,提到一些對于rgs注冊腳本的使用前提
Way back in the November and December 1999 issues of Microsoft Systems Journal (now known as MSDN®Magazine), I showed how to build a Band Object for Internet Explorer using the Active Template Library (ATL) IRegistrar interface. (Band Objects need to register a special category CATID_DeskBand.) IRegistrar is a really cool tool that lets you write a registration script (.RGS file) to add your registry entries, instead of calling registry functions like RegOpenKey, RegSetValue, and the rest. Figure 1 shows a typical script.
這一段講IRegistrar接口的作用,hoho
Minimize your app to systray in 8 easy steps
By Yasar Arslan. From codeproject
這篇文章內容比較基礎,最近看到覺得有用,順便翻譯一下
有空可以寫一個自己的TrayIcon類,化簡這些原始的操作。
Introduction
這篇文章解析了 Shell_NotifyIcon
這個函數用法--用來建立你自己的應用程序的系統托盤圖標.
這篇文章給了基本的縮小到托盤的操作過程并讓你從中了解.
這篇文章提供8個簡單的步驟讓你成功的實現在你的程序中建立系統托盤圖標.
源代碼提供了一個基于對話框的演示程序.
Tray Icons
為了用托盤圖標你需要用一個shell函數 :)
BOOL Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid );
The dwMessage
?可選的參數包括 the NIM_ADD,NIM_DELETE and NIM_MODIFY
功能分別是添加刪除以及修改圖標于系統圖標.
PNOTIFYICONDATA
結構包括這些系統需要處理的任務圖標狀態區域消息等信息.
typedef??struct _NOTIFYICONDATA {
??? DWORD cbSize;
??? HWND hWnd;
??? UINT uID;
??? UINT uFlags;
??? UINT uCallbackMessage;
??? HICON hIcon;
??? #if (_WIN32_IE < 0x0500)
??????? TCHAR szTip[64];
??? #else
??????? TCHAR szTip[128];
??? #endif??? #if (_WIN32_IE >= 0x0500)
??????? DWORD dwState;
??????? DWORD dwStateMask;
??????? TCHAR szInfo[256];
??????? union {
??????????? UINT? uTimeout;
??????????? UINT? uVersion;
??????? } DUMMYUNIONNAME;
??????? TCHAR szInfoTitle[64];
??????? DWORD dwInfoFlags;
??? #endif??? #if (_WIN32_IE >= 0x600)
??????? GUID guidItem;
??? #endif
} NOTIFYICONDATA, *PNOTIFYICONDATA;
*Note:?更完全的信息可以去參考MSDN
Creating the Application
Create a new VC++ dialog based project and call it TrayMin.
創建一個名叫TrayMin的基于對話框的VC++工程
Step: 1
自定義消息于 TrayMinDlg.h 頭文件.
#define WM_TRAY_MESSAGE (WM_USER + 1)
The WM_USER
常量用來幫助用戶定義自己的消息被用來建立個人的窗口類, 定義時通常用這種格式 WM_USER+X
,?這里 X 是一個整形變量.
*更詳細的看MSDN
Step: 2
現在在Now add the DECLARE_MESSAGE_MAP() 之前添加下面的用戶函數吧( TrayMinDlg.h file)?? afx_msg void OnTrayNotify(WPARAM wParam, LPARAM lParam);
?
當添加一個圖標到托盤時這有一個圖標的回調消息,注意到 NOTIFYICONDATA
結構中有uCallbackMessage成員是回調消息識別的關鍵,它會被傳給NIM_ADD(我們之后將會見到更詳細的)。當添加托盤圖標這個事件發生時,系統發送一個回調函數到由hWnd成員對象指定的窗口過程(winproc),wParam 參數可以用來被識別究竟發生了什么操作。lParam參數存放發生事件相關的鼠標或者鍵盤消息。舉個例子,當一個鼠標指針指向一個托盤圖標,lParam將包括WM_MOUSEMOVE
Step: 3
現在添加下面的這行在消息宏中(MessageMap)在TrayMinDlg.cpp
ON_MESSAGE(WM_TRAY_MESSAGE,OnTrayNotify)
?
現在應該是這樣的.
BEGIN_MESSAGE_MAP(CTrayMinDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_MESSAGE(WM_TRAY_MESSAGE ,OnTrayNotify)
END_MESSAGE_MAP()
Step: 4
現在在TrayMinDlg.cpp 定義OnTrayNotify函數,不要忘記在函數頭部添加afx_msg。
afx_msg void CTrayMinDlg::OnTrayNotify(WPARAM wParam, LPARAM lParam)
{
UINT uID;
UINT uMsg;
uID = (UINT) wParam;
uMsg = (UINT) lParam;
if (uID != 1)
return;
CPoint pt;
switch (uMsg )
{
case WM_LBUTTONDOWN:
GetCursorPos(&pt);
ClientToScreen(&pt);
OnTrayLButtonDown(pt);
break;
case WM_RBUTTONDOWN:
case WM_CONTEXTMENU:
GetCursorPos(&pt);
OnTrayRButtonDown(pt);
break;
}
return;
}
Step: 5
現在在TrayMinDlg類添加兩個成員函數來相應鼠標事件。
實現鼠標左鍵單擊的相應
-
函數類型:void
?
-
函數聲明:
?OnTrayLButtonDown(CPoint pt)
實現鼠標右鍵單擊的相應
- 函數類型:
void
- 函數聲明:
OnTrayRButtonDown(CPoint pt)
OnTrayLButtonDown(CPoint pt)的定義如下
.
void CTrayMinDlg::OnTrayLButtonDown(CPoint pt)
{
MessageBox("You have clicked Left mouse Button ");
}
The Declaration of OnTrayRButtonDown(CPoint pt)
is as following.
void CTrayMinDlg::OnTrayRButtonDown(CPoint pt)
{
m_menu.GetSubMenu(0)->TrackPopupMenu(TPM_BOTTOMALIGN|
TPM_LEFTBUTTON|TPM_RIGHTBUTTON,pt.x,pt.y,this);
}
Step: 6
Add two member variable to the CTrayMinDlg
.
為CTrayMinDlg添加兩個成員變量
- Variable Type:
NOTIFYICONDATA
- Variable Name:
m_TrayData
;
- Variable Type:
CMenu
- Variable Name:
m_menu
;
現在添加菜單資源
Step: 7
現在畫一個最小化的按鈕在對話框設計中
并且添加這個按鈕的執行函數
void CShellDlg::OnMinimize()
{
m_TrayData.cbSize = sizeof(NOTIFYICONDATA);
m_TrayData.hWnd = this->m_hWnd;
m_TrayData.uID = 1;
m_TrayData.uCallbackMessage = WM_TRAY_MESSAGE;
m_TrayData.hIcon = this->m_hIcon;
strcpy(m_TrayData.szTip,"My Icon");
m_TrayData.uFlags = NIF_ICON|NIF_MESSAGE;
valid data.
BOOL bSuccess = FALSE;
BOOL BSus = FALSE;
BSus = m_menu.LoadMenu(IDR_MENU1);
if(!(BSus))
MessageBox("Unabled to Loa menu");
bSuccess = Shell_NotifyIcon(NIM_ADD,&m_TrayData);
if(!(bSuccess))
MessageBox("Unable to Set Tary Icon");
else
{
this->ShowWindow(SW_MINIMIZE);
this->ShowWindow(SW_HIDE);
}
}
Step: 8
在退出菜單的執行函數寫下如下
Shell_NotifyIcon(NIM_DELETE,&m_TrayData);
? DestroyWindow();
現在可以運行程序,并且嘗試最小化按鈕的使用(他會最小化導系統托盤)。
現在盡情發揮,完善這些步驟,完成自己的系統托盤圖標吧!
Yasar Arslan
Someone recently asked me what I recommend for synchronizing worker threads and I suggested setting an event. This person's response was that you could not do that since worker threads do not support a message pump (UI threads are required to support messages). The confusion here is that events and messages are different animals under windows.
我忘記了我從哪里copy的這些例子代碼,他們可是非常簡單而有趣的。如果有人知道這些代碼的作者,我一定要好好感謝你和這位作者。
注意這里有很多對于沒有提及的MFC的支持。像_beginthread(一個C運行時庫調用)的API可以在MFC應用程序中替換成AfxBeginThread
無同步(No Synchronization)
這第一個例子描述了兩個互不同步的線程。進程中的首要線程--主函數循環,輸出全局整形數組的內容。還有一個線程“Thread”不停的給數組每個元素+1。
?The thread called "Thread" continuously populates the global array of integers.
#include <process.h>
#include <stdio.h>
int a[ 5 ];
void Thread( void* pParams )
{ int i, num = 0;
while ( 1 )
{
for ( i = 0; i < 5; i++ ) a[ i ] = num;
num++;
}
}
int main( void )
{
_beginthread( Thread, 0, NULL );
while( 1 )
printf("%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
return0;
}
注意這個例子的輸出,紅色的數處在一個主線程搶先于Thread工作過程中執行的打印動作
81751652 81751652 81751651 81751651 81751651
81751652 81751652 81751651 81751651 81751651
83348630 83348630 83348630 83348629 83348629
83348630 83348630 83348630 83348629 83348629
83348630 83348630 83348630 83348629 83348629
?
關鍵區域/臨界區域 對象(Critical Section Objects)
如果你想讓主線程等待Thread線程處理好全局數組再做打印,一種解決方法是使用關鍵區域對象。
關鍵區域對象提供同步于使用互斥器(Mutex)對象很相似, 除了關鍵區域對象之能在一個進程內發揮效用。Event, mutex,?以及 semaphore?對象也可以用在單進程的應用程序中, 但是關鍵區域對象提供一個相對快捷更加高效的同步機制. 就像互斥器一樣, 一個關鍵區域對象只能同時被一個線程擁有, 這個關鍵區域能夠在同時發生的數據存取時保護共享資源. 獲取關鍵區域的先后順序不定,可是不用太擔心,系統對于每一個線程都是平等的。
???
CRITICAL_SECTION cs;
int a[ 5 ];
void Thread( void* pParams )
{
int i, num = 0;
while ( TRUE )
{
EnterCriticalSection( &cs );
for ( i = 0; i < 5; i++ ) a[ i ] = num;
LeaveCriticalSection( &cs );
num++;
}
}
int main( void )
{
InitializeCriticalSection( &cs );
_beginthread( Thread, 0, NULL );
while( TRUE )
{
EnterCriticalSection( &cs );
printf( "%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
LeaveCriticalSection( &cs );
}
return 0;
}
If you are running Windows 9x/NT/2000, you can run this program by clicking here.
互斥器(Mutex Objects)
一個互斥器是一個信號狀態的同步對象,當它不屬于任何一個線程時就用信號來體現,當被擁有時他的信號狀態就為None. 同一時刻只有一個線程可以擁有互斥器, 互斥器這個名字來自于他們對于并列的線程存取共享資源時表現出的行為。舉個例子,避免兩個線程同時寫入一個共享內存,每一個線程當需要執行存取共享資源的代碼時首先等待直到自己獲得擁有權. 在存取共享資源之后,線程釋放對互斥器的擁有權。
兩個或以上的進程可以調用CreateMutex
來建立同樣名字的互斥器. 實際上第一個進程建立的這個互斥器, 隨后的進程只是得到了那個存在的互斥器的句柄. 這能使多進程共用一個互斥器, 當然用戶應該有確保建立互斥器的進程首先啟動的責任. 使用這種技術,你應該將這個 bInitialOwner標記設置成FALSE; 否則, 它可以因不同的進程最初擁有它而帶來困難.
多進程可以有同一個mutex對象的句柄, 讓mutex對象能夠用于多進程間同步. 下面的對象共享機制是適用的:
- 一個子進程通過
CreateProcess
?函數被建立,當CreateMutex的lpMutexAttributes?參數給予相應的mutex對象指針它可以繼承到一個mutex對象的句柄.
- 一個進程可以在
DuplicateHandle
函數中指定一個mutex對象句柄來建立一個句柄的拷貝由其他進程使用.
- 一個繼承可以指定一個mutex的名字通過
CreateMutex
函數得到這個mutex對象的句柄.
總的來說, 如果你想要進行線程同步,臨界區域更高效些.
#include <windows.h>
#include <process.h>
#include <stdio.h>
HANDLE hMutex;
int a[ 5 ];
void Thread( void* pParams )
{
int i, num = 0;
while ( TRUE )
{
WaitForSingleObject( hMutex, INFINITE );
for ( i = 0; i < 5; i++ ) a[ i ] = num;
ReleaseMutex( hMutex );
num++;
}
}
int main( void )
{
hMutex = CreateMutex( NULL, FALSE, NULL );
_beginthread( Thread, 0, NULL );
while( TRUE )
{
WaitForSingleObject( hMutex, INFINITE );
printf( "%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
ReleaseMutex( hMutex );
}
return0;
}
If you are running Windows 9x/NT/2000, you can run this program by clicking here.
Event Objects事件對象
若我們想要強制第二線程在主線程完成全局數組的內容輸出時執行該如何?這樣的話每行的輸出就只是遞增1。
一個事件對象也是一個可以通過SetEvent or PulseEvent
函數設置像信號般的狀態的同步對象. 下面是兩種類型的事件對象.
Object |
Description |
Manual-reset event 手動激發對象 |
只有使用ResetEvent 函數才可以將其設置為無激發狀態. 當它在激發狀態時, 它會激發所有正在等待的線程, 執行對相同 event對象的線程會立即從wait函數返回. |
Auto-reset event 自動激發對象 |
一個只相應一個線程的wait函數的事件對象(當這個對象是激發狀態),wait函數返回同時事件對象自動變成無激發狀態?,當沒有線程執行wait事件對象仍然是激發狀態. |
event object的用處就在于它可以在它發生時向等待著的線程發出信號標志從而使其wait結束.?舉個例子, 在overlapped I/O 操作時, 當異步操作完成時系統設置了那個由程序員指定(specified)的事件對象為信號狀態. A 一個單一線程可以指定許多不同的事件對象在許多同時發生的overlapped 操作運作, 調用一個多對象的wait函數可以當任意一個event object激發時結束等待.
在一個線程中可使用 CreateEvent
函數建立一個event object. 在這個線程中指定這個event object 的特性是manual-reset?或者 auto-reset . 在這個線程中也可以命名一個event object. 其他進程中的線程也可以使用 OpenEvent
通過event object的名字打開一個現存event object . 另外關于mutex, event, semaphore, 以及 timer objects的其他信息, 就參考《Interprocess Synchronization》的文章.
一個線程能夠用 PulseEvent
?函數設置一個event?object 為信號狀態而后激發當前適當數量的wait線程,之后切換為無信號狀態 .?對于一個manual-reset event object, 所有的等待線程被返回(release). 對于一個auto-reset event object, 這個函數只能釋放一個等待的線程, 即使有更多線程在等待. 如果沒有線程在函數調用時等待, PulseEvent
只是簡單的將事件狀態設為無信號并且返回(個人注釋,這應該是跟setevent最不相同的地方?。?
Collapse
#include <windows.h>
#include <process.h>
#include <stdio.h>
HANDLE hEvent1, hEvent2;
int a[ 5 ];
void Thread( void* pParams )
{
int i, num = 0;
while ( TRUE )
{
WaitForSingleObject( hEvent2, INFINITE );
for ( i = 0; i < 5; i++ ) a[ i ] = num;
SetEvent( hEvent1 );
num++;
}
}
int main( void )
{
hEvent1 = CreateEvent( NULL, FALSE, TRUE, NULL );
hEvent2 = CreateEvent( NULL, FALSE, FALSE, NULL );
_beginthread( Thread, 0, NULL );
while( TRUE )
{
WaitForSingleObject( hEvent1, INFINITE );
printf( "%d %d %d %d %d\n",
a[ 0 ], a[ 1 ], a[ 2 ],
a[ 3 ], a[ 4 ] );
SetEvent( hEvent2 );
}
return0;
}
If you are running Windows 9x/NT/2000, you can run this program by clicking here.
Summary of Synchronization Objects
The MSDN News for July/August 1998 has a front page article on Synchronization Objects. The following table is from that article:
Name
|
Relative speed
|
Cross process
|
Resource counting
|
Supported platforms
|
Critical Section |
Fast |
No |
No (exclusive access) |
9x/NT/CE |
Mutex |
Slow |
Yes |
No (exclusive access) |
9x/NT/CE |
Semaphore |
Slow |
Yes |
Automatic |
9x/NT |
Event |
Slow |
Yes |
Yes |
9x/NT/CE |
Metered Section |
Fast |
Yes |
Automatic |
9x/NT/CE |
by?William T. Block
from codeproject
謝謝回復的補充 ~~,上面拼錯了個詞,改過。。譯完了