トップ回答者
VB.Net と VC++ でのメモリ共有

質問
-
やりたいこと
VB.NetとVCとで共有メモリを使う。VB.Net側が常に立ち上がっているためCreateFileMappingはVB.Net側で行いたい。環境
VS2013 Comunity VB.Net、VC++上記環境でメモリの共有ができません。
VC側はOpenFileMappingAとOpenFileMappingWの両方試しましたがだめでした。
ネットでいろいろな情報をもとに試しているのですがよろしくお願いします。VB.Net側ソース抜粋(フォームロードにてCreateFileMappingを実行)
Public Const PAGE_READWRITE As Integer = &H4S
<DllImport("kernel32", entrypoint:="CreateFileMappingW")> _
Public Function CreateFileMapping( _
ByVal hFile As Integer, _
ByRef lpFileMappigAttributes As SECURITY_ATTRIBUTES, _
ByVal flProtect As Integer, _
ByVal dwMaximumSizeHigh As Integer, _
ByVal dwMaximumSizeLow As Integer, _
ByVal lpName As String _
) As System.IntPtr
End FunctionPrivate Sub Form1_Load
' セキュリティ構造をセット
Dim SecurityAttribute As SECURITY_ATTRIBUTES
With SecurityAttribute
.bInheritHandle = 0 '' ハンドルの継承しない。
.lpSecurityDescriptor = 0 '' セキュリティをセットしない。
.nLength = Len(SecurityAttribute) '' 構造体のサイズを設定
End With' マッピングオブジェクト作成(.Net側がサーバのためこちら側で作成)
hMap = CreateFileMapping(&HFFFFFFFF, SecurityAttribute, PAGE_READWRITE, 0, 256, "TEST")
End Sub
VC++側ソース抜粋
BOOL CMFCSampleDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
・・・中略・・・
// 共有メモリOpen
HANDLE hMap = ::OpenFileMappingA(FILE_MAP_READ, true, "TEST"); // hMapが0x000000になる
return TRUE; // フォーカスをコントロールに設定した場合を除き、TRUE を返します。
}
回答
-
また、今回のようにAとWの両方があるAPIは、今回のような「既定」の動作(マルチバイトに自動変換してAを使用)になるのでしょうか?
(それともAPI毎にことなる?)質問の趣旨からはずいぶん離れてしまいましたがついでですので答えます。またもや大きな勘違いをされています。
CreateFileMappingWであれCreateFileMappingAであれ、呼び出された先(OS内部)では更に文字コード変換しています。だからこそCreateFileA("a.txt",...)でもCreateFileW(L"a.txt",...)どちらでもファイルを開くことができるわけです。ですので、VB.NET側がCreateFileMappingA(..., "TEST")を呼び出し、VC++側がOpenFileMappingW(..., L"TEST")を呼び出したとしても成功します。
パターン1,2にとらわれず任意の組み合わせができます、が答えになります。
その上で、Windows NT系列はOS内部はUnicodeで動作しているので少なくともVB.NETではcharset:=charset.unicodeを使用して文字コード変換のかからない呼び出しをすると効率がいいです。
# ちなみにWindows 9x系列はANSIで動作していました。- 回答としてマーク Maru Masatoshi 2015年6月4日 8:06
-
Hongliangさんの指摘通りで特に.NET 4以降であればSystem.IO.MemoryMappedFiles名前空間を使うべきです。それ以外に1点、
<DllImport("kernel32", entrypoint:="CreateFileMappingW")>
と宣言されている部分が気になりました。DllImportAttribute.CharSetの既定値はCharSet.Ansiですので、UnicodeバージョンであるCreateFileMappingW()に対してANSI文字列が渡されます。entrypointを明示するよりも自動に任せて、それよりはcharsetを明示すべきです。
# CreateFileMapping()の第1引数はHANDLE型で、64bitの場合は64bit値となるのでInteger型よりはIntPtr型にすべきとかいろいろ…。- 編集済み 佐祐理 2015年6月2日 8:56
- 回答としてマーク Maru Masatoshi 2015年6月2日 15:29
-
1.CreateFileMappingの返値は問題なし(それらしい数値)でした
2.AnyCPUでしたがx86固定に変更しました
3.実機環境の制約で.Net FrameWork 3.5のため新しいSystem.IO.MemoryMappedFilesが使えませんでした
(今は4.5環境で開発してしまっていますが後々3.5にする予定....単純に変更したらエラーになったので別途確認中です)
4.VC側のOpenFileMappingAの戻り値が0になってしまっていました。
佐祐理さんのご指摘にあったCreateFileMappingの宣言を見直すことでVC側のOpenFileMappingAの戻り値が正常に取れました。
ありがとうございました。
- 回答としてマーク Maru Masatoshi 2015年6月2日 15:29
すべての返信
-
Hongliangさんの指摘通りで特に.NET 4以降であればSystem.IO.MemoryMappedFiles名前空間を使うべきです。それ以外に1点、
<DllImport("kernel32", entrypoint:="CreateFileMappingW")>
と宣言されている部分が気になりました。DllImportAttribute.CharSetの既定値はCharSet.Ansiですので、UnicodeバージョンであるCreateFileMappingW()に対してANSI文字列が渡されます。entrypointを明示するよりも自動に任せて、それよりはcharsetを明示すべきです。
# CreateFileMapping()の第1引数はHANDLE型で、64bitの場合は64bit値となるのでInteger型よりはIntPtr型にすべきとかいろいろ…。- 編集済み 佐祐理 2015年6月2日 8:56
- 回答としてマーク Maru Masatoshi 2015年6月2日 15:29
-
1.CreateFileMappingの返値は問題なし(それらしい数値)でした
2.AnyCPUでしたがx86固定に変更しました
3.実機環境の制約で.Net FrameWork 3.5のため新しいSystem.IO.MemoryMappedFilesが使えませんでした
(今は4.5環境で開発してしまっていますが後々3.5にする予定....単純に変更したらエラーになったので別途確認中です)
4.VC側のOpenFileMappingAの戻り値が0になってしまっていました。
佐祐理さんのご指摘にあったCreateFileMappingの宣言を見直すことでVC側のOpenFileMappingAの戻り値が正常に取れました。
ありがとうございました。
- 回答としてマーク Maru Masatoshi 2015年6月2日 15:29
-
ご指摘ありがとうございます。解決できました。
<DllImport("kernel32", entrypoint:="CreateFileMappingW")>
を以下に変更することでVC側でも見れるようになりました。
<DllImport("kernel32", entrypoint:="CreateFileMapping")>VB側がマルチバイト、VC側がUNICODEという意識はあったので、
ありがとうございました。
VC側はVB側に合わせてOpenFileMappingAを使っていたのですが、
なぜかVB側でCreateFileMappingWをつかってしまっていました(なんでこんなことしたのか謎ですが)。 -
解決したようですが、誤解があるため指摘します。
entrypointのデフォルト値はメソッド名ですので、メソッド名をCreateFileMappingとするのであればあえて宣言する必要はありません。
またVBはマルチバイトではありません。正確にはVB6まではマルチバイトでしたがVB.NET以降はUnicodeを使用しています。その上でDllImportした関数の呼び出しの際には(既定では)マルチバイトに変換してCreateFileMappingAを呼び出します。charset:=charset.unicodeと宣言するとUnicodeのままCreateFileMappingWを呼び出すようになります。
-
再度のご指摘ありがとうございます。
これを知らなかったです。
「(既定では)マルチバイトに変換してCreateFileMappingAを呼び出します」以下のように修正し動作は問題ありませんでした。
VC側:
OpenFileMappingを使用(内部ではOpenFileMappingWを使用)
VB.Net側:
宣言を以下に変更
<DllImport("kernel32", CharSet:=CharSet.Unicode)> _
Public Function CreateFileMapping( _
ByVal hFile As System.IntPtr, _
ByRef lpFileMappigAttributes As SECURITY_ATTRIBUTES, _
ByVal flProtect As Integer, _
ByVal dwMaximumSizeHigh As Integer, _
ByVal dwMaximumSizeLow As Integer, _
ByVal lpName As String _
) As System.IntPtr
対応として以下の2通りあるという認識でよろしいでしょうか?パターン1:
VC側:OpenFileMappingAを使用
VB.Net側:CharSetはセットしないで既定の
CreateFileMappingAが動作するようにする
パターン2:
VC側:OpenFileMappingWを使用
VB.Net側:CharSetにUnicodeをセットして、
CreateFileMappingWが動作するようにするどちらが良いということはないかと思いますが、
パターン2の方がVC側のソースがスッキリするのでこちらを使いたいと思います。
一般的にはどちらを使うのでしょうか?また、今回のようにAとWの両方があるAPIは、今回のような「既定」の動作(マルチバイトに自動変換してAを使用)になるのでしょうか?
(それともAPI毎にことなる?)お手数ですがよろしくお願いいたします。
-
また、今回のようにAとWの両方があるAPIは、今回のような「既定」の動作(マルチバイトに自動変換してAを使用)になるのでしょうか?
(それともAPI毎にことなる?)質問の趣旨からはずいぶん離れてしまいましたがついでですので答えます。またもや大きな勘違いをされています。
CreateFileMappingWであれCreateFileMappingAであれ、呼び出された先(OS内部)では更に文字コード変換しています。だからこそCreateFileA("a.txt",...)でもCreateFileW(L"a.txt",...)どちらでもファイルを開くことができるわけです。ですので、VB.NET側がCreateFileMappingA(..., "TEST")を呼び出し、VC++側がOpenFileMappingW(..., L"TEST")を呼び出したとしても成功します。
パターン1,2にとらわれず任意の組み合わせができます、が答えになります。
その上で、Windows NT系列はOS内部はUnicodeで動作しているので少なくともVB.NETではcharset:=charset.unicodeを使用して文字コード変換のかからない呼び出しをすると効率がいいです。
# ちなみにWindows 9x系列はANSIで動作していました。- 回答としてマーク Maru Masatoshi 2015年6月4日 8:06
-