0

TCPを介して組み込みデバイスを制御するためのwpfアプリケーションを作成しています。デバイスへの書き込みは簡単ですが、パケットの受信に問題があります。

tcp NetworkStreamを作成し、NetworkStream.BeginRead()を使用してTCPデータをリッスンしています。ただし、完全なパケットを作成したら、新しい情報を反映するようにGUIを更新したいと思います。非同期コールバックスレッドからこれを行うことは許可されていないようです。

ディスパッチャを介して一度に1つのリクエストでこれを行う方法があるようですが、GUIの実質的にすべてのコントロールを更新する必要があります。すべてのパケットケースとすべてのWPFコントロールに対してディスパッチャー関数を作成しているわけではありません。非同期スレッドからWPFコントロールに完全にアクセスするにはどうすればよいですか?

編集: ここにコードサンプルがあります:

NetworkUnion union = (NetworkUnion)ar.AsyncState;
                union.BytesRead += tcpStream.EndRead(ar);

                if (union.BytesRead < union.TargetSize)
                    tcpStream.BeginRead(union.ByteArray, union.BytesRead, union.TargetSize - union.BytesRead, new AsyncCallback(ReadCommandCallback), union);
                else
                {
                    NetworkUnion payload = new NetworkUnion();
                    NetworkPacket pkt = (NetworkPacket)union.getArrayAsStruct();

                    // Respond to the packet
                    // Read the payload
                    payload.ByteArray = new byte[pkt.payloadSize];
                    tcpStream.Read(payload.ByteArray, 0, pkt.payloadSize);

                    // Determine what the payload is!
                    switch (pkt.code)
                    {
                        case (int)PacketCode.STATE:
                            payload.ObjectType = typeof(NetworkState);
                            NetworkState state = (NetworkState)payload.getArrayAsStruct();
                            Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
                            break;

残量ゲージを更新しようとすると、InvalidOperationExceptionが発生します。別のスレッドがオブジェクトを所有しているため、呼び出し元のスレッドはアクセスできません。

'Handle'変数が使用されるのは、これが静的ユーティリティクラス内からのものであるためです。

主な質問は、私は本当にすべての行を置き換える必要がありますか?

Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);

Handle.fuelGauge.Dispatcher.Invoke(
                                System.Windows.Threading.DispatcherPriority.Normal,
                                new Action(
                                    delegate()
                                    {
                                        Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
                                    }
                            ));

?繰り返しが多すぎるようです。次のようなことを行う方が簡単です。

Handle.mainWindow.Dispather.Lock();
Handle.fuelGauge.Value = Convert.ToDouble(state.mainFuel);
change everything else...
Handle.mainWindow.Dispatcher.Unlock();
4

2 に答える 2

1

通常、.NETでは、UIリクエストをメインUIスレッドにマーシャリングする必要があります。これは、ネットワークの読み取り後にUIを更新しようとする場合の一般的なシナリオです。これは、ソケットを使用する場合、通常、他の(スレッドプール)スレッドで常にデータを受信するためです。

したがって、データを受信するコードでは、バイトをいくつかの有用なオブジェクトにラップし、メインUIスレッドのエントリポイントメソッドにマーシャリングして、UIの更新をファームアウトする責任を負う必要があります。スレッド。

これを行うには多くの方法がありますが、おそらくいくつかよりも柔軟なアプローチは、ソケットコードの呼び出し元がSynchronizationContextオブジェクトを渡し、それを使用して受信データをマーシャリングすることです。

彼らのSynchronizationContextの詳細についてはこちらをご覧ください

すべてのソケット呼び出しをポストバックする必要はありません。パケットの処理方法を決定するswithcステートメントをUIスレッドのクラスに移動し、そのメソッドを呼び出すだけです。次に、そのメソッドからのすべての呼び出しがすでに正しくマーシャリングされているため、そのメソッドは通常どおりUI更新を実行できます。

于 2012-06-01T21:15:55.997 に答える
0

いいえ、あなたが言ったようにすべての行を置き換える必要はありません。このようなものはどうですか:

NetworkUnion union = (NetworkUnion)ar.AsyncState;
            union.BytesRead += tcpStream.EndRead(ar);

            if (union.BytesRead < union.TargetSize)
                tcpStream.BeginRead(union.ByteArray, union.BytesRead, union.TargetSize - union.BytesRead, new AsyncCallback(ReadCommandCallback), union);
            else
            {
                NetworkUnion payload = new NetworkUnion();
                NetworkPacket pkt = (NetworkPacket)union.getArrayAsStruct();

                // Respond to the packet
                // Read the payload
                payload.ByteArray = new byte[pkt.payloadSize];
                tcpStream.Read(payload.ByteArray, 0, pkt.payloadSize);

                // Determine what the payload is!
                double yourvalue = new double(); 
                switch (pkt.code)
                {
                    case (int)PacketCode.STATE:
                        payload.ObjectType = typeof(NetworkState);
                        NetworkState state = (NetworkState)payload.getArrayAsStruct();
                        yourvalue = Convert.ToDouble(state.mainFuel);
                        break;
            /**
                Other Cases....

            */
         // After the switch 

                    Handle.fuelGauge.Dispatcher.Invoke(
                            System.Windows.Threading.DispatcherPriority.Normal,
                            new Action(
                                delegate()
                                {
                                    Handle.fuelGauge.Value = yourvalue;
                                }
                        )); 
于 2012-06-01T21:27:01.730 に答える