In the end this turned out to be a flow control issue. Some applications must turn this on without being set.
This forum post from 6 years ago pointed me in the right direction. https://community.nxp.com/message/72826#comment-72826
Basically keep start_transactions = TRUE; and comment out
case USB_APP_CDC_DTE_ACTIVATED:
if(start_app == TRUE)
{
start_transactions = TRUE;
}
break;
case USB_APP_CDC_DTE_DEACTIVATED:
if(start_app == TRUE)
{
start_transactions = FALSE;
}
break;
from the function USB_App_Class_Callback in virtual_com.c