dedecms织梦内容管理系统    
首页 | java | C/C++ | PHP | 操作系统 | ajax | 脚本编程 | 安全技术 | 本站下载页 | flex | CRM | 专题 | QQ群 | 测试中心 | 会员中心 | 积分规则
  当前位置:主页>C/C++>文章内容
VC和Delphi程序只运行一个实例的方法
来源: 天极开发 作者: 董斌
  有些时候,我们需要我们的只运行一个实例,笔者自己作程序也有这样的情况,于是自已探究一番。忙活一阵后,总算小有收获,不敢独享,在天极发表出来,供大家参考。

  既然是从根本上解决问题,对于Windows程序而言,就从WinMain函数入口,这是因为在VC中使用SDK的方式编写程序最透明,并且WinMain是作为VC编译器生成EXE文件的默认入口函数。

  WinMain的函数原型:

int WinMain(
 HINSTANCE hInstance,
 HINSTANCE hPrevInstance,
 LPSTR lpCmdLine,
 int nCmdShow
);

  在WinMain中一共有四个参数,其中第二参数hPrevInstance是一个HINSTANCE,表示程序运行上一个实例的句柄。根据Msdn的说明,这个参数在Win32系统上总为NULL。不过我们还可以通过使用GreateMutex函数来创建一个唯一命名的互斥对象的方法来检测是否已经存在了另一个实例。

  GreateMutex的函数原型:

HANDLE CreateMutex(
 LPSECURITY_ATTRIBUTES lpMutexAttributes,
 BOOL bInitialOwner,
 LPCTSTR lpName
);

  第一个参数lpMutexAttributes,指向一个SECURITY_ATTRIBUTES结构的指针,用来决定是否允许子进程继承函数返回的句柄。如果这个参数为NULL(空),则不允许被继承。

  第二个参数bInitialOwner,如果这个参数为真并且是由调用者(指调用CreateMutex函数的)创建互斥对象,那么由调用的线程(调用CreateMutex函数的线程)获得最初的拥有权。除此之外,调用的线程都不能获得最初的拥有权。决定是否由调用者创建互斥对象,请看“返回值”(Return Values)小节。

  第三个函数lpName,指向用来命名互斥对象的以NULL(空)结尾的字符串,这个名字的字符个数限制在MAX_PATH个数字符内。同时这个名字区分大小写。如果参数lpName为NULL(空),将创建一个没有命名的互斥对象。如果参数lpName所指向的字符串和下列各项之中任意一项匹配:existing event,semaphore,waitable timer,job,or file-mapping object,函数将失败并伴随着调用(尽快)GetLastError函数会返回ERROR_INVALID_HANDLE常数。引发这样的结果是由于为这些互斥对象分配了重复的命名空间(这些能够引起重复的命名空间可以在Msdn中通过搜索CreateMutex查看)。

  返回值,如果函数成功,返回值是一个新创建的互斥对象的句柄。如果函数失败,返回值为NULL(空)。如果得到的互斥对象是一个在这个函数之前(指这一次调用CreateMutex函数之前)就被命名了的互斥对象并且存在,那么返回值是已存在对象的句柄,同时(尽快)调用GetLastError函数会返回ERROR_ALREADY_EXISTS。无论如何,当第二个调用者(注1)只有有限的访问权限,CreateMutex将会失败,同时会伴随着使用GetLastError返回ERROR_ACCESS_DENIED,那么调用者应该使用OpenMutex函数(更多的信息大家可以到Msdn中查看,现在这些信息已经足够我们用来保证我们的程序只运行一个实例了)。

  现在我确定思路:首先创建一个互斥对象,如果创建成功(CreateMutex返回值不为NULL)并调用GetLastError函数返回ERROR_ALREADY_EXISTS常数,说明当前进程不是应用程序的第一个实例,结束程序的运行。

  我们在VC中以一个没有窗口,也不用MFC的Win32应用程序作例子。打开VC6,新建一个工程,类型选择Win32 Application,工程名为:OnlyOne,单击OK,选择一个空的工程,完成。为工程添加一个新的C++源代码文件,命名为:OnlyOne.c,并输入如下代码:

#include <Windows.h>

