OneNote, DInput-Hooking

posted in Journal #259850
Published February 09, 2006
Advertisement
Microsoft OneNote is becoming one of the most invaluable programs I have ever used in my life. Since I started using it, it dramatically affected the way I work, the way I surf, and, in general, the way I use my computer.

Even though it provides a lot of nice features, what really hit the sweet spot with me was the ability it gives you to write 'anywhere' on a given page. This lends itself naturally to the process of design as I conceive it. I've always preferred working with pencil and paper whenever creativity or even mere thinking was involved. I've never been able to exactly pinpoint what the main difference between pencil-and-paper and Word-Processor was, until I tried OneNote.

Freedom.

Freedom to write something at the top of the page, leave it and quickly scribble something at the top-right corner of the page, get back to what I was writing, then expanding whatever I've scribbled into a box on the same page, dragging an arrow from this box to that - that sort of thing. Typical word-processor software forces you into a limited top-down flow of words, and consequently, thoughts. And I've always hated that. And I really couldn't work around that inherent limitation, no matter how I tried.

Another fine difference is the definition of what a page is. Word-processors view pages as sequential entities - they come after each other, again, top-to-bottom - and as part of one larger entity, "The Document". Which really is not true at all during the design process, as it applies to me.

When designing, the concept of cards (for lack of a better term - I'm not specifically referring to
Class-Responsibility-Collaborator (CRC) Cards
, even though they might be part of the design process) takes over. Each page is a separate entity of its own, with its own title, and it becomes desirable to have all the pages visible at the same time. And if not possible, one should be able to switch between them quickly.

Another thing that Word-processors don't have is hierarchal organization. A project consists of a number of components. Each components may yet consist of additional sub-components. And each of these has, associated with it, a number of so-called cards. And that's not really a software-specific trait. Generalizing, anything consists of a number of component things. You can choose to break it down to its components or not depending on your desired level of abstraction - and the software program had better provide you with the ability to do just that.

Finally, all this isn't very useful if you don't have the ability to properly search these collections of pages or parts of it, in an information-hunting fashion, like a search engine.

OneNote solves all that through what I consider a very elegant design:



First, the "Projects" dropdown you see is part of a tree. A tree consisting of a number of folders, subfolders, and sections. Each section contains a number of pages. The section pages are tabbed to the right of the page, in a collapsible area. When you collapse it, only the page numbers appear, instead of their titles.

A new page is quickly created by clicking the 'New page' icon (numbered 3). You can quickly switch to other pages simply by clicking their tabs.

Whenever you click on a page, you get a writing area similar to that to the top-right. The one in the top-right has the mouse hovering it, which is why it has the additional rectangular bar on-top. You can resize any such area quickly using the arrows (numbered 2 in the picture). You can move the area around by clicking anywhere in the dotted rectangle (numbered 1) and dragging. Areas resize automatically when you type in them and when there's enough space to resize.

That enough makes it a super-cool tool for me - I now have a centralized place for my design documents, technical designs, journal posts, informative posts and notes. And I can save any of them as Word documents.

But I was pleasantly surprised when I discovered that you don't have an explicit saving mechanism. It saves everything transparently. Yes, that's the closest thing to orthogonal persistence I've seen on today's systems. Mind you, I've not tested ending the Windows session abruptly to check how much information I'd lose, but it's quite enough to me that it saves everything if you switch away or shutdown Windows normally.

So, in conclusion, give it a try. Now, let me get into what I actually started writing the entry for, before going off at a tangent and kissing OneNote's boots.

The following are snippets from my notes, written during the design and implementation phases of the DirectInput hooking component. Microsoft OneNote really shined in this part. It allowed me to easily layout the scenarios before-hand, in English, then fill in the details with the needed API calls and how they were to be used.



When developing the DirectInput hooking component - and before it, the Direct3D one - the only 'real' issue was external world communication. I needed such communication in 2 situations:

Hook application determination
It had to know the name of the application to be hooked. When writing the Direct3D hook, I went with the simple solution of using the registry, and it had worked fine. When writing the input hook, I saw no need to change the way this works.

Keyboard state control
After successfully hooking the target application, we need to be able to receive key-press commands and forward them to the target application.

Therefore, IPC.

After digging for a while in the documentation, the most simple IPC methods I found were:
. Pipes
. Mail slots
. Sockets

