Delphi Pages Forums  

Go Back   Delphi Pages Forums > Delphi Forum > General

Lost Password?

Reply
 
Thread Tools Display Modes
  #1  
Old 04-21-2006, 08:28 AM
DpM DpM is offline
Junior Member
 
Join Date: Apr 2006
Posts: 8
Default Using an XP MCE IR remote control in Delphi Applications.

This is my first post into the forum... I thought it worth a try seeing as there's some other really useful stuff that has been found in here.

I would like to understand if it is possible, to use Delphi (version 7 I'm afraid) to more fully utilise the MCE IR remote control that comes with media centres. Up to this point, I have code working to handle the WM_APPCOMMAND messages - the bundle of buttons that deal with things like APPCOMMAND_MEDIA_NEXTTRACK, APPCOMMAND_MEDIA_STOP and APPCOMMAND_MEDIA_PLAY - but would like to understand how a Delphi program could handle the WM_INPUT messages too and then I can use the other buttons and not be limited to what are basically the transport (PLAY,STOP,NEXT) and audio (VOL UP and DOWN, MUTE) buttons.

Doing a bit of research leads me to believe my starting point is the Windows API RegisterRawInputDevices which is defined in Winuser.h and only for use on Windows XP - that's fine with me. However, I can't get anything to work in Delphi and this is where I need to get help - does anyone have any pointers?

I have seen the following information thus far which has got me some way...
  • [LINK]http://www.delphipages.com/threads/thread.cfm?ID=160200&G=159982[/LINK]
  • [LINK]http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/rawinput/rawinputreference/rawinputfunctions/registerrawinputdevices.asp[/LINK]
  • [LINK]http://www.daniweb.com/techtalkforums/thread39555.html[/LINK]
  • [LINK]http://www.delphipages.com/threads/thread.cfm?ID=161453&G=161453[/LINK]

... but it's time to admit I need some help in implementing RegisterRawInputDevices and handling WM_INPUT messages.

In addition to all this, it would be really good to understand how to be able to receive and act upon the WM_KEYDOWN messages that have parameters of VK_RETURN etc. so that my program could utilise both the numeric keypad and the OK button on the MCE remote.

Cheers, DpM
------------------------------
http://www.hmusiccentre.org.uk
Reply With Quote
  #2  
Old 04-21-2006, 12:34 PM
DavidM DavidM is offline
Member
 
Join Date: Mar 2006
Posts: 37
Default RE: Using an XP MCE IR remote control in Delphi Applications.

I was recently working with RegisterRawInputDevices in order to capture the keystrokes that are sent by a "PowerPoint" navigator (a four-button wireless device used for controlling PowerPoint slide shows). I was able to get this working to my satisfaction, although I have suspended work on the application for now.

(1) I registered for the Raw Input using RegisterRawInputDevices;
(2) I wrote a procedure for the Application.OnMessage event to capture the messages I am interested in;
(3) When I found a keystroke of interest, I sent a user-defined message to my application;
(4) I wrote a message procedure in my form class to handle the user-defined message.

Here are the extracts of the code. See the Microsoft Developer's Network for details on RAW INPUT (http://msdn.microsoft.com/library/en...utrawinput.asp). The biggest problem I had, was identifying which device I was interested in.

[DELPHI]
function TRawObj.RegisterRaw(hForWindow: HWND): boolean;
{ This function will register the Application to receive RAW INPUT from the
keyboard.}
var
cSize: Cardinal;
// You have to pass an array of devices to capture
rRawDev: array[0..0] of RAWINPUTDEVICE;
begin
{ I could not find a list of which UsagePage and Usage values mean what. However
I did find somewhere that the combination below (1, 6) is for the keyboard
and mouse devices. There appears to be no way to separate them so you only
get keyboard messages.}
rRawDev[0].usUsagePage := 1;
rRawDev[0].usUsage := 6;
{ INPUTSINK tells the system to send the messages even when the specified
target window does NOT have the focus.
NOLEGACY tells the system not to translate the messages into the KEYUP and
KEYDOWN messages, so your application's OnKeyUp, OnKeyPress, OnKeyDown
events would never be executed.}
rRawDev[0].dwFlags := RIDEV_INPUTSINK; // RIDEV_NOLEGACY;
{ I was not able to get this working correctly using a TForm window handle
as the target, so I used the Application window handle and wrote a procedure
for the Application.OnMessage event.}
rRawDev[0].hwndTarget := Application.Handle;
cSize := sizeOf(RAWINPUTDEVICE);

if (RegisterRawInputDevices(@rRawDev, 1, cSize)) then
begin
result := True;
end else begin
result := False;
MessageDlg(oRawObj.GetLastErrorMsg (GetLastError(),
'RegisterRawInputDevices Failed: ' + #13#10),
mtError, [mbOK], 0);
end;

// The following are used by the TRawObj object
SkipLegacy := false;
Scanning := false;
SendMsgHwnd := hForWindow;
end;

procedure TRawObj.AppMsgHandler (var Msg: TMsg; var Handled: Boolean);
{ This procedure will be called by the Application's window procedure for EVERY
message received. It is critical that this procedure be as tight and error
free as possible (it will be executed hundreds of times per second).
Especially since we have registered for RAW INPUT from the mouse. }
begin
{ If you set Handled to True, then the message will not be passed on to your
application forms and controls. }
Handled := False;

{ The WM_INPUT message is the RAW INPUT that must be interrogated in order
to determine what key was pressed and on which device. Immediately after
this message the application will receive a WM_KEYDOWN, WM_KEYUP and
possibly other messages relating to the keystroke (these are referred to
as the legacy messages). The bSkipLegacy flag is set in AppMsg_WMINPUT if
the keystroke/device is handled.}
if ((hSendMsg <> 0) and ((oDevList.Count > 0) or (bScanning))) then
begin
case Msg.message of
WM_INPUT: Handled := AppMsg_WMINPUT(Msg);
WM_KEYFIRST..WM_KEYLAST: Handled := bSkipLegacy;
end;
end;
end;

function TRawObj.AppMsg_WMINPUT (var MsgIn: TMsg): boolean;
{ This function is called to handle a WM_INPUT message. It will determine if
it is a message of interest and post a message to my form if it is. The
function returns True if the message is handled, otherwise it returns false.
The function also sets this object's bSkipLegacy flag to True if the
message is handled, or false if it is not.}
var
iRtc: integer;
cSize: cardinal;
rMsgOut: TKbdMsg; // Message structure for application message
begin
result := false;

// Get the Header of the RAW INPUT
cSize := sizeOf(RAWINPUTHEADER);
G_rDta.header.dwSize := sizeOf(RAWINPUTHEADER);
iRtc := GetRawInputData(HRAWINPUT(MsgIn.LParam),
RID_HEADER, @G_rDta.header, cSize, sizeof(RAWINPUTHEADER));

if (iRtc >= 0) then
begin
{ Remember, we had to register for Keyboard and mouse messages. But we
are only interested in the KEYBOARD messages. }
if (G_rDta.header.dwType = RIM_TYPEKEYBOARD) then
begin
// Get the actual RAW INPUT DATA
cSize := G_rDta.header.dwSize;
iRtc := GetRawInputData (HRAWINPUT(MsgIn.LParam),
RID_INPUT, @G_rDta, cSize, sizeOf(RAWINPUTHEADER));

if (iRtc >= 0) then
begin
{ The navigator reports itself as a standard keyboard. So, in order
to locate it, we displayed a form asking the user to press a key
on the device and we set this object's Scanning property to True.
So, if we are scanning, then we want this keystroke. }
if (bScanning) then
begin
bSkipLegacy := true; // Do not send the legacy messages

with rMsgOut do // populate my Message structure
begin
wpSink := ShortInt(MsgIn.wParam = RIM_INPUTSINK);
wpVKey := G_rDta.keyboard.VKey;
wpFlags := G_rDta.keyboard.Flags;
wpMake := G_rDta.keyboard.MakeCode;
lpHandle := G_rDta.header.hDevice;
end;
// Post the user-defined message to the specified window.
PostMessage (hSendMsg, UM_SCANDEVICE,
rMsgOut.WParam, rMsgOut.LParam);
result := True; // We have handled the message so return True

end else if (not IsInDevList(G_rDta.header.hDevice)) then
{ We are not scanning for the device, so see if this message came from
one of the devices of interest. While I currently do not have a
need for multiple devices, the object is written to handle more than
one (for future enhancements) }
begin // Device is not in the device list. Call DefRawInputProc
bSkipLegacy := false;
DefRawInputProc (MsgIn.hwnd, MsgIn.message, MsgIn.wParam, MsgIn.lParam);

end else begin
{ The message came from a device we are interested in. Post a message
back to the specified form using my Message structure. Note that
the message ID is determined by adding WM_USER to the Keyboard Message
from the RAW INPUT DATA block. The message there will be WM_KEYDOWN
or WM_KEYUP. This is NOT the message from the Windows Message queue }
bSkipLegacy := true; // We do not want the Legacy Messages
with rMsgOut do // Populate the Message structure
begin
wpSink := ShortInt(MsgIn.wParam = RIM_INPUTSINK);
wpVKey := G_rDta.keyboard.VKey;
wpFlags := G_rDta.keyboard.Flags;
wpMake := G_rDta.keyboard.MakeCode;
lpHandle := G_rDta.header.hDevice;
end;
PostMessage (hSendMsg, WM_USER + G_rDta.keyboard.Message,
rMsgOut.WParam, rMsgOut.LParam);
result := True;
end;

{ If some error occurs, we really do NOT want to show a message box from
within the Message loop. So the error message is added to the object's
error list. The application can read them and clear them as desired.}
end else begin
oErrList.Add(GetLastErrorMsg(GetLastError(),
'GetRawInputData (RID_INPUT) Failed: '));
end;
end;
end else begin
oErrList.Add(GetLastErrorMsg(GetLastError(),
'GetRawInputData (RID_HEADER) Failed: '));
end;
end;

{ Place this definition in the TForm class definition. }
procedure msgUM_KEYUP (var msg: TKbdMsg); message UM_KEYUP;


procedure TfrmMain.msgUM_KEYUP (var msg: TKbdMsg);
{ This procedure will handle the UM_KEYUP message sent by TRawObj. This message
is sent when the Navigator reports a KEYUP event. }
var
iSub: integer;
begin
if (Assigned(oCurRemote)) then
begin
iSub := oCurRemote.GetIndexOfCode(msg.wpVKey);
if (iSub >= 0) then
begin
// Process the keystroke here
end;
end;
end;
[/DELPHI]

This has been a lengthy reply, and it still does not include all of the source for the TRawObj class. If you would like that code, I would be glad to email it to you.



Hope this helps!
Good Luck!

Code:
If (ItWorks(ThisAnswer)) then Click('Accept as Answer')
else Submit(YourResults, MoreDetails);
Reply With Quote
  #3  
Old 04-21-2006, 01:03 PM
DpM DpM is offline
Junior Member
 
Join Date: Apr 2006
Posts: 8
Default RE: Using an XP MCE IR remote control in Delphi Applications.

This has been very enlightening indeed - so I initially thank you for that. I have a second question - a dumber question to be sure... and that is what version of Delphi are you using?

I am still stuck back on version 7 and when I enter "Register" and try code completion, I don't have the RegisterRawInputDevices API available to me - I'm developing on XP so the OS level is OK - do I have to include another unit apart from Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls?

I see that your code here:

[DELPHI]if (RegisterRawInputDevices(@rRawDev, 1, cSize)) then
begin
result := True;
end else begin
result := False;
MessageDlg(oRawObj.GetLastErrorMsg (GetLastError(),
'RegisterRawInputDevices Failed: ' + #13#10),
mtError, [mbOK], 0);
end;[/DELPHI]...has what appears to be a neat call to the API... was this any trouble for you? I'm guessing not... but I could be wrong.

In closing, it is worth noting that my earlier research did prove quite useful in quickly identifying what parameters I will need in my RAWINPUTDEVICE - UsagePage should be 65,468 and Usage should be 136 for the MCE remote control. Flags 0 I believe... but I definitely noted your issue with the handle - one to keep an eye on.

If there is a chance of seeing some small application that will compile or extra code, that would be most excellent to be sure... many thanks! I'll press "Accept Answer" anyway as the info. is going to prove useful.

One extra thing of note to anyone interested in reading this thread... several of the MCE IR Remote Control buttons will work in any Windows application anyway - the left, right, up, down and OK buttons can be used to navigate lists and select items in those lists. Any application that has the focus will respond - that's because these kind of Remote Control buttons are sending WM_KEYDOWN as opposed to WM_INPUT messages. Worth knowing I guess.

Cheers, DpM
------------------------------
http://www.hmusiccentre.org.uk
Reply With Quote
  #4  
Old 04-21-2006, 04:24 PM
DavidM DavidM is offline
Member
 
Join Date: Mar 2006
Posts: 37
Default RE: Using an XP MCE IR remote control in Delphi Applications.

That reminds me ... #^) ... I'm sorry, I forgot about that ...

I am using Delphi 2005, but the declarations needed are apparently not distributed with Delphi. They are a part of the winuser.h include file (if you're using C). It took me a while to track it down and piece together the code for this stuff. I think I might spend some time soon and develop an article for this website on handling RAWINPUT.

The following code is the complete code from a unit I pieced together specifically for this. I think it includes everything needed for handling the raw input. Just create a new unit file, paste this code, and include it in your project, etc.

[DELPHI]
unit uRawInput;

interface

uses Windows;

//
// Raw Input Messages.
//

type
HRAWINPUT = THANDLE;
{$EXTERNALSYM HRAWINPUT}

//
// WM_INPUT wParam
//

//
// Use this macro to get the input code from wParam.
//

function GET_RAWINPUT_CODE_WPARAM(wParam: WPARAM): DWORD;
{$EXTERNALSYM GET_RAWINPUT_CODE_WPARAM}

//
// The input is in the regular message flow,
// the app is required to call DefWindowProc
// so that the system can perform clean ups.
//

const
RIM_INPUT = 0;
{$EXTERNALSYM RIM_INPUT}

//
// The input is sink only. The app is expected
// to behave nicely.
//

RIM_INPUTSINK = 1;
{$EXTERNALSYM RIM_INPUTSINK}

//
// Raw Input data header
//

type
tagRAWINPUTHEADER = record
dwType: DWORD;
dwSize: DWORD;
hDevice: THANDLE;
wParam: WPARAM;
end;
{$EXTERNALSYM tagRAWINPUTHEADER}
RAWINPUTHEADER = tagRAWINPUTHEADER;
{$EXTERNALSYM RAWINPUTHEADER}
PRAWINPUTHEADER = ^RAWINPUTHEADER;
{$EXTERNALSYM PRAWINPUTHEADER}
LPRAWINPUTHEADER = ^RAWINPUTHEADER;
{$EXTERNALSYM LPRAWINPUTHEADER}
TRawInputHeader = RAWINPUTHEADER;

//
// Type of the raw input
//

const
RIM_TYPEMOUSE = 0;
{$EXTERNALSYM RIM_TYPEMOUSE}
RIM_TYPEKEYBOARD = 1;
{$EXTERNALSYM RIM_TYPEKEYBOARD}
RIM_TYPEHID = 2;
{$EXTERNALSYM RIM_TYPEHID}

//
// Raw format of the mouse input
//

type
tagRAWMOUSE = record
//
// Indicator flags.
//
usFlags: WORD;

//
// The transition state of the mouse buttons.
//

union: record
case Integer of
0: (
ulButtons: ULONG);
1: (
usButtonFlags: WORD;
usButtonData: WORD);
end;

//
// The raw state of the mouse buttons.
//
ulRawButtons: ULONG;

//
// The signed relative or absolute motion in the X direction.
//
lLastX: LongInt;

//
// The signed relative or absolute motion in the Y direction.
//
lLastY: LongInt;

//
// Device-specific additional information for the event.
//
ulExtraInformation: ULONG;
end;
{$EXTERNALSYM tagRAWMOUSE}
RAWMOUSE = tagRAWMOUSE;
{$EXTERNALSYM RAWMOUSE}
PRAWMOUSE = ^RAWMOUSE;
{$EXTERNALSYM PRAWMOUSE}
LPRAWMOUSE = ^RAWMOUSE;
{$EXTERNALSYM LPRAWMOUSE}
TRawMouse = RAWMOUSE;

//
// Define the mouse button state indicators.
//

const
RI_MOUSE_LEFT_BUTTON_DOWN = $0001; // Left Button changed to down.
{$EXTERNALSYM RI_MOUSE_LEFT_BUTTON_DOWN}
RI_MOUSE_LEFT_BUTTON_UP = $0002; // Left Button changed to up.
{$EXTERNALSYM RI_MOUSE_LEFT_BUTTON_UP}
RI_MOUSE_RIGHT_BUTTON_DOWN = $0004; // Right Button changed to down.
{$EXTERNALSYM RI_MOUSE_RIGHT_BUTTON_DOWN}
RI_MOUSE_RIGHT_BUTTON_UP = $0008; // Right Button changed to up.
{$EXTERNALSYM RI_MOUSE_RIGHT_BUTTON_UP}
RI_MOUSE_MIDDLE_BUTTON_DOWN = $0010; // Middle Button changed to down.
{$EXTERNALSYM RI_MOUSE_MIDDLE_BUTTON_DOWN}
RI_MOUSE_MIDDLE_BUTTON_UP = $0020; // Middle Button changed to up.
{$EXTERNALSYM RI_MOUSE_MIDDLE_BUTTON_UP}

RI_MOUSE_BUTTON_1_DOWN = RI_MOUSE_LEFT_BUTTON_DOWN;
{$EXTERNALSYM RI_MOUSE_BUTTON_1_DOWN}
RI_MOUSE_BUTTON_1_UP = RI_MOUSE_LEFT_BUTTON_UP;
{$EXTERNALSYM RI_MOUSE_BUTTON_1_UP}
RI_MOUSE_BUTTON_2_DOWN = RI_MOUSE_RIGHT_BUTTON_DOWN;
{$EXTERNALSYM RI_MOUSE_BUTTON_2_DOWN}
RI_MOUSE_BUTTON_2_UP = RI_MOUSE_RIGHT_BUTTON_UP;
{$EXTERNALSYM RI_MOUSE_BUTTON_2_UP}
RI_MOUSE_BUTTON_3_DOWN = RI_MOUSE_MIDDLE_BUTTON_DOWN;
{$EXTERNALSYM RI_MOUSE_BUTTON_3_DOWN}
RI_MOUSE_BUTTON_3_UP = RI_MOUSE_MIDDLE_BUTTON_UP;
{$EXTERNALSYM RI_MOUSE_BUTTON_3_UP}

RI_MOUSE_BUTTON_4_DOWN = $0040;
{$EXTERNALSYM RI_MOUSE_BUTTON_4_DOWN}
RI_MOUSE_BUTTON_4_UP = $0080;
{$EXTERNALSYM RI_MOUSE_BUTTON_4_UP}
RI_MOUSE_BUTTON_5_DOWN = $0100;
{$EXTERNALSYM RI_MOUSE_BUTTON_5_DOWN}
RI_MOUSE_BUTTON_5_UP = $0200;
{$EXTERNALSYM RI_MOUSE_BUTTON_5_UP}

//
// If usButtonFlags has RI_MOUSE_WHEEL, the wheel delta is stored in usButtonData.
// Take it as a signed value.
//

RI_MOUSE_WHEEL = $0400;
{$EXTERNALSYM RI_MOUSE_WHEEL}

//
// Define the mouse indicator flags.
//

MOUSE_MOVE_RELATIVE = 0;
{$EXTERNALSYM MOUSE_MOVE_RELATIVE}
MOUSE_MOVE_ABSOLUTE = 1;
{$EXTERNALSYM MOUSE_MOVE_ABSOLUTE}
MOUSE_VIRTUAL_DESKTOP = $02; // the coordinates are mapped to the virtual desktop
{$EXTERNALSYM MOUSE_VIRTUAL_DESKTOP}
MOUSE_ATTRIBUTES_CHANGED = $04; // requery for mouse attributes
{$EXTERNALSYM MOUSE_ATTRIBUTES_CHANGED}

//
// Raw format of the keyboard input
//

type
tagRAWKEYBOARD = record
//
// The "make" scan code (key depression).
//
MakeCode: WORD;

//
// The flags field indicates a "break" (key release) and other
// miscellaneous scan code information defined in ntddkbd.h.
//
Flags: WORD;

Reserved: WORD;

//
// Windows message compatible information
//
VKey: WORD;
Message: UINT;

//
// Device-specific additional information for the event.
//
ExtraInformation: ULONG;
end;
{$EXTERNALSYM tagRAWKEYBOARD}
RAWKEYBOARD = tagRAWKEYBOARD;
{$EXTERNALSYM RAWKEYBOARD}
PRAWKEYBOARD = ^RAWKEYBOARD;
{$EXTERNALSYM PRAWKEYBOARD}
LPRAWKEYBOARD = ^RAWKEYBOARD;
{$EXTERNALSYM LPRAWKEYBOARD}
TRawKeyBoard = RAWKEYBOARD;

//
// Define the keyboard overrun MakeCode.
//

const
KEYBOARD_OVERRUN_MAKE_CODE = $FF;
{$EXTERNALSYM KEYBOARD_OVERRUN_MAKE_CODE}

//
// Define the keyboard input data Flags.
//

RI_KEY_MAKE = 0;
{$EXTERNALSYM RI_KEY_MAKE}
RI_KEY_BREAK = 1;
{$EXTERNALSYM RI_KEY_BREAK}
RI_KEY_E0 = 2;
{$EXTERNALSYM RI_KEY_E0}
RI_KEY_E1 = 4;
{$EXTERNALSYM RI_KEY_E1}
RI_KEY_TERMSRV_SET_LED = 8;
{$EXTERNALSYM RI_KEY_TERMSRV_SET_LED}
RI_KEY_TERMSRV_SHADOW = $10;
{$EXTERNALSYM RI_KEY_TERMSRV_SHADOW}

//
// Raw format of the input from Human Input Devices
//

type
tagRAWHID = record
dwSizeHid: DWORD; // byte size of each report
dwCount: DWORD; // number of input packed
bRawData: array [0..0] of BYTE;
end;
{$EXTERNALSYM tagRAWHID}
RAWHID = tagRAWHID;
{$EXTERNALSYM RAWHID}
PRAWHID = ^RAWHID;
{$EXTERNALSYM PRAWHID}
LPRAWHID = ^RAWHID;
{$EXTERNALSYM LPRAWHID}
TRawHid = RAWHID;

//
// RAWINPUT data structure.
//

tagRAWINPUT = record
header: RAWINPUTHEADER;
case Integer of
0: (mouse: RAWMOUSE);
1: (keyboard: RAWKEYBOARD);
2: (hid: RAWHID);
end;
{$EXTERNALSYM tagRAWINPUT}
RAWINPUT = tagRAWINPUT;
{$EXTERNALSYM RAWINPUT}
PRAWINPUT = ^RAWINPUT;
{$EXTERNALSYM PRAWINPUT}
LPRAWINPUT = ^RAWINPUT;
{$EXTERNALSYM LPRAWINPUT}
TRawInput = RAWINPUT;

function RAWINPUT_ALIGN(x: Pointer): Pointer;
{$EXTERNALSYM RAWINPUT_ALIGN}

function NEXTRAWINPUTBLOCK(ptr: PRawInput): PRawInput;
{$EXTERNALSYM NEXTRAWINPUTBLOCK}

//
// Flags for GetRawInputData
//

const
RID_INPUT = $10000003;
{$EXTERNALSYM RID_INPUT}
RID_HEADER = $10000005;
{$EXTERNALSYM RID_HEADER}

function GetRawInputData(hRawInput: HRAWINPUT; uiCommand: UINT; pData: POINTER;
var pcbSize: UINT; cbSizeHeader: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputData}

//
// Raw Input Device Information
//

const
RIDI_PREPARSEDDATA = $20000005;
{$EXTERNALSYM RIDI_PREPARSEDDATA}
RIDI_DEVICENAME = $20000007; // the return valus is the character length, not the byte size
{$EXTERNALSYM RIDI_DEVICENAME}
RIDI_DEVICEINFO = $2000000b;
{$EXTERNALSYM RIDI_DEVICEINFO}

type
PRID_DEVICE_INFO_MOUSE = ^RID_DEVICE_INFO_MOUSE;
{$EXTERNALSYM PRID_DEVICE_INFO_MOUSE}
tagRID_DEVICE_INFO_MOUSE = record
dwId: DWORD;
dwNumberOfButtons: DWORD;
dwSampleRate: DWORD;
end;
{$EXTERNALSYM tagRID_DEVICE_INFO_MOUSE}
RID_DEVICE_INFO_MOUSE = tagRID_DEVICE_INFO_MOUSE;
{$EXTERNALSYM RID_DEVICE_INFO_MOUSE}
TRidDeviceInfoMouse = RID_DEVICE_INFO_MOUSE;
PRidDeviceInfoMouse = PRID_DEVICE_INFO_MOUSE;

PRID_DEVICE_INFO_KEYBOARD = ^RID_DEVICE_INFO_KEYBOARD;
{$EXTERNALSYM PRID_DEVICE_INFO_KEYBOARD}
tagRID_DEVICE_INFO_KEYBOARD = record
dwType: DWORD;
dwSubType: DWORD;
dwKeyboardMode: DWORD;
dwNumberOfFunctionKeys: DWORD;
dwNumberOfIndicators: DWORD;
dwNumberOfKeysTotal: DWORD;
end;
{$EXTERNALSYM tagRID_DEVICE_INFO_KEYBOARD}
RID_DEVICE_INFO_KEYBOARD = tagRID_DEVICE_INFO_KEYBOARD;
{$EXTERNALSYM RID_DEVICE_INFO_KEYBOARD}
TRidDeviceInfoKeyboard = RID_DEVICE_INFO_KEYBOARD;
PRidDeviceInfoKeyboard = PRID_DEVICE_INFO_KEYBOARD;

PRID_DEVICE_INFO_HID = ^RID_DEVICE_INFO_HID;
{$EXTERNALSYM PRID_DEVICE_INFO_HID}
tagRID_DEVICE_INFO_HID = record
dwVendorId: DWORD;
dwProductId: DWORD;
dwVersionNumber: DWORD;
//
// Top level collection UsagePage and Usage
//
usUsagePage: WORD;
usUsage: WORD;
end;
{$EXTERNALSYM tagRID_DEVICE_INFO_HID}
RID_DEVICE_INFO_HID = tagRID_DEVICE_INFO_HID;
{$EXTERNALSYM RID_DEVICE_INFO_HID}
TRidDeviceInfoHid = RID_DEVICE_INFO_HID;
PRidDeviceInfoHid = PRID_DEVICE_INFO_HID;

tagRID_DEVICE_INFO = record
cbSize: DWORD;
dwType: DWORD;
case Integer of
0: (mouse: RID_DEVICE_INFO_MOUSE);
1: (keyboard: RID_DEVICE_INFO_KEYBOARD);
2: (hid: RID_DEVICE_INFO_HID);
end;
{$EXTERNALSYM tagRID_DEVICE_INFO}
RID_DEVICE_INFO = tagRID_DEVICE_INFO;
{$EXTERNALSYM RID_DEVICE_INFO}
PRID_DEVICE_INFO = ^RID_DEVICE_INFO;
{$EXTERNALSYM PRID_DEVICE_INFO}
LPRID_DEVICE_INFO = ^RID_DEVICE_INFO;
{$EXTERNALSYM LPRID_DEVICE_INFO}
TRidDeviceInfo = RID_DEVICE_INFO;
PRidDeviceInfo = PRID_DEVICE_INFO;

function GetRawInputDeviceInfoA(hDevice: THANDLE; uiCommand: UINT; pData: POINTER;
var pcbSize: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputDeviceInfoA}
function GetRawInputDeviceInfoW(hDevice: THANDLE; uiCommand: UINT; pData: POINTER;
var pcbSize: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputDeviceInfoW}

{$IFDEF UNICODE}
function GetRawInputDeviceInfo(hDevice: THANDLE; uiCommand: UINT; pData: POINTER;
var pcbSize: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputDeviceInfo}
{$ELSE}
function GetRawInputDeviceInfo(hDevice: THANDLE; uiCommand: UINT; pData: POINTER;
var pcbSize: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputDeviceInfo}
{$ENDIF}

//
// Raw Input Bulk Read: GetRawInputBuffer
//

function GetRawInputBuffer(pData: PRAWINPUT; var pcbSize: UINT; cbSizeHeader: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputBuffer}

//
// Raw Input request APIs
//

type
LPRAWINPUTDEVICE = ^RAWINPUTDEVICE;
{$EXTERNALSYM LPRAWINPUTDEVICE}
PRAWINPUTDEVICE = ^RAWINPUTDEVICE;
{$EXTERNALSYM PRAWINPUTDEVICE}
tagRAWINPUTDEVICE = record
usUsagePage: WORD; // Toplevel collection UsagePage
usUsage: WORD; // Toplevel collection Usage
dwFlags: DWORD;
hwndTarget: HWND; // Target hwnd. NULL = follows keyboard focus
end;
{$EXTERNALSYM tagRAWINPUTDEVICE}
RAWINPUTDEVICE = tagRAWINPUTDEVICE;
{$EXTERNALSYM RAWINPUTDEVICE}
TRawInputDevice = RAWINPUTDEVICE;

const
RIDEV_REMOVE = $00000001;
{$EXTERNALSYM RIDEV_REMOVE}
RIDEV_EXCLUDE = $00000010;
{$EXTERNALSYM RIDEV_EXCLUDE}
RIDEV_PAGEONLY = $00000020;
{$EXTERNALSYM RIDEV_PAGEONLY}
RIDEV_NOLEGACY = $00000030;
{$EXTERNALSYM RIDEV_NOLEGACY}
RIDEV_INPUTSINK = $00000100;
{$EXTERNALSYM RIDEV_INPUTSINK}
RIDEV_CAPTUREMOUSE = $00000200; // effective when mouse nolegacy is specified, otherwise it would be an error
{$EXTERNALSYM RIDEV_CAPTUREMOUSE}
RIDEV_NOHOTKEYS = $00000200; // effective for keyboard.
{$EXTERNALSYM RIDEV_NOHOTKEYS}
RIDEV_APPKEYS = $00000400; // effective for keyboard.
{$EXTERNALSYM RIDEV_APPKEYS}
RIDEV_EXMODEMASK = $000000F0;
{$EXTERNALSYM RIDEV_EXMODEMASK}

function RIDEV_EXMODE(mode: DWORD): DWORD;
{$EXTERNALSYM RIDEV_EXMODE}

function RegisterRawInputDevices(pRawInputDevices: PRAWINPUTDEVICE;
uiNumDevices: UINT; cbSize: UINT): BOOL; stdcall;
{$EXTERNALSYM RegisterRawInputDevices}

function GetRegisteredRawInputDevices(pRawInputDevices: PRAWINPUTDEVICE;
var puiNumDevices: UINT; cbSize: UINT): UINT; stdcall;
{$EXTERNALSYM GetRegisteredRawInputDevices}

type
PRAWINPUTDEVICELIST = ^RAWINPUTDEVICELIST;
{$EXTERNALSYM PRAWINPUTDEVICELIST}
tagRAWINPUTDEVICELIST = record
hDevice: THANDLE;
dwType: DWORD;
end;
{$EXTERNALSYM tagRAWINPUTDEVICELIST}
RAWINPUTDEVICELIST = tagRAWINPUTDEVICELIST;
{$EXTERNALSYM RAWINPUTDEVICELIST}
TRawInputDeviceList = RAWINPUTDEVICELIST;

function GetRawInputDeviceList(pRawInputDeviceList: PRAWINPUTDEVICELIST; var puiNumDevices: UINT;
cbSize: UINT): UINT; stdcall;
{$EXTERNALSYM GetRawInputDeviceList}

//function DefRawInputProc(paRawInput: PRAWINPUT; nInput: Integer; cbSizeHeader: UINT): LRESULT; stdcall;
{ The documentation says that this procedure is called "with the same parameters received by the window procedure"
so, eventhough the documentation shows the parameter list above, I have changed it to
the standard windows message procedure parameters.}
function DefRawInputProc(hWnd: HWND; Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
//function DefRawInputProc(Msg: UINT; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
{$EXTERNALSYM DefRawInputProc}

implementation

const
user32 = 'user32.dll';

function GET_RAWINPUT_CODE_WPARAM(wParam: WPARAM): DWORD;
begin
Result := wParam and $ff;
end;

function RAWINPUT_ALIGN(x: Pointer): Pointer;
begin
Result := Pointer((Integer(x) + SizeOf(DWORD) - 1) and not (SizeOf(DWORD) - 1));
end;

function NEXTRAWINPUTBLOCK(ptr: PRawInput): PRawInput;
begin
Result := PRAWINPUT(DWORD(RAWINPUT_ALIGN(ptr)) + ptr^.header.dwSize);
end;


{$IFDEF DYNAMIC_LINK}
var
_GetRawInputData: Pointer;

function GetRawInputData;
begin
GetProcedureAddress(_GetRawInputData, user32, 'GetRawInputData');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputData]
end;
end;
{$ELSE}
function GetRawInputData; external user32 name 'GetRawInputData';
{$ENDIF DYNAMIC_LINK}

{$IFDEF DYNAMIC_LINK}
var
_GetRawInputDeviceInfoA: Pointer;

function GetRawInputDeviceInfoA;
begin
GetProcedureAddress(_GetRawInputDeviceInfoA, user32, 'GetRawInputDeviceInfoA');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputDeviceInfoA]
end;
end;
{$ELSE}
function GetRawInputDeviceInfoA; external user32 name 'GetRawInputDeviceInfoA';
{$ENDIF DYNAMIC_LINK}

{$IFDEF DYNAMIC_LINK}
var
_GetRawInputDeviceInfoW: Pointer;

function GetRawInputDeviceInfoW;
begin
GetProcedureAddress(_GetRawInputDeviceInfoW, user32, 'GetRawInputDeviceInfoW');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputDeviceInfoW]
end;
end;
{$ELSE}
function GetRawInputDeviceInfoW; external user32 name 'GetRawInputDeviceInfoW';
{$ENDIF DYNAMIC_LINK}
{$IFDEF UNICODE}

{$IFDEF DYNAMIC_LINK}
var
_GetRawInputDeviceInfo: Pointer;

function GetRawInputDeviceInfo;
begin
GetProcedureAddress(_GetRawInputDeviceInfo, user32, 'GetRawInputDeviceInfoW');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputDeviceInfo]
end;
end;
{$ELSE}
function GetRawInputDeviceInfo; external user32 name 'GetRawInputDeviceInfoW';
{$ENDIF DYNAMIC_LINK}
{$ELSE}

{$IFDEF DYNAMIC_LINK}
var
_GetRawInputDeviceInfo: Pointer;

function GetRawInputDeviceInfo;
begin
GetProcedureAddress(_GetRawInputDeviceInfo, user32, 'GetRawInputDeviceInfoA');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputDeviceInfo]
end;
end;
{$ELSE}
function GetRawInputDeviceInfo; external user32 name 'GetRawInputDeviceInfoA';
{$ENDIF DYNAMIC_LINK}
{$ENDIF}