int
WINAPI
WinMain(
 HINSTANCE hInstance,
 HINSTANCE hPrevInstance,
 LPSTR lpCmdLine,
 int nShowCmd
)
{
 char strAppName[] = "OnlyOne";
 HANDLE hMutex = NULL;

 //创建互斥对象
 hMutex = CreateMutex(NULL, FALSE, strAppName);
 if (hMutex != NULL)
 {
  if (GetLastError() == ERROR_ALREADY_EXISTS)
  {
   MessageBox(NULL,TEXT("不是第一次运行这个程序。"),TEXT("OnlyOne"),MB_OK | MB_ICONINFORMATION);
   //关闭互斥对象,退出程序
   CloseHandle(hMutex);
   return (-1);
  } else
  {
   MessageBox(NULL,TEXT("第一次运行这个程序。"),TEXT("OnlyOne"),MB_OK | MB_ICONINFORMATION);
  }
  } else
  {
   MessageBox(NULL,TEXT("创建互斥对象失败。"),TEXT("OnlyOne"),MB_OK | MB_ICONINFORMATION);
  }
  //关闭互斥对象
  CloseHandle(hMutex);
  return (-1);
}

  同理,这个方法适用于所有的原生Win32应用程序,能够正常调用GreateMutex和GetLastError两个函数即可,Delphi程序(这次是一个带有窗口的程序)可以参考以下步骤和代码:

  在Delphi中建立一个应用程序,然后单击菜单“Project”,单击“View Source”,这样就在代码编辑器中打开了工程文件,它的代码看起来像这样:

program OnlyOne;

uses
Forms,
uOnlyOneWindow in 'uOnlyOneWindow.pas' {OnlyOneWindow };

{$R *.res}

begin
 Application.Initialize;
 Application.CreateForm(TOnlyOneWindow, OnlyOneWindow);
 Application.Run;
end.

  我将其代码更改如下:

program OnlyOne;

uses
Windows,
Forms,
uOnlyOneWindow in 'uOnlyOneWindow.pas' {OnlyOneWindow};

{$R *.res}

var
hAppMutex: THandle;

begin
Application.Initialize;
//创建互斥对象
hAppMutex := CreateMutex(nil, false, PChar('OnlyOne'));
if (hAppMutex = 0) then
begin
 MessageBox(0,PChar('创建互斥对象失败!'),PChar('Error'),MB_OK + MB_ICONINFORMATION);
 exit;
end;
//查看是否是第一次运行程序
if ((hAppMutex <> 0) and (GetLastError() = ERROR_ALREADY_EXISTS)) then
begin
 MessageBox(0,PChar('不是第一次运行这个程序!'),PChar('OK'),MB_OK + MB_ICONINFORMATION);
 //关闭互斥对象,退出程序
 CloseHandle(hAppMutex);
 exit;
end;
Application.CreateForm(TOnlyOneWindow, OnlyOneWindow);
Application.Run;
//关闭互斥对象
CloseHandle(hAppMutex);
end.

  注意:

  1.在User中,要把Windows放在Form前头;

  2.开始创建互斥对象的代码要在Application.Initialize之后;

  3.关闭互斥对象操作要放在Application.Run之后;

  这样,我们只用了较少的代码和较少的系统资源消耗就实现了应用程序检测自己是否被多次运行,从而保证只运行一个示例这样的效果。

  以上程序在Visual C++ 6.0(SP6)、Delphi 7(Build 8.1)中编译,在Windows XP SP2中测试通过。

  注1:当某调用者所请求创建的互斥对象已经被命名了并且存在,这时这个调用者为“第二个调用者”。

上一篇:使用VC6.0实现窗口的任意分割   下一篇:解读VC++编程中的文件操作API和CFile类
[收藏] [推荐] [评论(0条)] [返回顶部] [打印本页] [关闭窗口]  
用户名: 新注册) 密码: 匿名评论
评论内容:(不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。
 §最新评论
  热点文章
·C语言学习
·C语言指针
·C语言数组排序小结
·c语言 最简单的推箱子游戏
·c语言 全国计算机编程大赛复赛试
·c语言 全国计算机编程大赛复赛试
·c++ 数组与指针
·C++中的内存管理(new、delete、
·如何用C语言开发DSP嵌入式系统
·如何用C语言开发DSP嵌入式系统
·Linux下C语言编程
·学习arm的话先看哪本书?
  相关文章
·使用VC6.0实现窗口的任意分割
·解读VC++编程中的文件操作API和C
·摆脱DLL"地狱"的困扰之获取进程
·VC中利用原始套接字实现网络监听
·实现MFC扩展DLL中导出类和对话框
·基于Visual C++6.0的声音文件操
·掀起你的盖头来——谈VC++对象模
·用VC++制作播放AVI视频流的动画
·双缓冲技术及其在VC的GDI环境下
·利用MFC文档视图框架编写打印程
·VC中用DAO实现树型控件的数据库
·在VC中使用Windows管道技术编程
  相关信息
copy right @ 百家拳软件项目研究室 2007 辽ICP备07011763