質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

2281閲覧

下記のようにC#からC++にstring(unicode)を渡した結果、c++側で受け取った文字列wstring上で文字数が半分になってしまいました。

crew8573

総合スコア33

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/12/21 11:42

下記のようにC#からC++にstring(unicode)を渡した結果、c++側で受け取った文字列wstring上で文字数が半分になってしまいました。

C#

1 2[DllImport(DllName, EntryPoint = "Test", CharSet =CharSet.Unicode)] 3internal static extern void Test(IntPtr ptr, string text_str); 4-------------- 5 6main(){ 7string text = "1234567890"; 8 9Test(handle, text); 10}

C++

1 2extern "C" viod Test( 3 TestObj * pObject, 4 const wchar_t* text_wch, 5) 6{ 7 8 std::wstring text_wstr = std::wstring(text_wch); 9 int length = text_wstr .length(); 10 11 12} 13

上記のようにC#のmain関数からc++のTestメソッドにstringの値をunicodeとしてを渡したところ、
受け取ったwchar_t型のtext_wchをwstring型に変換し、長さを確認したところ"5"という値になっていました。

元々のstringの値は"1234567890"なので、10が返ってくるべきなのですが、なぜこのような結果になっているかわからないです。

もしお分かりの方がいましたらアドバイスをお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

y_waiwai

2021/12/21 11:48

どういう方法でその長さを算出したんでしょうか
FKD

2021/12/22 01:11

呼び出し規約は合わせてあるのでしょうか?
FKD

2021/12/22 03:37

↑コンパイラオプション等で調整していないなら、 C#初期値はstdcall、C++初期値はcdeclですよ。
crew8573

2021/12/22 11:35

ありがとうございます。 >y_waiwaiさん wstringのlength()で値を取得しました。 >FKDさん C#側でcallingconventionをcdeclとstdcallにして試してみましたが、結果は変わらなかったです。 下記両方を試してみました。 [DllImport(DllName, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Test", CharSet =CharSet.Unicode)] [DllImport(DllName, CallingConvention = CallingConvention.StdCall, EntryPoint = "Test", CharSet =CharSet.Unicode)]
退会済みユーザー

退会済みユーザー

2021/12/22 13:30 編集

> extern "C" viod Test voidの間違いですよね?ちゃんと書いてください。何にしても、呼び出し規約は合わせないとスタックが壊れます。 あと、C++側で受け取ったwchar_t*の中身を出力してください。正しいデータは渡っていますか?
guest

回答2

0

ベストアンサー

私はXamarin上で開発しているのですが

Xamarin Studioって事ですか。
C#だからWindowsという先入観が・・・

だとすると

  • C#(.net)のCharSet.UnicodeはUTF-16

仕様作成元がMicrosoft(Windows仕様)なのでUnicode=UTF-16

  • C++のwchar_tはUTF-32

となり、Length=5という流れだと推測します。
対策としては、CharSet.Ansiで渡せばよいと思います。(もしくはCharSet自体削除)
※WindowsじゃなければAnsi=UTF-8

投稿2021/12/23 02:52

FKD

総合スコア268

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cx20

2021/12/23 03:12

wchar_tは環境によってサイズが異なるケースがあるということを失念していました(16ビットだと思い込んでいました)。UTF-32ならLength=5というのも腑に落ちます。 ■ wchar_tは何ビット?ワイド文字の取扱いメモ https://qiita.com/everylittle/items/25d5f407f51f5e515d29
FKD

2021/12/23 03:28

メモリ状況次第なので、こんなに都合よくLength=5になるかは自信ないんですけどね・・・ ただ、C#側のCharSet仕様を踏まえてもらえれば、質問者さんがうまい事対応できるのではないかと思っています。 #Xamarin環境よく分からないので。
crew8573

2021/12/23 12:25

皆様 ご協力ありがとうございます。 ただ、C#側をCharsSet.Ansiで設定して試したところ、 length = 3となってしまいました。。。 string は 1234567890を渡しています。 C++側の呼び出し規約も何かしら設定が必要でしょうか?現状は特に何も設定していません。
退会済みユーザー

退会済みユーザー

2021/12/24 00:55 編集

C#側のCharSet.Unicodeはそのままで、C++側をchar16_t*にすれば良さそうな。 コンパイラによって呼び出し規約の書き方違うんで、自分のコンパイラ名と合わせて調べてください。判らなければ、C++側そのままでC#側をcdecl に設定してください。
FKD

2021/12/24 01:39

> C#側をCharsSet.Ansi C#側しか修正していないなら、以下の形になっている事は理解できていますか。 ・C#側はUTF-8文字列ポインタをC++関数に渡す ・C++側はUTF-32文字列ポインタとして受け取る = Lengthおかしい! C++側をchar *とすればUTF-8なのでうまくいくはずです。 >呼び出し規約 開発環境の詳細分からないので、radianさんがが言われている通りにしてください。 他言語連携をしたいなら、呼び出し規約の意味が分からないまま使わない方がよいです。 >C++側をchar16_t* なんとなくですが、最終的にOSSのC++ライブラリを使いたいのではないかと思い、 UTF-8に統一した方が良いのかなと思った次第です。(文字列が大体charのため)
crew8573

2021/12/27 10:17

返信が遅くなり申し訳ございません。 受け渡す文字のサイズが異なっていたためズレが生じていたとのこと理解できました。 C#側を下記コードに修正し、 [DllImport(DllName, CallingConvention = CallingConvention.Cdecl,EntryPoint = "Test", CharSet = CharSet.Unicode)] C++側の受け取る型をchar16_tに変えたところ適切に文字の受け渡しができていました。 私の開発環境はXamarin(Windows Visual Studio)でios,android用のライブラリとして開発しています。 今回、C++コード側に"__stdcall "のような呼び出し規約を設定することが出来なかったです。 上記規約を関数定義に追加しようとすると”識別子__stdcallが定義されていません”というエラーがでました。 これはなぜだかおわかりでしょうか?
FKD

