C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[16]: vb.netからC++の文字列配列引数付DLLの呼び出し


(過去ログ 117 を表示中)

[トピック内 19 記事 (1 - 19 表示)]  << 0 >>

■68914 / inTopicNo.1)  vb.netからC++の文字列配列引数付DLLの呼び出し
  
□投稿者/ unagi (1回)-(2013/11/25(Mon) 09:51:01)

分類:[C++/CLI] 

C++の具体的な処理方法がわからず困っています。

vb.netからvs.2008のC++で作成された以下のようなDLLを呼び出処理を行いたいのですが、DLL関数の文字列配列
の受け渡しができなくて困っています。

 int dllfunc(int intDataNum, char * strInputDts[], char * OutputDts[])

上記で 「strInputDts[]」はvb.net側からDLL関数に渡す入力引数、「OutputDts[]」はDLL関数からの出力引数です。
これらの文字列配列を受け渡しができません。
やり方は色々あると思うのですが、C++のマネージ拡張機能を使用すればできるというのを聞いたので、C++側で
関数のインターフェースを変換する被せた関数を作ることを考えました。


int dllfunc_new(int intDataNum, array<string^>^ strNewInputDts, array<string^>^ OutputDts)
{

 // DLL関数入力文字列配列 --> DLL関数引数


 // 旧DLL関数の呼び出し
dllfunc(intDataNum, strInputDts, OutputDts)

 // DLL関数出力文字列配列 --> 呼び出し関数引数


}

イメージとしては、上記のようになると思うのですが、C++を全くやったことがなくnet上のサンプル等
をみてもほとんど理解できません。
このような丸投げに近い質問で申し訳ないのですが、何卒よろしくお願いいたします。
ちなみに、もっとスマートな方法があれば、上記の方法にはこだわりません。

なお、文字列1個の最大文字数は256文字です。

引用返信 編集キー/
■68915 / inTopicNo.2)  Re[1]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ 魔界の仮面弁士 (422回)-(2013/11/25(Mon) 10:02:08)
2013/11/25(Mon) 10:02:22 編集(投稿者)

No68914 (unagi さん) に返信
> C++の具体的な処理方法がわからず困っています。
外部公開された関数を作りたいという話かと思いますが、
従来からのアンマネージな C++ であっても、
.NET 対応の C++/CLI でも良いのでしょうか?

前者は、VB からは Declare ステートメント(あるいは DllImport 属性)経由での利用、
後者は、VB からは参照設定にて利用することになります。
引用返信 編集キー/
■68916 / inTopicNo.3)  Re[2]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (3回)-(2013/11/25(Mon) 10:27:25)
No68915 (魔界の仮面弁士 さん) に返信
> 2013/11/25(Mon) 10:02:22 編集(投稿者)
>
> ■No68914 (unagi さん) に返信
>>C++の具体的な処理方法がわからず困っています。
> 外部公開された関数を作りたいという話かと思いますが、
> 従来からのアンマネージな C++ であっても、
> .NET 対応の C++/CLI でも良いのでしょうか?
>
> 前者は、VB からは Declare ステートメント(あるいは DllImport 属性)経由での利用、
> 後者は、VB からは参照設定にて利用することになります。

ご対応ありがとうございます。
今回の主たる目的は、「 .NET 対応の C++/CLI」での具体的なやり方が判らないと云うことです。



引用返信 編集キー/
■68937 / inTopicNo.4)  Re[3]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ Jitta (110回)-(2013/11/25(Mon) 22:41:14)
Jitta さんの Web サイト
No68916 (unagi さん) に返信

 質問内容を整理してください。

VB から、VC++2008 で作成された DLL を呼び出す。
VC++2008 での宣言は、次の通り。
  int dllfunc(int intDataNum, char * strInputDts[], char * OutputDts[])

…ここまでは分かりました。次の部分は、なんでしょう?

> int dllfunc_new(int intDataNum, array<string^>^ strNewInputDts, array<string^>^ OutputDts)
> {
>  // DLL関数入力文字列配列 --> DLL関数引数
>
>  // 旧DLL関数の呼び出し
> dllfunc(intDataNum, strInputDts, OutputDts)
>
>  // DLL関数出力文字列配列 --> 呼び出し関数引数
> }

