3D Printed Keyboard for PocketChip

Clicky button goodness

I used this face plate keyboard https://www.thingiverse.com/thing:1998427

I printed the plate with clear PLA and 36% infill. It was too much infill and it got rough around the display, still usable. I’d probably reprint with 25%.

I used the PLA version of the buttons with 25% infill. I also made a small modification to the bridge that connects the escape button to the d-pad per a suggestion on the author’s GitLab account. The bridge connects to the up arrow instead of the left arrow. Not sure that it helped much because the left arrow still doesn’t feel as clicky as the rest.

You have to be super careful pulling the buttons off the printer plate to avoid breaking the bridges between the buttons. I broke two but used the tip of a hot glue gun to melt them back together.

The buttons! What do they do?

For the label, I scanned the face plate and placed all the letters in PhotoShop. It took some trial and error, but I ended up with a font and size that looks ok. I laser printed the label on removable vinyl then cut it out on the Cricut. It fit perfectly.

Cut cut cut

Here’s my edited keyboard button model and my keyboard label files. WordPress hates zip files so tar.gz it is.

300DPI (4.217″ wide)

Connecting 8BitDo Zero to PocketChip

Well this was way more involved than I had expected.

I wanted to hook up a bluetooth gamepad to my PocketChip so I could play PICO-8 games a little more ergonomically. I had a few controllers laying around, but one seemed perfect for the job: the 8BitDo Zero. It’s really small and it fits the “pocket” ethos of the PocketChip. The only downside is that I’ve never actually gotten the damn thing to work correctly on any system I’ve connected it to.

8BitDo Zero

It has a few different modes for different systems. One is a keyboard emulation which just “types” letters when you push the buttons. This is the easiest mode to get working and it works on any device that will accept a bluetooth keyboard. The downside is that you have to remap all the key bindings in the programs you’re trying to control. PICO-8 doesn’t have control profiles or anything that would make this a friendly option.

It also has a gamepad mode. That sounds promising! First thing you have to do is get it connected. Luckily I had just spent a ton of time trying to get my bluetooth speaker connected so I was familiar with bluetoothctl. power on; agent on; scan on; pair; trust; connect.

Out of the gate it looked promising because the d-pad worked like the arrow keys. I was able to navigate SPLORE, but sadly the other buttons didn’t work. Looking at the PICO-8 log, I saw that there were no joysticks discovered. Well why not?

