The right way to do this, I believe, is to create a source and sink filter as part of your application and connect the plugin between them. When you call the source filter from your application, it delivers the data to the plugin (a DirectShow transform filter) and the processed data arrives at your sink filter when done, from where it goes back to your application.
To demonstrate the use of filters within the application to process data like this, I have created an AppFilter sample that you can download here in source form. This is an MFC application that receives the streamed data at a custom sink filter created within the application.
Construct the two filters as C++ objects by calling new (rather than registering them with class ids and calling CoCreateInstance). That way, you can call public methods on the objects without needing to define a custom COM interface to get access to them. They still need to be COM objects of course (in particular, don't forget to initialise the refcount to 1 after creating them, and delete them by Release rather than delete).
The source filter is derived from CBaseFilter and has a single output pin derived from CBaseOutputPin. When the app wants to deliver data, you call GetDeliveryBuffer, fill with data, and then Deliver (both these methods are on the output pin.
The sink filter is also derived from CBaseFilter and has a single input pin based on CBaseInputPin. The data arrives at the Receive method of the input pin from where you can either call out to your app or wait for your app to collect it.
Note that the delivery of data from the plug-in to your sink filter is not necessarily tied to the delivery of data from your source into the plug-in. Most transforms (eg most of those written to the Sonic Foundry template) will do the transform synchronously: during your source filter's Deliver call, the data will be processed and delivered to the sink filter, but there is no guarantee that this is the case (or that, eg, the same size and count of buffers are used). A simple approach used by some hosts is to only support 'synchronous' plug-ins: call Deliver with the data, then call the sink filter to pick up the processed data and if it hasn't arrived, complain that the plug-in is not supported. I don't like this idea because it is unnecessarily restrictive on design of transform filters (eg you couldn't do mpeg decode like that). A better idea is to have an async model where the sink filter calls back to your app when the data arrives.
Other things to worry about are: