如果一个程序打开着,这时双击文件,程序不在另起,但是文件在原 鼠标双击不能打开程序
线程中CreateEvent和SetEvent及WaitForSingleObject的用法 2008-08-11 10:56
首先介绍CreateEvent是创建windows事件的意思,作用主要用在判断线程退出,程锁定方面.
CreateEvent
函功能描述:创建或打开一个命名的或无名的事件对象.
EVENT有两种状态:发信号,不发信号。
SetEvent/ResetEvent分别将EVENT置为这两种状态分别是发信号与不发信号。
WaitForSingleObject()等待,直到参数所指定的OBJECT成为发信号状态时才返回,OBJECT可以是EVENT,也可以是其它内核对象。
当你创建一个线程时,其实那个线程是一个循环,不像上面那样只运行一次的。这样就带来了一个问题,在那个死循环里要找到合适的条件退出那个死循环,那么是怎么样实现它的呢?在Windows里往往是采用事件的方式,当然还可以采用其它的方式。在这里先介绍采用事件的方式来通知从线程运行函数退出来,它的实现原理是这样,在那个死循环里不断地使用WaitForSingleObject函数来检查事件是否满足,如果满足就退出线程,不满足就继续运行。当在线程里运行阻塞的函数时,就需要在退出线程时,先要把阻塞状态变成非阻塞状态,比如使用一个线程去接收网络数据,同时使用阻塞的SOCKET时,那么要先关闭SOCKET,再发送事件信号,才可以退出线程的。
当然我感觉重要应用方面还是用来锁定,实现所谓的pv功能。
下面介绍函数功能,参数等
1.CreateEvent
函数功能描述:创建或打开一个命名的或无名的事件对象
函数原型:
HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性
BOOL bManualReset, // 复位方式
BOOL bInitialState, // 初始状态
LPCTSTR lpName // 对象名称
);
参数:
lpEventAttributes:
[输入]一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承。
Windows NT/2000:lpEventAttributes的结构中的成员为新的事件指定了一个安全符。如果lpEventAttributes是NULL,事件将获得一个默认的安全符。
bManualReset:
[输入]指定将事件对象创建成手动复原还是自动复原。如果是TRUE,那么必须用ResetEvent函数来手工将事件的状态复原到无信号状态。如果设置为FALSE,当事件被一个等待线程释放以后,系统将会自动将事件状态复原为无信号状态。
bInitialState:
[输入]指定事件对象的初始状态。如果为TRUE,初始状态为有信号状态;否则为无信号状态。
lpName:
[输入]指定事件的对象的名称,是一个以0结束的字符串指针。名称的字符格式限定在MAX_PATH之内。名字是对大小写敏感的。
如果lpName指定的名字,与一个存在的命名的事件对象的名称相同,函数将请求EVENT_ALL_ACCESS来访问存在的对象。这时候,由于bManualReset和bInitialState参数已经在创建事件的进程中设置,这两个参数将被忽略。如果lpEventAttributes是参数不是NULL,它将确定此句柄是否可以被继承,但是其安全描述符成员将被忽略。
如果lpName为NULL,将创建一个无名的事件对象。
如果lpName的和一个存在的信号、互斥、等待计时器、作业或者是文件映射对象名称相同,函数将会失败,在GetLastError函数中将返回ERROR_INVALID_HANDLE。造成这种现象的原因是这些对象共享同一个命名空间。
终端服务(Terminal Services):名称中可以加入"Global"或是"Local"的前缀,这样可以明确的将对象创建在全局的或事务的命名空间。名称的其它部分除了反斜杠(),可以使用任意字符。详细内容可参考Kernel Object Name Spaces。
Windows 2000:在Windows 2000系统中,没有终端服务运行,"Global"和"Local"前缀将被忽略。名称的其它部分除了反斜杠(),可以使用任意字符。
Windows NT 4.0以及早期版本, Windows 95/98:名称中除了反斜杠(),可以使用任意字符。
返回值:
如果函数调用成功,函数返回事件对象的句柄。如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。
如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。
备注:
调用CreateEvent函数返回的句柄,该句柄具有EVENT_ALL_ACCESS权限去访问新的事件对象,同时它可以在任何有此事件对象句柄的函数中使用。
在调用的过程中,所有线程都可以在一个等待函数中指定事件对象句柄。当指定的对象的状态被置为有信号状态时,单对象等待函数将返回。
对于多对象等待函数,可以指定为任意或所有指定的对象被置为有信号状态。当等待函数返回时,等待线程将被释放去继续运行。
初始状态在bInitialState参数中进行设置。使用SetEvent函数将事件对象的状态置为有信号状态。使用ResetEvent函数将事件对象的状态置为无信号状态。
当一个手动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至明确调用ResetEvent函数将其置为无符号状态。
当事件的对象被置为有信号状态时,任意数量的等待中线程,以及随后开始等待的线程均会被释放。
当一个自动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待,事件对象的状态将保持有信号状态。
多个进程可持有同一个事件对象的多个句柄,可以通过使用此对象来实现进程间的同步。下面的对象共享机制是可行的:
·在CreateEvent函数中,lpEventAttributes参数指定句柄可被继承时,通过CreateProcess函数创建的子进程继承的事件对象句柄。
·一个进程可以在DuplicateHandle函数中指定事件对象句柄,从而获得一个复制的句柄,此句柄可以被其它进程使用。
·一个进程可以在OpenEvent或CreateEvent函数中指定一个名字,从而获得一个有名的事件对象句柄。
使用CloseHandle函数关闭句柄。当进程停止时,系统将自动关闭句柄。当最后一个句柄被关闭后,事件对象将被销毁。
使用环境:
Windows NT/2000:需要3.1或更高版本
Windows 95/98:需要Windows 95或更高版本
头文件:定义在Winbase.h;需要包含 Windows.h。
导入库:user32.lib
Unicode:在Windows NT/2000中,以 Unicode 和 ANSI 执行
一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle()
来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent()
来使其无信号,用WaitForSingleObject()或WaitForMultipleObjects()来等待
其变为有信号.
PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event
对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的.
对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于
人工复位的Event对象,它释放所有等待的thread.
2. WaitForSingleObject的用法
WaitForSingleObject的用法
DWORD WaitForSingleObject(
HANDLE hHandle,
DWORD dwMilliseconds
);
参数hHandle是一个事件的句柄,第二个参数dwMilliseconds是时间间隔。如果时间是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIMEOUT。
hHandle可以是下列对象的句柄:
Change notification
Console input
Event
Job
Memory resource notification
Mutex
Process
Semaphore
Thread
Waitable timer
WaitForSingleObject函数用来检测hHandle事件的信号状态,当函数的执行时间超过dwMilliseconds就返回,但如果参数dwMilliseconds为INFINITE时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到WaitForSingleObject有返回直才执行后面的代码。在这里举个例子:
先创建一个全局Event对象g_event:
CEvent g_event;
在程序中可以通过调用CEvent::SetEvent设置事件为有信号状态。
下面是一个线程函数MyThreadPro()
UINT CFlushDlg::MyThreadProc( LPVOID pParam )
{
WaitForSingleObject(g_event,INFINITE);
For(;;)
{
………….
}
return 0;
}
在这个线程函数中只有设置g_event为有信号状态时才执行下面的for循环,因为g_event是全局变量,所以我们可以在别的线程中通过g_event. SetEvent控制这个线程。
还有一种用法就是我们可以通过WaitForSingleObject函数来间隔的执行一个线程函数的函数体
UINT CFlushDlg::MyThreadProc( LPVOID pParam )
{
while(WaitForSingleObject(g_event,MT_INTERVAL)!=WAIT_OBJECT_0)
{
………………
}
return 0;
}
在这个线程函数中可以可以通过设置MT_INTERVAL来控制这个线程的函数体多久执行一次,当事件为无信号状态时函数体隔MT_INTERVAL执行一次,当设置事件为有信号状态时,线程就执行完毕了(return 0)。
等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止。这些等待函数中最常用的是WaitForSingleObject:
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
当线程调用该函数时,第一个参数hObject标识一个能够支持被通知/未通知的内核对象。第二个参数dwMilliseconds.允许该线程指明,为了等待该对象变为已通知状态,它将等待多长时间。调用下面这个函数将告诉系统,调用函数准备等待到hProcess句柄标识的进程终止运行为止:
WaitForSingleObject(hProcess, INFINITE);
第二个参数告诉系统,调用线程愿意永远等待下去(无限时间量),直到该进程终止运行。
通常情况下, INFINITE是作为第二个参数传递给WaitForSingleObject的,不过也可以传递任何一个值(以毫秒计算)。顺便说一下, INFINITE已经定义为0xFFFFFFFF(或-1)。当然,传递INFINITE有些危险。如果对象永远不变为已通知状态,那么调用线程永远不会被唤醒,它将永远处于死锁状态,
不过,它不会浪费宝贵的CPU时间。
下面是如何用一个超时值而不是INFINITE来调用WaitForSingleObject的例子:
DWORD dw = WaitForSingleObject(hProcess, 5000);
switch(dw)
{
case WAIT_OBJECT_0:
// The process terminated.
break;
case WAIT_TIMEOUT:
// The process did not terminate within 5000 milliseconds.
break;
case WAIT_FAILED:
// Bad call to function (invalid handle?)
break;
}
上面这个代码告诉系统,在特定的进程终止运行之前,或者在5 0 0 0 m s时间结束之前,调用线程不应该变为可调度状态。因此,如果进程终止运行,那么这个
函数调用将在不到5000ms的时间内返回,如果进程尚未终止运行,那么它在大约5000ms时间内返回。注意,不能为dwMilliseconds传递0。如果传递了0,WaitForSingleObject函数将总是立即返回。WaitForSingleObject的返回值能够指明调用线程为什么再次变为可调度状态。如果线程等待的对象变为已通知状态,那么返回值是WAIT_OBJECT_0。如果设置的超时已经到期,则返回值是WAIT_TIMEOUT。如果将一个错误的值(如一个无效句柄)传递给WaitForSingleObject,那么返回值将是WAIT_FAILED(若要了解详细信息,可调用GetLastError)。
下面这个函数WaitForMultipleObjects与WaitForSingleObject函数很相似,区别在于它允许调用线程同时查看若干个内核对象的已通知状态:
DWORD WaitForMultipleObjects(DWORD dwCount,
CONST HANDLE* phObjects,
BOOL fWaitAll,
DWORD dwMilliseconds);
dwCount参数用于指明想要让函数查看的内核对象的数量。这个值必须在1与MAXIMUM_WAIT_OBJECTS(在Windows头文件中定义为64)之间。phObjects参数是指向内核对象句柄的数组的指针。
可以以两种不同的方式来使用WaitForMultipleObjects函数。
一种方式是让线程进入等待状态,直到指定内核对象中的任何一个变为已通知状态。
另一种方式是让线程进入等待状态,直到所有指定的内核对象都变为已通知状态。fWaitAll参数告诉该函数,你想要让它使用何种方式。如果为该参数传递TRUE,那么在所有对象变为已通知状态之前,该函数将不允许调用线程运行。
dwMilliseconds参数的作用与它在WaitForSingleObject中的作用完全相同。如果在等待的时候规定的时间到了,那么该函数无论如何都会返回。同样,通常为该参数传递INFINITE,但是在编写代码时应该小心,以避免出现死锁情况。
WaitForMultipleObjects函数的返回值告诉调用线程,为什么它会被重新调度。可能的返回值是WAIT_FAILED和WAIT_TIMEOUT,这两个值的作用是很清楚的。如果fWaitAll参数传递TRUE,同时所有对象均变为已通知状态,那么返回值是WAIT_OBJECT_0。如果为fWaitAll传递FALSE,那么一旦任何一个对象变为已通知状态,该函数便返回。在这种情况下,你可能想要知道哪个对象变为已通知状态。返回值是WAIT_OBJECT_0 与(WAIT_OBJECT_0 + dwCount-1)之间的一个值。换句话说,如果返回值不是WAIT_TIMEOUT,也不是WAIT_FAILED,那么应该从返回值中减去WAIT_OBJECT_0。产生的数字是作为第二个参数传递给WaitForMultipleObjects的句柄数组中的索引。该索引说明哪个对象变为已通知状态。
下面是说明这一情况的一些示例代码:
HANDLE h[3];
h[0] = hProcess1;
h[1] = hProcess2;
h[2] = hProcess3;
DWORD dw = WaitForMultipleObjects(3, h, FALSE, 5000);
switch(dw)
{
case WAIT_FAILED:
// Bad call to function (invalid handle?)
break;
case WAIT_TIMEOUT:
// None of the objects became signaled within 5000 milliseconds.
break;
case WAIT_OBJECT_0 + 0:
// The process identified by h[0] (hProcess1) terminated.
break;
case WAIT_OBJECT_0 + 1:
// The process identified by h[1] (hProcess2) terminated.
break;
case WAIT_OBJECT_0 + 2:
// The process identified by h[2] (hProcess3) terminated.
break;
}
如果为fWaitAll参数传递FALSE,WaitForMultipleObjects就从索引0开始向上对句柄数组进行扫描,同时已通知的第一个对象终止等待状态。这可能产生一些你不希望有的结果。例如,通过将3个进程句柄传递给该函数,你的线程就会等待3个子进程终止运行。如果数组中索引为0的进程终止运行,WaitForMultipleObjects就会返回。这时该线程就可以做它需要的任何事情,然后循环反复,等待另一个进程终止运行。如果该线程传递相同的3个句柄,该函数立即再次返回WAIT_OBJECT_0。除非删除已经收到通知的句柄,否则代码就无法正确地运行。
2.代码
BOOL CIcadDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
/*/ TT-15774 - WEI Gang - 10-11-09 打开文件完成后,获取等待句柄,存在则设置事件
/*/
class _InOpenDocSetOpenedEvent_
{
public:
_InOpenDocSetOpenedEvent_(CIcadDoc* pThis, LPCWSTR lpName)
{
m_pThisDoc = pThis;
m_strName = lpName;
}
~_InOpenDocSetOpenedEvent_()
{
if (m_pThisDoc->m_dbDrawing)
{
m_strName.Replace(L'\', L':');
m_strName.Replace(L'/', L':');
m_strName.MakeUpper();
HANDLE hEvt = OpenEventW(EVENT_ALL_ACCESS, TRUE, m_strName/*_T("OPEN_EVENT_HANDLE")*/);
if (NULL != hEvt)
{
SetEvent(hEvt);
CloseHandle(hEvt);
}
}
}
CIcadDoc* m_pThisDoc;
CStringW m_strName;
};
_InOpenDocSetOpenedEvent_ evt(this, wszName);
//*/
}
BOOL CIcadApp::InitInstance()
{
bool bNewHasTemplate = false;
if(m_cmdInfo.m_nShellCommand==CCommandLineInfo::FileOpen)
{
if(!m_cmdInfo.m_strFileName.IsEmpty() && 0 == (m_cmdInfo.m_strFileName.Right(4)).CompareNoCase(".dwt"))
{
bNewHasTemplate = true;
m_cmdInfo.m_nShellCommand = CCommandLineInfo::FileNew;
}
else
{
HWND hwnd;
if(NULL!=(hwnd=FindWindow("ICADApplicationWindow" ,NULL)))
{
// We found another version of ourself. Lets defer to it:
if(IsIconic(hwnd))
{
ShowWindow(hwnd,SW_RESTORE);
}
SetForegroundWindow(hwnd);
//*** Pass the file name(s) to the window via the WM_DROPFILES message.
DWORD dwBytes = (sizeof(DROPFILES)+(m_cmdInfo.m_strFileName.GetLength())+2);
HANDLE hGlobal = GlobalAlloc((GHND | GMEM_SHARE),dwBytes);
LPDROPFILES pDropFiles = (LPDROPFILES)GlobalLock(hGlobal);
//*** Write the files names to the memory that is allocated after the
//*** DROPFILES structure.
pDropFiles->pFiles = sizeof(DROPFILES);
LPSTR pszFile = ((LPSTR)pDropFiles)+sizeof(DROPFILES);
strcpy(pszFile,m_cmdInfo.m_strFileName.GetBuffer(0));
GlobalUnlock(hGlobal);
OpenClipboard(NULL);
EmptyClipboard();
if(NULL==SetClipboardData(CF_HDROP,hGlobal))
{
DWORD dwErr = GetLastError();
CString strError;
strError.Format(ResourceString(IDC_ICADAPP_ERROR_WRITING_TO_0, "Error writing to clipboard (%d)." ),dwErr);
sds_alert(strError);
CloseClipboard();
try
{
GrxAppsLoader::SendEvent(OdRx::kQuitMsg, 0);
GrxAppsLoader::getAppsLoader().unloadAll();
}
catch(...)
{
}
return FALSE;
}
CloseClipboard();
CString strName = m_cmdInfo.m_strFileName;
strName.Replace('\', ':');
strName.Replace('/', ':');
strName.MakeUpper();
HANDLE hEvt = CreateEvent(NULL, FALSE, FALSE, strName);
::SendMessage(hwnd,WM_DROPFILES,(WPARAM)NULL,0L);
if (NULL != hEvt)
{
WaitForSingleObject(hEvt, 120000);
CloseHandle(hEvt);
}
try
{
GrxAppsLoader::SendEvent(OdRx::kQuitMsg, 0);
GrxAppsLoader::getAppsLoader().unloadAll();
}
catch(...)
{
}
m_bReopenExit = TRUE;
return FALSE;
}
}
}
}
void CMainWindow::OnDropFiles(HDROP hDropInfo)
{
UINT ct,nStrSize,nFiles;
char* ps1;
HDROP hDrop;
::SetForegroundWindow(this->m_hWnd);//EBATECH(FUTA) 2001-02-01 ICAD Window activate
//extern int SDS_AtCmdLine;
//if(!SDS_AtCmdLine) return;
if(NULL==hDropInfo)
{
OpenClipboard();
if(NULL==(hDrop=(HDROP)GetClipboardData(CF_HDROP)))
{
sds_alert(ResourceString(IDC_ICADMAIN_ERROR__INVALID_22, "ERROR: Invalid file type." ));
CloseClipboard();
return;
}
}
else hDrop=hDropInfo;
/*/TTWD-1673 - jia yuting - 05-12-20 - 出现假死程序的情况
/*/
resbuf rb;
if(sds_getvar("CMDACTIVE", &rb) == RTNORM && rb.resval.rint)
{
sds_alert(ResourceString(ID_PDG_SRTING_071,"请结束当前命令后再打开文件n"));
CloseClipboard();
return;
}
//*/
nFiles=DragQueryFile(hDrop,0xFFFFFFFF,NULL,0);
for(ct=0; ct<nFiles; ct++)
{
if((nStrSize=DragQueryFile(hDrop,ct,NULL,0))<=0) continue;
if((ps1 = new char[++nStrSize])==NULL)
{
sds_alert(ResourceString(IDC_ICADMAIN_ERROR__INSUFFI_23, "ERROR: Insufficient memory." ));
return;
}
DragQueryFile(hDrop,ct,ps1,nStrSize);
/*/TTJR-4733-4734 - Fu Hai - 2007-05-22 - VBA二次开发
/*/
if (g_Vba6Installed && !SDS_AutoSave)
{
VARIANT_BOOL bCancel = VARIANT_FALSE;
FireDropFile(ps1, bCancel);
if (bCancel == VARIANT_TRUE) continue;
}
//*/
char szPath[IC_ACADBUF];
if(sds_findfile(ps1,szPath)!=RTNORM ||
!OpenFileUnknown(szPath,TRUE))
{
CString str;
str.Format(ResourceString(IDC_ICADMAIN_ERROR____24, "ERROR: "%s" is not a valid file." ),ps1);
sds_alert(str);
}
delete ps1;
}
if(NULL==hDropInfo)
{
//*** Clean-up the clipboard
EmptyClipboard();
CloseClipboard();
}
if(NULL!=m_pOpenOleList)
{
//*** If files are dragged and dropped, we want to save them in a list
//*** and create the ole objects last.
POSITION pos = m_pOpenOleList->GetHeadPosition();
CString strPath;
while(NULL!=pos)
{
strPath = m_pOpenOleList->GetNext(pos);
if(!OpenFileUnknown(strPath.GetBuffer(0),FALSE))
{
CString str;
str.Format(ResourceString(IDC_ICADMAIN_ERROR____24, "ERROR: "%s" is not a valid file." ),strPath);
sds_alert(str);
}
}
delete m_pOpenOleList;
m_pOpenOleList=NULL;
}
}
更多阅读
今天为儿子设计了一个寻宝游戏,推荐给大家 室内寻宝游戏设计
国庆节没有去外地玩,担心人太多了。不过还是想给儿子找些乐子,就为儿子设计了一个寻宝游戏,今天在奥林匹克森林公园实践了一下,还挺受他欢迎,推荐给大家。今天我们选择的地点在奥林匹克森林公园的东门附近,那里人不多,环境还不错,没有车,比
我觉得如果再不改变阅读习惯,也许就要和《上海壹周》这份我支持
我觉得如果再不改变阅读习惯,也许就要和《上海壹周》这份我支持了将近七年的报纸说再见了。天知道对我这样一个极度恋旧重度迟钝,新产品推广过程中最难攻克的那一类用户读者来说,改变习惯是一件多么难得事情,但为了重拾每周一早晨(原来是
打印机突然出现spoolsv.exe应用程序错误,内存不能written spoolsv.exe修复 360
在使用hp1020打印机过程中出现spoolsv.exe应用程序错误,内存不能written.问题描述:在使用打印机过程中出现“spoolsv.exe应用程序错误......内存不能written”的提示,检查打印驱动,打印机设置选项无法打开。重启后在打开打印机时就出现
如果你去光明农场,这里是最好的吃饭地方! 深圳光明农场
如果你去光明农场,这里是最好的吃饭地方!聚友阁山庄,深圳本土私房烧味撰文/陈保才私房是一种心情说到私房菜的起源,有一个故事说,清末光绪年间,祖籍广
如果时光能够回到从前,你会告诉自己什么...? 回到从前
虽然说,过去的就让它过去,但是如果能够回到从前,你会告诉自己什么呢?告诉自己要好好学习?还是告诉自己在年轻的时候多出去看看世界?我也知道说“如果……”是没有什么意义的。因为现在就知道以后的事情,我可能会变得有些骄傲自大。困难让我