Why am I not getting messages in my control when I use the "message" directive?

When my control is created, I have to use DeviceWnd:=AllocateHWnd(DeviceWindowProc);

to receive the message WM_DEVICECHANGE

. And then...

procedure TFileList.DeviceWindowProc(var Message: TMessage);
begin
  case Message.Msg of
    WM_DEVICECHANGE: begin
      case Message.WParam of
        DBT_DEVICEREMOVECOMPLETE, DBT_DEVICEARRIVAL:
          if PDEV_BROADCAST_HDR(Message.LParam).dbch_devicetype = DBT_DEVTYP_VOLUME then
           OnDeviceChange;
      end;
    end;
  end; 
  Message.Result:=DefWindowProc(DeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;

      

This works well, but why don't I understand this message when I like it?

TFileList = class(TCustomControl)
private
  procedure DeviceChage(var AMessage:TMessage); message WM_DEVICECHANGE;
end;

procedure TFileList.DeviceWindowProc(var Message: TMessage);
begin
  Message.Result:=DefWindowProc(DeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;

procedure TFileList.DeviceChage(var AMessage:TMessage);
begin
 case AMessage.WParam of
   DBT_DEVICEREMOVECOMPLETE, DBT_DEVICEARRIVAL:
     if PDEV_BROADCAST_HDR(AMessage.LParam).dbch_devicetype = DBT_DEVTYP_VOLUME then
       OnDeviceChange;
 end;
end;

      

+3


source to share


2 answers


From the documentation RegisterDeviceNotification

:

Apps send event notifications using the BroadcastSystemMessage function. Any application with a top-level window can receive basic notifications by processing the WM_DEVICECHANGE message. Applications can use the RegisterDeviceNotification function to register to receive device notifications.

The window created with AllocateHWnd

is the top-level window. Hence, it receives broadcast messages.



Your custom control is not a top-level window. If you want it to receive messages, you need to call RegisterDeviceNotification

it by passing its window handle. If you do, be sure to take a vacation at the VCL by registering at CreateWnd

and deregistering at DestroyWnd

.

This is generally the AllocateHWnd

preferred way to listen for notifications. This is because it is not subject to rest in VCL windows and therefore cannot miss notifications. When the VCL window is recreated, there is a window of opportunity to send notifications, but your application does not have a window ready to receive.

This will definitely be a problem in your case, so you should use AllocateHWnd

. You can arrange that the window you create with AllocateHWnd

belongs to your custom control, and then you can forward the notification to that control code.

+3


source


WM_DEVICECHANGE

broadcasts to top-level windows. Your custom control window is not a top-level window, it is a child control window Parent

. This is why your handler is message

not called - the message never arrives at your control WndProc()

, so it can be sent to the handler message

.

For most device notifications you can use RegisterDeviceNotification()

to send WM_DEVICECHANGE

messages to a specific one HWND

. However, in your example, volume messages cannot be logged like this:

the function does not work if dbch_devicetype is DBT_DEVTYP_VOLUME .



So, in order for your custom control to receive messages WM_DEVICECHANGE

, it MUST highlight its own top-level window, for example with a function AllocateHwnd()

:

type
  // TCustomControl is meant to be used for developing **visual**
  // controls. If your custom control is not visual, you should
  // derive from `TComponent` instead...
  TFileList = class(TCustomControl)
  private
    DeviceWnd: HWND;
    procedure DeviceWindowProc(var Message: TMessage);
  public
    constructor Create(Owner: TComponent); override;
    destructor Destroy; override;
  end;

constructor TFileList.Create(Owner: TComponent);
begin
  inherited Create(Owner);
  if not (csDesigning in ComponentState) then
    DeviceWnd := AllocateHwnd(DeviceWindowProc);
end;

destructor TFileList.Destroy;
begin
  if DeviceWnd <> 0 then
    DeallocateHwnd(DeviceWnd);
  inherited Destroy;
end;

procedure TFileList.DeviceWindowProc(var Message: TMessage);
begin
  if Message.Msg = WM_DEVICECHANGE then
  begin
    case Message.WParam of
      DBT_DEVICEREMOVECOMPLETE, DBT_DEVICEARRIVAL:
        if PDEV_BROADCAST_HDR(Message.LParam).dbch_devicetype = DBT_DEVTYP_VOLUME then
          OnDeviceChange;
    end;
  end;
  Message.Result := DefWindowProc(DeviceWnd, Message.Msg, Message.WParam, Message.LParam);
end;

      

+2


source







All Articles