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()
ありがとう!