I see your point, with regards to simply concatenating strings and parsing them with a delimiter. Again, this is where someone coming from C/C++ and other tightly type defined languages would fumble a bit. But it's a perfectly acceptable solution.
While I cannot site a real world example to demonstrate a use where string parsing couldn't be used, it's a lot less code than having to create a loop anytime you want to work with the string list. I think it would be beneficial to support a generic container type internall. Since you already support strings, and is number checks, you could support a simple string container, and treat the data the same.
The reason I find this useful, for example, let's use a game script. Dropping morality for a moment, let's consider a pretty complicated automation script. We want it to reply to private communications (log parsing), and respond accordingly. Some actions through private communications may require the script to delay before processing additional communication. In more complicated languages with lists, I would continue to process the communications, queue them into a list of strings, and when the script isn't busy with a simulated delay, it pops the next command of the queue and processes it.
Now, of course, you COULD do this with string lists, but consider the overwhelming code required to loop, check, concatenate on, strip off the head of the string, and so on. What would be a few lines of code as I described, becomes a number of loops, which each contain a bunch of repeated code.
I digress on this point, because strings CAN be used, but in terms of functionality, using string concatenation to simulate a list is not something most people would consider. I see a great benefit in adding the list support, but it's only by personal opinion. I would like to hear other users opinions on this before either of us could say whether it's a good idea or not.
Now, back on the more important subject of input. I've looked into moufiltr, and there is a function, MouFilter_ServiceCallback, which suggests data could be inserted into the stream. However, this is a callback, that is called internally, I don't think this is the right place to work with.
Having done as much research as I have, I've concluded with all my failures, that DeviceIoControl is the best way to have an EXE talk to a device directly. This means it interacts with mouclass and kbdclass. This can be determined by a quick search in your registry for PointerClass0, which should render a key pointing to mouclass.
Through trial and much error, I can determine that replacing mouclass.sys is possible. However, as you suggested, service packs may wish to update this in the future. The only solution I can provide to this, is once I get the keyboard and mouse class drivers working, I can write a small patch that can easily be incorporated into later versions of DDK source. I can only assume that the Win2003DDK contains the latest code for mouclass. I make this assumption on the fact that mouclass works just fine, unless I start playing with DeviceIoControl and the IO CTL that I have added to mouclass. Otherwise, things work like normal.
I am in full agreement to release the drivers as a separate component, that AutoHotkey can use if present. It's not hard, simply try using IOCTL_MOUSE_INSERT_DATA, and if it fails, it's not supported. At least, that's how I intend to test.
However, I've run into something of a brick wall. Chris, I've added you to my ICQ, I'm waiting to see you pop online. However, I'll cover the current issue now.
Within mouclass.c, there is a function:
Code:
NTSTATUS
MouseClassDeviceControl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
This function is called when DeviceIoControl is called on the driver. The arguments are, obviously a pointer to the device object itself, and an IRP. As far as I understand it, an IRP is an internal structure for handling generic IO to devices. The IRP contains a control code, a status, a value for the bytes written, and the input/output buffers. Most of this is passed in from DeviceIoControl.
I got to this point, because I was searching for IOCTL_MOUSE_INSERT_DATA, but the only definition at the same place as it, was IOCTL_MOUSE_QUERY_ATTRIBUTES. And this I managed to find in a switch statement in this function.
Code:
switch (ioctl = stack->Parameters.DeviceIoControl.IoControlCode) {
case IOCTL_MOUSE_QUERY_ATTRIBUTES:
So my next step, was to look at how default case statements are handled, along with a number of other generic HID based IOCTL codes like IOCTL_HID_GET_DRIVER_CONFIG... They all pass like this:
Code:
... more above...
case IOCTL_HID_GET_INDEXED_STRING:
if (deviceExtension->PnP && (deviceExtension != Globals.GrandMaster)) {
IoSkipCurrentIrpStackLocation (Irp);
status = IoCallDriver (deviceExtension->TopPort, Irp);
break;
}
default:
status = STATUS_INVALID_DEVICE_REQUEST;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
So, according to this, I assumed that if I set status to STATUS_SUCCESS, that instead of failing DeviceIoControl, it would return successful. The IoSkipCurrentIrpStackLocation and IoCallDriver refer to passing the IOCTL code to the next driver, which I believe, if anything, is vendor supplied drivers, maybe some other internal windows driver.
Now, with this default setup, passing IOCTL_MOUSE_INSERT_DATA would render GetLastError returning an error of "Incorrect function.", which, after sending random control codes in, I assumed this is the return of the default handler.
So, I setup my own case for IOCTL_MOUSE_INSERT_DATA as follows:
Code:
case IOCTL_MOUSE_INSERT_DATA:
status = STATUS_SUCCESS;
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
break;
So, the only difference between here, and the default case handler, is that status is STATUS_SUCCESS instead of STATUS_INVALID_DEVICE_REQUEST. However, now when I run the same call with DeviceIoControl, instead of failing, I get an unhandled exception thrown by kernel32.dll, or at least that's as far as the debugger is seeing it thrown from (DeviceIoControl).
So the problem I'm first facing right now, is figuring out how to have my special case for INSERT_DATA, to return successfully without doing anything except handling an empty IRP. This is where I'm struggling at the moment. I've tried 5 or 6 variations of the driver, with no luck so far.
The exception cannot be caught with try/catch either it seems.
Here is the exact error produced:
Unhandled exception at 0x7c578542 in PTBot.exe: 0xC0000005: Access violation writing location 0x00000000.
This suggests that because of STATUS_SUCCESS, when calling IoCompleteRequest, some different logic is being called and it's expecting something it's not getting. I'm thinking it might be the output buffer, which I'm passing in as NULL, as follows:
Code:
if(DeviceIoControl(m_hMouse, IOCTL_MOUSE_INSERT_DATA, (LPVOID)&midInject, sizeof(MOUSE_INPUT_DATA), NULL, 0, NULL, NULL))
bReturn = true;
So the only thing I can do is try to populate the NULL arguments one by one now and see if the error changes... That's my next archaic attempt to find my eureka
Hope to see you on ICQ soon Chris.