これは、「やってみようとしていること」でしょうか。それとも、「やってみたこと」でしょうか。
そのあたりが分かりません。なぜ「.NET 対応の C++/CLI」が出てくるのでしょう?
色々調べたのはわかります。それを、

> やり方は色々あると思うのですが、C++のマネージ拡張機能を使用すればできるというのを聞いた

で、済ませないでください。
これでは、どんな情報を得て、それをどのように考えて、
「C++ マネージ拡張機能を使おう」という結論にいたったのかわかりません。
もちろん、「私が聞いていることにだけ答えてくれれば良いのだ」とおっしゃる方は、大勢います。
ただ、余計な苦労をしているようです。
苦労したい、というのを止めはしませんが、それで良いですか?

なお、「string」は文字列です。
array<string> とすると、「文字列配列」になります。
そして、.NET Framework では、UNICODE です。
「char*」は、文字へのポインターです。
そして、ANSI コードです。
変換できません。


VB は分からないけど、C# なら、こうかな。

[DllImport("dll の名前", CharSet = CharSet.Ansi, SetLastError = true)]
static extern int dllfunc(int intDataNum, string strInputDts, StringBuilder OutputDts);

で、VC++2008 のプロジェクトが、C++/CLI で作られているなら、
なんの苦労もなく、DLL を参照するだけでいいはず。

引用返信 編集キー/
■68938 / inTopicNo.5)  Re[3]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ Azulean (243回)-(2013/11/25(Mon) 22:45:29)
No68916 (unagi さん) に返信
> 今回の主たる目的は、「 .NET 対応の C++/CLI」での具体的なやり方が判らないと云うことです。

C++ 同士であればこのように使うというサンプルが見えないと、具体的なコードの提示は難しいと思います。

文字列型の変換であれば、こちらの記事も参考にならないでしょうか。
http://codezine.jp/article/detail/4774
引用返信 編集キー/
■68939 / inTopicNo.6)  Re[4]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ Azulean (244回)-(2013/11/25(Mon) 22:47:06)
No68937 (Jitta さん) に返信
> なお、「string」は文字列です。
> array<string> とすると、「文字列配列」になります。

野暮ですが、char * [] なので文字列型配列で間違っていないかと。
引用返信 編集キー/
■68940 / inTopicNo.7)  Re[4]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ Jitta (111回)-(2013/11/25(Mon) 22:51:54)
Jitta さんの Web サイト
編集キーを変更してしまったようだ。編集できないので、追加。

No68937 (Jitta) に返信

> で、VC++2008 のプロジェクトが、C++/CLI で作られているなら、
> なんの苦労もなく、DLL を参照するだけでいいはず。

宣言に char* が使ってあるのだから、C++/CLI というのは無いですね。
これは、削除。


> [DllImport("dll の名前", CharSet = CharSet.Ansi, SetLastError = true)]
> static extern int dllfunc(int intDataNum, string strInputDts, StringBuilder OutputDts);

よく見ると、文字へのポインターの配列だった。
→ static extern int dllfunc(int intDataNum, string[] strInputDts, StringBuilder[] OutputDts);

で、配列の数を指定してやらねばならなかったはず。
このへんかな。
http://msdn.microsoft.com/ja-jp/library/z6cfh6e6.aspx

あるいは、IntPtr で受けて、受けてからごそごそ変換するか。

引用返信 編集キー/
■68944 / inTopicNo.8)  Re[5]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (4回)-(2013/11/26(Tue) 00:22:59)
Jitta さん、Azulean さんありがとうございます。
また、こちらが勉強不足で申し訳ありません。

No68937 (Jitta さん) に返信


> int dllfunc_new(int intDataNum, array<string^>^ strNewInputDts, array<string^>^ OutputDts)
> {
>  // DLL関数入力文字列配列 --> DLL関数引数
>
>  // 旧DLL関数の呼び出し
> dllfunc(intDataNum, strInputDts, OutputDts)
>
>  // DLL関数出力文字列配列 --> 呼び出し関数引数
> }

 これは、これから「やってみようとしていること」です。
 この旧DLL関数dllfuncに被せたdllfunc_new関数(これをvb.netから呼ぶことを考えている)を作成
 しようとしています。
 ここで、dllfunc_new関数の入力引数「array<string^>^ strNewInputDts」を旧DLL関数dllfuncの
 入力引数「char * strInputDts[]」に渡してやる方法
 および
 旧DLL関数dllfuncの出力引数「char * strOutputDts[]」からdllfunc_new関数の出力引数
