我正在开发一个多平台的C ++应用程序(主要是Windows和Linux),现在我需要能够限制可能同时运行的应用程序的最大实例数(在同一台机器上)
我已经使用了一个共享内存模块:
在linux中,我可以轻松控制使用这种代码运行的实例数量:
#include <stdio.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
struct shmid_ds shm;
int shmId;
key_t shmKey = 123456; // A unique key...
// Allocating 1 byte shared memory segment
// open it if already existent and rw user permission
shmId = shmget(shmKey, 1, IPC_CREAT|0x0180);
// Attach to the shared memory segment
shmat(shmId, (char *) 0, SHM_RDONLY);
// Get the number of attached "clients"
shmctl(shmId, IPC_STAT, &shm);
// Check limit
if (shm.shm_nattch > 4) {
printf("Limit exceeded: %ld > 4\n", shm.shm_nattch);
exit(1);
}
//...
sleep(30);
}
这段代码的好处是,当应用程序被终止或崩溃时,系统会小心减少连接客户端的数量。
现在我的问题是,如何在Windows中实现它?(使用内存映射文件)。转换为Windows内存映射文件的&#34;相同&#34; 代码将(或多或少):
void functionName(void)
{
// Create the memory mapped file (in system pagefile)
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE|SEC_COMMIT,
0, 1, "Global\\UniqueShareName");
// Map the previous memory mapped file into the address space
char *addr = (char*)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0);
// How can I check now the number of views mapped?
}
我一直在寻找相当长的时间,但我找不到如何获得打开的观看次数。
文件映射对象的映射视图维护内部引用 对象和文件映射对象直到全部关闭 对它的引用已经发布。因此,要完全关闭文件 映射对象,应用程序必须取消映射文件的所有映射视图 通过调用UnmapViewOfFile映射对象并关闭文件映射 通过调用CloseHandle来处理对象句柄。可以调用这些函数 任何订单。
取消映射文件的映射视图会使占用的范围无效 进程的地址空间中的视图并生成范围 可用于其他分配。它删除了工作集条目 每个未映射的虚拟页面,它是工作集的一部分 处理并减少过程的工作集大小。 它也 减少相应物理页面的共享计数。
但是我无法获得共享计数,并且唯一有关此问题的堆栈溢出问题(我发现)无法回答:Number of mapped views to a shared memory on Windows
如果有人能帮助我,我真的很感激。
(注意:虽然可能不是100%可靠,但请参阅评论部分)
来自RbMm和eryksun的评论(谢谢!)我可以使用此代码解决问题:
#include <stdio.h>
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (__stdcall *NtQueryObjectFuncPointer) (
HANDLE Handle,
OBJECT_INFORMATION_CLASS ObjectInformationClass,
PVOID ObjectInformation,
ULONG ObjectInformationLength,
PULONG ReturnLength);
int main(void)
{
_PUBLIC_OBJECT_BASIC_INFORMATION pobi;
ULONG rLen;
// Create the memory mapped file (in system pagefile) (better in global namespace
// but needs SeCreateGlobalPrivilege privilege)
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE|SEC_COMMIT,
0, 1, "Local\\UniqueShareName");
// Get the NtQUeryObject function pointer and then the handle basic information
NtQueryObjectFuncPointer _NtQueryObject = (NtQueryObjectFuncPointer)GetProcAddress(
GetModuleHandle("ntdll.dll"), "NtQueryObject");
_NtQueryObject(hMap, ObjectBasicInformation, (PVOID)&pobi, (ULONG)sizeof(pobi), &rLen);
// Check limit
if (pobi.HandleCount > 4) {
printf("Limit exceeded: %ld > 4\n", pobi.HandleCount);
exit(1);
}
//...
Sleep(30000);
}
但是为了正确我应该使用全局内核命名空间,它需要一个特权(SeCreateGlobalPrivilege)。所以最后我可以采用命名的管道解决方案(非常漂亮和整洁)。
答案 0 :(得分:4)
正如eryksun所说,最可靠的方法是使用CreateNamedPipe
功能。我们可以使用nMaxInstances
参数 - 可以为此管道创建的最大实例数。以下一种方式。启动时应用程序的每个实例都尝试创建管道实例。如果这样可以 - 我们可以运行,否则达到限制。
代码可以是下一个:
BOOL IsLimitReached(ULONG MaxCount)
{
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);
SECURITY_ATTRIBUTES sa = { sizeof(sa), &sd, FALSE };
HANDLE hPipe = CreateNamedPipe(L"\\\\.\\pipe\\<some pipe>",
PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, MaxCount, 0, 0, 0, &sa);
if (hPipe == INVALID_HANDLE_VALUE)
{
ULONG dwError = GetLastError();
if (dwError != ERROR_PIPE_BUSY)
{
// handle error
}
return TRUE;
}
return FALSE;
}
并使用,例如N个实例
if (!IsLimitReached(N))
{
MessageBoxW(0, L"running..",0,0);
}