これはARM上で特に発生し、x86またはx64では発生しない問題です。私はこの問題をユーザーが報告し、Windows IoT経由でRaspberry Pi 2のUWPを使用して再現することができました。以前はこのような問題を抱えていましたが、P/Invoke宣言でCdeclを指定しています。ネイティブ側に__cdeclを明示的に追加してみました。P/Invoke引数が渡されたときに何が異常になる可能性がありますか?
P /呼び出し宣言(reference):
[DllImport(Constants.DllName, CallingConvention = CallingConvention.Cdecl)]
public static extern FLSliceResult FLEncoder_Finish(FLEncoder* encoder, FLError* outError);
C#の構造体(reference):
internal unsafe partial struct FLSliceResult
{
public void* buf;
private UIntPtr _size;
public ulong size
{
get {
return _size.ToUInt64();
}
set {
_size = (UIntPtr)value;
}
}
}
internal enum FLError
{
NoError = 0,
MemoryError,
OutOfRange,
InvalidData,
EncodeError,
JSONError,
UnknownValue,
InternalError,
NotFound,
SharedKeysStateError,
}
internal unsafe struct FLEncoder
{
}
Cヘッダーの機能(reference)
ここでいくつかの情報でありますFLSliceResult FLEncoder_Finish(FLEncoder, FLError*);
FLSliceResultは、i tは値によって返され、ネイティブ側にはC++のものがいくつかありますか?
ネイティブ側の構造体には実際の情報がありますが、C APIではFLEncoderはas an opaque pointerと定義されています。上記の方法をx86とx64で呼び出すときはスムーズに動作しますが、ARMでは次のことがわかります。最初の引数のアドレスはSECOND引数のアドレスであり、2番目の引数はnullです(たとえば、C#側のアドレスを記録すると、たとえば0x054f59b8と0x0583f3bcが得られますが、ネイティブ側の引数0x0583f3bcと0x00000000です)。この種のアウトオブオーダーの問題を引き起こす原因は何ですか?私は困惑していますので、誰もがここで
は私が再現するために実行コードです...、任意のアイデアを持っています:unsafe {
var enc = Native.FLEncoder_New();
Native.FLEncoder_BeginDict(enc, 1);
Native.FLEncoder_WriteKey(enc, "answer");
Native.FLEncoder_WriteInt(enc, 42);
Native.FLEncoder_EndDict(enc);
FLError err;
NativeRaw.FLEncoder_Finish(enc, &err);
Native.FLEncoder_Free(enc);
}
には、次のとC++アプリケーションを実行すると、正常に動作します:
auto enc = FLEncoder_New();
FLEncoder_BeginDict(enc, 1);
FLEncoder_WriteKey(enc, FLSTR("answer"));
FLEncoder_WriteInt(enc, 42);
FLEncoder_EndDict(enc);
FLError err;
auto result = FLEncoder_Finish(enc, &err);
FLEncoder_Free(enc);
このロジックは、最新のdeveloper buildでクラッシュを引き起こす可能性がありますが、残念ながら、Nugetを介してネイティブのデバッグシンボルを確実に提供できるようになったため、ソースからすべてのものをビルドするだけです。 )デバッグはちょっとawkwarですなぜなら、ネイティブコンポーネントと管理コンポーネントの両方をビルドする必要があるからです。私は、誰かが試してみたいと思えば、これをもっと簡単にする方法についての提案をしています。しかし、これを経験したことがある人や、なぜこれが起こるのかについてのアイデアがある場合は、回答を追加してください。ありがとう!もちろん、誰かが再現を望むなら(ソースステッピングを提供しないものを作るのは簡単か、それを構築するのは難しい)、コメントを残しておきたいが、私はそれを作るプロセスを踏みたいとは思わない私はC#での「偽物」の署名とは、その後、第二のパラメータを削除した場合:誰もそれを使用しようとされていない場合
EDIT興味深いアップデート(私はどのように人気のある実際のARM上のWindowsのものであるを実行しているかわかりません)最初のものはOKを通過します。
EDIT 2第二に興味深いアップデート:私はUIntPtr
からulong
にサイズのC#FLSliceResult定義を変更する場合は、引数が正しく入って来... ARMのsize_t
がunsigned int型でなければなりませんので、意味を成しません。
EDIT 3[StructLayout(LayoutKind.Sequential, Size = 12)]
をC#の定義に追加すると、この作業になりますが、なぜですか?このアーキテクチャのC/C++でのsizeof(FLSliceResult)は、必要に応じて8を返します。 C#で同じサイズを設定するとクラッシュしますが、12に設定すると動作します。
EDIT 4私はC++テストケースも作成できるようにテストケースを最小化しました。 C#UWPでは失敗しますが、C++ UWPでは成功します。
EDIT 5Here比較のためのC++とC#の両方のために逆アセンブルされた命令は、(C#ののに私が取ることがどのくらいか分からないので、私はあまり服用の側で誤りを犯した)
EDIT 6詳細な解析では、 "良い"実行中に、構造体がC#で12バイトであると言いますと、戻り値はr0、r2を介して他の2つのargsとともにレジスタr0に渡されます。しかし2つの引数がR0、R1経由で来ているように、悪い目で、これを超えるシフトされ、戻り値はどこか別の場所である(スタックポインタ?)
EDIT 7私はProcedure Call Standard for the ARM Architectureを相談しました。私はこの引用符を見つけました: "呼び出し元と calleeの両方で静的にサイズを判別できない4バイト以上の複合型は、関数が呼び出されたときに余分な引数として渡されたアドレスのメモリに格納されます(§5.5、 ルールA.4)。結果に使用されるメモリは、関数呼び出し中の任意の時点で変更することができます。これは、(呼び出し規約には引数の数を指定する方法がないため)追加の引数が最初の引数を意味するため、r0に渡すことは正しい動作であることを意味します。 CLRがこれを別の規則と混同しているのではないかと疑問に思います基本 64ビットのデータ型: "long long、double、64ビットのコンテナ化されたベクトルなどのdouble-wordサイズの基本データ型はr0で返され、r1。 "
編集8ここで間違ったことをしているCLRを指摘する多くの証拠がありますので、私はbug reportを提出しました。私は誰かがそのレポの問題を投稿したすべての自動ボットの間にそれを気付くことを願っています:-S。
コメントは議論の対象外です。この会話は[チャットに移動]されています(http://chat.stackoverflow.com/rooms/157727/discussion-on-question-by-borrrden-what-could-cause-p-invoke-arguments-to-be-でる)。 – Andy
60 upvotesと賞金が提供されていません...それは奇妙です –
@MauricioGraciaGutierrez私は "これはJITエンジンのバグです"と私はこの質問に答えることができたと思います。(私はほとんどの人がアップウォートに興味があるのでバグの解決) – borrrden