「array<string^>^ strNewOutputDts」に渡してやる方法
が判らないという状況です。
==================================================================
int dllfunc_new(int intDataNum, array<string^>^ strNewInputDts, array<string^>^ OutputDts)
{

// dllfunc_new関数の入力引数「array<string^>^ strNewInputDts」
// を旧DLL関数dllfuncの入力引数「char * strInputDts[]」に渡してやる方法
--> この処理部分がよくわからない

 // 旧DLL関数の呼び出し
dllfunc(intDataNum, strInputDts, OutputDts)

 // 旧DLL関数dllfuncの出力引数「char * strOutputDts[]」からdllfunc_new関数の出力引数
//「array<string^>^ strNewOutputDts」に渡してやる方法
--> この処理部分がよくわからない

}

 、
「C++ マネージ拡張機能を使おう」というのはインターネットで調べているうちに、マネージ拡張機能
 があるのを知ったので、これを使用すればできるのではないかと思ったからです。
 (これでは答えになっていなかもしれませんが。。。) 


No68939 (Azulean さん) に返信

 こちらがやりたいことを上記にもう少し具体的に書いてみました。
 やりたいことは、文字列変換ですので、教えて頂いたページも勉強させてもらいます。



引用返信 編集キー/
■68946 / inTopicNo.9)  Re[6]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ 774RR (121回)-(2013/11/26(Tue) 06:47:46)
わざわざ C++/CLI 関数を被せなくても
VB.NET/C# から C++ native 関数を呼ぶ方法はあるわけだが。

wrapper 関数を作ることが目的 であれば止めないけど
単に native 関数呼び出しが目的 ならば wrapper は要らない。

前者は見聞を広めるにはいいかもしれないけど時間がかかりそう。
後者のほうが目的を達するに要する時間は短いと思う。
引用返信 編集キー/
■68947 / inTopicNo.10)  Re[7]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (5回)-(2013/11/26(Tue) 08:06:16)
No68946 (774RR さん) に返信
> わざわざ C++/CLI 関数を被せなくても
> VB.NET/C# から C++ native 関数を呼ぶ方法はあるわけだが。
>
> wrapper 関数を作ることが目的 であれば止めないけど
> 単に native 関数呼び出しが目的 ならば wrapper は要らない。
>
> 前者は見聞を広めるにはいいかもしれないけど時間がかかりそう。
> 後者のほうが目的を達するに要する時間は短いと思う。

774RR さんありがとうございます。
最初、native 関数呼び出しを目指して、試行錯誤をやっていたのですが、
うまくゆかないので、方向転換してC++/CLI 関数を被せようとしてる状況です。
べつに、方法にはこだわりませんので、native 関数呼び出しに関してアドバス
頂けると助かります。

引用返信 編集キー/
■68950 / inTopicNo.11)  Re[8]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ 774RR (122回)-(2013/11/26(Tue) 09:31:42)
細かいことに入る前に確認すべき事があるわけだが

C# の char は Visual C では wchar_t 相当であるから
C# の文字列変換目的で C や C++ の char な関数を使うと問題あり、というのは認識済み?

wchar_t -> char -> wchar_t なる無駄な変換が入って、なおかつ文字化けの可能性ありって事で


引用返信 編集キー/
■68974 / inTopicNo.12)  Re[9]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (6回)-(2013/11/26(Tue) 22:59:53)
No68950 (774RR さん) に返信
> 細かいことに入る前に確認すべき事があるわけだが
>
> C# の char は Visual C では wchar_t 相当であるから
> C# の文字列変換目的で C や C++ の char な関数を使うと問題あり、というのは認識済み?
>
> wchar_t -> char -> wchar_t なる無駄な変換が入って、なおかつ文字化けの可能性ありって事で
>
>

実はC++の文字列変換の資料とかを見てる内に不安になってきたところですが、受け渡しする文字列は
Tab区切りの半角英数字のはずですので問題はないのではないかと思ってます。

引用返信 編集キー/
■68978 / inTopicNo.13)  Re[10]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ 774RR (124回)-(2013/11/27(Wed) 07:28:24)
No68937 で既に指摘済みの話なわけでいまさら感がするわけだが
> そして、.NET Framework では、UNICODE です。
> 「char*」は、文字へのポインターです。
> そして、ANSI コードです。
> 変換できません。

