Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > .NET Frameworks (точка нет фреймворки) > Windows Forms
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 06.04.2016, 16:45   #1
Cha1000000
Пользователь
 
Регистрация: 08.05.2014
Сообщений: 17
По умолчанию C++/CLI Windows Forms Обработка сообщения WM_DEVICECHANGE

Доброго всем времени суток, коллеги!
Пишу проект на C++/CLI Windows Forms в Visual Studio 2010(2013).
В программе мне нужно отлавливать сообщение WM_DEVICECHANGE (всякий раз, когда во время работы программы происходит извлечение, или вставка какого-либо USB устройства) и обрабатывать его (обновлять дерево устройств, ну как в Диспетчере устройств в общем).
Для этого написал вот такой класс:

Код:
// Класс обработки системных сообщений.
	public ref class MessageFilter : public System::Windows::Forms::IMessageFilter
	{
	private: 
			 HDEVNOTIFY NotificationHandle;
			 HDEVNOTIFY hDeviceNotify;

	public:
		MessageFilter()
		{
			DEV_BROADCAST_DEVICEINTERFACE dbch;
			dbch.dbcc_size = sizeof(dbch);
			dbch.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
			for (int i = 0; i < sizeof(GuidUSB_HIDList); i++)
			{
				dbch.dbcc_classguid = GuidUSB_HIDList[i];
				dbch.dbcc_name[0] = '\0';
			}
			hDeviceNotify = RegisterDeviceNotification(this->NotificationHandle,
				&dbch,
				DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);	// DEVICE_NOTIFY_WINDOW_HANDLE

			if (!hDeviceNotify)
				MessageBox::Show("Не удалось зарегистрировать приложение", "Проблема! :(");
		}
	event EventHandler^ DeviceConnected;
	virtual bool PreFilterMessage(Message % m){
			if (m.Msg == WM_DEVICECHANGE)
			{
				if (   (m.WParam.ToInt64() == DBT_DEVNODES_CHANGED)    || (m.WParam.ToInt64() == DBT_DEVICEARRIVAL) 
					|| (m.WParam.ToInt64() == DBT_DEVICEREMOVEPENDING) || (m.WParam.ToInt64() == DBT_DEVICEREMOVECOMPLETE) 
					|| (m.WParam.ToInt64() == DBT_CONFIGCHANGED)  )
			   {
				   //Rise the event, more processing is needed to check for a certain device.
				   DeviceConnected(this, nullptr);
				   MessageBox::Show("Вот оно: " + m.Msg + " " + m.WParam.ToInt32());  // пока для отладки, но как раз этот MessageBox и не появляется пока никак(((
				   return true;
			   }
			}
			return false;
		}
	protected:
		~MessageFilter(){}
	};
Но программа почему-то никак не перехватывает сообщение WM_DEVICECHANGE Есть подозрение, что может в функцию RegisterDeviceNotification в качестве первого параметра (хэндл окна) я его как-то не верно передаю?? Не знаю уже даже... Вариантов и способов этой обработки уже кучу перепробовал, но сообщение все равно поймать не могу, и как проверить регистрирует ли действительно приложение функция RegisterDeviceNotification тоже не знаю.
Подскажите, пожалуйста, кто знает в чем может быть дело, или кто имел опыт с такой задачей для C++/CLI Windows Forms. Заранее спасибо!

P.S. Если понадобятся исходники всего проекта, приложу его тоже.
Cha1000000 вне форума Ответить с цитированием
Старый 06.04.2016, 16:54   #2
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

Так NotificationHandle вроде ж тут не инициализировано никак?

Это хендл окна, которому будут сообщения отправляться.

Наверно вам надо передать сюда хендл своей формы.
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.
Alex11223 вне форума Ответить с цитированием
Старый 06.04.2016, 16:55   #3
p51x
Старожил
 
Регистрация: 15.02.2010
Сообщений: 15,706
По умолчанию

Если указали DEVICE_NOTIFY_ALL_INTERFACE_CLASSES , то dbcc_classguid будет игнорироваться и заполнять его не обязательно. А вот DEVICE_NOTIFY_WINDOW_HANDLE или DEVICE_NOTIFY_SERVICE_HANDLE все-таки стоит. Ну и, естественно, хендл должен быть хендлом, а не просто полем.
p51x вне форума Ответить с цитированием
Старый 06.04.2016, 22:23   #4
Cha1000000
Пользователь
 
