Здравствуйте, OldDino, Вы писали:
OD>Здравствуйте, Pablo Cabaneiro, Вы писали:
PC>>А если бы Вы разъяснили, что есть EventTypes, было бы вообще супер =)
Если его запускать в отдельном потоке, то ничего работать не будет (видимо, потому, что это не GUI-поток).
Это кто такое сказал? Вы проверяли?
Если у вас при этом летит исключение(забыл уже какое), то возможно, поможет вызов SetApartmentState(ApartmentState.STA); у треда, в котором вызываете Application.Run();
Хочется сделать класс UsbInformer с двумя событиями — UsbFlashDriveInserted и UsbFlashDriveRemoved. Как отлавливать события, связанные с usb, я здесь уже нашел — это переопределение WndProc в форме и отсев нужных сообщений (можно еще через WMI, но но работает как-то криво, через раз). Фишка в том, что хотелось бы использовать этот класс в любых приложениях, в т.ч. службах и консоли. Для этого можно создать невидимую форму, в которой и переопределяется WndProc. Проблема в том, куда девать Application.Run(), т.е. как организовать цикл обработки сообщений. Ведь этот метод не возвращает управления до завершения приложения. Если его запускать в отдельном потоке, то ничего работать не будет (видимо, потому, что это не GUI-поток). Как быть? Может быть, есть какой-то способ создать второй GUI-поток со своим циклом обработки сообщений? Или, возможно, существует альтернативный способ отлова сообщений? Гуру говорят, что сообщения обычно посылаются окнам... заранее благодарен.
12.10.06 19:19: Перенесено модератором из '.NET' — IT
NC>Если его запускать в отдельном потоке, то ничего работать не будет (видимо, потому, что это не GUI-поток).
NC>Это кто такое сказал? Вы проверяли? NC>Если у вас при этом летит исключение(забыл уже какое), то возможно, поможет вызов SetApartmentState(ApartmentState.STA); у треда, в котором вызываете Application.Run();
Я пробовал. Рекомендованный Вами вызов, увы, не помог. На всякий случай приведу код, вдруг я совсем ерунду написал
class UsbInformer : Form
{
const int WM_DEVICECHANGE = 0x0219;
const int DBT_DEVICEARRIVAL = 0x8000;
const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
public UsbInformer()
{
MethodInfo mi = GetType().GetMethod("CreateControl", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(bool) }, null);
mi.Invoke(this, new object[] { true });
Thread t = new Thread(new ThreadStart(Go));
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
void Go()
{
Application.Run();
}
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_DEVICECHANGE)
{
switch (m.WParam.ToInt32())
{
case DBT_DEVICEARRIVAL:
{
Console.WriteLine("ARRIVAL");
break;
}
case DBT_DEVICEREMOVECOMPLETE:
{
Console.WriteLine("REMOVE");
break;
}
}
}
base.WndProc(ref m);
}
}
class Program
{
UsbInformer ui = new UsbInformer();
Console.ReadLine();
}
Здравствуйте, Pablo Cabaneiro, Вы писали:
PC>Я пробовал. Рекомендованный Вами вызов, увы, не помог. На всякий случай приведу код, вдруг я совсем ерунду написал
Попробуй как-то так:
namespace ConsoleApplication1
{
#region Using's
using System;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
#endregion Using's
class Program
{
static void Main(string[] args) {
using(MyController controller = new MyController()) {
controller.Start();
Console.WriteLine("Controller started.");
Console.ReadLine();
Console.WriteLine("Application shutdown.");
}//using
}
}
class MyController : IDisposable
{
#region Fields
private readonly Thread worker;
#endregion Fields
#region Constructors\Destructor
public MyController() {
worker = new Thread(ThreadProc);
worker.SetApartmentState(ApartmentState.STA);
}
#endregion Constructors\Destructor
#region Properties
private Thread WorkerThread {
get { return worker; }
}
#endregion Properties
#region Methods
public void Start() {
WorkerThread.Start();
}
private class ListenerForm : Form
{
protected override void WndProc(ref Message m) {
Debug.WriteLine(m);
base.WndProc(ref m);
}
}
[STAThread]
private static void ThreadProc() {
try {
using(ListenerForm listener = new ListenerForm()) {
Application.Run(listener);
}//using
Debug.Print("Listener thread shutdowned");
} catch(ThreadAbortException) {
throw;
} catch(Exception ex) {
Debug.Print("Listener thread exception: {0}.", ex);
}//try
}
#endregion Methods
#region IDisposable Members
public void Dispose() {
if(WorkerThread.ThreadState != System.Threading.ThreadState.Suspended) {
WorkerThread.Abort();
}//if
}
#endregion IDisposable Members
}
}
Рекомендую переделать MyController.Dispose() через объект синхронизации ну и так далее…
... << RSDN@Home 1.2.0 alpha rev. 652>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Рекомендую переделать MyController.Dispose() через объект синхронизации ну и так далее…
О, работает! Огромное спасибо! Не будет ли слишком нагло с моей стороны спросить, как переделать Dispose()? Везде пишут, что использовать Thread.Abort() некошерно, к тому же ThreadAbortException перехватывается почему-то спустя весьма заметное время после вызова Thread.Abort(). Я бы как обычно использовал какой-нить EventWaitHandle, но не могу понять, где ждать сигнала, т.к. Application.Run(), повторюсь, не возвращает управление. Пока получилось сделать следующим образом — я в Вашем коде вместо перехвата ThreadAbort просто вызываю listener.Close() (правда, предварительно переключившись в GUI-поток), что приводит к прекращению цикла обработки сообщений и незамедлительному завершению потока. Вроде работает =) Хотя, возможно, это бред =)
Здравствуйте, Pablo Cabaneiro, Вы писали:
PC>О, работает! Огромное спасибо! Не будет ли слишком нагло с моей стороны спросить, как переделать Dispose()? Везде пишут, что использовать Thread.Abort() некошерно, к тому же ThreadAbortException перехватывается почему-то спустя весьма заметное время после вызова Thread.Abort().
Правильно.
PC>Я бы как обычно использовал какой-нить EventWaitHandle,
Правильно.
PC>но не могу понять, где ждать сигнала, т.к. Application.Run(), повторюсь, не возвращает управление.
Мы крутимся в WndProc и проверять событие можно в ней.
PC>Пока получилось сделать следующим образом — я в Вашем коде вместо перехвата ThreadAbort просто вызываю listener.Close() (правда, предварительно переключившись в GUI-поток), что приводит к прекращению цикла обработки сообщений и незамедлительному завершению потока. Вроде работает =) Хотя, возможно, это бред =)
Похоже на правду. ThreadProc и listener сделали члемами MyController? Переключаетесь в GUI-поток через Control.Invoke(…)? Должно работать. на самом деле вариантов реализации может быть полно и это, скорее, уже дело вкуса :о)
... << RSDN@Home 1.2.0 alpha rev. 652>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Похоже на правду. ThreadProc и listener сделали члемами MyController? Переключаетесь в GUI-поток через Control.Invoke(…)? Должно работать. на самом деле вариантов реализации может быть полно и это, скорее, уже дело вкуса :о)
Дада, именно так я и сделал. Еще раз огромное спасибо.
Здравствуйте, Pablo Cabaneiro, Вы писали:
PC>можно еще через WMI, но но работает как-то криво, через раз
Пару недель назад решал задачу с "отловом" сообщений о вставке и удалении в том числе и флэшек. А посему позвольте не согласиться с процитированным выше заявлением. У меня всё работает "прямо" и отрабатывает нормально каждый раз, а не через раз. Обрабатываются сообщения VolumeChange.
Если нужно, могу привести код.
Здравствуйте, OldDino, Вы писали:
OD>Пару недель назад решал задачу с "отловом" сообщений о вставке и удалении в том числе и флэшек. А посему позвольте не согласиться с процитированным выше заявлением. У меня всё работает "прямо" и отрабатывает нормально каждый раз, а не через раз. Обрабатываются сообщения VolumeChange. OD>Если нужно, могу привести код.
Здравствуйте, Pablo Cabaneiro, Вы писали:
PC>Если не сложно, приведите. Спасибо.
Не сложно. Привожу.
#region __Подготовка к началу обработки событий об изменении состояния устройств__
WqlEventQuery volumeChangeEventQuery = new WqlEventQuery( "SELECT * FROM Win32_VolumeChangeEvent" );
volumeChangeEventWatcher = new ManagementEventWatcher( volumeChangeEventQuery );
volumeChangeEventArrivedEventHandler = new EventArrivedEventHandler( VolumeChangeEventWatcher_EventArrived );
volumeChangeEventWatcher.EventArrived += volumeChangeEventArrivedEventHandler;
volumeChangeEventWatcher.Start();
#endregion
Вот, собственно, и всё. Работает всегла и везде. Проверялось на флэшках и картах. Единственное, что можно добавить, так это проверку на тип (REMOVABLE или FIXED) и интерфейс (USB или нет) устройства.
Здравствуйте, Vlad37, Вы писали:
V>Здравствуйте, OldDino, Вы писали:
V>
OD>> WqlEventQuery volumeChangeEventQuery = new WqlEventQuery( "SELECT * FROM Win32_VolumeChangeEvent" );
OD>> volumeChangeEventWatcher = new ManagementEventWatcher( volumeChangeEventQuery );
OD>> volumeChangeEventArrivedEventHandler = new EventArrivedEventHandler( VolumeChangeEventWatcher_EventArrived );
OD>> volumeChangeEventWatcher.EventArrived += volumeChangeEventArrivedEventHandler;
OD>> volumeChangeEventWatcher.Start();
V>
V> Мне, таким образом, получить сообщение о вставке/удалении флешки не удалось.
V> Функция volumeChangeEventQuery, почему-то, не выполнилась ни разу ни при каких условиях.
V>____________Почему!?!?!?
V> У кого-нибудь ещё была такая проблема? Буду благодарен любым рассуждениям.
Проблема решилась просто
Чтобы отследить подключение/отключение флэшки, в запросе надо указывать Win32_DeviceChangeEvent вместо
Win32_VolumeChangeEvent
PC> MethodInfo mi = GetType().GetMethod("CreateControl", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(bool) }, null);
PC> mi.Invoke(this, new object[] { true });
PC>
Про визибильность формы.
У меня на висте не заработало почему-то. Да и не слишком ли это хитро, а то смахивает на хакерство. Может лучше что-нибудь навроде SetVisibilityCore?
Re[3]: Как организовать цикл обработки сообщений?
От:
Аноним
Дата:
04.03.08 09:56
Оценка:
OD>>Пару недель назад решал задачу с "отловом" сообщений о вставке и удалении в том числе и флэшек. А посему позвольте не согласиться с процитированным выше заявлением. У меня всё работает "прямо" и отрабатывает нормально каждый раз, а не через раз. Обрабатываются сообщения VolumeChange.
А как насчет того, что если взять флешку и быстро переткнуть ее из одного порта USB в другой, то на медленных машинах событие отключения диска от первого порта приходит позже, чем событие подключения в другой порт? В результате, в какой-то момент имеем два логических диска с одинаковыми буквами тома!!!