C/C++ 側で char を使ってしまうと
System.Char (UTF-16) -> char (MBCS/CP932) -> System.Char (UTF-16) の変換が入るので

・ UTF-16 で表記できるが CP932 にない文字は真に文字化けする
・不必要な変換が入るので遅い
ということになるわけで、お勧めぢゃない。

No68914
> int dllfunc(int intDataNum, char * strInputDts[], char * OutputDts[])
この関数原型宣言を見た時点で俺や Azulean 氏は以下のような感想をいだくわけだ。

・文字列バッファ長を指定する何かがないのでバッファオーバーフロー脆弱性を含んでいる。
No68938
> C++ 同士であればこのように使うというサンプルが見えないと、具体的なコードの提示は難しいと思います。
というのは OutputDts の使い方がわからない、って意味。

実装例1:
for (i=0; i<intDataNum; ++i) {
strcpy(OutputDts[i], strInputDts[i]);
}

実装例2:
for (i=0; i<intDataNum; ++i) {
OutputDts[i]=strdup(strInputDts[i]));
}
どっちかで .NET 側実装はまったく違う。

引用返信 編集キー/
■69032 / inTopicNo.14)  Re[11]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (7回)-(2013/11/28(Thu) 22:46:19)
No68978 (774RR さん) に返信
> No68937 で既に指摘済みの話なわけでいまさら感がするわけだが
>>そして、.NET Framework では、UNICODE です。
>>「char*」は、文字へのポインターです。
>>そして、ANSI コードです。
>>変換できません。
>
> C/C++ 側で char を使ってしまうと
> System.Char (UTF-16) -> char (MBCS/CP932) -> System.Char (UTF-16) の変換が入るので
>
> ・ UTF-16 で表記できるが CP932 にない文字は真に文字化けする
> ・不必要な変換が入るので遅い
> ということになるわけで、お勧めぢゃない。
>
> No68914
>>int dllfunc(int intDataNum, char * strInputDts[], char * OutputDts[])
> この関数原型宣言を見た時点で俺や Azulean 氏は以下のような感想をいだくわけだ。
>
> ・文字列バッファ長を指定する何かがないのでバッファオーバーフロー脆弱性を含んでいる。
> No68938
>>C++ 同士であればこのように使うというサンプルが見えないと、具体的なコードの提示は難しいと思います。
> というのは OutputDts の使い方がわからない、って意味。
>
> 実装例1:
> for (i=0; i<intDataNum; ++i) {
> strcpy(OutputDts[i], strInputDts[i]);
> }
>
> 実装例2:
> for (i=0; i<intDataNum; ++i) {
> OutputDts[i]=strdup(strInputDts[i]));
> }
> どっちかで .NET 側実装はまったく違う。
>

返信が遅くなり申し訳ありません。

実装例1:
for (i=0; i<intDataNum; ++i) {
strcpy(OutputDts[i], strInputDts[i]);
}

に該当します。

引用返信 編集キー/
■69033 / inTopicNo.15)  Re[12]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (8回)-(2013/11/29(Fri) 01:10:40)
No69032 (unagi さん) に返信
> ■No68978 (774RR さん) に返信
>>No68937 で既に指摘済みの話なわけでいまさら感がするわけだが
> >>そして、.NET Framework では、UNICODE です。
> >>「char*」は、文字へのポインターです。
> >>そして、ANSI コードです。
> >>変換できません。
>>
>>C/C++ 側で char を使ってしまうと
>>System.Char (UTF-16) -> char (MBCS/CP932) -> System.Char (UTF-16) の変換が入るので
>>
>>・ UTF-16 で表記できるが CP932 にない文字は真に文字化けする
>>・不必要な変換が入るので遅い
>>ということになるわけで、お勧めぢゃない。
>>
>>No68914
> >>int dllfunc(int intDataNum, char * strInputDts[], char * OutputDts[])
>>この関数原型宣言を見た時点で俺や Azulean 氏は以下のような感想をいだくわけだ。
>>
>>・文字列バッファ長を指定する何かがないのでバッファオーバーフロー脆弱性を含んでいる。
>>No68938
> >>C++ 同士であればこのように使うというサンプルが見えないと、具体的なコードの提示は難しいと思います。
>>というのは OutputDts の使い方がわからない、って意味。
>>
>>実装例1:
>>for (i=0; i<intDataNum; ++i) {
>> strcpy(OutputDts[i], strInputDts[i]);
>>}
>>
>>実装例2:
>>for (i=0; i<intDataNum; ++i) {
>> OutputDts[i]=strdup(strInputDts[i]));
>>}
>>どっちかで .NET 側実装はまったく違う。
> >
>
> 返信が遅くなり申し訳ありません。
>
> 実装例1:
> for (i=0; i<intDataNum; ++i) {
> strcpy(OutputDts[i], strInputDts[i]);
> }
>
> に該当します。
>
 補足すると、
