如果一个程序打开着,这时双击文件,程序不在另起,但是文件在原 鼠标双击不能打开程序

1.基础知识

线程中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;

}

}

  

爱华网本文地址 » http://www.413yy.cn/a/25101013/182431.html

更多阅读

今天为儿子设计了一个寻宝游戏,推荐给大家 室内寻宝游戏设计

国庆节没有去外地玩,担心人太多了。不过还是想给儿子找些乐子,就为儿子设计了一个寻宝游戏,今天在奥林匹克森林公园实践了一下,还挺受他欢迎,推荐给大家。今天我们选择的地点在奥林匹克森林公园的东门附近,那里人不多,环境还不错,没有车,比

如果时光能够回到从前,你会告诉自己什么...? 回到从前

虽然说,过去的就让它过去,但是如果能够回到从前,你会告诉自己什么呢?告诉自己要好好学习?还是告诉自己在年轻的时候多出去看看世界?我也知道说“如果……”是没有什么意义的。因为现在就知道以后的事情,我可能会变得有些骄傲自大。困难让我

声明:《如果一个程序打开着,这时双击文件,程序不在另起,但是文件在原 鼠标双击不能打开程序》为网友明日輝煌分享!如侵犯到您的合法权益请联系我们删除