Named pipes were the most obvious and simple solution. So I laid out the following scenarios in an English-only pass, then filled it with API references and information in the second pass, using the MSDN documentation.



Named pipes - The server side
First, a named pipe is created by the keyboard device using CreateNamedPipe. The pipe handle is stored within the keyboard device for further use. Pipe creation parameters are:
. Name: \\.\pipe\DInput-Hook
. Open Mode: In-bound
. Pipe mode: PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE
. Max instances: 1
. Out buffer size: 0
. In buffer size: 256
. Default timeout: 200
. Security attributes: null

Whenever the hooked application retrieves the keyboard state, we peek into the pipe to see if we've got any data. A peek is done using PeekNamedPipe, as follows:
. Named pipe: Pipe handle
. Buffer: null
. Buffer size: 0
. Bytes read: 0
. Total bytes available: 0
. Bytes left in this message: messageBytes

The call retrieves the number of bytes available in the message in messageBytes.

If we've got some data, we read them via ReadFile:
. Handle: Pipe handle
. Buffer: A 256 byte buffer
. Num bytes to read: messageBytes
. Num bytes read: numBytesRead
. Overlapped: null

The message consists of a string of numbers, each number representing a scan-code.

Named pipes - The client side
The application should loop endlessly, calling WaitNamedPipe until the pipe is created - this signals the target application's launch and creation of a keyboard device.

It then connects using CreateFile:
. File name: \\.\pipe\DInput-Hook
. Access: GENERIC_WRITE
. Share mode: 0
. Security attributes: null
. Disposition: OPEN_EXISTING
. Attributes: FILE_ATTRIBUTE_NORMAL
. Template file: null

It can keep forwarding messages using WriteFile:
. Handle: Pipe handle
. Buffer: The message buffer
. Num bytes to write: Message size
. Num bytes written: numWritten
. Overlapped: null

When it's done, or an error occurs, it can go ahead and close the pipe using CloseHandle.



Using the above scenarios, I quickly laid out how I want my classes (which was really simple given how simple the scenarios are)

NamedPipeServer

Operations
. Create
. Destroy
. IsDataAvailable
. Read

Attributes
. Pipe handle

NamedPipeClient

Operations
. IsServerAvailable
. Connect
. Write
. Disconnect

Attributes
. Pipe handle



DI7

And the implementation of the above design worked almost right away, hassle-free, and life was good. Until Daphyd The Bard contacted me - he was having trouble to hook a (cool) asteroids game he's been working on. Whenever his launcher loaded the hooking DLL, VB6 would fail with a "File Not Found" error. I tested it on my PC with a VB6 application, and it worked. And when I sent the generated EXE and DLL to him, it didn't.

I had Jack "OMG-FIFTY-REPLIES-PER-SECOND" Hoxely test the same sample and it worked on his PC, promptly popping up a message box saying "LOL", which was the expected behavior. And then it hit me. Both Jack and I use Visual C++ 8, so we both have the runtime installed. And guess what C++ runtime VC8 uses by default? Yep, Multi-threaded DLL runtime. Which David didn't have.

So I switched to the static runtime and the DLL loaded successfully on his computer. But it still didn't work with his game.

After loads of experimentation, debugging and hair-pulling, I discovered that his game was written using DI7. And of course I only hooked DI8 applications.

[lol]

So, I decided to support DI7 too. Initially, I thought it was going to be a minimal amount of work - a copy-n-paste job. Hook DirectInputCreate and there you go. Unfortunately, that didn't work. And Oh God, it didn't work in so many ways.

After a bit of code revision and fiddling to make sure it's not something in my code, I decided it wasn't. The application in question wasn't calling DirectInputCreate - simple as that. So I went looking for alternative ways to create the DirectInput object, and found:
1. DirectInputCreateEx
2. CoCreateInstance

I hooked both of them. And it didn't work - the application wasn't calling them either.
So now I was back at square one. How does the application go about creating a DirectInput object without these?

Then it struck me. DirectInputCreate is actually a macro that resolves to either DirectInputCreateA or DirectInputCreateW, depending on whether the code in question is compiled with the UNICODE flag. So I quickly hooked these two too, and ran the target application.

And it still didn't work.

Another time, I revised the code, did a couple of experiments, but still couldn't get it. Then It Finally Hit Me. We hook functions by simply searching for references to their container DLLs in the EXE headers and replacing their entries in the import tables with entries of our own functions in our DLL. There's no way that's going to work when you're not directly calling the functions in questions, because then you wouldn't have them listed in your headers.