Регистрация: 08.05.2014
Сообщений: 17
По умолчанию

Цитата:
Сообщение от Alex11223 Посмотреть сообщение
Так NotificationHandle вроде ж тут не инициализировано никак?

Это хендл окна, которому будут сообщения отправляться.

Наверно вам надо передать сюда хендл своей формы.
Ну по идее да. А не подскажете этот самый хэндл формы тут передаётся? А то я с этим хэндлами уже запутался откуда какой и как получить...
Cha1000000 вне форума Ответить с цитированием
Старый 06.04.2016, 22:26   #5
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

myForm.Handle https://msdn.microsoft.com/en-us/lib...vs.110%29.aspx
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.
Alex11223 вне форума Ответить с цитированием
Старый 06.04.2016, 22:36   #6
Cha1000000
Пользователь
 
Регистрация: 08.05.2014
Сообщений: 17
По умолчанию

Цитата:
Сообщение от p51x Посмотреть сообщение
Если указали DEVICE_NOTIFY_ALL_INTERFACE_CLASSES , то dbcc_classguid будет игнорироваться и заполнять его не обязательно. А вот DEVICE_NOTIFY_WINDOW_HANDLE или DEVICE_NOTIFY_SERVICE_HANDLE все-таки стоит. Ну и, естественно, хендл должен быть хендлом, а не просто полем.
Ну на счет DEVICE_NOTIFY_ALL_INTERFACE_CLASSES и DEVICE_NOTIFY_WINDOW_HANDLE я вроде бы разобрался... Это в последней версии я уже попробовал подсунуть этой функции DEVICE_NOTIFY_ALL_INTERFACE_CLASSES , думал, может что изменится... А нет. А так вообще использую DEVICE_NOTIFY_WINDOW_HANDLE.
Но это сейчас не суть. Похоже, что действительно проблема с хэндлом формы. Даже в дебаге, когда по шагам смотрел значения полей, на хэндле пишет <неопределенное значение>. Вот только как взять хэндл формы чет запутался.
Cha1000000 вне форума Ответить с цитированием
Старый 06.04.2016, 23:28   #7
Cha1000000
Пользователь
 
Регистрация: 08.05.2014
Сообщений: 17
По умолчанию

Цитата:
Сообщение от Alex11223 Посмотреть сообщение
Спасибо за подсказку. Добавил в класс:
Код:
Form form;
FormHWND = (HANDLE)form.Handle;
и в функцию соответственно: RegisterDeviceNotification(FormHWND , &dbch, DEVICE_NOTIFY_WINDOW_HANDLE);

Но сообщения все равно не отлавливаются(( Запускаю программу, вытаскиваю и вставляю любое USB устройство и NULL реакции Что еще может быть не так?

На всякий случай прикреплю проект. Может так удобнее будет посмотреть...
https://yadi.sk/d/t5qiQWdhqo5cM

Последний раз редактировалось Alex11223; 07.04.2016 в 15:50.
Cha1000000 вне форума Ответить с цитированием
Старый 07.04.2016, 15:59   #8
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

Цитата:
Код:
Form form;
FormHWND = (HANDLE)form.Handle;
Это бессмысленно, вы во-первых создали новую форму, во-вторых она скорее всего уничтожится после завершения метода.
Вам нужен Handle экземпляра формы используемого в вашем приложении.

Я кстати не в курсе что за IMessageFilter/AddMessageFilter и как оно работает, вроде обычно WndProc вместо этого используют в WinForms.

Вот пример на C# http://stackoverflow.com/a/16245901/964478

Код:
public Form1()
{
    InitializeComponent();
    UsbNotification.RegisterUsbDeviceNotification(this.Handle);
}

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);
    
    if (m.Msg == UsbNotification.WmDevicechange)
    {
        switch ((int)m.WParam)
        {
            case UsbNotification.DbtDeviceremovecomplete:
                Usb_DeviceRemoved(); // this is where you do your magic
                break;
            case UsbNotification.DbtDevicearrival:
                Usb_DeviceAdded(); // this is where you do your magic
                break;
        }
    }
}
Код:

using System;
using System.Runtime.InteropServices;

internal static class UsbNotification
{
    public const int DbtDevicearrival = 0x8000; // system detected a new device        
    public const int DbtDeviceremovecomplete = 0x8004; // device is gone      
    public const int WmDevicechange = 0x0219; // device change event      
    private const int DbtDevtypDeviceinterface = 5;
    private static readonly Guid GuidDevinterfaceUSBDevice = new Guid("A5DCBF10-6530-11D2-901F-00C04FB951ED"); // USB devices
    private static IntPtr notificationHandle;