OutputDts[i]には、vb.net側で1文字列(OutputDts[0]、OutputDts[1]等の単位)
 ごとに256バイトの空白文字列を確保してあるところに、DLL側で応答文字列を
 設定するようなことを行っています。

引用返信 編集キー/
■69035 / inTopicNo.16)  Re[13]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ 774RR (125回)-(2013/11/29(Fri) 08:57:49)
えーと・・・なんだか目的と手段を取り違えているような気がするけど・・・
目的=真にやりたいことは何?

・「文字列変換がしたい」 のであれば、その手段としては
変換関数の C または C++ ソースがあるとして
 (A) その変換関数を VB で書く( C++ ソースから移植する )
 (B) その変換関数を C# で書く (〃)
 (C) その変換関数を C/C++ で書き直す ( VB から p/invoke しやすい形に書き直す )
 (D) その変換関数自体を C++/CLI で書き直す ( manage コードで直接変換する )
変換関数のソースがなくて native DLL バイナリだけがあるとき
 (E) その変換関数に対する wrapper を C++/CLI で書く
 (F) VB/C# で直接当該 native DLL を p/invoke する

俺は当初の話は (E) だと思っていたので、それはいささか無駄っぽい
No68946 Native 関数を呼ぶこともできるよ (F) がもっと楽なのでは?としたわけだ。

ただ、提示された C 関数宣言を見るに
・その関数は C の char を使っているあたり manage コード向きでは無さそうだ
・バッファオーバーフロー脆弱性も含んでいそうだ
・そのまま使うのはあまり推奨できない
と思ったのでそう書いたまで。
ソースが無くてバイナリだけある、という状況なら「推奨できない関数でも使うしかない」よね。

変換関数のソースコードがあるなら (E) (F) など愚の骨頂で (遅いし)
(A) (B) のほうがいいに決まっている。

・「 p/invoke すること」が目的ならばまた話は別
引用返信 編集キー/
■69049 / inTopicNo.17)  Re[14]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (9回)-(2013/11/30(Sat) 10:39:14)
No69035 (774RR さん) に返信
> えーと・・・なんだか目的と手段を取り違えているような気がするけど・・・
> 目的=真にやりたいことは何?
>
> ・「文字列変換がしたい」 のであれば、その手段としては
> 変換関数の C または C++ ソースがあるとして
>  (A) その変換関数を VB で書く( C++ ソースから移植する )
>  (B) その変換関数を C# で書く (〃)
>  (C) その変換関数を C/C++ で書き直す ( VB から p/invoke しやすい形に書き直す )
>  (D) その変換関数自体を C++/CLI で書き直す ( manage コードで直接変換する )
> 変換関数のソースがなくて native DLL バイナリだけがあるとき
>  (E) その変換関数に対する wrapper を C++/CLI で書く
>  (F) VB/C# で直接当該 native DLL を p/invoke する
>
> 俺は当初の話は (E) だと思っていたので、それはいささか無駄っぽい
> No68946 Native 関数を呼ぶこともできるよ (F) がもっと楽なのでは?としたわけだ。
>
> ただ、提示された C 関数宣言を見るに
> ・その関数は C の char を使っているあたり manage コード向きでは無さそうだ
> ・バッファオーバーフロー脆弱性も含んでいそうだ
> ・そのまま使うのはあまり推奨できない
> と思ったのでそう書いたまで。
> ソースが無くてバイナリだけある、という状況なら「推奨できない関数でも使うしかない」よね。
>
> 変換関数のソースコードがあるなら (E) (F) など愚の骨頂で (遅いし)
> (A) (B) のほうがいいに決まっている。
>
> ・「 p/invoke すること」が目的ならばまた話は別


