作者在 2008-11-29 15:27:55 发布以下内容
实现思路
来源:黑白网络 作者: 日期:2008-05-12
这类程序的典型代表就是木马的服务端了,我们想要达到目得,会有这样的想法:程序运行时先检查有没有另一
个实例在运行,没有的话就运行自己,有的话就退出自己.
编程实现
通常有两种方法可以实现我们分别来介绍
1.使用互斥对像
使用API函数CreateMutex来创建命名互斥对象来实现程序互斥是一个比较通用的方法,我们可以在主函数加入如下代码:
HANDLE hObject = CreateMutex(NULL,FALSE,"LengFeng");
if(GetLastError() == ERROR_ALREADY_EXISTS)//程序已经运行
{
CloseHandle(hObject);
ExitProcess(0);
}
以上的CreateMutext函数创建一个称为"LengFeng"的命名的互斥对象,当程序的第二个实例运例时
调用CreateMutex 返回ERROR_ALREADY_EXISTS,表示名为"LengFeng"的互斥对像以存在,也就是说明程序以运行
其完整代码如下:
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HANDLE hObject = CreateMutex(NULL,FALSE,"LengFeng");
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(hObject);
MessageBox(NULL,"程序已经运行!",NULL,NULL);
ExitProcess(0);
}
while(1)
{}
return 0;
}
使用一个循环是禁止程序退出,以便更好的观察效果^_^
2.用编译器创建新节
能不能为程序中加入一个全局变量?让这个全局变量可被程序的多个实例所共享,每当程序实例运行时就对该
全局变量进行修改,通过对该全局变量的访问,就可以知道有多少个实例在运行了.
当然为了系统的安全和稳定性,默认情况下是不允许这样做的,为了阻止这种事情的发生系统使用copy-on-write(写入时拷贝)机制
不过我们就使用创建新节的方法来绕过系统的copy-on-write机制其方法如下
#pragma data_seg("Shared")
int volatile g_lAppInstance =0;
#pragma data_seg()
我们来看一下上面的内容:
第一句 #pragma data_seg("Shared") 创建一个称为Shared 的新节。
第二句 int volatile g_lAppInstance =0 将 g_lAppInstance 放入Shared节中。注意此时只有将g_lAppInstance初始化,编译器才会将其放入Shared节中,否则,将放入Shared以外的节
第三句指示编译器Shared 节结束。
仅仅告诉编译器将某些变量放入它们自己的节中,是不足以实现对这些变量的共享的。还必须告诉链接程序
某个节中的变量是需要加以共享的。若要进行这项操作,可以使用链接程序的命令行上的/SECTION开关:
在冒号的后面,是想要改变其属性的节的名字。在我们的例子中,想要改变Shared节的属性因此应该创建下面的链接程序开关:
#pragma comment(linker,"/section:Shared,RWS")
这一句,我们使编译链接器知道我们的Shared节具有读,写,共享的属性。这是我们实现互斥运行的关键
这样我们就可以在应用程序之间的多个实例之间共享g_lAppInstance 变量了。
下面我们看一下在SDK程序中它的完整实现代码如下:
#include <windows.h>
#pragma data_seg("Shared") //创建新节
int volatile g_lAppInstance =0; //必须有初示化
#pragma data_seg() //结束
#pragma comment(linker,"/section:Shared,RWS")
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
if(++g_lAppInstance>1)
{
MessageBox(NULL,"程序已经运行!",NULL,NULL);
ExitProcess(0);
}
while(1)
{
}
}
回头再看这两种方法,在实现上使用互斥对像比较简单,但功能却不如创建新节的方法强大,不过在无窗口程序中
使用互斥对像还是比较实用的.
基于窗口程序的实现
在有些地方是用EnumWindows来枚举窗口并结合FindWindow来查找窗口,然后发送WM_CLOSE消息,虽然这种方法也可以实现
但实现效果不是太好....而使用上面介绍的互斥对像也是可以的,但它的不足之处是不能将已经启动的实例激活。
这里我们仍然用“创建新节”方法来实现一下
目标是一个基于对话框的程序,其效果如图所示
其实现代码如下
#pragma data_seg("Shared")
HWND hwnd=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")
在对话框OnInitDialog()中加入以下代码:
if(hwnd==NULL)
{
hwnd=m_hWnd;//没有其它实例则保存实例句柄
}else
{
::SetForegroundWindow(hwnd);//若有其它实例则激活
ExitProcess(0);//退出本身
}
当然上面的这些代码完全可以无需修改的加入到自己的程序中,用这种方法实现在感觉上是不是比PostMessage(hwnd,WM_CLOSE,0,0)要文明多了?
这类程序的典型代表就是木马的服务端了,我们想要达到目得,会有这样的想法:程序运行时先检查有没有另一
个实例在运行,没有的话就运行自己,有的话就退出自己.
编程实现
通常有两种方法可以实现我们分别来介绍
1.使用互斥对像
使用API函数CreateMutex来创建命名互斥对象来实现程序互斥是一个比较通用的方法,我们可以在主函数加入如下代码:
HANDLE hObject = CreateMutex(NULL,FALSE,"LengFeng");
if(GetLastError() == ERROR_ALREADY_EXISTS)//程序已经运行
{
CloseHandle(hObject);
ExitProcess(0);
}
以上的CreateMutext函数创建一个称为"LengFeng"的命名的互斥对象,当程序的第二个实例运例时
调用CreateMutex 返回ERROR_ALREADY_EXISTS,表示名为"LengFeng"的互斥对像以存在,也就是说明程序以运行
其完整代码如下:
#include <windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HANDLE hObject = CreateMutex(NULL,FALSE,"LengFeng");
if(GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(hObject);
MessageBox(NULL,"程序已经运行!",NULL,NULL);
ExitProcess(0);
}
while(1)
{}
return 0;
}
使用一个循环是禁止程序退出,以便更好的观察效果^_^
2.用编译器创建新节
能不能为程序中加入一个全局变量?让这个全局变量可被程序的多个实例所共享,每当程序实例运行时就对该
全局变量进行修改,通过对该全局变量的访问,就可以知道有多少个实例在运行了.
当然为了系统的安全和稳定性,默认情况下是不允许这样做的,为了阻止这种事情的发生系统使用copy-on-write(写入时拷贝)机制
不过我们就使用创建新节的方法来绕过系统的copy-on-write机制其方法如下
#pragma data_seg("Shared")
int volatile g_lAppInstance =0;
#pragma data_seg()
我们来看一下上面的内容:
第一句 #pragma data_seg("Shared") 创建一个称为Shared 的新节。
第二句 int volatile g_lAppInstance =0 将 g_lAppInstance 放入Shared节中。注意此时只有将g_lAppInstance初始化,编译器才会将其放入Shared节中,否则,将放入Shared以外的节
第三句指示编译器Shared 节结束。
仅仅告诉编译器将某些变量放入它们自己的节中,是不足以实现对这些变量的共享的。还必须告诉链接程序
某个节中的变量是需要加以共享的。若要进行这项操作,可以使用链接程序的命令行上的/SECTION开关:
在冒号的后面,是想要改变其属性的节的名字。在我们的例子中,想要改变Shared节的属性因此应该创建下面的链接程序开关:
#pragma comment(linker,"/section:Shared,RWS")
这一句,我们使编译链接器知道我们的Shared节具有读,写,共享的属性。这是我们实现互斥运行的关键
这样我们就可以在应用程序之间的多个实例之间共享g_lAppInstance 变量了。
下面我们看一下在SDK程序中它的完整实现代码如下:
#include <windows.h>
#pragma data_seg("Shared") //创建新节
int volatile g_lAppInstance =0; //必须有初示化
#pragma data_seg() //结束
#pragma comment(linker,"/section:Shared,RWS")
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
if(++g_lAppInstance>1)
{
MessageBox(NULL,"程序已经运行!",NULL,NULL);
ExitProcess(0);
}
while(1)
{
}
}
回头再看这两种方法,在实现上使用互斥对像比较简单,但功能却不如创建新节的方法强大,不过在无窗口程序中
使用互斥对像还是比较实用的.
基于窗口程序的实现
在有些地方是用EnumWindows来枚举窗口并结合FindWindow来查找窗口,然后发送WM_CLOSE消息,虽然这种方法也可以实现
但实现效果不是太好....而使用上面介绍的互斥对像也是可以的,但它的不足之处是不能将已经启动的实例激活。
这里我们仍然用“创建新节”方法来实现一下
目标是一个基于对话框的程序,其效果如图所示
其实现代码如下
#pragma data_seg("Shared")
HWND hwnd=NULL;
#pragma data_seg()
#pragma comment(linker,"/section:Shared,RWS")
在对话框OnInitDialog()中加入以下代码:
if(hwnd==NULL)
{
hwnd=m_hWnd;//没有其它实例则保存实例句柄
}else
{
::SetForegroundWindow(hwnd);//若有其它实例则激活
ExitProcess(0);//退出本身
}
当然上面的这些代码完全可以无需修改的加入到自己的程序中,用这种方法实现在感觉上是不是比PostMessage(hwnd,WM_CLOSE,0,0)要文明多了?