none
ソフトで作った一時ファイルを削除したい

    質問

  • VS2010でMFCでソフトを開発しています。

    目的はソフトで作った一時ファイルを削除する事です。

    PC再起動で削除されるフォルダがありましたらそのフォルダを教えてください。

    そのようなフォルダがなければ、ソフトで一時ファイルを削除しますが、ソフトは多重起動できて、複数起動されている場合がありますので、その場合は最後の1つが削除されるタイミングを得る方法を教えてください。

    ご教授よろしくお願いします


    2017年12月21日 5:13

回答

  • PC再起動で削除されるフォルダがありましたらそのフォルダを教えてください。

    MoveFileEx 関数を使うと、OS 再起動時にファイルを削除することができます。具体的には、第一引数に削除したいファイルのパスを指定し、第二引数は NULL を指定し、第三引数で MOVEFILE_DELAY_UNTIL_REBOOT を指定します。

    参考サイト: https://msdn.microsoft.com/ja-jp/library/cc429621.aspx

    ソフトで一時ファイルを削除しますが、ソフトは多重起動できて、複数起動されている場合がありますので、その場合は最後の1つが削除されるタイミングを得る方法を教えてください。

    例えば下記のようなコードで実行ファイルパスに紐づいたプロセスの数を調べられます。

    int SelfProcessCount()
    {
    	WCHAR szModulePath[MAX_PATH];
    	{
    		GetModuleFileNameW(0, szModulePath, _countof(szModulePath));
    		WCHAR szDrive[_MAX_DRIVE];
    		WCHAR szDirectory[_MAX_DIR];
    		WCHAR szFile[_MAX_FNAME];
    		WCHAR szExt[_MAX_EXT];
    		_wsplitpath_s(szModulePath, szDrive, _MAX_DRIVE, szDirectory, _MAX_DIR, szFile, _MAX_FNAME, szExt, _MAX_EXT);
    		WCHAR szDosDevice[MAX_PATH];
    		QueryDosDeviceW(szDrive, szDosDevice, _countof(szDosDevice));
    		lstrcpyW(szModulePath, szDosDevice);
    		lstrcatW(szModulePath, szDirectory);
    		lstrcatW(szModulePath, szFile);
    		lstrcatW(szModulePath, szExt);
    	}
    	int nCount = 0;
    	DWORD dwProcessID[1024], dwNeeded;
    	if (EnumProcesses(dwProcessID, sizeof(dwProcessID), &dwNeeded))
    	{
    		const DWORD dwProcessCount = dwNeeded / sizeof(DWORD);
    		for (DWORD i = 0; i < dwProcessCount; i++)
    		{
    			WCHAR szNameProc[MAX_PATH];
    			HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID[i]);
    			if (hProcess)
    			{
    				if (GetProcessImageFileNameW(hProcess, szNameProc, _countof(szNameProc)) != 0)
    				{
    					if (lstrcmpiW(szNameProc, szModulePath) == 0)
    					{
    						nCount++;
    					}
    				}
    				CloseHandle(hProcess);
    			}
    		}
    	}
    	return nCount;
    }

    プログラムの終了時に上記の関数で 1 が返ってきた場合に、一時ファイルを削除するようにしてみてはいかがでしょうか?

    ※同プロセスかどうか判定するために実行ファイルパスを使っていますので、同じ EXE でも異なるファイルパスから起動された場合には使えません。また、複数人が同時に OS にサインインしている場合も想定されていません。

    2017年12月21日 6:44

すべての返信

  • よい方法というのは何を指していますか?

    一時ファイルを削除する方法ですか?

    それとも、複数起動可能で最後の1つが終了するときですか?

    複数起動を判断するための材料が何もないので、もう少し詳しい情報を載せるとより適切な回答がつくのではないかな?と思います。


    とっちゃん@わんくま同盟, Visual Studio and Development Technologies http://blogs.wankuma.com/tocchann/default.aspx

    2017年12月21日 5:50
  • 良い方法というか、一般論になってしまいます。

    1.アプリケーション終了時に行う処理は OnEndSession()、WM_ENDSESSIONでやます。

    2.同期をとるには同期クラスを使います。
     プロセス間で同期をとるには、ミューテックス、セマフォ、名前付きイベント等を
     使用して同期のタイミングをとります。

    (参考)https://msdn.microsoft.com/ja-jp/library/cc429052.aspx

    色々なアプローチのしかたが考えられるので適材適所といった感じでしょうか。

    2017年12月21日 6:44
  • PC再起動で削除されるフォルダがありましたらそのフォルダを教えてください。

    MoveFileEx 関数を使うと、OS 再起動時にファイルを削除することができます。具体的には、第一引数に削除したいファイルのパスを指定し、第二引数は NULL を指定し、第三引数で MOVEFILE_DELAY_UNTIL_REBOOT を指定します。

    参考サイト: https://msdn.microsoft.com/ja-jp/library/cc429621.aspx

    ソフトで一時ファイルを削除しますが、ソフトは多重起動できて、複数起動されている場合がありますので、その場合は最後の1つが削除されるタイミングを得る方法を教えてください。

    例えば下記のようなコードで実行ファイルパスに紐づいたプロセスの数を調べられます。

    int SelfProcessCount()
    {
    	WCHAR szModulePath[MAX_PATH];
    	{
    		GetModuleFileNameW(0, szModulePath, _countof(szModulePath));
    		WCHAR szDrive[_MAX_DRIVE];
    		WCHAR szDirectory[_MAX_DIR];
    		WCHAR szFile[_MAX_FNAME];
    		WCHAR szExt[_MAX_EXT];
    		_wsplitpath_s(szModulePath, szDrive, _MAX_DRIVE, szDirectory, _MAX_DIR, szFile, _MAX_FNAME, szExt, _MAX_EXT);
    		WCHAR szDosDevice[MAX_PATH];
    		QueryDosDeviceW(szDrive, szDosDevice, _countof(szDosDevice));
    		lstrcpyW(szModulePath, szDosDevice);
    		lstrcatW(szModulePath, szDirectory);
    		lstrcatW(szModulePath, szFile);
    		lstrcatW(szModulePath, szExt);
    	}
    	int nCount = 0;
    	DWORD dwProcessID[1024], dwNeeded;
    	if (EnumProcesses(dwProcessID, sizeof(dwProcessID), &dwNeeded))
    	{
    		const DWORD dwProcessCount = dwNeeded / sizeof(DWORD);
    		for (DWORD i = 0; i < dwProcessCount; i++)
    		{
    			WCHAR szNameProc[MAX_PATH];
    			HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessID[i]);
    			if (hProcess)
    			{
    				if (GetProcessImageFileNameW(hProcess, szNameProc, _countof(szNameProc)) != 0)
    				{
    					if (lstrcmpiW(szNameProc, szModulePath) == 0)
    					{
    						nCount++;
    					}
    				}
    				CloseHandle(hProcess);
    			}
    		}
    	}
    	return nCount;
    }

    プログラムの終了時に上記の関数で 1 が返ってきた場合に、一時ファイルを削除するようにしてみてはいかがでしょうか?

    ※同プロセスかどうか判定するために実行ファイルパスを使っていますので、同じ EXE でも異なるファイルパスから起動された場合には使えません。また、複数人が同時に OS にサインインしている場合も想定されていません。

    2017年12月21日 6:44
  • kenjinoteさ

    >同じ EXE でも異なるファイルパスから起動された場合には使えません。また、複数人が同時に OS にサインインしている場合も想定されていません。

    この状態もありそうですが、 

    MoveFileEx は使えそうですので、テストしてみます。

    ありがとうございました。

    2017年12月21日 7:48
  • BOOL bRet = ::MoveFileEx( strTmpPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT );

    MoveFileEx はちょっと苦戦していて、
    "アクセスが拒否されました" のエラーになります。


    GetTempPathで得られたパスに ファイルを作って (C:\Users\USER\AppData\Local\Temp\testfile")
    ファイル(testfile)を上書き可能モードにした状態で上記MoveFileExを使うとエラーになったので、

    C:temp\test  を作って中の全てのフォルダとファイルを上書き可能状態にして このパスを使っても結果は同じでした。

    OSはWindows7でAdministratorです。

    MoveFileExのエラー回避のために考えられる事はありますでしょうか?

    2017年12月21日 9:49
  • 検証してみました。MoveFileEx 関数を使って再起動時にファイルを消すためには、管理者権限でプロセスを起動する必要があるようですね。ドキュメントにもありますが、再起動時に削除するファイルは下記のレジストリに登録されるようです。

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRenameOperations

    管理者権限でプロセスを立ち上げることが難しい場合はやはり自前で一時ファイルを消す必要がありそうです。複数のユーザーや複数プロセスを考慮して厳密に対応することは簡単ではないかもしれません。。

    2017年12月21日 11:29
  • kenjinoteさん

    ご検証たいへんありがとうございます。

    完全ではないですが、

    できれば削除したい という方針ですので、

    ソフト起動時に作成した一時ファイルがある程度古かったら(作成時間が2日前など)削除する。ソフト終了時他のプロセスで一時ファイルが使われてなければ削除 という事で凌ごうと思います。

    2017年12月21日 11:53