状況としては、

 変換関数「int dllfunc(int intDataNum, char * strInputDts[], char * OutputDts[])」
 の C++ ソースはあるのですが、関数の内部処理は結構あるのですぐには置き換え
(VB等で書き直す)られないので、まずは今のままの変換関数のインターフェースで
 VB.netから呼び出そうとしてうまくゆかない状態になっています
 最初、vb.net側で

  Declare Ansi Funtion dllfunc Lib "FuncDll.dll" _
(Byval intDataNum As System.Int32, _
   Byval strInputDts() As System.Text.StringBuilder, _
   Byval strOutputDts() As System.Text.StringBuilder) As System.Int32

の宣言をやって使用してみましたが、文字列配列の受け渡しができません。
 その後、文字列配列の引数の部分をクラスや構造体に変えたりしてみましたが
 うまくゆきません。
 そこで、wrapper を C++/CLIを作れば、できるのではないかと思ったのですが
 C++/CLIのところが全然わからないので、皆様のお知恵を拝借しようとした次第
 です。 

 目的は、変換関数がやっている処理をvb.netから呼び出して使用することです。
 結果的にこれができれば方法は極端に云えばなんでもかないません(できれば、
 なるべく簡単で、処理速度も極端に遅くならない方法であれば助かります)

引用返信 編集キー/
■69050 / inTopicNo.18)  Re[15]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ Azulean (246回)-(2013/11/30(Sat) 12:39:23)
C++/CLI で試そうと思ったら、元のマネージ String の長さがわからないからコピーを用意して、そのコピーのポインタ配列を用意してとややこしいことに
なってしまった。
対象の関数が '\0' までしかバッファを見ないのなら、vector を1つ減らせると思います。
(marshal_context のスコープを長くして、sourceBuffers を消して、marshal_as<const char*> の戻り値を sourceArray に入れる)

また、呼び出し時に 255 バイトを超える文字列がくる可能性や、MBCS 文字セットで表現できない文字がくる可能性を想定していませんので注意。

// 知恵の拝借じゃなくて、丸投げだと思う。
// キーワードを示して話をしたところで解決が遅く、サンプルコードを示した方が速い聞き方になっているので。

<コードを利用する方へ>
このコードは現状有姿のまま提供しているもので、不具合や脆弱性、非効率な処理が存在する可能性があります。
このコードを利用した時点で、利用者自身がすべての責任を負うこととし、投稿者は責任を一切持ちません。


#include <vector>
#include <msclr/marshal.h>
#include <cstring>

using namespace System;

namespace CppCliLib {

	public ref class CppWrapper
	{
	private:
		static const size_t BUFFER_SIZE = 256;
	public:
		static int Execute(array<String^>^ sourceTexts,
			[System::Runtime::InteropServices::OutAttribute] array<String^>^% destinationTexts)
		{
			int dataCount = sourceTexts->Length;
			// ポインタ配列
			// 元の配列の長さが可変長なのでvectorで管理
			std::vector<char*> sourceArray(dataCount);
			std::vector<char*> destinationArray(dataCount);
			// 文字列バッファ
			// 元の文字列の長さが不明であることと、DLLの関数が256バイトあることを前提に作っている
			// 可能性を加味して、必ず自前のバッファにコピーする
			std::vector<std::vector<char>> sourceBuffers(dataCount);
			std::vector<std::vector<char>> destinationBuffers(dataCount);

			for (int i = 0; i < dataCount; ++i)
			{
				// バッファの準備
				sourceBuffers[i].resize(BUFFER_SIZE);
				destinationBuffers[i].resize(BUFFER_SIZE);
				// ポインタ配列(vector)への代入
				sourceArray[i] = &sourceBuffers[i][0];
				destinationArray[i] = &destinationBuffers[i][0];

				// 元の文字列のバッファへのコピー
				// marshal_asでメモリが確保されるが、marshal_contextのスコープが切れたら解放される
				msclr::interop::marshal_context context;
				strcpy_s(sourceArray[i], BUFFER_SIZE, context.marshal_as<const char*>(sourceTexts[i]));
			}

			// DLL呼び出し
			// TODO: 戻り値によって後の処理をする・しないを分ける必要があるかどうか?
			int result = dllfunc(dataCount, &sourceArray[0], &destinationArray[0]);

			// 出力先バッファから出力先String^配列を作る
			destinationTexts = gcnew array<String^>(dataCount);
			for (int i = 0; i < dataCount; ++i)
			{
				// 出力先String^配列にバッファから文字列をコピーする
				msclr::interop::marshal_context context;
				destinationTexts[i] = context.marshal_as<String^>(destinationArray[i]);
			}

			// 残しておいた戻り値を返す
			return result;
		}
	};
}