{$IFDEF DYNAMIC_LINK}
var
_GetRawInputBuffer: Pointer;

function GetRawInputBuffer;
begin
GetProcedureAddress(_GetRawInputBuffer, user32, 'GetRawInputBuffer');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputBuffer]
end;
end;
{$ELSE}
function GetRawInputBuffer; external user32 name 'GetRawInputBuffer';
{$ENDIF DYNAMIC_LINK}

function RIDEV_EXMODE(mode: DWORD): DWORD;
begin
Result := mode and RIDEV_EXMODEMASK;
end;


{$IFDEF DYNAMIC_LINK}
var
_RegisterRawInputDevices: Pointer;

function RegisterRawInputDevices;
begin
GetProcedureAddress(_RegisterRawInputDevices, user32, 'RegisterRawInputDevices');
asm
mov esp, ebp
pop ebp
jmp [_RegisterRawInputDevices]
end;
end;
{$ELSE}
function RegisterRawInputDevices; external user32 name 'RegisterRawInputDevices';
{$ENDIF DYNAMIC_LINK}

{$IFDEF DYNAMIC_LINK}
var
_GetRegisteredRawInputDevices: Pointer;

function GetRegisteredRawInputDevices;
begin
GetProcedureAddress(_GetRegisteredRawInputDevices, user32, 'GetRegisteredRawInputDevices');
asm
mov esp, ebp
pop ebp
jmp [_GetRegisteredRawInputDevices]
end;
end;
{$ELSE}
function GetRegisteredRawInputDevices; external user32 name 'GetRegisteredRawInputDevices';
{$ENDIF DYNAMIC_LINK}

