再次发帖:一个关于判断自己的实例是否多开的问题(讨论,有分)。

时间:2022-08-29 19:22:09
前些天我发了一个帖子,内容如下。
我有一个客户端程序,他与我们的服务器程序连接,进行通信。
我们要求客户端最多两开。请问有什么方法?
我想到的方法是:
1,利用互斥体。
2,客户端获得硬件信息,然后发送到服务器,如果有两个以上的同一个硬件id的客户端就不会同意连接。
3,利用注册表,每开一个客户端就会做一个记录.
4,或者把客户端启动的信息放到一个文件里。
5,扫描进程,看看有几个自己的客户端。

感觉3,4 如果客户端异常退出,将不会删除自己的记录,那样下次启动会影响判断,5好像更不行了,如果用户改了名字就判断不到了


在这非常感谢babilife 做出如下完美的回答。
经常会碰到有人问如何保证程序只运行一个实例,原来我也零碎的给过两三个方法,今天干脆来个大总结,希望对大家在做程序设计的时候有所帮助。  

  一个程序只运行一个实例(或限制实例数量)通常可以采用如下方法:  

 1)FindWindow 之<窗口标题>  
  通过查找窗口标题来确定上一实例是否正在运行,不适合窗口标题动态变化的程序。  

 2)FindWindow 之<任务栏按纽标题>  
  通过查找任务栏按纽标题来确定上一实例是否正在运行,不适合按纽标题动态变化的程序(如Winamp)。通常情况下,该方法还是优先考虑,因为按纽标题是一般是固定的。  

 3)Window Property  
  将某个数据(可以是字符串或句柄)通过SetProp加入到指定窗口的property list,程序运行时枚举窗口并检查该数据是否存在来确定上一实例是否正在运行。  

 4)全局Atom  
  将某个特定字符串通过GlobalAddAtom加入全局原子表(Global Atom Table),程序运行时检查该串是否存在来确定上一实例是否正在运行。该方法有个局限,就是程序终止前必须显式调用GlobalDeleteAtom来释放atom,否则该atom不会自动释放,如果程序运行时意外终结了,那么下一个实例就无法正常执行。早期版本的realplayer就存在这个现象,不知道是不是采用了该方法。  

 5)Mutex/Event/Semaphore  
  通过互斥对象/信号量/事件等线程同步对象来确定实例是否存在,在NT下要注意权限问题(SID)。  

 6)DLL全局共享区域  
  VC下的DLL工程可以通过下面代码来建立一个进程间共享数据段:  
  #pragma data_seg(".share")  
  //shared for all processes that attach to the dll  
  DWORD dllgs_dwRunCount = 1; //一定要在这里对变量进行初始化,否则工夫白做!  
  #pragma data_seg()  
  #pragma comment(linker,"/section:.share,rws")  
    
  导出3个函数,分别为:  
  DWORD IncRunCount(void); //运行计数器加1,返回计数器结果  
  DWORD DecRunCount(void); //运行计数器减1,返回计数器结果  
  DWORD GetRunCount(void); //取当前运行计数器  
    
  由于DLL全局共享段在映射到各个进程地址空间时仅会被初始化一次,并且是在首次被windows加载时,所以利用该共享段数据就能对程序实例进行可靠计数。  

 7)内存映射文件(File Mapping)  
  通过把程序实例信息(如窗口句柄、计数器等等)放置到跨进程的内存映射文件,同样可以控制程序实例运行的数量,道理与DLL全局共享区域类似。  

 8)其它  
  曾经见过有人通过注册表、磁盘文件等途径来处理实例控制问题,但由于这些参考对象均为非易失性资源,在碰到程序非正常结束且没有清除实例标识时相当麻烦,真正使用起来具有很大的局限性。  

  总结:前面三种方法适用于拥有窗体的程序,而后面几种则没有这个限制,但相对而言后者实现起来较复杂。不管采用哪种方法,参考对象均必须具有可共享、跨进程、易失性、重启自复位等必要性质。 



我想说的是第5项,只能判断是否多开,应该不能判断是否双开吧。
6,7项有一个问题,如果用开开了两个实例,我们在共享区域做了记录,也表示了是两个实例。这时用户如果开第三个实例我们就不允许打开了。但是如果这时有一个实例被突然终止了进程,也就是说他还没来的及去共享区域把表示开了几个实例的地方修改呢就被关掉了。那现在用户再开一个实例还是会被认为是第三个实例,也还是打不开。
请大家讨论一下,给个答案。
我觉得123项虽然容易被破解,但是实现起来却比较稳定。

8 个解决方案

#1


学习下,马克先,后面几种都没用过

#2


学习。

#3


可以考虑给文件加锁的方式

#4


学习了。。。

#5


针对红字5,在window平台下,大部分内核对象和用户对象都可以用来控制多开,例如一个file句柄(file独占模式)不光mutex等

#6


引用 5 楼 libinfei8848 的回复:
针对红字5,在window平台下,大部分内核对象和用户对象都可以用来控制多开,例如一个file句柄(file独占模式)不光mutex等


我是说控制让用户只可以 最多双开,而不是不允许双开。

#7


针对5.
可以建立2个互斥变量。只有当这2个变量都有实例以后才不能运行新的实例,也就实现了限制最多2开了吧。
没试过,临时想出来的,不知道对不对。

#8


引用 7 楼 morebread 的回复:
针对5.
可以建立2个互斥变量。只有当这2个变量都有实例以后才不能运行新的实例,也就实现了限制最多2开了吧。
没试过,临时想出来的,不知道对不对。


我想,确实可以。好想法

#1


学习下,马克先,后面几种都没用过

#2


学习。

#3


可以考虑给文件加锁的方式

#4


学习了。。。

#5


针对红字5,在window平台下,大部分内核对象和用户对象都可以用来控制多开,例如一个file句柄(file独占模式)不光mutex等

#6


引用 5 楼 libinfei8848 的回复:
针对红字5,在window平台下,大部分内核对象和用户对象都可以用来控制多开,例如一个file句柄(file独占模式)不光mutex等


我是说控制让用户只可以 最多双开,而不是不允许双开。

#7


针对5.
可以建立2个互斥变量。只有当这2个变量都有实例以后才不能运行新的实例,也就实现了限制最多2开了吧。
没试过,临时想出来的,不知道对不对。

#8


引用 7 楼 morebread 的回复:
针对5.
可以建立2个互斥变量。只有当这2个变量都有实例以后才不能运行新的实例,也就实现了限制最多2开了吧。
没试过,临时想出来的,不知道对不对。


我想,确实可以。好想法