引用返信 編集キー/
■69069 / inTopicNo.19)  Re[16]: vb.netからC++の文字列配列引数付DLLの呼び出し
□投稿者/ unagi (10回)-(2013/12/01(Sun) 18:48:38)
No69050 (Azulean さん) に返信
> C++/CLI で試そうと思ったら、元のマネージ String の長さがわからないからコピーを用意して、そのコピーのポインタ配列を用意してとややこしいことに
> なってしまった。
> 対象の関数が '\0' までしかバッファを見ないのなら、vector を1つ減らせると思います。
> (marshal_context のスコープを長くして、sourceBuffers を消して、marshal_as<const char*> の戻り値を sourceArray に入れる)
>
> また、呼び出し時に 255 バイトを超える文字列がくる可能性や、MBCS 文字セットで表現できない文字がくる可能性を想定していませんので注意。
>
> // 知恵の拝借じゃなくて、丸投げだと思う。
> // キーワードを示して話をしたところで解決が遅く、サンプルコードを示した方が速い聞き方になっているので。
>
> <コードを利用する方へ>
> このコードは現状有姿のまま提供しているもので、不具合や脆弱性、非効率な処理が存在する可能性があります。
> このコードを利用した時点で、利用者自身がすべての責任を負うこととし、投稿者は責任を一切持ちません。
>
>
> #include <vector>
> #include <msclr/marshal.h>
> #include <cstring>
>
> using namespace System;
>
> namespace CppCliLib {
>
> public ref class CppWrapper
> {
> private:
> static const size_t BUFFER_SIZE = 256;
> public:
> static int Execute(array<String^>^ sourceTexts,
> [System::Runtime::InteropServices::OutAttribute] array<String^>^% destinationTexts)
> {
> int dataCount = sourceTexts->Length;
> // ポインタ配列
> // 元の配列の長さが可変長なのでvectorで管理
> std::vector<char*> sourceArray(dataCount);
> std::vector<char*> destinationArray(dataCount);
> // 文字列バッファ
> // 元の文字列の長さが不明であることと、DLLの関数が256バイトあることを前提に作っている
> // 可能性を加味して、必ず自前のバッファにコピーする
> std::vector<std::vector<char>> sourceBuffers(dataCount);
> std::vector<std::vector<char>> destinationBuffers(dataCount);
>
> for (int i = 0; i < dataCount; ++i)
> {
> // バッファの準備
> sourceBuffers[i].resize(BUFFER_SIZE);
> destinationBuffers[i].resize(BUFFER_SIZE);
> // ポインタ配列(vector)への代入
> sourceArray[i] = &sourceBuffers[i][0];
> destinationArray[i] = &destinationBuffers[i][0];
>
> // 元の文字列のバッファへのコピー
> // marshal_asでメモリが確保されるが、marshal_contextのスコープが切れたら解放される
> msclr::interop::marshal_context context;
> strcpy_s(sourceArray[i], BUFFER_SIZE, context.marshal_as<const char*>(sourceTexts[i]));
> }
>
> // DLL呼び出し
> // TODO: 戻り値によって後の処理をする・しないを分ける必要があるかどうか?
> int result = dllfunc(dataCount, &sourceArray[0], &destinationArray[0]);
>
> // 出力先バッファから出力先String^配列を作る
> destinationTexts = gcnew array<String^>(dataCount);
> for (int i = 0; i < dataCount; ++i)
> {
> // 出力先String^配列にバッファから文字列をコピーする
> msclr::interop::marshal_context context;
> destinationTexts[i] = context.marshal_as<String^>(destinationArray[i]);
> }
>
> // 残しておいた戻り値を返す
> return result;
> }
> };
> }
>

Azulean さんへ
 
 返信が遅くなりましたが、丸投げ質問に対応頂き誠にありがとう御座います。
 ようやくC++の言語も判りかけてきたように思いますので、取り組んでみます。
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -