2

SerialPort からデータを取得する C# アプリを開発しています。次に、C++ プロジェクト (変更できない) を使用して読み取りデータを計算します。

C++ プロジェクトでは、データの計算時に C# 関数を呼び出すネイティブ C コードを使用しています。

これは、PInvoke を使用して C++ 関数を呼び出す呼び出された C# コードの例です。

    [DllImport("MyLib.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    public static extern void PacketHandler_HandlePacket(IntPtr packetHandler, IntPtr packet, int packetType);

    [SuppressUnmanagedCodeSecurity]
    [DllImport("MyLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    private static extern float DeviceManager_AddSampleToBattery(IntPtr self, float sample, double sampleRate);

    [SuppressUnmanagedCodeSecurity]
    [DllImport("MyLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    private static extern double DeviceManager_GetTimestamp(IntPtr self, int packetType);

    [DllImport("MyLib.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    private static extern void PacketHandler_AddCallbackBattery(IntPtr self, Delegate @delegate);

次に、C++ コード:

extern "C"
{
    __declspec(dllexport) void _cdecl PacketHandler_HandlePacket(int * packetHandler, char * packet, int packetType){
        PacketHandler_handlePacket((PacketHandlerStruct *)packetHandler, packet, (PacketHandlerPacketType)packetType);}
}

この C 関数は、次のように設定した C# 関数を呼び出しています。

__declspec(dllexport) void _cdecl  PacketHandler_AddCallbackBattery(int * packetHandler, void(*f)(void * obj, unsigned short int sample)){
    PacketHandlerStruct * myPointer = ((PacketHandlerStruct *)packetHandler);
    myPointer->delegate.addSampleToBattery = f;
}

最後に、C コードは、この C# コールバックである「addSampleToBattery」関数を呼び出します (最初の 2 つは、私が投稿した最初のもののような PInvoke 呼び出しです)。

private static float CallbackBattery(IntPtr self, ushort sample)
    {
        var value = DeviceManager_AddSampleToBattery(DeviceManager, sample, Const.BaseBvpSampleRate);
        var timestamp = DeviceManager_GetTimestamp(DeviceManager, (int)PacketHandlerPacketType.Battery);
        SocketManager.OnNewBatteryArrived(value, timestamp);
        return value;
    }

その他の詳細:

C# デリゲートは次のように宣言されます。

 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
 private delegate float DelegateBattery(IntPtr self, short sample);

 private static readonly DelegateBattery DelegateCallbackBattery = CallbackBattery;

そして、次のように設定します:

var intptrDelegate = Marshal.GetFunctionPointerForDelegate(DelegateCallbackBattery);
var a = Marshal.GetDelegateForFunctionPointer(intptrDelegate, typeof(DelegateBattery));
PacketHandler_AddCallbackBattery(packetHandler, a);

したがって、すべてが機能しているように見えますが、多くの場合、IndexOutOfRangeException が発生します。主な問題は、すべてのシンボルが読み込まれたデバッグ モードでも、逆アセンブリ ビューしか利用できないため、例外をスローしている行が見えず、もちろんそこから意味のある情報を取得できないことです。

Unhandled Exception: 'MyProgram.exe' (Win32): 
The program '[3000] MyProgram.exe' has exited with code 0 (0x0).
 System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at System.Threading.ThreadPoolWorkQueue.Dequeue(ThreadPoolWorkQueueThreadLocals tl, IThreadPoolWorkItem& callback, Boolean& missedSteal)
   at System.Threading.ThreadPoolWorkQueue.Dispatch()
   at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

ありがとう!

4

1 に答える 1

0

IndexOutOfRangeExceptionに対応するセマンティクスは、ベース メモリ アドレスから離れすぎたメモリにアクセスしようとしたことを示しているため、メモリ内のそのポイントを参照しようとするべきではありません。

3 要素の int 配列を考えてみましょう。[3] のインデックスを作成しようとすると、*(base_address + (sizeof(int) * 3))* が逆参照されるため、メモリ内のその場所にある次の 4 バイトが値として int として提供されます。有効なインデックスは 0、1、および 2 のみです。これらのオフセットは、スペースを割り当てた 3 つの要素の範囲内にあるためです。

これが例外メッセージに関連する場所がわからないため、さらに多くのコードが必要です。

于 2015-10-18T03:33:33.010 に答える