この記事は?
『C#』『VB』『C++/CLI』『MFC』を使ったプロジェクトで、共通のLoggerを作成する上でのモジュール間呼び出しに関するtipsをまとめたメモ。
やり取りに関係しないところは適当に簡略化してます。
<必要な要素>
- IFは共通であること
- ファイル名、関数名、行数を勝手に表示すること
誰向け?
- C#やVBで出来たソリューションにMFCの古い秘伝のソースを入れる必要が出た悲しき定めの人
- ログ出力初心者さん?
全体観
LogModule
『C++/CLI』で作成。
C#, VBからもC++/CLI、MFCからも呼ぶ必要があるためです。
今回の話に全く関係ありませんが、NLog使いました。
void CLogger::WriteInfoMessage(String^ message, String^ file, String^ func, int line)
{
CLogger::Initialize();
CLogger::logger->WriteLog(Loglv::Info, message, file, func, line);
}
<簡単な説明>
秘伝のソース過ぎて最初にどこを通るか全くわからないので、全ての外部IFにインスタンス作成等のInitializeがついてます。
WriteLog内でメッセージをフォーマットして、Nlogのインスタンスに対しInfoメソッドを呼んでいます。
C#, VB用IF
『C#』で作成。
LogModuleのdllをLib参照しているプロジェクト。
public static void WriteInfoMessage(
string message,
[CallerFilePath]string file = "",
[CallerMemberName]string func = "",
[CallerLineNumber]int line = 0)
{
Logger.WriteInfoMessage(message, file, func, line);
}
<簡単な説明>
属性名 | 効果 |
---|---|
CallerFilePath | 呼び出し元ファイル名を取得 |
CallerMemberPath | 呼び出し元関数名を取得 |
CallerLineNumber | 呼び出し元行数を取得 |
LogModuleのWriteInfoMessage等ログ出力関数を直接を呼び出しています. |
C++/CLI, MFC用ヘッダファイル
LogModule, C++/CLIプロジェクト、MFCプロジェクト共用。
# ifdef LOGDLL_EXPORTS
# define LOGDLL_API __declspec(dllexport)
# else
# define LOGDLL_API __declspec(dllimport)
# endif
# define WriteInfoMessage(message) _WriteInfoMessage(message, __FILE__, __FUNCTION__, __LINE__)
extern "C" {
LOGDLL_API void _WriteInfoMessage(const char* message, const char* file, const char* func, int line);
}
<簡単な説明>
定義名 | 効果 |
---|---|
__FILE__ | 呼び出し元ファイル名を取得 |
__FUNCTION__ | 呼び出し元関数名を取得 |
__LINE__ | 呼び出し元行数を取得 |
WriteInfoMessageをDefineで定義し、ファイル名等を追加で取得してextern"C"の内部関数に渡しています。 | |
また、LogModule内のログ出力クラスにてヘッダInclude時にLOGDLL_EXPORTSを定義することで、呼び出し元/呼び出し先のdllimport/dllexportを分けています。(お決まり) |
# include "Logger.h"
# define LOGDLL_EXPORTS
呼び出し部分
C#
using static Logs.LoggerIF;
...
WriteInfoMessage("test");
VB
Imports Logs.LoggerIF;
...
WriteInfoMessage("test")
C++/CLI, MFC
# include "LoggerIF.h"
...
WriteInfoMessage("test");
妥協点
LogModuleのあるC++/CLIにもCallerFilePath等は存在しているため、C#, VB用IFを使わずC#側から直接LogModuleを呼び出すことはもちろん可能です。
ただし、VB側から呼び出してもファイル名などが入ってきませんでした。
おそらく中間コードからVBに直す際、VBの仕様に合わせるために初期値が設定されていないと強制的に後からNothingを入れてしまうから・・・?CallerMemberPath等は値が設定されている場合無視されてしまうので、結果ファイル名が入って来ないのではないかと。
OptionalAttributeやDefaultValue等試してみてもうまく出来ず断念。そのため、C#でIFを作成しています。
対処法がわかればこのIFは不要です。
C#側からは初期値問題が発生しないので直接LogModuleを呼び出しても問題ありませんが、どうせC#でIFを作るなら参照しない理由がないので、C#側からもこのIFを参照しています。