... dusting cobwebs off driver knowledge ...
I'm assuming you have the process path in the driver, stored as either ANSI or UNICODE available in the driver.
It looks like you've got the right infrastructure set up for your IOCTL. I'd create a new code to make it easy in the driver to tell the difference between the ulong and the string. The code is just a number and simple code for each type is better than stuffing two different types into a single buffer.
Change your DllImport for DeviceIoctl to not be ulong-specific. Make it a plain pointer instead of the ByRef as ULong; the VB equivalent of C# IntPtr. Your ulong VB code then needs to allocate an
unmanaged buffer, call the driver using the pointer to the buffer as the output parameter, then retrieve the ulong value from the pointer. I don't speak VB, so I don't know how to do that, something like C#'s Marshall.AllocHGlobal with a size of 8 and then Marshall.ReadInt64 to get the value.
You'll need to revise your driver code to validate the length of the buffer against the length of a ulong. It's a driver so you should be writing very defensive code in there.
Once that revised code is working, doing the string should be simple. Only difference should be using the new IOCTL code and the size of the buffer, and a new switch case in the driver to fill the buffer with the string value. Once it's back in the VB, something like C# Marshall.PtrToStringUni or Marshall.PtrToStringAnsi.
update: I just noticed that in your OnProcess function in the driver, you do
ProcPath = CreateInfo->ImageFileName;
That is a mistake. You are saving the
pointer to the string, not the actual string. That pointer will not be valid once the notification is complete. You need to initialize your own UNICODE_STRING and copy the provided string into that buffer. Then use your UNICODE_STRING as the source when processing your IOCTL.