C# SharpPcap Недопустимая операция перекрестного потока
Вопрос
Привет, я пытаюсь преобразовать захват пакетов в примере 3 в новой версии SharpPcap SharpPcap-2.2.0rc1.src из консольного приложения в приложение Windows Forms.
Я столкнулся с проблемой: когда я попытался добавить пакеты, которые были захвачены, в элемент управления ListView, я получил ошибку:
(Операция с поперечной резьбой недействительна:Элемент управления listViewPackets, доступ к которому осуществляется из потока, отличного от потока, в котором он был создан.)
в этой строке:
listViewPackets.Items.Add(e.Packet.ToString());
какой-нибудь совет по решению этой проблемы???
вот мой код:
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());
}
}
}
.....................................................................................вот оригинальный код:
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());
}
}
}
Решение
При вызове элемента управления из другого потока:
if (listView1.InvokeRequired)
{
listView1.BeginInvoke(new MethodInvoker(
() => /*whatever you want with listview */));
}
else
{
/* whatever you want with listview */
}
Если вы точно знаете, что он всегда будет в другом потоке, просто забудьте об if/else и используйте вызов.
РЕДАКТИРОВАТЬ:
поэтому в вашем случае это будет выглядеть так:
if(listView1.InvokeRequired)
{
listView1.BeginInvoke(new MethodInvoker(
() => listViewPackets.Items.Add(e.Packet.ToString()) ));
}
else
{
listViewPackets.Items.Add(e.Packet.ToString());
}
(опять же, или просто вызов BeginInvoke, если он всегда будет выполняться в другом потоке)
РЕДАКТИРОВАТЬ 2 Вы заметите, что Shane использует Invoke, и я использую BeginInVoke.Я использую это как силу привычки.Использование Invoke блокирует поток пользовательского интерфейса, а если вы выполняете операцию, которая занимает больше времени, использование BeginInvoke выполняет обновление пользовательского интерфейса асинхронно.
Другие советы
Вам нужно использовать Вызов потому что пакет поступает в другой поток.Элементы управления пользовательского интерфейса не могут быть изменены в потоке, отличном от того, в котором они созданы.Invoke выполнит данный делегат в потоке пользовательского интерфейса.Например, вы можете сделать это:
this.Invoke(new MethodInvoker(() => listViewPackets.Items.Add(e.Packet.ToString())), null);