operazione non valida C # SharpPcap cross-thread
Domanda
Ciao Sto cercando di convertire la cattura dei pacchetti in Example3 in SharpPcap nuova versione SharpPcap-2.2.0rc1.src da applicazione console di Windows Form.
Sono di fronte a un problema quando ho cercato di aggiungere i pacchetti è stato catturato al controllo ListView mi metterò un errore che è:
(operazione cross-thread non valida:. Controllo 'listViewPackets' si accede da un thread diverso dal thread è stato creato su)
in questa linea:
listViewPackets.Items.Add(e.Packet.ToString());
tutte le consulenze per risolvere questo problema ???
Ecco il mio codice:
using SharpPcap;
namespace Packets
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// Retrieve the device list
private void btnLiDevicest_Click(object sender, EventArgs e)
{
var devices = LivePcapDeviceList.Instance;
// If no devices were found print an error
if (devices.Count < 1)
{
MessageBox.Show("No devices were found on this machine");
return;
}
int i = 0;
// Print out the devices
foreach (LivePcapDevice dev in devices)
{
///* Description */
//Console.WriteLine("{0}) {1} {2}", i, dev.Name, dev.Description);
cmbListDevice.Items.Add(dev.Name + " " + dev.Description);
i++;
}
LivePcapDevice device = devices[1];
// Register our handler function to the 'packet arrival' event
device.OnPacketArrival += new PacketArrivalEventHandler(device_OnPacketArrival);
// Open the device for capturing
int readTimeoutMilliseconds = 1000;
device.Open(DeviceMode.Promiscuous, readTimeoutMilliseconds);
device.StartCapture();
}
//Console.WriteLine();
//Console.WriteLine("-- Listening on {0}, hit 'Enter' to stop...",
// device.Description);
/// <summary>
/// Prints the time and length of each received packet
/// </summary>
///
protected void device_OnPacketArrival(object sender, CaptureEventArgs e)
{
DateTime time = e.Packet.PcapHeader.Date;
uint len = e.Packet.PcapHeader.PacketLength;
//Console.WriteLine("{0}:{1}:{2},{3} Len={4}",
// time.Hour, time.Minute, time.Second, time.Millisecond, len);
// Console.WriteLine(e.Packet.ToString());
listViewPackets.Items.Add(e.Packet.ToString());
}
}
}
................................. .................................... ecco il codice originale:
using System;
using System.Collections.Generic;
using SharpPcap;
namespace SharpPcap.Test.Example3
{
/// <summary>
/// Basic capture example
/// </summary>
public class BasicCap
{
public static void Main(string[] args)
{
// Print SharpPcap version
string ver = SharpPcap.Version.VersionString;
Console.WriteLine("SharpPcap {0}, Example3.BasicCap.cs", ver);
// Retrieve the device list
var devices = LivePcapDeviceList.Instance;
// If no devices were found print an error
if(devices.Count < 1)
{
Console.WriteLine("No devices were found on this machine");
return;
}
Console.WriteLine();
Console.WriteLine("The following devices are available on this machine:");
Console.WriteLine("----------------------------------------------------");
Console.WriteLine();
int i = 0;
// Print out the devices
foreach(LivePcapDevice dev in devices)
{
/* Description */
Console.WriteLine("{0}) {1} {2}", i, dev.Name, dev.Description);
i++;
}
Console.WriteLine();
Console.Write("-- Please choose a device to capture: ");
i = int.Parse( Console.ReadLine() );
LivePcapDevice device = devices[i];
// Register our handler function to the 'packet arrival' event
device.OnPacketArrival +=
new PacketArrivalEventHandler( device_OnPacketArrival );
// Open the device for capturing
int readTimeoutMilliseconds = 1000;
device.Open(DeviceMode.Promiscuous, readTimeoutMilliseconds);
Console.WriteLine();
Console.WriteLine("-- Listening on {0}, hit 'Enter' to stop...",
device.Description);
// Start the capturing process
device.StartCapture();
// Wait for 'Enter' from the user.
Console.ReadLine();
// Stop the capturing process
device.StopCapture();
Console.WriteLine("-- Capture stopped.");
// Print out the device statistics
Console.WriteLine(device.Statistics().ToString());
// Close the pcap device
device.Close();
}
/// <summary>
/// Prints the time and length of each received packet
/// </summary>
private static void device_OnPacketArrival(object sender, CaptureEventArgs e)
{
DateTime time = e.Packet.PcapHeader.Date;
uint len = e.Packet.PcapHeader.PacketLength;
Console.WriteLine("{0}:{1}:{2},{3} Len={4}",
time.Hour, time.Minute, time.Second, time.Millisecond, len);
Console.WriteLine(e.Packet.ToString());
}
}
}
Soluzione
Quando si effettua una chiamata a un controllo da un altro thread:
if (listView1.InvokeRequired)
{
listView1.BeginInvoke(new MethodInvoker(
() => /*whatever you want with listview */));
}
else
{
/* whatever you want with listview */
}
Se si sa per certo che sarà sempre su un altro thread, poi basta dimenticare il if / else e utilizzare l'Invoke.
EDIT:
Quindi nel tuo caso, sarebbe assomigliare:
if(listView1.InvokeRequired)
{
listView1.BeginInvoke(new MethodInvoker(
() => listViewPackets.Items.Add(e.Packet.ToString()) ));
}
else
{
listViewPackets.Items.Add(e.Packet.ToString());
}
(di nuovo, o semplicemente la chiamata BeginInvoke, se sarà sempre eseguito su un thread diverso)
EDIT 2 Si noterà che Shane utilizza Invoke e io uso BeginInvoke. Io lo uso come forza dell'abitudine. Utilizzando Invoke bloccherà sul thread dell'interfaccia utente, e se si sta eseguendo un'operazione che richiede più tempo, utilizzando BeginInvoke esegue l'aggiornamento per l'interfaccia utente in modo asincrono.
Altri suggerimenti
È necessario utilizzare Invoke perché il pacchetto è in arrivo su un thread diverso. controlli dell'interfaccia utente non possono essere modificati su un thread diverso da quello che stanno creati. Invocare eseguirà la data delegato nel thread UI. Ad esempio, si potrebbe fare questo:
this.Invoke(new MethodInvoker(() => listViewPackets.Items.Add(e.Packet.ToString())), null);