Monday, July 31, 2017

Copying slots in the P/Invoke tool

This tool really needs a name, because typing out "my P/Invoke command-line tool" is going to get dry pretty quick.

Today I added a copyslot instruction that (surprise!) copies the contents of one slot to another.

newslot int oneInt = 3;
newslot int anotherInt = 2;
copyslot anotherInt = oneInt;
readslot anotherInt

That outputs 3. The instruction also has the option of specifying a field ID in a structure:

newslot block someBlock = int 4, byte 5;
newslot byte oneByte = 0;
copyslot oneByte = someBlock field 1;
readslot oneByte

That outputs 5, because field 1 of the block (numbering starting at zero) contains the byte 5. Finally, the instruction can use a raw byte offset:

newslot block someBlock = int 4, byte 5;
newslot byte oneByte = 0;
copyslot oneByte = someBlock offset 4;
readslot oneByte

That again outputs 5, because at byte 4 (zero-based), after four bytes' worth of int, there's the byte.

Unrelatedly, there's now a GUID kind that (no surprise) produces GUID structures.

Sunday, July 30, 2017

More operations on slots for the P/Invoke tool

Yesterday and today, I made a couple more additions to my P/Invoke command-line tool. The call instruction now takes an optional /into switch specifying the name of a slot. If the slot doesn't exist, it is created with the kind specified by the /return switch.

For a few Windows API functions, it's necessary to pass both the address of a field and its length. Allocated slots have the allocsize keyword, but that applies to the buffer pointed to by the slot's data, not the slot's data itself. Now there's also a slotsize keyword that produces the size of a slot's data, supporting the as and in modifiers just like allocsize.

Previously it was only possible to create slots of standard kinds. I introduced the block psuedo-kind that, in the newslot command, creates a custom kind based on a list of fields. This allows structures to be returned.

Saturday, July 29, 2017

FMod - v2.9.2

I discovered today that Abiathar simply crashes when viewing the Level Format tab of the advanced New Project Wizard when there are no extended formats installed. I'm not sure how I managed to miss that when testing v2.9. Apparently nobody else noticed, but this bug made the advanced NPW completely unusable, so I made a one-line change and published it as v2.9.2.

Friday, July 28, 2017

Buffer allocation for the P/Invoke tool

Some Windows functions require a buffer to be allocated before calling the function so that a string (or other chunk of data) of variable length can be returned. To make my P/Invoke command-line tool work with these functions, I introduced an instruction to create a slot of a pointer-typed kind, allocate a buffer of a given length, and fill the slot with the pointer to the buffer. Slots can be allocated with variable size, i.e. with a length provided by an existing slot's data. The length of a slot's buffer can be passed in a call using another new instruction. Since some functions actually care about character length rather than raw byte length, a unit - "bytes" or "chars" can be supplied.

This command (broken across lines for readability) takes advantage of these features to get the computer's name.

newslot int size = 0;
call kernel32.dll!GetComputerNameW (nullptr, slotptr size); 
allocslot lpwstr name: size chars;
call kernel32.dll!GetComputerNameW (slotdata name, slotptr size);
readslot name

Thursday, July 27, 2017

FMod - v2.9.1

A new user was having trouble saving their new Abiathar project. I am still unable to reproduce the issue, and there's no debugging information in that particular error dialog, so I'm stuck when it comes to helping this person. To get some data on what's happening here, I added an error logging procedure to the saving method. It produces the same kind of log as loading failures do.

A few days ago, I also added a little configuration option that controls the background color that appears behind the level and the tileset. This is useful in case people's color settings make one of the 16 EGA colors difficult to see on the Windows default beige (like f.lux does for me for yellow).

I published these changes earlier today as v2.9.1.

Wednesday, July 26, 2017

Further progress on the P/Invoke command-line tool

Continuing on from yesterday, I added more features to my P/Invoke command-line tool that expanded the set of functions it can handle. First I added support for null-terminated strings, both ANSI and Unicode, under the type names lpstr and lpwstr.

Then I went about supporting "out" parameters, those that take a pointer and put something in it. To do that, I introduced the concept of "slots," which can be allocated, passed to a function, and then printed to the screen. Of course, multiple-instruction commands required a way to pass multiple logical lines on the command line, so that required a little parser adjustment to treat a semicolon like a line separator. To implement the definition of slots, I refactored the other data-type select statements into a "kind" system. A kind object provides information on how to display and store values of that type.

Slots can be created with the newslot command (which takes the kind, name, and optionally an initial value):

newslot int quota

Slots can be used in calls. To pass the address of a slot (i.e. its pointer), use the slotptr keyword. To pass its current value, use slotdata.

call kernel32.dll!GetSystemRegistryQuota (nullptr, slotptr quota)

Finally, a slot can be printed with the readslot command:

readslot quota

Chaining all those together with semicolons retrieves and displays the current size of the Registry.

To see why a function is failing, it's useful to get the last Win32 error code. Unfortunately, dynamically generated P/Invoke methods don't seem to have a way of setting the last error for ease of managed access. So I just P/Invoke GetLastError after every call and store it in case the user issues a lasterror to retrieve it.

Tuesday, July 25, 2017

Starting a PInvoke command-line tool

It frustrates me that people continue to misuse rundll32 to try to run arbitrary functions. Unfortunately, there's no convenient alternative - nobody wants to compile a full C/C++ program just to run one function, and using P/Invoke in PowerShell is gnarly. If it's easy to do the wrong thing and hard to do the right thing, people will probably do the wrong thing, so the solution here is to make the right thing easy.

Yesterday I started poking around with .NET reflection and P/Invoke; I managed to get my program invoking an arbitrary parameterless function. Today I built on that. I now have these features working:

  • Numeric parameters are accepted in hex or decimal, from 16 to 64 bits long. The native specifier corresponds to the .NET IntPtr type in that it becomes a 32- or 64-bit field as appropriate for the system.
  • A return value can be accepted if its type is given (default is none). Currently only 32-bit or wider numeric types are supported for returns.
  • The calling convention can be specified: stdcall (default), cdecl, or thiscall.
  • Pointers to structures can be easily passed with the blockptr specifier. blockptr takes a list of parameters just like a function, creates a structure containing them, gets a pointer to that structure, and passes that to the main function. These can be nested.
For example, this command line sets the window text color to a pretty blue and returns whether the function worked:

user32.dll!SetSysColors /return int (int 1, blockptr(int 8), blockptr(int 0xAA4400))