0

StackTrace: System.Threading._IOCompletionCallback.PerformIOCompletionCallback (UInt32 errorCode、UInt32 numBytes、NativeOverlapped* pOVERLAP) ソース: mscorlib

ターゲット ソースにリフレクションに関する何かが表示されます。そこで、リフレクションを使用する非常に少量のコードを投稿します。

通常、NullReferenceExceptions は簡単に見つけることができます。これは私のコードではないようですが、命を救うためにそれを理解することはできません。私はこのコード ボットを VB.net と C#.Net に持っています。どちらも同じ問題を抱えています。私の問題はポインターの動作にあると思いますが、間違っている可能性があります。

コードは簡単です。COM モードの USB 経由で接続されたバーコード スキャナがあります。完全に機能するこの同じバーコードを使用する別のプログラムからコードの一部をコピーしました。しかし、それが何をするかを要約すると、それが開き、リフレクションを使用して、開いているCOMポートへのポインタを取得し、DataEventListenerを設定します。何かをスキャンすると、100 ミリ秒待ってから、データを文字列形式で吐き出します。可能な限りシンプル。私のコードと問題なく動作する他のコードとの違いは、ポインターを取得する必要があることです。スキャナをオンにするには、IntPtr を使用して COMPORT へのポインタが必要です。C# では IntPtr を使用し、VB では SafeFileHandle を使用します。どちらも同じエラーです。エラーが発生するのは、データ イベントが発生したときだけです。これが、ハンドルと一緒でなければならないと思う理由です。奇妙なのは、イメージャーのオンとオフを切り替えるにはハンドルが必要であるため、有効なハンドルがあることがわかっていることです。(それが保持されているかどうかはわかりません)では、なぜこのエラーが発生するのかについて、誰か助けやリソースがありますか?

最初に.. VB.NET Public Class ImagerOposDevice Inherits AbstractOPOSDevice Dim PortHandle As SafeFileHandle Dim ImagerPort As SerialPort Public exitCode = 1 Sub New(ByVal comName As String) ImagerPort = New SerialPort(comName) End Sub

Protected Overrides Function Open() As Boolean
    ImagerPort.Open()
    PortHandle = GetHandleFromSerialPort(ImagerPort)

    If Not PortHandle.IsInvalid Then
        AddHandler ImagerPort.DataReceived, AddressOf DataReceivedHandler
    End If

    Return (Not PortHandle.IsInvalid)
End Function
Protected Overrides Sub Close()
    Try
        ImagerPort.Close()
        ImagerPort.Dispose()
    Catch ex As Exception
        Console.WriteLine(ex.Message)
        Console.ReadLine()
    End Try

End Sub

Protected Overrides Function RunCommand(ByVal X As Integer) As Boolean
    If X = 0 Then
        Return TurnImagerOn()
    ElseIf X = 1 Then
        Return TurnImagerOff()
    Else
        Return False
    End If
End Function

Private Shared Function GetHandleFromSerialPort(ByVal sp As SerialPort) As SafeFileHandle
    Dim BaseStream As Object = sp.BaseStream
    Dim BaseStreamType As Type = BaseStream.GetType
    Return BaseStreamType.GetField("_handle", BindingFlags.NonPublic Or BindingFlags.Instance).GetValue(BaseStream)
End Function
Private Sub DataReceivedHandler(ByVal sender As Object, ByVal e As SerialDataReceivedEventArgs)
    Try
        'Dim sp As SerialPort = CType(sender, SerialPort)
        System.Threading.Thread.Sleep(100)
        Dim len = ImagerPort.BytesToRead
        Dim buffer(len - 1) As Byte
        ImagerPort.Read(buffer, 0, len)

        Dim indata As String = System.Text.ASCIIEncoding.ASCII.GetString(buffer)
        Console.WriteLine("Data Received:")
        Console.Write(indata)
        TurnImagerOff()
    Catch ex As Exception
        Console.WriteLine(ex.Message)
    End Try
    exitCode = 0
End Sub