2021/12/28 01:13 編集

Xamarin分かりませんが、ググった感じAndroid(ARM系)では呼び出し規約意味ないそうです。 ※ARM Procedure CallStandardで統一=そもそも定義がない プログラム記述方法ではなく、そういった根本的な所を確認してみてください。 また、Microsoftのサンプルでも呼び出し規約指定がないので、iOSも同様なのかな? https://docs.microsoft.com/ja-jp/xamarin/cross-platform/cpp/
crew8573

2021/12/28 02:18

>FKDさん ありがとうございます。私は上記サンプルをベースに実装していました。 Android系は呼び出し規約が意味がないのですね。もう少し自分で調べてみたいと思います。 いったん本件は解決しましたのでクローズとさせていただきます。 ご協力ありがとうございました。
crew8573

2021/12/28 04:39

ごめんなさい、本件ですが文字を16bitのchar16_tで扱えればよいかと思っていたのですが、 関数の処理的にあらゆる文字に対して対応が必要でした。そのため、もともと予定していたwchar_tのように4バイトの文字として扱う必要がありました。 このような場合、C#の定義はどのような定義にすべきでしょうか? CharSet.ANSI、CharSet.Unicodeともに4バイトの文字としてマーシャリングされるわけでは無さそうでした。。。 https://docs.microsoft.com/ja-jp/dotnet/api/system.runtime.interopservices.charset?view=net-6.0
guest

0

試してみましたが事象再現しませんでした。下記コードだと「10」となりました。

C#

1class Program 2{ 3 [DllImport("TestDll.dll", EntryPoint = "Test", CharSet = CharSet.Unicode)] 4 internal static extern void Test(string text_str); 5 6 static void Main(string[] args) 7 { 8 string text = "1234567890"; 9 Test(text); 10 } 11}

C++

1extern "C" void Test(const wchar_t* text_wch) 2{ 3 4 std::wstring text_wstr = std::wstring(text_wch); 5 int length = text_wstr.length(); 6 wprintf(L"length = [%d]\n", length); 7};

■ 実行結果

length = [10]

■ デバッグ実行結果
イメージ説明

<参考>
■ 【C++/C#】C++で作成したDLLをC#で呼ぶ
https://qiita.com/tera1707/items/81042abaa62dc97e26ae
■ ネイティブ相互運用性のベスト プラクティス
https://docs.microsoft.com/ja-jp/dotnet/standard/native-interop/best-practices

投稿2021/12/21 15:38

編集2021/12/22 11:05
cx20

総合スコア4648

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cx20

2021/12/21 15:42

文字列として何が渡されているか、デバッグ実行して確認してみると良いと思います。 C++側の DLL のプロジェクトのプロパティでデバッグのコマンドに C# 側の EXE を指定すればデバッグ実行が行えるかと思います。
crew8573

2021/12/22 11:37 編集

ありがとうございます。 cx20さんの環境だとうまくいくのですね、、 私はXamarin上で開発しているのですが、何かしら設定が必要なのでしょうかね。 デバッグ実行しC++のコードに入ることはできたのですが、 肝心のwstring, wcharの値がなぜかローカルやウォッチから見ることができないようになっていました。
cx20

2021/12/22 11:15

試した感じだと C# 側で「CharSet = CharSet.Ansi」とした場合は長さが「5」となりました。
cx20

2021/12/22 11:26

そういえば、引数1個しか試していませんでした(汗) 引数2個以上の場合は、FKD さんの指摘にあるように呼び出し規約を揃える必要がありそうです。 試しに、引数1つにして事象が改善されるか確認して頂けますか?
cx20

2021/12/23 03:55

> 肝心のwstring, wcharの値がなぜかローカルやウォッチから見ることができないようになっていました。 ウォッチが使えないということでしたら、コンソール出力やファイル出力することで確認することも可能です。 以下は、簡易的な方法ですが printf で変数のメモリの内容をコンソール出力するコードになります。 ----------------------------------------------- wchar_t* data = L"1234567890"; int size = sizeof(wchar_t)*(wcslen(data)+1); unsigned char* buf = new unsigned char[size]; memcpy(buf, (void*)data, size); for (int i = 0; i < size; i++ ) {   printf("0x%02x\n", buf[i]); } delete[] buf; -----------------------------------------------
crew8573

2021/12/23 12:30

上記実装を試したところ、コンソールに出力はできませんでした。。 ただし、text_wch=0xc3ffb430 L"\x34333231\x38373635\x3039"という値が入っていることは出力画面で確認できました。 なぜか、途中で渡した情報が切れてしまっている?
cx20

2021/12/23 14:28 編集

> text_wch=0xc3ffb430 L"\x34333231\x38373635\x3039" メモリ上の並びとしては、以下のような感じになってそうです。 ■ Ansi→UTF-32 [0x31][0x32][0x33][0x34] … 1文字目(UTF-32) [0x35][0x36][0x37][0x38] … 2文字目(UTF-32) [0x39][0x30][0x00][0x00] … 3文字目(UTF-32) CharSet.Unicode とすると5文字になっていた理由としては、以下のような状況になっていたのではないかと思います。 ■ Unicode(UTF-16)→UTF-32 [0x31][0x00][0x32][0x00] … 1文字目(UTF-32) [0x33][0x00][0x34][0x00] … 2文字目(UTF-32) [0x35][0x00][0x36][0x00] … 3文字目(UTF-32) [0x37][0x00][0x38][0x00] … 4文字目(UTF-32) [0x39][0x00][0x30][0x00] … 5文字目(UTF-32)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問