最終更新日 2024-09-25

秀丸を閉じる時に実行する関数 (秀丸エディタ 8.66以上)

概要

C++で作成したdllは非同期処理などを含むことができますので、
「秀丸を閉じる」際などに、「スレッドを止める」あるいは「ファイルに書き込む」など必要な処理を行う必要があるような
dllを作成したいと思うこともあるでしょう。
秀丸エディタ 8.98以上ならば、COM呼び出しに対して、オブジェクト解放時にメソッドを指定して実行させることが出来ます。

DllDetachFunc_After_Hm866

DllDetachFunc_After_Hm866が定義されていれば、秀丸マクロは、freedll、あるいは、プロセス終了時にそれを呼び出します。

DllDetachFunc_After_Hm866 と setdlldetachfunc DllDetachFunc_After_Hm866 という名称を変更したい場合には、マクロ側で「setdlldetachfunc」を利用することで変更可能です。
(ただし変更する理由があまり見いだせないため、この機能を利用することは稀でしょう)

dllmain.cpp
#include "HmCppInvoke.h"
#include <thread>

using namespace Hidemaru;
using namespace std;

bool bThreadCenceller = false;
const int sleepInterval = 1000;
HANDLE hThread = NULL;

DWORD WINAPI ThreadFunc(LPVOID pParam)
{

	while(true) {
		Sleep(sleepInterval);

		if (bThreadCenceller) {
			break;
		}

		auto inMacro = Hm.Macro.isExecuting();
		Hm.OutputPane.output(L"非同期関数中でマクロ実行中?:" + to_wstring(inMacro) + L"\r\n");
	}

	Hm.OutputPane.output(L"スレッドが終了");
	return 0;
	// スレッド終了
}

extern "C" __declspec(dllexport) THmNumber DllDetachFunc_After_Hm866(THmNumber n) {

	Hm.funcDllExport();

	bThreadCenceller = true;
	WaitForSingleObject(hThread, sleepInterval * 2);

	MessageBox(NULL, to_wstring(n).data(), L"DllDetachFunc_After_Hm866", NULL);

	if (n == 1) {
		// 8.66以降。freedll
	}
	else if (n == 2) {
		// 8.66以降。loaddll文による入れ替え
	}
	else if (n == 3) {
		// 8.66以降。マクロから呼び出したdllが、プロセス終了時にまだロードされた状態であれば、この値で呼び出される
	}
	else if (n == 4) {
		// 8.98以降。keepdll #dll, 0; のエラー発生時解放が指定してあり、エラーが発生した理由によりマクロが終了し、dllが解放されようとしている時
	}
	else {
		// 未知の終了理由
	}
	return 0;
}

extern "C" __declspec(dllexport) THmNumber test() {

	Hm.funcDllExport();

	bThreadCenceller = false;

	hThread = CreateThread(
		NULL,
		0,
		ThreadFunc,
		NULL,
		0,
		NULL
	);

	auto inMacro = Hm.Macro.isExecuting();
	Hm.OutputPane.output(L"同期関数中でマクロ実行中?:" + to_wstring(inMacro) + L"\r\n");

	Hm.OutputPane.output(L"test関数の終了直前\r\n");

    return 1;
}

呼び出し側

「DllDetachFunc_After_Hm866」を利用したいと思う場合、以下のように一般的には、freedllをせずdllを常駐させるといった形となるでしょう。

test.mac
#dll = loaddll(currentmacrodirectory + @"\test.dll");
#r = dllfuncw(#dll, "test");
// freedll(#dll); // freedllしないことでdllを解放せず、常駐タイプに出来る。

マクロ実行後に必ずdllを解放するならば、keepdllが必須 (秀丸エディタ 8.98以降)

常駐させるのではなく、「マクロ実行後は必ずdllを解放する」ような通常の同期型のdllならば、 以下のようにkeepdllとfreedllを両方記述するようにしましょう。
秀丸 8.98未満の場合には、keepdllは使えませんのでやむをえません。freedllのみの記述にしましょう。

test.mac
#dll = loaddll(currentmacrodirectory + @"\test.dll");
keepdll #dll, 0; // マクロで致命的エラーがあった時でもfreedllされるようになる。マクロが順当に終わった際でも一番最後にfreedllされる。
#r = dllfuncw(#dll, "test");

message 111; // 致命的マクロエラーが発生する。しかし「keep #dll, 0」による指定が発動し、dllは解放される。
freedll(#dll); // 1つ上の行で致命的マクロエラーが発生するため、この行は実行されない。freedll のみの記述だとdllの解放はあてに出来ない。

秀丸エディタ 8.98未満の場合にkeepdllでエラーが出る現象を回避するには

keepdll を利用する場合、秀丸8.98以上でないとマクロ自体がエラーが出てしまいます。
if文でバージョンを判定したとしても、先にマクロ全体がチェックされてしまうため、秀丸8.97以下ではkeepdllの記述が存在するだけでエラーとなります。
これを回避するには、evalを利用するのが良いでしょう。
これにより 秀丸エディタ 8.66以上であればエラーが出ない形に出来ます。

test.mac
#dll = loaddll(currentmacrodirectory + @"\test.dll");

if (version > 898) { eval "keepdll #dll, 0"; }
// evalにすることで、keep命令文が文字列内に隠れる。
// これにより 秀丸8.66以降(evalが使えるバージョン)~8.97の間のバージョンならこの行がスキップする。

#r = dllfuncw(#dll, "test");

message 111; // 致命的マクロエラーが発生する。
freedll(#dll);