{$IFDEF DYNAMIC_LINK}
var
_GetRawInputDeviceList: Pointer;

function GetRawInputDeviceList;
begin
GetProcedureAddress(_GetRawInputDeviceList, user32, 'GetRawInputDeviceList');
asm
mov esp, ebp
pop ebp
jmp [_GetRawInputDeviceList]
end;
end;
{$ELSE}
function GetRawInputDeviceList; external user32 name 'GetRawInputDeviceList';
{$ENDIF DYNAMIC_LINK}

{$IFDEF DYNAMIC_LINK}
var
_DefRawInputProc: Pointer;

function DefRawInputProc;
begin
GetProcedureAddress(_DefRawInputProc, user32, 'DefRawInputProc');
asm
mov esp, ebp
pop ebp
jmp [_DefRawInputProc]
end;
end;
{$ELSE}
function DefRawInputProc; external user32 name 'DefRawInputProc';
{$ENDIF DYNAMIC_LINK}

end.
[/DELPHI]

Most, if not all, of it came from a group of files found in Win32API.zip, which can be found at the Project JEDI home page, located at http://delphi-jedi.org. These files are worth looking at, they are open source, so there is no cost, and he did an excellent job of putting them together.

One other thing I should have mentioned in the last reply. I had a lot of trouble using GetRawInputData() for some reason, if the variable was defined local to my function, it always failed. I finally moved the variable declaration outside of the function and everything works fine.

