First, understand that all communication on a USB bus is always initiated by the host (the computer).
The most basic useful data transaction is a control transfer, which carries two parts: a setup packet and a data packet. Basically, the setup packet says how many bytes are in the data packet, and which way the data packet goes. We use control transfers for data going from the computer to the Trinket.
To get data going from the Trinket to the computer, we open an interrupt-in endpoint to send it. The computer will automatically ask "is there anything in the endpoint" once every 2 ms, and if Trinket answers "yes", then the transfer begins.
The middle-man software I wrote (named TrinketFakeUsbSerialHostSW) will communicate with the Trinket using LibUsbDotNet, which is a libusb-win32 wrapper for C#. The data is relayed to a fake serial port created by com0com.