apt install jstest-gtk
Running jstest I was able to see the buttons were actually working but that was on the `/dev/input/js0′ device. Also, the d-pad wasn’t actually getting interpreted as gamepad buttons – instead normal keyboard scan code were being sent. Sigh. After a bunch of reading, it turns out that SDL2 no longer supports the joystick devices, but instead looks at event devices.

There’s a known problem with SDL on Linux where the udev devices aren’t readable by normal users. The fix is to add add your account to the input group. That wasn’t the case on the PocketChip. The chip user was already in the input group.

I ran across some forum posts suggesting that the controller mapping may need to be updated. The “normal” way to do this is to use a tool that is only released in binary form and, sadly, there wasn’t an ARM build for the PocketChip. One user suggested building the controllermap tool from source on the device. Sounded promising, but it also sounded like a lot of typing for that tiny keyboard.

apt install openssh-server
In order to make it easier to futz around with building on the PocketChip I installed ssh server so I could at least work on a real laptop instead of the tiny screen with the tiny keyboard. Then I followed the instructions on the forum post … and it didn’t build. It wouldn’t build because configure wanted some sdl_config tool in the path… and I didn’t have that. Ok…

apt install libsdl-dev
Don’t do this. You don’t need to. I didn’t need to either, but I didn’t know that yet. That solved my problem and I was able to configure the tests and build the controllermap tool. Then I ran it… “SDL was built without joystick support.” Wat.

Well.. I already had the SDL2 sources downloaded so might as well build the whole thing. It took about 20 minutes and when it was done, I had a shiny new SDL2 from source and was able to build controllermap. This time when I ran it I got a useful error: “ERROR: Couldn’t initialize SDL: No available video device.” I guess that’s progress. Set up X forwarding… run again… “There are 0 joysticks available.” Progress?

More searching. I’m in the “3 results” part of Google now but I found a post which lead me to this udev rules file on GitHub. After a little reading about udev I gave it a shot and added this rule.

# 8Bitdo ZERO
SUBSYSTEM=="input", ATTRS{name}=="8Bitdo Zero GamePad", MODE="0666", ENV{ID_INPUT_JOYSTICK}="1"

That actually worked…ish. It worked well enough that the controllermap program at least could find gamepad. Unfortunately I wasn’t able to complete the mapping wizard because the d-pad still wasn’t working. Looking at that rules file, the author had linked to the SDL2 readme and labelled it “Useful reading“. In the last section it introduced a few new commands to try: evtest and udevadm.

Running evtest shows you a list of the event devices along with their friendly names. I picked the 8BitDo and it started logging the buttons that were pushed. Everything made sense except the d-pad which was still emitting keyboard scan codes. So I checked out the other command. sudo udevadm info --query=all --name=input/eventXX, using the event device number from evtest I got this.

P: /devices/platform/soc@01c00000/1c28c00.serial/tty/ttyS1/hci0/hci0:5/0005:05
A0:3232.0002/input/input4/event3
N: input/event3
E: BACKSPACE=guess
E: DEVNAME=/dev/input/event3
E: DEVPATH=/devices/platform/soc@01c00000/1c28c00.serial/tty/ttyS1/hci0/hci0:5
/0005:05A0:3232.0002/input/input4/event3
E: ID_INPUT=1
E: ID_INPUT_JOYSTICK=1
E: ID_INPUT_KEY=1
E: ID_INPUT_KEYBOARD=1
E: MAJOR=13
E: MINOR=67
E: SUBSYSTEM=input
E: USEC_INITIALIZED=2417702041
E: XKBLAYOUT=us
E: XKBMODEL=pc105

Hmm… INPUT_KEYBOARD doesn’t sound right. At this point I was getting frustrated so I hooked up a SteelSeries Nimbus and tried with that… after burning 30 minutes on that hot mess (something was borked with the d-pad), I tried with the Nintendo Pro controller and worked just fine. I was able to run controllermap and generate the mapping, paste it into PICO-8’s sdl_controllers.txt file and for the first time actually see that this is possible.

Looking at the the udevadm output for the Nintendo Pro controller I didn’t see anything about a ID_INPUT_KEYBOARD so I wondered if I could remove that from the 8BitDo with the udev rule. Much reading later…

# 8Bitdo ZERO
SUBSYSTEM=="input", ATTRS{name}=="8Bitdo Zero GamePad", MODE="0666", ENV{ID_INPUT_JOYSTICK}="1", ENV{ID_INPUT_KEY}="0", ENV{ID_INPUT_KEYBOARD}="0"

I was hoping that if I turned off INPUT_KEYBOARD it would keep the d-pad from sending keyboard events. I wrote the rule to /etc/udev/rules.d/99-8bitdo.rules and reloaded the rules udevadm control --reload-rules. Then the rules needed to be triggered again so I disconnected and reconnected the gamepad. This time when I ran evtest I got sane looking events from the d-pad! Next step was to build the controllermap. Moment of truth! controllermap 0 > ~/controls.txt Hey it worked! I pasted the output controller map into PICO-8’s mapping list and booted up SPLORE and it worked! All the buttons “just worked.”

So I’m really happy. This is the first time my little 8BitDo Zero has ever actually worked correctly in all the years I’ve had it. Now I can play my PICO-8 games with a nicer controller and what a difference it makes for some of the platformers that decided that “up arrow” should be jump (mumble mumble)…

If you’re one of the very few people in the world who is trying to hook up a 8BitDo to a PocketChip (or Pi) I hope this helps. Also, hopefully I can save you a step and you can just use my PocketChip build of controllermap from the SDL2 tests.

  1. Add the udev rule above to /etc/udev/rules.d/99-8bitdo.rules
  2. Connect the gamepad with bluetoothctl
  3. Run controllermap 0 > map.txt
  4. Add the result to ~/.lexaloffle/pico-8/sdl_controllers.txt
  5. Run PICO-8

Connecting PocketChip to a Bluetooth Speaker

This ended up being more of a pain than I expected so I figured I’d write it down for the next person who tries.

  1. Set up your package manager sources if you haven’t already. Follow these instructions.
  2. Add the following packages pulseaudio, pulseaudio-utils, pulseaudio-module-bluetooth
    1. sudo apt install pulseaudio pulseaudio-utils pulseaudio-module-bluetooth
  3. Put your speaker into pairing mode.
  4. Run bluetoothctl and run the following in the prompt:
    1. power on
    2. agent on
    3. scan on
  5. Wait until you see your speaker show in the scan list and note its hardware address.
  6. In the bluetoothctl prompt run the following:
    1. pair <hardware address>
      1. Hint: type the first few digits and then tab-completion works.
    2. trust <hardware address>
    3. connect <hardware address>
  7. If everything worked correctly it should say connected: yes. After that your PocketChip should remember the speaker. You may need to run connect again, to reconnect if you turn the speaker off and on, but you won’t need to re-pair.
  8. In the prompt run:
    1. paired-devices
      1. You should see the speaker in the list.
    2. quit
  9. Try playing a sound (you can use PICO-8 or SunVox), you should hear sound from the connected speaker.
  10. If the sound is crazy loud (probably is) use alsamixer to adjust the volume.

Update
I’ve found that if pulse audio isn’t running before attempting to connect to the speaker, the bluetooth connection will fail. I wrote a script that helps make reconnecting easier.

#!/bin/bash
pulseaudio -k # kill existing service
pulseaudio --start
bluetoothctl -- connect <your speaker hardware address>
sleep 8
pacmd set-default-sink 1 # where 1 is the card id
amixer set Master 20%

Good luck and I hope this helped.

PICO-8 Generic Dispatch with Parameters

PICO-8 has this nice helper function called foreach(table, func) which will call the function for each item. I like to use this in my _draw function to dispatch drawing to game objects. The trouble is that most of the time I want to call a method (table-bound function) instead of a function. Normally this required a helper function per-method to invoke.

Consider the following PICO-8 code (it’s just Lua with some sugar).

-- returns a table with table-bound method 'draw'
function dot(x, y)
 return {x=x, y=y,
  draw=function(self,col)
   local c=col or 7
  	pset(self.x,self.y,c)
  end
 }
end

-- returns a function with captured upvalues
function dot2(x, y)
 return function(col)
  local c=col or 7
  pset(x,y,c)
 end
end

function _init()
 drawable={}
 add(drawable, dot(64, 64))
 add(drawable, dot2(64, 68))
end

In this example, I’m showing two different ways to make “drawable” instances. dot returns its instance as a table with a method named draw. dot2 returns a function which draws itself when invoked. I’ll start with the dot2 case.

function invoke(o)
 o()
end

function _draw()
 cls()
 foreach(drawable, invoke)
end

The nice thing about the approach of making drawable a function closure is that you can make a generic invoke helper function which just calls instance. This means you only need a single helper function. The downside is that you can only return one callable behavior this way which isn’t ideal for more complex objects.

function call_draw(o)
 o:draw()
end

function _draw()
 cls()
 foreach(drawable, call_draw)
end

This approach is more flexible and allows for more methods to be defined, but now you need a call_foo helper for each different method to call. Another problem with both approaches is you can’t pass in arguments to the method calls.

It turns out that Lua gives us a ton of flexibility to build our generic dispatch. Let’s start with the problem of dispatching by name.

-- Call Method
function callm(method)
 return function(o)
  o[method](o)
 end
end

function _draw()
 cls()
 foreach(drawable, callm("draw"))
end

callm is a pretty simple helper. The core of it just does a table lookup by name and invokes the result. callm returns a function because foreach expects a function that takes the object being iterated. There are a few problems with this approach. First this crashes if there isn’t a draw method on the instance. Second, the dot.draw method takes an argument and there’s no way to pass one in.

-- Call Method
function callm(method, ...)
 local params={...}
 return function(o)
  if type(o)=="table" then
   local m=o[method]
   if type(m)=="function" then
    m(o,unpack(params))
   end
  end
 end
end

function _draw()
 cls()
 foreach(drawable, callm("draw", 10))
end

This version adds support for varargs that get forwarded to the method when the returned function is called. This allows parameters to be passed to the method. This also adds some type checking to ensure the instance passed in is a table and has a function named method. You can see the foreach call site looks pretty good.

Finally, if you want to continue to use callable objects instead of tables, we can take this same concept and make it work with those too.

-- Call function 'object'
function call(...)
 local params={...}
 return function(o)
  if type(o)=="function" then
   o(unpack(params))
  end
 end
end

function _draw()
 cls()
 foreach(drawable, call(10))
end

Final thoughts

This adds overhead to the calls, and it’s almost certainly more efficient to use for x in all(XS). That said, it’s a neat little piece of code and I think it’s pretty cool that it can be accomplished with Lua.

Try out the code.
function _init()
 game_items={}
 -- add items
end

function _update()
 -- call 'update' on each instance that implements it
 foreach(game_items, callm('update')
end

function _draw()
 -- call 'draw' on each instance that implements it
 foreach(game_items, callm('draw')
end

Pocket C.H.I.P.

After entering the PICO-8 rabbit hole, I found out about the coolest little computer… 5 years too late.

Now _that’s_ pretty dang cool.

I picked one up from this site. When it got here, I charged it up and turned it on. Pretty cool little computer. You can jump right into a shell and it’s Linux. It didn’t take long until I had PICO-8 updated and I was working on my little toy game.

Unfortunately the parent company is defunct and so this little gem is stuck in the past. That’s probably ok… but it would be pretty cool to have access to a full distro, especially because it’s just so hackable. It takes me back to the early 2000s when I picked up my Sharp Zaurus 5500. There’s something especially nerdy about a tiny terminal you can bring with you.

I’m sure I’ll have more to say as I get to know it, but for now I’m using this post to bookmark a few resources.

P5.js GLSL Shaders on Retina Display

I’ve been playing around with GLSL Shaders and was trying out a few P5.js examples on my Mac. I couldn’t figure out why my results weren’t looking like the examples.

The following is the “Hello, world” of shaders. It simply renders a gradient from black to red smoothly across the canvas.

let header =
  'precision highp float;';

// the vertex shader is called for each vertex
let vs =
  header +
  'attribute vec3 aPosition;' +
  'void main() {' +
  '  vec4 positionVec4 = vec4(aPosition, 1.0);' +
  '  positionVec4.xy = positionVec4.xy * 2.0 - 1.0;' + // Correct for GL offset bug.
  '  gl_Position = positionVec4;' +
  '}';

// the fragment shader is called for each pixel
let fs =
  header +
  'uniform vec2 u_resolution;' +
  'void main() {' +
  '  vec2 st = gl_FragCoord.xy/u_resolution.xy;' +
  '  gl_FragColor = vec4(st.x,st.y,0.0,1.0);' + 
  '}';

let myShade;
function setup() {
  createCanvas(100, 100, WEBGL); // (0,0) is center
  //pixelDensity(1);  // <-- Uncomment to fix scaling.

  // create and initialize the shader
  myShade = createShader(vs, fs);
  shader(myShade);
  fill(64);
  stroke(128); 
}

function draw() {
  background(192);
  myShade.setUniform("u_resolution", [width, height]);
  rect(-40, -40, 80, 80);
}

Unfortunately when I run this example on my Mac the gradient stops halfway across the canvas.

That’s not right. It’s not supposed to be completely red until the right edge.

The problem is that by default there’s pixel scaling because of the Retina display which causes the pixel math to be incorrect. The solution is to specify the pixelDensity in the setup function. If the pixel density is set to 1, the shader works as expected.

Set pixelDensity(1) in the script setup to fix the shader scaling problem.

Creating an ETW Provider Step by Step

Event Tracing for Windows is a strongly-typed, high-volume, low-latency tracing framework for Windows. It’s also frustratingly difficult to use. MSDN has a ton of documentation but no simple ‘Hello World’ end-to-end example. Hopefully this post will help you get started writing your own ETW events from your application.

Creating an ETW provider requires the following steps. Each step is described in detail below.

  1. Create the event manifest.
  2. Compile the manifest to produce headers and resources.
  3. Register application as a provider and write events.
  4. Register the event manifest with ETW.

Background

Before diving in, you will need to know a few basics about ETW. First of all ETW is not a very friendly API. It’s fast and extensible but producing and consuming events is challenging. Just getting a program set up to receive events takes almost 100 lines of C code (mostly esoteric flags and parameters). Strangely, producing events is much easier than reading them.

In C# you can use the EventSource and EventListener classes in System.Diagnostics.Tracing but those libraries don’t produce events that are easily read by existing tools like logman or Message Analyzer (which Microsoft deprecated). These libraries aren’t an option from native code so you’re stuck with the C API.

Before you continue, take a look over the following articles to get acquainted with ETW jargon.

Creating the Event Manifest

ETW events are structured – they have a strongly typed schema. This means that you need to plan for what you will be logging before you get started. You could simply define an event with a single string field and use it to log strings (there’s actually a special case for that), but you’d be missing out on one of the best features of ETW.

Define the events

The first thing you need to do is decide what your events should look like. For this tutorial we’ll build a system that logs an event that carries a single integer metric and a string describing the metric.

// simple metric event
struct metric {
    const wchar_t*  metric_name
    int             metric_value;
};

NOTE: this code is not required, this just shows the shape of the data to emit

You can use this event in your code to emit metrics like the number of items processed or the number of milliseconds spent in a call. It’s very simple but it allows you to add arbitrary metrics in new code without recompiling the manifest and you can write a consumer application that can monitor these metrics in real time.

Create the manifest file

Once you know the shape of the data you want to log, you will need to write a manifest. The manifest provides the following information about events.

  • The provider Id – this is unique on the system and is used to identify your provider.
  • The resource file name – this is the absolute path to the location where you will eventually put the binary (DLL or exe) that contains the schema for your events. Yes, you have to include a path to the location where you will put the DLL that contains the compiled version of the manifest in the manifest!
  • The event Id – this Id is unique within the provider
  • The event name and description – these provide human-readable strings that describe the event. These strings can be localized.
  • The event template – this is basically a schema but it can be reused across multiple events. It’s optional if you don’t have any additional data to send.

Or as MSDN puts it:

Metadata fields are combined for each event and defined in an tag that is uniquely associated with an event ID. Event layouts are specified with a tag which describes user-specified context data for each event. The layout can specify data fields such as strings and integers, or other more complex data structures. Template information does not have to be specified for all events; however, if unspecified for a particular event, it will have no user context data.

There are many other things that can be defined in the manifest file but these are the only things we need to know for now.

The manifest file is an XML document that has a pretty complex structure. You can download an XSD and wing it in your favorite editor or you can use ECManGen.exe which provides a questionably (try it, you’ll see) nicer experience for creating and editing manifest files. ECManGen.exe comes with the Windows SDK and on my machine is found here: C:\Program Files (x86)\Windows Kits\8.1\bin\x64\ecmangen.exe. I’m not going to describe how to use this tool – although not beautiful, it’s pretty self-explanatory.

TIP: after creating your provider, create the template first – you will need to reference it when creating the event.

Here’s the finished manifest file. It defines a provider called Hello World Metric Provider with a symbol HELLO_METRIC. The symbol is the name given to the GUID value in the generated headers. The provider has one event called SIMPLE_METRIC with an event Id of 1 and a schema defined by template Simple Metric. The Simple Metric template has a Unicode string field called metric_name and an integer field called metric_value.

Notice the resource and message file names. helloworld.dll will need to be generated and added to the Windows folder so other applications will be able to find the schema.

helloworldmetric.man

<?xml version="1.0" encoding="UTF-16"?>
<instrumentationManifest
    xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd"
    xmlns="http://schemas.microsoft.com/win/2004/08/events"
    xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:trace="http://schemas.microsoft.com/win/2004/08/events/trace">
    <instrumentation>
        <events>
            <provider
                name="Hello World Metric Provider"
                guid="{06A2EA53-FC6C-42E5-9176-18749AB2CA13}"
                symbol="HELLO_METRIC"
                resourceFileName="c:\Windows\helloworld.dll"
                messageFileName="c:\Windows\helloworld.dll">
                <events>
                    <event
                        symbol="SIMPLE_METRIC"
                        value="1"
                        version="1"
                        template="Simple Metric"
                        message="$(string.Hello World Metric Provider.event.1.message)">
                    </event>
                </events>
                <templates>
                    <template tid="Simple Metric">
                        <data
                            name="metric_name"
                            inType="win:UnicodeString"
                            outType="xs:string">
                        </data>
                        <data
                            name="metric_value"
                            inType="win:Int32"
                            outType="xs:int">
                        </data>
                    </template>
                </templates>
            </provider>
        </events>
    </instrumentation>
    <localization>
        <resources culture="en-US">
            <stringTable>
                <string
                    id="Hello World Metric Provider.event.1.message"
                    value="A simple metric">
                </string>
            </stringTable>
        </resources>
    </localization>
</instrumentationManifest>

Note: the file encoding must match the encoding specified in the XML tag.

Compile the Event Manifest

Once you have a manifest defined you need to compile it. This will produce a header file that has the provider GUID and event descriptors defined as well as a resource that needs to be included in a binary which will be registered with ETW as the source for schemas for schemas for events from your provider. You can also produce C# files that can log your events. The generated code is… functional (read: it’s gross).

To compile the manifest, you need the mc.exe Message Compiler tool. Message compiler is installed with Visual Studio and is found here on my machine: C:\Program Files (x86)\Windows Kits\8.1\bin\x64\mc.exe. Specify the -um parameter which will generate macros for logging your events. This is optional, but without it, you’ll have to manage registering the provider and logging events yourself.

mc.exe -um helloworldmetric.man

This will compile the manifest and generate the resources and headers that you need to include when generating the helloworld.dll.

  • helloworldmetric.h – defines the provider guid and event descriptors. If -um was specified, also contains generated helper macros to register/unregister the provider as well as log events.
  • helloworldmetric.rc – a resource script that includes the compiled bin files.
  • helloworldmetricTEMP.BIN – the compiled schema for your events. This is a binary file that the ETW system will use to unpack the events logged by your provider.
  • MSG00001.bin – contains the localized strings from the manifest.

From Visual Studio

You can include these files directly into a project or generate them during the build. The simplest way is to add a pre-build step that runs mc.exe to generate the files. You can specify parameters to mc that let you control where generated files are written. Then you’ll have to manually modify the project file to include the generated files. Just remember that the output of the build must eventually be written to the path specified in the manifest.

In my sample project, I use a batch file to run mc.exe and rc.exe to produce the files above and compile the resource file. In the linker options for the project, specify the .res file generated by rc.exe as an “Additional Dependency”. Once built, you can verify that your executable has the manifest embedded by opening the .dll or .exe in Visual Studio. In the resources you should see a resource name starting with WEVT_.

From command line

If you don’t want to set up a project, you have everything you need at this point to build the helloworld.dll. You need to compile the resource file using Resource Compiler found in the same directory as mc.exe. Finally, you’ll have to link the compiled resource into a DLL using link.exe (in the Visual Studio bin folder).

From a Visual Studio Developer Prompt (which has the PATH set up already)

mc.exe `helloworldmetric.man`
rc.exe `helloworldmetric.rc`
link.exe  /dll /noentry /machine:x64 helloworldmetric.res /OUT:helloworld.dll

Registering the Provider and Writing Events

Now that you have the generated header, you can register your program as a provider. This basically tells ETW that you’ll be writing events as this provider (based on its GUID). You might not have considered this, but because the program gets to pick which providers and events to log, it means that any program can log as any provider. This makes sense for things like .NET were any program can log the .NET events like garbage collections or context switches. It also means that you can write applications to simulate other providers emitting events which can be useful for testing or spoofing events.

Any program can log events as any provider.

You’ll need to include Windows.h, Evnprov.h and the header from the generated manifest in the code you want to log from.

#include 
#include 
#include "helloworldmetric.h"

void do_work(int i)
{
    // actually log an event
    EventWriteSIMPLE_METRIC(L"test event", i);
}

int main(int argc, char** argv)
{
    // register this program with ETW
    EventRegisterHello_World_Metric_Provider();

    for (int i = 0; i < 10; ++i) {
        do_work(i);
    }

    // unregister this program with ETW
    EventUnregisterHello_World_Metric_Provider(); 

    return 0;
}

Register the Manifest

Now that you have some code that writes an ETW event, you can start to listen to the provider. Run Message Analyzer and listen to the provider by specifying the GUID from the manifest (you’ll have to add a Custom Provider). When you run the test program, you’ll see your events but they won’t have a schema associated with them. This is because the manifest has not been registered with ETW.

To register the manifest, you’ll need to use WevtUtil.exe with the im parameter.

wevtutil.exe im helloworldmetric.man

Remember the helloworld.dll? This is when you actually need the file specified in the manifest to exist. The compiled resources themselves don’t actually contain the path so you can update the paths in the manifest before registering the event, the DLL just has to exist when you run wevtutil.exe.

Now if you run Message Analyzer, you’ll find ‘Hello World Metric Provider’ in the list of system providers. If you listen to that provider, and run the test program, you’ll see the events are received but have a schema now. Huzzah!

If you want to modify the manifest, you must unregister the manifest first using the um parameter before reinstalling.

wevtutil.exe um helloworldmetric.man

Wrap up

Getting set up to log and consume manifest events is complicated, but once you’ve done it a few times it’s not too bad. Using ETW allows you to log events that you can dump to files, the Windows Event Log or even consume the traces in real-time from other processes. You can build powerful watchdog/monitor systems this way or use existing tools like Message Analyzer to collect and analyze trace data.

UPDATE: Microsoft has deprecated the Message Analyzer. Their suggestion is to use something like WireShark. I haven’t tried that, but I have found a few extensions for WireShark which claim to process ETW. Your best bet for visualization may be to write your own tooling with KrabsETW or EventTrace in .NET.


Additional Resources