Code:
implementation

uses Forms, SysUtils, Dialogs;

var
  G_rDta: RAWINPUT;  // For some reason this does not work as a local variable
I'll post another reply with my test code


Hope this helps!
Good Luck!

Code:
If (ItWorks(ThisAnswer)) then Click('Accept as Answer')
else Submit(YourResults, MoreDetails);
Reply With Quote
  #5  
Old 11-21-2006, 06:24 AM
DpM DpM is offline
Junior Member
 
Join Date: Apr 2006
Posts: 8
Default RE: Using an XP MCE IR remote control in Delphi Applications.

Well - it's been a really long time since I decided to look into this. I originally wanted to be able to handle all the buttons on the Microsoft / OEM IR remote controls that come with Windows Media Centre Edition - however, I then came to the conclusion that the WM_APPCOMMAND and WM_KEYDOWN types of buttons were enough for my needs - so my investigations into the WM_INPUT side of things died a bit of a death.

Now, though, I'm coming to the conclusion that my application really needs the "i" button - or "Details" at the very least - so I resurrected my investigations. After going back around what DavidM had done for his Powerpoint keyboard and looking at the JEDI stuff - I came up with the following - very concise code that at least recognises that a WM_INPUT button has been pressed on the MCE remote control... and shows each button has a unique identifier - I'm sure there are constants assigned somewhere and some formula for matching up properly - but, for the time being, the following is where I'm at...

