我的第一篇关于堆栈溢出的文章。
我不是一个程序员,我有时为了好玩而涉足编码,因此我不花太多时间去理解基本面,而是找到任何可行的解决方案,即使它有点“丑陋”。
这就引出了我的问题:我用一个对话框和一个DlgProc用C编写了一个简单的winapi程序。它接受文件并对它们做一些事情,为了简化起见,它所做的只是创建一个扩展名为*.BAK的文件的副本。
我向注册表(HKEY_CLASSES_ROOT*\shell\BKUP\command)添加了一个键,这样我就可以在windows中选择几个文件,并可以选择"Create“将它们的所有名称发送到我的程序中,但这会分别调用每个文件的程序。所以我搜索了一下,做了一些阅读,发现我需要一些叫做IPC ()的东西,阅读一些选项,WM_COPYDATA消息看起来是最简单和最简单的解决方案,所以我使用它,它的工作就像一种魅力,但有时它就是不.首先,我将解释我所做的事情:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
COPYDATASTRUCT dsIPC;
hwnd=FindWindow("#32770","Backup program");
if(hwnd)
{
// send "__argv[1]" via SendMessage(hwnd,WM_COPYDATA... etc.
return(0);
}
return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}我使用FindWindow()检查程序的实例是否正在运行,如果没有正常运行,则将文件名发送到FindWindow()找到的窗口,然后完全退出该程序实例。
在对话框进程中,我使用代码填充数组中的文件名,并为每个数组设置一个带有SetTimer()的短计时器,当接收到所有文件名时,计时器就会关闭,然后开始复制文件。
同样,所有这些都很好,但有时程序的两个甚至三个实例正在打开,文件在它们之间被分割,这意味着FindWindow()有时找不到第一个窗口。示例:
我在windows资源管理器中选择10个文件,右击它们并选择“创建备份”。我的程序的两个窗口打开。
第一窗口输出:
"File 00.DAT" Backed up successfully.
"File 01.DAT" Backed up successfully.第二窗口输出:
"File 02.DAT" Backed up successfully.
"File 03.DAT" Backed up successfully.
"File 04.DAT" Backed up successfully.
"File 05.DAT" Backed up successfully.
"File 06.DAT" Backed up successfully.
"File 07.DAT" Backed up successfully.
"File 08.DAT" Backed up successfully.
"File 09.DAT" Backed up successfully.然后,我关闭两个窗口,再次选择相同的10个文件,并再次选择"Create“,但这一次和接下来的几次尝试中,我只得到一个窗口:
第一窗口输出:
"File 00.DAT" Backed up successfully.
"File 01.DAT" Backed up successfully.
"File 02.DAT" Backed up successfully.
"File 03.DAT" Backed up successfully.
"File 04.DAT" Backed up successfully.
"File 05.DAT" Backed up successfully.
"File 06.DAT" Backed up successfully.
"File 07.DAT" Backed up successfully.
"File 08.DAT" Backed up successfully.
"File 09.DAT" Backed up successfully.有人能解释一下为什么会发生这种事吗?
如果有关系的话,我正在Windows7 x64上进行测试。
编辑--2017年5月16日,这是zett42 42代码的精简版,它对测试非常有用,但在我的待办事项清单上,我写下来阅读和理解zett42 42代码的其余部分,因为我的代码可能在某种程度上存在缺陷。
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
HWND hwnd;
COPYDATASTRUCT dsIPC;
int i;
DWORD err;
HANDLE hMutex;
hMutex = CreateMutex(NULL, TRUE, "56f0e348-2c1a-4e01-a98e-3e6c8198f9aa");
err = GetLastError();
if(!hMutex)
{
MessageBox(NULL, "Cannot create mutex object. Click OK to exit.", "Error:", MB_OK | MB_ICONSTOP);
return (1);
}
if(err == ERROR_ALREADY_EXISTS)
{
for(i=0 ; i<1000 ; i++)
{
hwnd=FindWindow("#32770","Backup program");
if(hwnd) break;
Sleep(30);
}
if(i==1000) return (1);
dsIPC.dwData=666;
dsIPC.cbData=lstrlen(__argv[1])+1;
dsIPC.lpData=__argv[1];
SendMessage(hwnd, WM_COPYDATA, (WPARAM)hInstance, (LPARAM)&dsIPC);
return(0);
}
return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}发布于 2017-05-14 13:52:37
正如特伦杰所建议的那样:
可能会出现一个争用条件,其中两个程序实例同时启动,它们都没有机会创建对话框窗口,这导致FindWindow在这两种情况下返回NULL。您可以通过使用互斥对象来检查另一个实例是否已经在运行来防止这种情况发生。
下面是一个代码示例,说明如何使用这样的互斥来避免竞争条件:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
// Try to create a mutex. Replace the string by something globally unique,
// for instance a GUID created by using the GuidGen utility, that comes with
// Visual Studio (look in the "Extras" menu).
HANDLE hMutex = CreateMutexW(nullptr, TRUE, L"REPLACE-WITH-YOUR-GUID");
// Make sure to put no other code in between the CreateMutex() and the
// GetLastError() calls to prevent the last error value from being messed up.
DWORD err = GetLastError();
if(!hMutex)
{
// TODO: error handling
return 1;
}
if(err == ERROR_ALREADY_EXISTS)
{
// An instance of this process is already running, but it might not
// have created the window yet, so FindWindow() could still fail.
// You could call FindWindow() in a loop but that would waste resources.
// So I'm using an event object to wait until the window has been created.
// This event object must be set to "signaled" state in WM_INITDIALOG
// handler of the dialog.
HANDLE hWindowCreatedEvent = CreateEventW(nullptr, TRUE, FALSE,
L"PUT-ANOTHER-GUID-HERE");
if(hWindowCreatedEvent)
{
// Wait with timeout of 30s because the 1st process might have failed
// to create the window for some reason.
DWORD waitRes = WaitForSingleObject(hWindowCreatedEvent, 30 * 1000);
if(waitRes == WAIT_OBJECT_0)
{
// The event is signaled so now we know for sure that the window
// has been created.
HWND hwnd;
COPYDATASTRUCT dsIPC;
hwnd=FindWindow("#32770","Backup program");
if(hwnd)
{
// send "__argv[1]" via SendMessage(hwnd,WM_COPYDATA... etc.
}
}
else
{
// TODO: error handling
}
CloseHandle(hWindowCreatedEvent);
}
else
{
// TODO: error handling
}
}
else
{
// This is the first instance of this process.
return DialogBox(hInstance, MAKEINTRESOURCE(ID_DIALOG), NULL, DlgProc);
}
CloseHandle(hMutex);
}编辑DialogProc以设置向窗口创建的进程的其他实例发出信号的事件对象:
INT_PTR CALLBACK DialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch(uMsg)
{
case WM_INITDIALOG:
{
// Use the same event name as in WinMain()!
HANDLE hWindowCreatedEvent = CreateEventW(nullptr, TRUE, FALSE,
L"PUT-GUID-FROM-WINMAIN-HERE");
if(hWindowCreatedEvent)
{
SetEvent(hWindowCreatedEvent);
CloseHandle(hWindowCreatedEvent);
}
// other initialization code...
return TRUE;
}
}
return FALSE;
}另一个建议:不要搜索窗口标题,因为“备份程序”是通用的,可以被其他应用程序使用。然后将WM_COPYDATA发送到错误的进程。
相反,具有全局唯一名称的注册窗口类只搜索类名(调用带有NULL的FindWindow()作为lpWindowName的参数)。还必须在对话框模板中指定类名。
https://stackoverflow.com/questions/43959259
复制相似问题