    /// <summary>
    /// Registers a window to receive notifications when USB devices are plugged or unplugged.
    /// </summary>
    /// <param name="windowHandle">Handle to the window receiving notifications.</param>
    public static void RegisterUsbDeviceNotification(IntPtr windowHandle)
    {
        DevBroadcastDeviceinterface dbi = new DevBroadcastDeviceinterface
        {
            DeviceType = DbtDevtypDeviceinterface,
            Reserved = 0,
            ClassGuid = GuidDevinterfaceUSBDevice,
            Name = 0
        };

        dbi.Size = Marshal.SizeOf(dbi);
        IntPtr buffer = Marshal.AllocHGlobal(dbi.Size);
        Marshal.StructureToPtr(dbi, buffer, true);

        notificationHandle = RegisterDeviceNotification(windowHandle, buffer, 0);
    }

    /// <summary>
    /// Unregisters the window for USB device notifications
    /// </summary>
    public static void UnregisterUsbDeviceNotification()
    {
        UnregisterDeviceNotification(notificationHandle);
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr RegisterDeviceNotification(IntPtr recipient, IntPtr notificationFilter, int flags);

    [DllImport("user32.dll")]
    private static extern bool UnregisterDeviceNotification(IntPtr handle);

    [StructLayout(LayoutKind.Sequential)]
    private struct DevBroadcastDeviceinterface
    {
        internal int Size;
        internal int DeviceType;
        internal int Reserved;
        internal Guid ClassGuid;
        internal short Name;
    }
}
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.

Последний раз редактировалось Alex11223; 07.04.2016 в 16:08.
Alex11223 вне форума Ответить с цитированием
Старый 07.04.2016, 19:16   #9
Cha1000000
Пользователь
 
Регистрация: 08.05.2014
Сообщений: 17
По умолчанию

Цитата:
Сообщение от Alex11223 Посмотреть сообщение
Я кстати не в курсе что за IMessageFilter/AddMessageFilter и как оно работает, вроде обычно WndProc вместо этого используют в WinForms.
Итак по порядку.
IMessageFilter - это класс в котором реализуется обработка сообщений Message. Он и подставляется в мэйн функции в AddMessageFilter, в качестве параметра, и в идеале так же в фоне работы программы отслеживает и обрабатывает виндоус сообщения, по тем инструкциям, какими это заданно внутри IMessageFilter, а конкретнее в функции virtual bool PreFilterMessage(Message % m)
Код:
// Windows USB Device Application.cpp: главный файл проекта.

#include "stdafx.h"
#include "Form1.h"

using namespace WindowsUSBDeviceApplication;
using namespace MyFilter;

[STAThreadAttribute]
int main(array<System::String ^> ^args)
{
	Application::EnableVisualStyles();
	Application::SetCompatibleTextRenderingDefault(false); 

	// Фильтр сообщений
	Application::AddMessageFilter(gcnew MessageFilter);

	// Создание главного окна и его запуск
	Application::Run(gcnew Form1());
	return 0;
}
А вот уже переписанный, после ваших, коллега, советов и представленного примера, код класса MessageFilter:

Код:
using namespace System;
using namespace System::Data;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;

namespace MyFilter{
	// Класс регистрации приложения в системе и обработки системных сообщений.
	public ref class MessageFilter : public System::Windows::Forms::IMessageFilter
	{
	private:
		Message mes;
	public:
		MessageFilter(void)
		{
			PreFilterMessage(mes);
		};
		static void RegisterUsbDeviceNotification(IntPtr windowHandle)
		{

			DEV_BROADCAST_DEVICEINTERFACE dbch;
			dbch.dbcc_size = sizeof(dbch);
			dbch.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
			for (int i = 0; i < sizeof(GuidUSB_HIDList); i++)
			{
				dbch.dbcc_classguid = GuidUSB_HIDList[i];
				dbch.dbcc_name[0] = '\0';
			}
			if (!RegisterDeviceNotification((HANDLE)windowHandle, &dbch, DEVICE_NOTIFY_WINDOW_HANDLE))
				MessageBox::Show("Не удалось зарегистрировать приложение", "Проблема! :(");
		}
		// Обработка сообщений
		virtual bool PreFilterMessage(Message % m){
			 //Blocks all the messages relating to the left mouse button.
			//if (m.Msg == WM_LBUTTONDOWN)	// WM_DEVICECHANGE
			//{
			//	MessageBox::Show("WM_LBUTTONDOWN is: " + m.Msg + m.WParam.ToInt32());
			//	return true;
			//}
			//else return false;

			if (m.Msg == WM_DEVICECHANGE)
			{
				if ((m.WParam.ToInt64() == DBT_DEVNODES_CHANGED) || (m.WParam.ToInt64() == DBT_DEVICEARRIVAL)
					|| (m.WParam.ToInt64() == DBT_DEVICEREMOVEPENDING) || (m.WParam.ToInt64() == DBT_DEVICEREMOVECOMPLETE)
					|| (m.WParam.ToInt64() == DBT_CONFIGCHANGED))
				{
					MessageBox::Show("Вот оно: " + m.Msg + " " + m.WParam.ToInt32());
					return true;
				}
			}
			return false;
		}
	protected:
		~MessageFilter(){}
	};
}
Далее.
С Вашей помощью решить проблему с регистрацией приложения, передав правильный хэндл, похоже удалось! За что отдельное спасибо!
Однако это пока так и не решило проблему по перехвату сообщения WM_DEVICECHANGE. Если разкомментировать участок кода с проверкой сообщения WM_LBUTTONDOWN, то его фильтр AddMessageFilter обрабатывает прекрасно, а вот сообщение WM_DEVICECHANGE все еще почему-то не видит...
Далее.
Вы говорили про возможность воспользоваться функцией WndProc. По приведенному Вами примеру кода на С#, я переписал под С++ эту функцию так:
Код:
virtual void WndProc(Message % m) override
		{
			if (m.Msg == WM_DEVICECHANGE)
			{
				if ((m.WParam.ToInt64() == DBT_DEVNODES_CHANGED) || (m.WParam.ToInt64() == DBT_DEVICEARRIVAL)
					|| (m.WParam.ToInt64() == DBT_DEVICEREMOVEPENDING) || (m.WParam.ToInt64() == DBT_DEVICEREMOVECOMPLETE)
					|| (m.WParam.ToInt64() == DBT_CONFIGCHANGED))
				{
					MessageBox::Show("Вот оно: " + m.Msg + " " + m.WParam.ToInt32());
					return WndProc(m);
				}
			}
		}
И вставил её (как и в примере в раздел protected: сразу после
Код:
public ref class Form1 : public System::Windows::Forms::Form
	{
	public:
		Form1(void)
		{
			InitializeComponent();
			MessageFilter::RegisterUsbDeviceNotification(this->Handle);	 //	Регистрация приложения в системе	
			TreeUpdate();					// Заполняем узлы дерева устройств 
		}

	protected:
 . . .
Но в таком случае при запуске программы вылетает ошибка Необработанное исключение...
Какие будут мысли, предложения, варианты?

P.S. обновил проект, вот ссылка, если кому понадобится https://yadi.sk/d/mxBfzORIqpPQ2
Изображения
Тип файла: png Снимок.PNG (11.7 Кб, 97 просмотров)

Последний раз редактировалось Cha1000000; 07.04.2016 в 19:24.
Cha1000000 вне форума Ответить с цитированием
Старый 07.04.2016, 19:21   #10
Alex11223
Старожил
 
Аватар для Alex11223
 
Регистрация: 12.01.2011
Сообщений: 19,500
По умолчанию

Скорее всего в WndProc надо родительскую функцию WndProc вызвать в начале.
Ушел с форума, https://www.programmersforum.rocks, alex.pantec@gmail.com, https://github.com/AlexP11223
ЛС отключены Аларом.
Alex11223 вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск


Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Блок редактирования первой формы через вторую- C++/CLI Windows Forms kirill1507 Windows Forms 9 05.03.2016 14:31
DirectInput и джойстик (C++/CLI, Windows Forms) Vistaern Win Api 1 21.02.2013 06:48
вывод сообщения в windows forms василий- Windows Forms 0 23.10.2012 11:46
Проблема с выделением памяти в глобальной области windows forms(C++\CLI) DartLenin Помощь студентам 2 24.01.2012 20:58
Проблема с отображением русского языка (Windows Forms C++ CLI) Alex TGM Помощь студентам 0 17.08.2010 11:13