[delphi]unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, JWAWinUser;

type
TForm1 = class (TForm)
procedure FormCreate (Sender : TObject);
private
public
procedure HandleWM_INPUTMessage (var Msg : TMessage); message WM_INPUT;
end;

var
Form1 : TForm1;
RawInputDevices : array[0..1] of RAWINPUTDEVICE;
myRawInput : RAWINPUT;

implementation

{$R *.dfm}


procedure TForm1.FormCreate ( Sender : TObject
);
begin
{ Usage page and collection usage ID... }
RawInputDevices[0].usUsagePage := $FFBC;
RawInputDevices[0].usUsage := $88;
RawInputDevices[0].dwFlags := 0;

{ Usage page and collection usage ID... }
RawInputDevices[1].usUsagePage := $C;
RawInputDevices[1].usUsage := $1;
RawInputDevices[1].dwFlags := 0;

{ Register for raw input... }
if (not RegisterRawInputDevices (@RawInputDevices,2,SizeOf (RawInputDevices[0]))) then
begin
ShowMessage ('Failed to register RawInputDevices...');
end;
end;


procedure TForm1.HandleWM_INPUTMessage ( var Msg : TMessage
);
var
dwSize : Cardinal;
Loop : Integer;
ID : String;
begin
dwsize := SizeOf (myRawInput);