私のメインフォームでは、それは簡単です

    imager = New ImagerOposDevice("COM7")
    imager.OpenDevice()
    imager.SendCommand(0)
    For index = 1 To 100 Step 1
        System.Threading.Thread.Sleep(100)
        If imager.exitCode = 0 Then
            Exit For
        End If
    Next
    Console.WriteLine("Press Enter to Close")
    Console.ReadLine()
    CloseDevices()

次に C# コードです。ウィンドウ形式ですが、コンセプトはほぼ同じです。

    private SerialPort devicePort;
    private IntPtr deviceHandle;
    public Imager(string Port) : base()
    {
        devicePort = new SerialPort(Port);
    }
    protected override bool Open()
    {
        try
        {
            devicePort.Open();
            deviceHandle = GetHandleFromSerialPort(devicePort);
            Console.WriteLine("Device Handle:{0}", deviceHandle);
            Console.WriteLine("Device Open:{0}", devicePort.IsOpen);
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        return devicePort.IsOpen;
    }
    protected override bool Close()
    {
            devicePort.Close();
            devicePort.Dispose();
        return true;
    }
    protected override CommandReturnCodes RunCommand(int command)
    {
        switch (command)
        {
            case 0:
                TurnImagerOn();
                break;
            case 1:
                TurnImagerOff();
                break;
            case 2:
                ReadLatch();
                break;
            case 3:
                GetPartNumber();
                break;
            case 4:
                GetSerialNumber();
                break;
            case 5:
                GetProductString();
                break;
            default:
                return CommandReturnCodes.FAIL;
        }
        return CommandReturnCodes.SUCCESS;
    }
    private static IntPtr GetHandleFromSerialPort(SerialPort sp)
    {
        Type t = typeof(SerialPort);
        BindingFlags bf = BindingFlags.Instance | BindingFlags.NonPublic;
        FieldInfo fi = t.GetField("internalSerialStream", bf);
        object ss = fi.GetValue(sp);
        Type t2 = fi.FieldType;
        FieldInfo fi2 = t2.GetField("_handle", bf);
        SafeFileHandle _handle = (SafeFileHandle)fi2.GetValue(ss);
        Type t3 = typeof(SafeFileHandle);
        FieldInfo fi3 = t3.GetField("handle", bf);
        IntPtr handle = (IntPtr)fi3.GetValue(_handle);
        return handle;
    }
    public void TurnOnImagerEventListener()
    {
        devicePort.DataReceived += new SerialDataReceivedEventHandler(devicePort_DataReceived);
    }

    void devicePort_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {
        System.Threading.Thread.Sleep(100);
        SerialPort imager = sender as SerialPort;
        byte[] buffer = new byte[imager.BytesToRead];
        imager.Read(buffer, 0, imager.BytesToRead);
        var scannedText = System.Text.Encoding.ASCII.GetString(buffer).Trim();
        SendBarcodeEvent(scannedText);
        TurnImagerOff();
        Console.WriteLine(scannedText);
    }

それを呼び出すことは同じことです

        im = new Imager(comportBox.Text);
        im.OpenDevice();
        im.TurnOnImagerEventListener();
        im.BarcodeScanned += new DataAvailableHandler(DeviceInformationReceived);
4

1 に答える 1

0

LightStriker のおかげで、昨日は目がくらんでいたこの霧の向こうを見ることができました。私はまだ vb.net バージョンを動作させていませんが、最終的には、C# プログラムで使用されている IntPtr が別のスレッド (つまり dataevent) が呼び出したときに保持されていませんでした。データ イベント部分をステップ実行すると、TurnImagerOff() 部分を呼び出すまで失敗しませんでした。シリアル ポートと IntPtr の両方を静的メンバーに変更するだけでよかったので、簡単に修正できました。私はVB.netコードでそれを試しました...しかし、明らかに私はそれを理解するほど賢くありません. ああ、今日は別の日で、私のコードの少なくとも 50% は機能しています。

したがって、null 参照エラーがあり、どこにも見つからない人のために。また、スタック トレースでは行番号が表示されません。ポインタであるメンバーを呼び出している別のスレッドで実行されているものを確認してください。(またはポインターとして反映されます)。データ イベント、スレッド プール、バックグラウンド ワーカー、タイマー、デリゲートなどを確認してください。

于 2012-11-08T12:21:49.043 に答える