こんにちは。
今回はちょっと変わった質問かもしれません。
実は、現在ローカルシステムサービスで起動するExeとクライアントが実行す
るExeの2つをセットで作成しています。
サービス側では、名前付きパイプを作成し、クライアントからのイベントが上
がるのを、WaitForSingleObject関数で待機し、イベントが上がるとパイプの
読込を行うように作っています。
クライアント側のExeでは、SetEvent関数でイベントをサービスへ通知後、パ
イプにデータを送り込む用に作り込んでいます。
ここで質問なのですが、パイプで渡すデータに、クライアントExeを終了する
と言うフラグを設けており、クライアントexeの終了時にこのフラグをたてて
パイプを送るようにしているのですが・・・
(これを行わないと、クライアントのみをいったん終了→起動と行った時点で
パイプに接続できなくなってしまいます[サービスを再起動すると復活します])
このクライアント用のexeはタスクトレイに常駐し、左クリックでメニューを
表示、選択した処理の命令をパイプでサービスに送る、という動きになってい
るため、ユーザがクライアントexeを意図的に終了すると言うことはあまりあ
りません。
そこで問題になっているのが、コンピュータをログオフした時の動作です。
ログオフすると、そのユーザで起動していたアプリケーションは強制的に終了
されますよね?
この時に、クライアント側から終了パイプを送れないため、サービスを再起動
しない限り、クライアント側からパイプを送り込めなくなってしまいます。
このような場合、クライアント/サーバそれぞれの作りを変更して改善するこ
とが可能なのでしょうか?
長々とすみませんが、よろしくおねがいいたします。
念の為、サーバ側と、クライアント側のパイプのやり取りにかかわる部分のみ
を抜粋してのせておきます。
[サーバ側]
HANDLE hSettingThread = NULL;
HANDLE hEventSvToCl = NULL;
HANDLE hEventClToSv = NULL;
//Serviceのメインスレッド
DWORD ServiceThread(LPDWORD param)
{
DWORD l_dwThreadID;
static HANDLE l_hPipe;
DWORD dwPipeDataSize;
SECURITY_ATTRIBUTES l_sa;
SECURITY_DESCRIPTOR l_sd;
if(!InitializeSecurityDescriptor(&l_sd, SECURITY_DESCRIPTOR_REVISION))
{
return -10;
}
if(!SetSecurityDescriptorDacl(&l_sd, TRUE, NULL, FALSE))
{
return -20;
}
l_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
l_sa.bInheritHandle = FALSE;
l_sa.lpSecurityDescriptor = &l_sd;
hEventClToSv = CreateEvent(&l_sa, FALSE, FALSE, "DoraClToSv");
hEventSvToCl = CreateEvent(&l_sa, FALSE, FALSE, "DoraSvToCl");
if(hEventSvToCl == NULL || hEventClToSv == NULL)
{
return -30;
}
while(1)
{
if(l_hPipe)
{
FlushFileBuffers(l_hPipe);
DisconnectNamedPipe(l_hPipe);
CloseHandle(l_hPipe);
}
l_hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 0, 150, &l_sa);
if(l_hPipe == INVALID_HANDLE_VALUE)
{
return -40;
}
if(!ConnectNamedPipe(l_hPipe, (LPOVERLAPPED)NULL))
{
CloseHandle(l_hPipe);
return -50;
}
hSettingThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SettingThreadFunc, (LPVOID)l_hPipe, 0, &l_dwThreadID);
if(hSettingThread == NULL)
{
CloseHandle(l_hPipe);
return -60;
}
WaitForSingleObject(hSettingThread, INFINITE);
}
if(hEventClToSv)
{
CloseHandle(hEventClToSv);
}
if(hEventSvToCl)
{
CloseHandle(hEventSvToCl);
}
if(hSettingThread)
{
CloseHandle(hSettingThread);
}
return 0;
}
すみません、ソース書きかけで書込みしちゃいました。
ソースの部分のみ書き直します。
[サーバ側]
HANDLE hSettingThread = NULL;
HANDLE hEventSvToCl = NULL;
HANDLE hEventClToSv = NULL;
//Serviceのメインスレッド
DWORD ServiceThread(LPDWORD param)
{
DWORD l_dwThreadID;
static HANDLE l_hPipe;
DWORD dwPipeDataSize;
SECURITY_ATTRIBUTES l_sa;
SECURITY_DESCRIPTOR l_sd;
if(!InitializeSecurityDescriptor(&l_sd, SECURITY_DESCRIPTOR_REVISION))
{
return -10;
}
if(!SetSecurityDescriptorDacl(&l_sd, TRUE, NULL, FALSE))
{
return -20;
}
l_sa.nLength = sizeof(SECURITY_ATTRIBUTES);
l_sa.bInheritHandle = FALSE;
l_sa.lpSecurityDescriptor = &l_sd;
hEventClToSv = CreateEvent(&l_sa, FALSE, FALSE, "DoraClToSv");
hEventSvToCl = CreateEvent(&l_sa, FALSE, FALSE, "DoraSvToCl");
if(hEventSvToCl == NULL || hEventClToSv == NULL)
{
return -30;
}
while(1)
{
if(l_hPipe)
{
FlushFileBuffers(l_hPipe);
DisconnectNamedPipe(l_hPipe);
CloseHandle(l_hPipe);
}
l_hPipe = CreateNamedPipe(PIPE_NAME, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 0, 0, 150, &l_sa);
if(l_hPipe == INVALID_HANDLE_VALUE)
{
return -40;
}
if(!ConnectNamedPipe(l_hPipe, (LPOVERLAPPED)NULL))
{
CloseHandle(l_hPipe);
return -50;
}
hSettingThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SettingThreadFunc, (LPVOID)l_hPipe, 0, &l_dwThreadID);
if(hSettingThread == NULL)
{
CloseHandle(l_hPipe);
return -60;
}
WaitForSingleObject(hSettingThread, INFINITE);
}
if(hEventClToSv)
{
CloseHandle(hEventClToSv);
}
if(hEventSvToCl)
{
CloseHandle(hEventSvToCl);
}
if(hSettingThread)
{
CloseHandle(hSettingThread);
}
return 0;
}
//パイプでやり取りをして必要な処理をする関数
DWORD SettingThreadFunc(LPVOID lpParam)
{
PIPEDATA l_PipeData; //Named Pipeでやり取りするデータを格納するオリジナルの構造体
DWORD dwByteRead;
DWORD dwByteWrite;
HANDLE l_hPipe;
char Temp[2048];
l_hPipe = (HANDLE)lpParam;
while(1)
{
//スレッド終了フラグを下ろしておく
l_PipeData.fStop = FALSE;
WaitForSingleObject(hEventClToSv, INFINITE);
if(!ReadFile(l_hPipe, &l_PipeData, sizeof(PIPEDATA), &dwByteRead, (LPOVERLAPPED)NULL))
{
break;
}
//パイプから終了フラグが上がっているかを確認
if(l_PipeData.fStop)
{
ResetEvent(hEventClToSv);
ResetEvent(hEventSvToCl);
break;
}
//様々な処理を行いクライアント側に返送するデータを作成
if(!SetEvent(hEventSvToCl))
{
break;
}
if(!WriteFile(l_hPipe, &l_PipeData, sizeof(PIPEDATA), &dwByteWrite, (LPOVERLAPPED)NULL))
{
break;
}
ResetEvent(hEventClToSv);
ResetEvent(hEventSvToCl);
}
return 0;
}
[クライアント側]
HANDLE hFile;
HANDLE hEventSvToCl;
HANDLE hEventClToSv;
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static PIPEDATA l_PD;
static DWORD l_dwReturn;
DWORD l_dwByteRead;
DWORD l_dwByteWrite;
switch (msg)
{
case WM_CREATE:
hEventClToSv = OpenEvent(EVENT_ALL_ACCESS, FALSE, "DoraClToSv");
if(hEventClToSv == NULL)
{
MessageBox(hWnd, "起動エラー\n\nサーバへ送るイベントを取得できませんでした。", ClassName, MB_OK);
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
}
hEventSvToCl = OpenEvent(EVENT_ALL_ACCESS, FALSE, "DoraSvToCl");
if(hEventSvToCl == NULL)
{
MessageBox(hWnd, "起動エラー\n\nサーバから受け取るイベントを取得できませんでした。", ClassName, MB_OK);
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
}
hFile = CreateFile(PIPE_NAME, GENERIC_READ | GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
if(hFile == INVALID_HANDLE_VALUE)
{
MessageBox(hWnd, "起動エラー\n\nパイプをオープンできませんでした。", ClassName, MB_OK);
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
}
・・・中略
case WM_COMMAND:
switch(LOWORD(wParam)
{
case IDC_XXXX:
if(SetEvent(hEventClToSv) != TRUE)
{
MessageBox(hWnd, "イベントを通知できません", "エラー", MB_OK);
break;
}
if(!WriteFile(hFile, &l_PD, sizeof(PIPEDATA), &l_dwByteWrite, (LPOVERLAPPED)NULL))
{
MessageBox(hWnd, "名前付きパイプに書き込めません", "エラー", MB_OK);
break;
}
WaitForSingleObject(hEventSvToCl, INFINITE);
if(!ReadFile(hFile, &l_PD, sizeof(PIPEDATA), &l_dwByteRead, (LPOVERLAPPED)NULL))
{
MessageBox(hWnd, "名前付きパイプを読み込めません", "エラー", MB_OK);
ResetEvent(hEventSvToCl);
break;
}
//戻ってきたパイプデータで様々な処理
break;
}
case WM_DESTROY:
l_PD.fStop = TRUE;
if(SetEvent(hEventClToSv) != TRUE)
{
MessageBox(hWnd, "終了エラー\n\nイベントを通知できません。", ClassName, MB_OK);
break;
}
if(!WriteFile(hFile, &l_PD, sizeof(PIPEDATA), &l_dwByteWrite, (LPOVERLAPPED)NULL))
{
MessageBox(hWnd, "終了エラー\n\n名前付きパイプに書き込めません。", ClassName, MB_OK);
break;
}
PostQuitMessage(0);
break;
}
return (0L);
}
一部変数の宣言漏れなどがあったらごめんなさい。
長々と申し訳ありませんが、よろしくおねがいいたします。
開発環境は Windows XP Pro with SP2 + VS.NET 2003 Enterprise です。
メッセージループを回して、WM_QUERYENDSESSION、WM_ENDSESSION を処理しては?
WM_QUERYENDSESSION等のタイミングで接続解除処理を
行えば良いだけの気がしますが。どうなんでしょう。
RAPTさん、中澤@失業者さん
ありがとうございます。
こんな単純だったんですね。
case WM_DESTROY:
l_PD.fStop = TRUE;
if(SetEvent(hEventClToSv) != TRUE)
{
MessageBox(hWnd, "終了エラー\n\nイベントを通知できません。", ClassName, MB_OK);
break;
}
if(!WriteFile(hFile, &l_PD, sizeof(PIPEDATA), &l_dwByteWrite, (LPOVERLAPPED)NULL))
{
MessageBox(hWnd, "終了エラー\n\n名前付きパイプに書き込めません。", ClassName, MB_OK);
break;
}
PostQuitMessage(0);
break;
を
case WM_QUERYENDSESSION:
case WM_ENDSESSION:
case WM_DESTROY:
l_PD.fStop = TRUE;
if(SetEvent(hEventClToSv) != TRUE)
{
MessageBox(hWnd, "終了エラー\n\nイベントを通知できません。", ClassName, MB_OK);
break;
}
if(!WriteFile(hFile, &l_PD, sizeof(PIPEDATA), &l_dwByteWrite, (LPOVERLAPPED)NULL))
{
MessageBox(hWnd, "終了エラー\n\n名前付きパイプに書き込めません。", ClassName, MB_OK);
break;
}
PostQuitMessage(0);
break;
とちょっとはしょりましたがこれであっさりいきました!!
この2つのメッセージをしらなかった自分がちょっと情けないです。
こんな簡単な問題だったのに長々と書き込んですみませんでした。
ツイート | ![]() |