初学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
谢谢回复的补充 ~~,上面拼错了个词,改过。。译完了