← Blog

Why loading an extension needs the DevTools pipe

Perch 1 min read

Perch drives its own Chrome over the DevTools protocol. It could do that over a localhost port. It does not, and the reason is that --remote-debugging-port opens a listener any local process can drive — including anything else running as you.

So it uses --remote-debugging-pipe. That is also the only option available: Extensions.loadUnpacked, the method that loads Perch's extension, is pipe-only.

The problem Windows creates#

On POSIX the pipe is simply file descriptors 3 and 4. Windows has no file descriptor inheritance. The C runtime rebuilds its descriptor table at startup from an undocumented blob in STARTUPINFO.lpReserved2.

Which means .NET's Process.Start cannot launch Chrome this way at all. It has to be CreateProcess through P/Invoke with that blob built by hand: a count, then one flag byte per descriptor, then one handle per descriptor.

This is a known technique rather than a new one — Node's child_process does exactly this for stdio beyond index 2, which is how Puppeteer drives Chrome over a pipe on Windows. It is also the single most likely thing in the port to go wrong, which is why it was built and proven before anything else.

One detail that cost an afternoon#

IGraphicsCaptureItemInterop::CreateForWindow wants the WinRT interface IID:

79C3F95B-31F7-4EC2-A464-632EF5D30760

Not typeof(GraphicsCaptureItem).GUID, which is the projected class GUID and a different value. Passing the wrong one returns E_NOINTERFACE, which surfaces in C# as an InvalidCastException with nothing in it pointing at the cause.