最終更新日:
MFCについての簡単な紹介。
基本的な使い方
.NET Framework 2002年から展開
VC++, VB, VC#, VF#, JavaScript, Pythonが.NET Framework上で動作する。
iniファイルの読み書きやドライバ開発は.NET Frameworkだけでは完結しない。
Windows APIやMFCのようなOSと直接対話できるライブラリ群が必要。
マネージドコード:.NET Frameworkに依存 → MSIL (中間言語)。ガベージコレクタが存在する。
アンマネージドコード:非依存
Visual C++では上記の2つを混在させることができる。
マネージドコード | アンマネージドコード | |
開発 | 易しい | 難しい |
開発期間 | 短い | 長い |
動作速度 | 同じ | 同じ(マネージドコードより速くなる場合もある) |
安全性 | 高い | 低い |
メモリ管理 | 自動 | 手動 |
生成コード | 中間コード | ネイティブコード |
実行 | .NET Framework上 | Windows OS直接 |
依存 | .NET Frameworkに依存 | OSに依存 |
歴史 | 先進的 | 伝統的 |
Visual Studio Community 2017インストール
以下の2つを入れる:
- .NETデスクトップ開発
- C++によるデスクトップ開発
オプションで
- 「MFCとATLのサポート(x86とx64)」
- 「C++/CLIサポート」
- 「標準ライブラリモジュール」
- 「Windows 10 SDK(最新バージョン)」
Visual C++データ型 (C++データ型を内包)
C++データ型
Visual C++データ型の例:
- BOOL
- BYTE
- DWORD
- HANDLE
- HDC
- HWND
- LPVOID
- LPCTSTR
- TCHAR
- UINT
- ULONG
Visual C++で扱える文字コード
文字コード | 表現できる文字 | 利用されている範囲 |
ASCII | 英数字記号のみ | ほとんどすべてのコンピュータ |
Shift_JIS | 英数字記号と日本語 | (昔からある)日本語のアプリケーション |
Unicode | 多言語に対応 | .NET Frameworkで採用 |
Shift_JIS → char型
Unicode → wchar_t型。
Visual C++では、_T(_TEXT)マクロが用意されている。
_Tマクロで生成される文字列はTCHAR型に変換される。
TCHAR型の文字列はビルド時にchar型かwchar_t型に変換される。
MFCクラス
Windows APIはすべての関数が同列に見える。
MFCはWindows APIを目的別にクラス単位にまとめたもの。
必要最小限なプログラミング量でアプリケーションを作成できる。
MFCでクラスを追加するには、クラスウィザードを使う。
メンバー関数、メンバー変数を追加するときも、クラスウィザードを使う。
MFCの基本的なクラス
[CString]
CString : 文字列(ANSIとUnicodeに対応している)
CString cSample(_T("Visual C++"));のように初期化する。
文字列連結は+を使う。
TRACEマクロ。デバッグ文字列表示。_Tマクロは不要。\nで改行できる。
[CFile]
CFile : ファイル操作。
ファイルの情報を取得することもできる。
CFileStatus status;
BOOL bExist;
bExist = CFile::GetStatus(_T("C:\\test.txt"),status);
[COleDateTime]
日付・時刻。
COleDateTime cTime = COleDateTime::GetCurrentTime();
.GetYear();などで年月日・時分秒を取得できる。
コモンコントロール
イベント追加もクラスウィザードからできる。
クラスウィザードはProject -> Class Wizardで起動できる。
エディットボックスから文字列を取得する場合:
メンバー変数の追加でValueを選択する(コントロールを制御する場合はControlを選択する)。
テキストを取得したいのでCString型を選ぶ。
BOOL 結果 = CWnd::UpdateData(bool データを交換する方向);
UpdateData()は引数が
TRUEの場合は、コントロールからメンバー変数へ
FALSEの場合は、メンバー変数からコントロールへ値が代入される。
DDX(ダイアログデータエクスチェンジ)という機能により実現されている。
ラジオボタン
GropuをTrueにすると、グループボックス内に配置されているコントロールのグループ化を設定する。
複数のラジオボタンの中の1つだけが選択されるようになる。
グループ化する司書のコントロールだけをTrueにする。
スライダコントロール
スライダコントロールがスライドされたことは、スライダコントロールのメッセージハンドラでは知ることができない。
ダイアログ画面に通知されるWM_HSCROLLメッセージを利用する。
エディットコントロールのテキストが変わると、
コントロールに対してEN_UPDATEメッセージが通知される。
プログレスバー
SetTimerで指定した時間が経過すると、WM_TIMERメッセージが通知される。
デバイスコンテキスト
画像描画。
DialogのプロパティーでBorderをDialog FrameからResizingにするとウィンドウのサイズ変更できる。
すると描画がずれる。
WM_SIZEメッセージを受けたら再描画する。
メッセージの処理
BEGIN_MESSAGE_MAP
END_MESSAGE_MAP
の中で結び付けられる。
ダイアログデータバリデーション
入力制限。DDV (Dialog Data Validation)。
MDI
初期状態で子ウィンドウの表示を防ぐには、
CxxxxApp::InitInstance()のRegisterShellFileTypes(TRUE)のあとに、
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
を追加する。
MDIでの拡張子フィルタは
リソースファイルの
IDR_xxxTYPEを
\nxxx\nxxx\nPicture Files (*.jpg *.gif*.png)\n.jpg;.gif;.png\nxxx.Document\nxxx.Document
のように変更する。
スレッド
MFCで作成できるスレッドは、
ワーカースレッド
ユーザーインタフェーススレッド
の2つ。
CWinThread* AfxBeginThread(
AFX_THREADPROC ワーカースレッドの制御関数,
LPVOID ワーカースレッドの制御関数に渡す引数,
int 優先度 = THREAD_PRIORITY_NORMAL,
UINT スタックサイズ = 0,
DWORD スレッドの制御追加フラグ = 0,
LPSECURITY_ATTRIBUTES セキュリティ属性 = NULL);
ワーカースレッドの制御関数は、
UINT 関数名(LPVOID 引数);
とする。
スレッドの終了を知るには、
スレッドの処理終了を待つ → WaitForSingleObjectを使う
スレッドの処理終了を定期的に調べる → タイマーを使う。
の2つがある。
DWORD 待ち状態から戻った原因 = WaitForSingleObject(HANDLE オブジェクトのハンドル, DWORDタイムアウト時間);
タイムアウト時間にINFINITEを指定すると無限に待つ。0を指定すると、ポーリングしてすぐに戻る。それ以外は指定時間待つ。
オブジェクトの処理が終了すると待機関数にシグナル状態が設定される。
ただし、WaitForSingleObjectを使った状態でUIの更新をかけるとデッドロックが発生する。
スレッドのハンドルは、
HANDLE スレッドのハンドル = インスタンス名.m_hThread;
で取得できる。
AfxBeginThread()で作成したワーカースレッドは、処理終了時にスレッドのハンドルを自動的に削除してしまう。
スレッドの状態を定期的に調べているときに、自動的に削除されないようにするには、
インスタンス名.m_bAutoDelete = TRUE: 削除する / FALSE: 削除しない;
で制御する。
ただし、FALSEにしたときは、処理終了後にCWinThreadのインスタンスを破棄するコードを記述する必要がある。
スレッドを停止状態で生成した後にスレッドを再開させるには、
DWORD サスペンドカウント = インスタンス名.ResumeThread();
を使う。
AfxBeginThread関数でCREATE_SUSPENDEDを指定すると、サスペンドカウントは1に設定され停止状態になる。
同期処理
スレッドの同期処理。
複数スレッドの場合はリソースの競合に気を付ける。
MFCの同期クラス
クラス名 | 内容 | 同期方法 |
CSemaphore | 同時にリソースにアクセスできるスレッドの数を限定する機能を提供する同期クラス | 間接 |
CMutex | 複数のプロセスまたはスレッドのうちの1つだけがリソースに履いたアクセスできる機能を提供する同期クラス | 間接 |
CCriticalSection | 複数のスレッドの1つだけがリソースに排他アクセスできる機能を提供する同期クラス | 間接 |
CEvent | イベントが発生したことを、あるスレッドが別のスレッドに通知できる機能を提供する同期クラス | 直接 |
同期クラスはアクセス制御クラスのCSingleLock、CMultiLockを経由して使用する。
アクセス制御クラスを経由して同期クラスを使用すると、同期クラスの違いをほとんど意識することなく使用できる。
汎用的に利用されるプログラムやライブラリには、複数のプロセスやスレッドから同時に呼び出された場合に
問題が発生しないことを考慮して作られているものがあり、このことをスレッドセーフという。
ソケット
同期型(CSocket)、非同期型(CAsyncSocket)の2つがある。
ソケットを使うにはウィザードで「Windows Socket」を有効にする。
非同期型はMFCクラス追加ウィザード画面で基底クラスをCAsyncSocket型に変える。
このクラスをDlgクラスから参照できるように、メンバ変数にポインタを追加する。
逆にSocketクラスにもDlgクラスへ参照できるように、メンバ変数にポインタを追加する。
Visual Studioのデバッガで複数のプログラムを起動するには、
ソリューションエクスプローラでプロジェクトを選択して右クリックし、
「デバッグ」→「新しいインスタンスを開始」を選択する。
同期型ソケットだと接続待ちの間、UIの更新ができない。
非同期型ソケットだとこの問題が解決できる。
[iniファイル]
iniファイルのフォーマット:
[VisualStudio]
Language=VisualC++
MFCのWinAppクラスにより扱うこともできるが、
Windows APIを使用してアクセスできる。
/* iniファイルのキー値を文字列として読み込む */
DWORD ::GetPrivateProfileString(
LPCTSTR セクション名,
LPCTSTR キー名,
LPCTSTR 既定の文字列, /* iniファイルが無い、セクションやキーが見つからない場合に返す文字列 */
LPTSTR 情報が格納されるバッファ,
DWORD 情報バッファのサイズ,
LPCTSTR iniファイル名);
/* iniファイルからキー値を数値として読み込む */
DWORD ::GetPrivateProfileInt(
LPCTSTR セクション名,
LPCTSTR キー名,
INT 既定の数値, /* iniファイルが無い、セクションやキーが見つからない場合に返す数値 */
LPCTSTR iniファイル名);
/* iniファイルに書き込む */
BOOL ::WritePrivateProfileString(
LPCTSTR lpAppName, //セクション名
LPCTSTR lpKeyName, //キー名
LPCTSTR lpString, // 書き込む文字列
LPCTSTR lpFileName); // iniファイル名
[直前のエラーの取得]
Windows OSの場合、GetLastErrorで直近のエラー理由を取得できる。
DWORD型の戻り値。
戻り値の意味は、Visual Studioの「ツール」→「エラー検索」ツールにより確認できる。
[名前付きパイプ]
Windows OSでプロセス間通信の仕組みの一つ(ソケットの他)。
パイプには、
名前なしパイプ
名前付きパイプ
の2つがある。
名前なしパイプ | 名前付きパイプ | |
通信相手 | 同一PC上のプロセスに限定 | 他のPC上のプロセスとも行える |
データの通信方向 | 片方向 | 双方向 |
プロセス間通信は、大量にデータを送る場合は、ソケットよりもパイプの方が高速。
アンマネージコードでパイプを使用する場合、MFCではクラスが提供されていないので、
Windows APIを使用する必要がある。
サーバーパイプ:
①名前つきパイプを作成し、パイプハンドルを取得する
②クライアントの接続を待つ
③クライアントが接続されたら、パイプハンドルを利用してデータの送受信を行う
④送受信が終了したら、パイプを切断する
クライアントパイプ:
①サーバーパイプに接続する
②サーバーパイプに接続したら、パイプハンドルを取得してデータの送受信を行う
③送受信が終了したら、パイプを切断する
HANDLE CreateNamedPipe(
LPCTSTR パイプ名,
DWORD パイプを開くモード,
DWORD パイプ固有のモード,
DWORD インスタンスの最大数,
DWORD 出力バッファのサイズ,
DWORD 入力バッファのサイズ,
DWORD タイムアウトの間隔,
LPSECURITY_ATTRIBUTES セキュリティ記述子);
パイプ名のフォーマットは「\\.\pipe\パイプ名」の形式とする。
パイプを開くモード
モード | 内容 |
PIPE_ACCESS_DUPLEX | サーバーとクライアントの両方がパイプの読み書きを行う |
PIPE_ACCESS_INBOUND | パイプ内のデータの流れを、クライアントからサーバー方向に限定する |
PIPE_ACCESS_OUTBOUND | パイプ内のデータの流れを、サーバーからクライアント方向に限定する |
パイプ固有のモード
モード | 内容 |
PIPE_TYPE_BYTE | バイトストリーム(バイナリ)のデータをパイプに書き込む |
PIPE_TYPE_MESSAGE | メッセージストリーム(テキスト)のデータをパイプに書き込む |
PIPE_READMODE_BYTE | パイプのデータを、バイトストリーム(バイナリ)のデータとして読み取る |
PIPE_READMODE_MESSAGE | パイプのデータを、メッセージストリーム(テキスト)のデータとして読み取る |
CreateFile関数はファイルを作成するためのWindows APIだが、パイプ通信でも使用する。
CloseHandle関数は、取得したハンドルを閉じるための関数だが、この関数を利用することで、パイプを切断できる。
[共有メモリ]
プロセス間でデータを交換する手段の1つ。
MFCには共有メモリを扱うCSharedFileクラスが用意されているが、
Windows APIで実現することもできる。
①ファイルマッピングオブジェクトとを作成する
②ファイルマッピングオブジェクトをメモリに割り当てる(マップする)
③作成された共有メモリのハンドルを取得する
④必要であれば、共有メモリのハンドルから共有メモリのポインタを取得する
⑤共有メモリを読み書きする
⑥共有メモリを使い終わったら、共有メモリを解放する
共有メモリはファイルの一種として扱う。メモリマップドファイルと呼ぶ。
HANDLE CreateFileMapping(
HANDLE ファイルのハンドル, /* 共有メモリを作成する場合は0xFFFFFFFFを設定する */
LPSECURITY_ATTRIBUTES セキュリティ記述子,
DWORD 保護属性,
DWORD 共有メモリサイズの上位DWORD, /* 2048バイトなら0 */
DWORD 共有メモリサイズの下位DWORD,/* 2048バイトなら2048 */
LPCTSTR 共有メモリ名);
LPVOID MapViewOfFile(
HANDLE ファイルマッピングオブジェクトのハンドル,
DWORD アクセスモード,
DWORD オフセットの上位DWORD,
DWORD オフセットの下位DWORD,
SIZE_T マップ対象のバイト数);
アプリケーション終了時(WM_CLOSE)には、共有メモリを解放すること。
ダイナミックリンクライブラリ(DLL)
新しいプロジェクトでMFC DLLを選択する。
DLLの場合は「ソリューションのディレクトリを作成」にチェックすると良い。
DLLを作成するときには目的の関数がわかるように、エクスポート関数という関数の入り口(エントリポイント)がわかるようにする。
以下の2つの方法がある:
__declspec(dllexport)キーワードを使用する
モジュール定義ファイル(*.def)を使用する
アプリケーションとDLLをリンクするには
暗黙的なリンク
(インポートライブラリ*.libを使って静的にリンクする。.libの他、エクスポート関数が定義されているヘッダファイルと.dllが必要)
明示的なリンク(LoadLibrary関数を使う。.dllが必要)
の2つがある。
DLLのデバッグには、スタートアッププログラムにDLLの呼び出し側を設定して、デバッグ実行する。
DLLの開発手順:
①MFC DLLプロジェクトの作成(ソリューションのフォルダも作る)
②MFC DLLプロジェクト上でDLLを作成する。
実体は、ヘッダファイル(xxx.h)とソースファイル(xxx.cpp)を追加してそこに作成する。
③MFC DLLプロジェクトで*.defを編集する
LIBRARY xxx /* DLL名 */
EXPORTS
yyy /* 関数名 */
zzz /* 関数名 */
④DLLを使用するアプリケーションのプロジェクトを追加する。
<暗黙的なリンクの場合>
⑤追加の依存ファイルに.libを追加する。
パスの指定には、マクロを使う。
$(OutDir)\xxx.libとする。
⑥追加のインクルードディレクトリを指定する。
$(SolutionDir)\xxxのようにする。
⑦アプリケーション側でDLLのヘッダをインクルードする。
⑧スタートアッププロジェクトにアプリケーションのプロジェクトを設定する。
⑨アプリケーション側のプロジェクトの依存関係にDLLのプロジェクトを指定する
<明示的なリンクを使用する場合>
⑤エクスポート関数の定義を追加する
typedef xxx xxxxFunc(xxx xxx);
⑥DLLをロードする
HMODULE hDll = LoadLibrary(_T("xxx.dll"));
⑦DLLからエクスポート関数のポインタを取得する
yyy* pYyy = (yyy*)GetProcAddress(hDll, "yyy"); /* yyyは関数名*/
⑧ポインタ経由で関数を呼び出す。
⑨DLLハンドルを解放する
FreeLibrary(hDll);
C++/CLI
.NET Frameworkは提供されているクラスの数が多く、クラス名の衝突が起きやすいので、名前空間(namespace)を使う。
[マネージコードのデータ型]
マネージコードのデータ型 | アンマネージコードのデータ型 |
System::Int32 | int, INT, long, LONG |
System::Char | char |
System::Int16 | short |
System::UInt32 | unsigned int, UINT, unsigned long, DWORD |
System::Byte | unsigned char, BYTE |
System::UInt16 | unsigned short, WORD |
System::Single | float |
System::Double | double |
System::Boolean | bool, BOOL |
System::IntPtr | HANDLE |
System::String | TCHAR, LPCTSTR, char* |
System::Void | void |
マネージコードのデータ型には
値型:アドレスの内容が代入されている
参照型:参照するアドレスが代入されている
の2つがある。
値型の変数は、スタックに生成される。
参照型の変数は、マネージヒープに生成される。
マネージヒープは、.NET Frameworkのガベージコレクタにより管理され、不要になった変数は自動的に破棄される。
マネージコードで参照型のクラスを定義するには、refキーワードをつける。
ref class クラス名
値型のクラスを定義するには、
value class クラス名
とする。
参照型の変数を生成するには、gcnew演算子を使う。
参照型^ 変数名 = gcnew 参照型();
ポインタと区別するため、マネージコードではハンドルと呼ぶ。
Stringクラスでインスタンスを生成する場合、
gcnewは省略可能だが、
String^ test;
のように^は必要。
マネージコードのプロジェクトを作成するには、
[Visual C++][CLR][空のプロジェクト]のプロジェクトテンプレートを使う。
Visual Studio 2013以降から、Visual C++でのWindowsフォームアプリケーションの作成は非推奨となった。
"空のプロジェクト"作成後に、[プロジェクト]メニューから[新しい項目の追加]から
[Visual C++][UI][Windowsフォーム]を選択すると作ることができる。
作成されたSampleCLRForm.cppに以下を追加する:
using namespace System;
using namespace System::Windows::Forms;
[STAThread]
int main(array<String^>^args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew SampleCLRForm::SampleCLRForm());
}
次にコンパイラがmain関数がアプリケーションの開始位置であることを教えてあげる。
[構成プロパティ][リンカ][詳細設定]の[エントリポイント]にmainを入力する。
次にexeファイルの実行方法を設定する。[構成プロパティ][リンカ][システム][サブシステム]に[Windows(/SUBSYSTEM:WINDOWS)]を選択する。
これらの手順によりエラーが解消される。
ヘッダファイルをダブルクリックするとForm Designerが起動する。
(表示されない場合は、xxx.hを閉じたうえで、一度ソリューションを開きなおす)
[マネージコードからアンマネージコードを使用する]
マネージコードでアプリケーションを作成するとき、
既存のアンマネージコードを流用して作成した場合。
.NET Frameworkには、既存のアンマネージ(ネイティブ)DLLを呼び出せる仕組みが提供されている。
プラットフォーム呼び出し(P/Invoke)とマーシャリング。
アンマネージDLL <-------------プラットフォーム呼び出し------------- マネージコード
DLL関数 ------------------> マーシャリング ----------------->
<----------------- <----------------
アンマネージデータ型 マネージデータ型
プラットフォーム呼び出しを使用するには?
①使用したいアンマネージ関数が、どのDLLに定義されているのかを調べる
②プラットフォーム呼び出しをラップするクラスを作成する
③作成したクラスに、アンマネージDLL関数を呼び出すマネージ関数の定義を行う
④定義したマネージ関数を呼び出す。
手順②において、アンマネージ関数を使用する側にアンマネージのDLL関数であることを意識させないように、
マネージクラスを作成して包み隠す(ラップする)。
手順③では、System::Runtime::InteropServices名前空間のDllImport属性(DllImportAttributeクラス)を使用して、以下のようにマネージ関数の定義を行う:
[DllImport(DLLファイル名)]
関数の戻り値 関数名(関数の引数);
[マーシャリング]
.NET Frameworkでは、マネージコードとアンマネージコードのデータ型を相互に変換するマーシャリングは自動的に行われる。
しかし、既定のマーシャリングだけで期待した値に変換されるのかは、実行してみないとわからない場合もある。
マーシャリングの実行コストは小さくないので、実行してみて問題が見つかれば、手動(コーディング)によるマーシャリングを行う。
[文字列のマーシャリング]
1つのアンマネージ関数において、扱うことのできる文字コードはANSIまたはUnicodeのどちらか。
使用したいアンマネージ関数が扱う文字コードを把握して、既定のマーシャリングで実行した結果に問題があれば、
手動マーシャリングにより文字コードを変換する。
DllImport属性を使用した方法もある。
[DllImport(DLLファイル名,
EntryPoint=エントリポイント,
CharSet=文字セット)]
関数の戻り値 関数名(関数の引数);
エントリポイントはDLL関数のエントリポイントを設定する。EntryPointを省略すると、「関数名」がエントリポイントになる。
文字セットの設定 | 内容 |
CharSel::Ansi | ANSIとして文字列をマーシャリング |
CharSet::Auto | Windows NT系OSの場合はUnicode、Windows 9x系OSの場合はAnsiとして文字列をマーシャリング |
CharSet::Unicode | Unicodeとして文字列をマーシャリング |
Windows APIのGetPrivateProfileInt関数を例にすると、
GetPrivateProfileIntA(); // ANSI
GetPrivateProfileIntW(); // Unicode
Visual Studioのプロジェクトプロパティに設定されている「文字セット」によって使用する関数が決定される仕組み。
ANSIとして使う場合は以下のように記載する:
[DllImport("kernel32.dll",
EntryPoint = "GetPrivateProfileIntA",
CharSet = CharSet::Ansi)]
static int GetPrivateProfileInt(String^ lpAppName, String^ lpKeyName,. int nDefault, String^ lpFileName);
これにより、マネージコードからGetProfileInt()を呼び出すと、
Windows APIのGetPrivateProfileIntA()が、引数に渡された文字列をANSIにマーシャリングして実行される。
プラットフォーム呼び出しの関数定義:
[DllImport("IsbnDll.dll",
CharSet = CharSet::Unicode,
CallingConvention = CallingConvention::Cdecl)]
static bool CheckIsbn(String^ lpIsbn);
関数の引数の渡し方の規約:
可変長の引数を渡すことができる:Cdecl (MFCで作成したDLLのデフォルト※の呼び出し規約)
固定長の引数を渡すことができる:StdCall (Windows APIの呼び出し規約、DllImportの規定の動作)
※MFCのDLLのプロジェクトプロパティの「呼び出し規約」を参照。
[アンマネージコードとマネージコードの混在]
プラットフォーム呼び出しはVBやVC#など.NET Frameworkアプリのプログラミング言語で扱うことができるが、
マネージコードしか扱えないプログラミング言語にとっては、
アンマネージな部分が完全にラップされてマネージコードだけが見えるようになると、
マーシャリングを考慮する必要がなくなるため開発しやすくなる。
VC++はアンマネージコードの中にマネージコードを記述したり、
その逆もできる。
Visual Studioのプロジェクトプロパティの[構成プロパティ][全般][共通言語ランタイムサポート]を「共通言語ランタイムサポート(/clr)」にする。
設定値 | 内容 |
共通言語ランタイムサポートを使用しない | アンマネージコードのみ許可 |
共通言語ランタイムサポート(/clr) | マネージコードとアンマネージコードの混在を許可 |
純粋MSIL共通言語ランタイムサポート(/clr:pure) | マネージコードのみ許可 |
安全なMSIL共通言語ランタイムサポート(/clr:safe) | マネージコードのみ許可し、検証可能なタイプセーフ(データ型に対して安全な)コードを出力する |
<親またはプロジェクトの規定値から継承> | 親またはプロジェクトの規定値から継承された設定が有効 |
C++ InteropとはVC++だけが記述できるプラットフォーム呼び出し機能のこと。
マーシャリングライブラリは名前空間msclr::interopに定義されている。
使用時は
using namespace msclr::interop;
#include <msclr\marshal.h>
とする。
アンマネージコードの文字列をマネージコードの文字列に変換する場合、
マーシャリングライブラリのmarshal_as関数を使用する。
アンマネージコードのLPCTSTR型(const wchar_t*型)の文字列をマネージコードのString型の文字列に変換するには以下のようにする:
String^ 変換後の文字列 = marshal_as<String^>(LPCTSTR 変換前の文字列);
マネージコードの文字列をアンマネージコードの文字列に変換する場合、marshal_contextクラスを使用する。
LPCTSTR 変換後の文字列 = marshal_context クラスのインスタンス名->marshal_as<LPCTSTR>(String^ 変換前の文字列);
①[Visual C++][CLR][クラスライブラリ]で新しくプロジェクトを追加する。
②[共通言語ランタイムサポート]を[共通言語ランタイムサポート(/clr)]にする。
③MFCのコードをコンパイラが解釈できるようにする。
xxxCLRプロジェクトのプロパティページを開き、[構成プロパティ][全般][MFCの使用]を[共有DLLでMFCを使う]にする
④xxxCLRプロジェクトで生成されたプリコンパイル済みヘッダを使わないように設定する。
これによりアンマネージプロジェクトに定義されているプリコンパイル済みヘッダが有効になる。
⑤xxxDll(MFC)プロジェクトのヘッダ(*.h)とソース(*.cpp)ファイルへの参照を、xxxCLRプロジェクトに追加する。
既存の項目の追加でファイルを追加する。
⑥追加したヘッダをインクルードする。
stdafx.hに#include "..\xxxDll\xxx.h"のようにインクルードする。
#include <Windows.h>も追加することで、VC++独特のデータ型も認識できる。
⑦アンマネージコードのラッパークラスを作成する。
⑧アプリケーションプロジェクトにxxxDllCLRの参照を追加する。
[追加][参照]で[xxxDllCLR]を選択する。
⑨アプリケーションプロジェクトのコードを修正する。
参考資料
- かんたんVisual C++ 改訂2版(技術評論社) サンプルコード