Visual Basic 6 does this. It calls into dx7vb.dll and dx8vb.dll for 7 and 8, respectively. A quick look at dx8vb.dll using the pexports utility that comes with MinGW wasn't very enlightening. The only exported function that looked promising was DllGetClassObject. I hooked that but it wasn't being called.

Unfortunately, I don't have much experience when it comes to COM and all the VB-C++ interaction. I have a couple of ideas, and I might try them out later, but for now I'll move on to something else. I'm currently at a dead-end with VB-based applications.

Not so random link

Hilarious comics by our very own 'boolean'

EDIT: Made the image a clicky.
Previous Entry DirectInput hooking
Next Entry Mult-tasking
0 likes 4 comments

Comments

jollyjeffers
[wow] Nice short entry you've got there.

I suppose I'll have to get my hands on OneNote now - it does sound pretty cool. You say you can write anywhere on the page, is that just typed/keyboard entry, or is it hand-drawn text?

Quote:Jack "OMG-FIFTY-REPLIES-PER-SECOND"...
I'm trying to cut down. Honest...

Regarding the VB6/DLL thing... maybe a possible way to look at it is to write up some 'test cases' in VB6. That is, ones that call some simple functions from a DLL that you *know* are being called - so when it comes to searching for and hooking they should definitely be there. Thus, if you can find out how to hook a known call/function, you might be able to hook the (unknown) DX functions...?

Jack
February 09, 2006 03:42 PM
Muhammad Haggag
Quote:I suppose I'll have to get my hands on OneNote now - it does sound pretty cool. You say you can write anywhere on the page, is that just typed/keyboard entry, or is it hand-drawn text?

If you have a tablet or something, you can input handwriting. And you can also record voice, if you have a microphone (Which, I must note, you should).

Quote:I'm trying to cut down. Honest…

You better do that soon enough or I'll lock you up in a rehabilitation center. You know, I was *quite certain* you were going to be the first one to reply. It has to be. It's like you're sitting between the GDNet website frontend and the database, processing incoming requests and responses [lol]

Quote:Regarding the VB6/DLL thing... maybe a possible way to look at it is to write up some 'test cases' in VB6. That is, ones that call some simple functions from a DLL that you *know* are being called - so when it comes to searching for and hooking they should definitely be there. Thus, if you can find out how to hook a known call/function, you might be able to hook the (unknown) DX functions...?

Doesn't sound like a bad plan. But the problem is that I'm sure I'm doing this somewhat wrong - I used a Type Library Viewer on the DLL and it reports dozens of interfaces and objects. I have no idea where it got that information from, but I'll try to see what's going on - that's one of the 'ideas'.
February 09, 2006 04:10 PM
jollyjeffers
Quote:You better do that soon enough or I'll lock you up in a rehabilitation center.
NOooooo!! don't send me away... [sad]

Quote:I was *quite certain* you were going to be the first one to reply.
Well I didn't want to disappoint.

Quote:It's like you're sitting between the GDNet website frontend and the database
Maybe you should connect that idea with your comments on me being a robot... could just be that I'm not actually a real person (anyone who says otherwise is a liar!) and am in fact a sentient AI that exists as part of the internet. I am the internet [evil].

Jack
February 10, 2006 04:36 AM
Snaily
I've been using a tablet PC with OneNote to take notes during lectures since november, but I'm not fully satisfied. I can live with it not being able to recognise my Swedish handwriting (which I knew before getting it), but damn, does it have to _try_? Also, it seems to be locked to some kind of otherworldly note-taking where you should double-space your writing and jump three rows down to start a new paragraph. Not much after-lecture corrections have been made, either - since it's so damn stubborn in it's way of working I can't move my text nor my equations without them screwing up like there's no tomorrow. Maybe it's more for taking textual notes than fourier analysis - I don't know.

Meh. I'm still taking the notes, though - but I'd like something even more freeform.
February 11, 2006 05:56 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement

Latest Entries

Opera 9 w000000t

1279 views

Damn

1168 views

VBE fun, #2

1277 views

VBE fun

1319 views

French, #1

1404 views

Mult-tasking

1164 views

DirectInput hooking

1050 views

MDX overlay sample

1319 views
Advertisement