GetRawInputData (Msg.lParam,RID_INPUT,@myRawInput,dwSize,SizeOf (RAWINPUTHEADER));

if (myRawInput.header.dwType = RIM_TYPEHID) then
begin
{ Collate a unique ID for the remote button - they are constant... }
ID := '';
for Loop := 0 to myRawInput.hid.dwSizeHid - 1 do
begin
ID := ID + IntToStr (myRawInput.hid.bRawData[Loop]);
end;
{ Display the ID - as that is all we can do at the moment... }
ShowMessage (ID);
end;
end;


end.[/delphi]

So... what's my current problem?

Well - I know DavidM said he'd had some trouble with handles as well, but my issues is that if I put anything on the TForm, a TLabel, a TListBox, TButton or whatever then the WM_INPUT messages are not received and, of course, nothing happens.

I've tried an Application.OnMessage and I've given overriding WndProc a quick go as well - no joy so far. I wonder if anyone (maybe someone with Delphi and a Media Centre remote control - you just need XP Professional, not Media Centre Edition) can help me out here? To get past the final hurdle. Any ideas as to how I can ensure that the WM_INPUT messages are heard even if I have some other stuff on the form?

There is the JEDI Win32API stuff in here as well of course.

So close... yet still nowhere really.

Cheers, DpM
------------------------------
http://www.hmusiccentre.org.uk
Reply With Quote
  #6  
Old 04-26-2007, 06:55 AM
JaRoen JaRoen is offline
Junior Member
 
Join Date: Apr 2007
Posts: 1
Default RE: Using an XP MCE IR remote control in Delphi Applications.

I've had the same problem with the form with components.
By added one rule it seems to work.

[DELPHI]
{ Usage page and collection usage ID... }
RawInputDevices[0].usUsagePage := $FFBC;
RawInputDevices[0].usUsage := $88;
RawInputDevices[0].dwFlags := 0;
//Answer:
RawInputDevices[0].hwndTarget := Handle;

{ Following lines can be removed }
// RawInputDevices[1].usUsagePage := $C;
// RawInputDevices[1].usUsage := $1;
// RawInputDevices[1].dwFlags := 0;


//Little change in this rule too
{ Register for raw input... }
if (not RegisterRawInputDevices (@RawInputDevices,1,SizeOf (RawInputDevices[0]))) then
begin
ShowMessage ('Failed to register RawInputDevices...');
end;
[/DELPHI]


Jeroen
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is On

Forum Jump


All times are GMT. The time now is 04:47 AM.


Powered by vBulletin® Version 3.8.7
Copyright ©2000 - 2014, vBulletin Solutions, Inc.