IMFStreamSink::ProcessSample() not being called on a custom media sink - c++

I am trying to implement a custom Windows Media Foundation media sink for use in a UWP application that will use it via MediaCapture::PrepareLowLagRecordToCustomSinkAsync().
However, I am currently running into an issue where the ProcessSample() function of my IMFStreamSink is never called, even though I queue the event in my IMFMediaSink::OnClockStart().
I have followed the documentation here ( https://learn.microsoft.com/en-us/windows/win32/medfound/media-sinks ) and also taken a look at the "Simple Communication" sample.
Here is the output that I am getting, I have logged every function call and the HRESULT of every call that my sinks execute:
MyCaptureMediaSink::SetPresentationClock
OK: Clock->AddClockStateSink(this)
MyCaptureStreamSink::BeginGetEvent
OK: MediaEventQueue->BeginGetEvent(pCallback, punkState)
MyCaptureMediaSink::OnClockStart
MyCaptureStreamSink::QueueEvent
OK: MediaEventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue)
OK: Sink->QueueEvent(MEStreamSinkStarted, GUID_NULL, S_OK, nullptr)
MyCaptureStreamSink::QueueEvent
OK: MediaEventQueue->QueueEventParamVar(met, guidExtendedType, hrStatus, pvValue)
OK: Sink->QueueEvent(MEStreamSinkRequestSample, GUID_NULL, S_OK, nullptr)
What I think I should be seeing after the last line is a call to MyCaptureStreamSink::EndGetEvent() due to MEStreamSinkStarted being received, followed by another BeginGetEvent() and EndGetEvent() pair and a call ProcessSample() because the next event would be the MEStreamSinkRequestSample event.
Am I missing some functions that I still need to call in order to get a call to these functions by the MediaCapture system?

I have found the solution to my problem:
I re-implemented my stream sink without using winrt::implements, which seems to work as intended. What I assume (I'm still not 100% sure, though) is happening is that since IMFStreamSink derives from IMFMediaGenerator, the winrt::implemnets does not generate the QueryInterface properly for the base type. In my own implementation, I need to explicitly handle it.
This is the old class declaration, which doesn't work:
class MyStreamSink : public winrt::implements<MyStreamSink, IMFStreamSink>
{
// ...
};
and this is the class that works:
class MyStreamSink: public IMFStreamSink
{
virtual HRESULT QueryInterface(const IID& riid, void** ppvObject) override
{
if (riid == __uuidof(IMFMediaEventGenerator))
{
*ppvObject = static_cast<IMFMediaEventGenerator*>(this);
}
//...
}
}

An even better approach would be to override winrt::is_guid_of. By this approach you get one shared v-table and not two separate v-tables. For more information: How do I use C++/WinRT to implement a classic COM interface that derives from another classic COM interface
template<>
inline bool winrt::is_guid_of<IMFStreamSink>(guid const& id) noexcept
{
return winrt::is_guid_of<IMFStreamSink, IMFMediaEventGenerator>(id);
}
class MyStreamSink : public winrt::implements<MyStreamSink, IMFStreamSink>
{
//...
};

Related

ActiveX and COM for interprocess communication

I have a closed source program that generates ActiveX events, and I need to modify mine so that these events can be received. So we have two separate executables, and no GUI stuff is used. So far I got to this point, which only works if the event is generated and received by the same process:
[event_source(native)]
class CSource {
public:
__event void MyEvent(int nValue);
};
[event_receiver(native)]
class CReceiver {
public:
void MyHandler(int nValue) { ... }
void hookEvent(CSource* pSource) {
__hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler);
}
void unhookEvent(CSource* pSource) {
__unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler);
}
};
int main() {
CSource source;
CReceiver receiver;
receiver.hookEvent(&source);
__raise source.MyEvent(123);
receiver.unhookEvent(&source);
}
Now, the event_source is in the application I need to interface with. How can I connect the receiver and the source when they are in two separate processes?
Either an explanation or some references will be useful.
You don't want attributed C++ since even though it is still supported, reality is that it has been deprecated since at the very least VS 2008
You need event_receiver(com, not native type since ActiveX events are in question
The items above suggest that you don't use attributes and instead use IDispEventImpl or IDispEventSimpleImpl class to implement receiver of ActiveX control events (the article gives code snippet and references sample project).

Out-of-process COM server migrated to in-process COM server causes callbacks to block

We have an existing network messaging server that implements a custom communications protocol that we are migrating to an in-process COM object. The server is implemented as a free threaded out-of-process COM server. The clients can register with the server (think publish-subscribe) to receive messages.
After migration we noticed several dead locks when calling GUI related functions, e.g. SetWindowPos, RedrawWindow, EnumWindows, etc. After doing some research I found that this was due to callbacks happening on a thread other than the main GUI thread (message pump). The callbacks are custom COM callbacks that derive from IUnknown, so we are not using connection points.
What's interesting is if a create a simple MFC dialog project, everything works. But in our main application it fails. Our main application (20+ years old and doing things now it was never designed for) is an MFC dialog project. In response to a message, the MFC dialog project loads a DLL that, in turn, creates an MFC dialog and registers with the COM server for messages. When the DLL dialog receives a message it calls one of the three example GUI related functions above, and blocks.
This use to work for an out-of-process COM server, so I know there is a way to get the callback to be within the context of the clients main GUI thread but have been unable find the 'magic' code to make it work. I did stumble upon SendMessageCallback but can't get the asynchronous callback function to be called.
The COM callbacks into the client are handled via a thread inside the in-process COM server. The thread is initialized with CoInitialize, which from my research means STA. I've tried changing this to CoInitializeEx and tested with MTA. I've also tried changing the COM server threading model to STA, MTA, Both, and Free. As you can see, I'm really starting to throw darts. here.
Any help would be appreciated.
I did order the Don Box books, Essential COM and Effective COM, but they will not arrive until later this week.
EDIT:
Our main application:
Inside of CApp derived class' InitInstance
AfxOleInit()
Inside of CDialog derived class' OnInitDialog
CoCreateInstance (Messaging COM Object)
Allocate callback pointer. The client callback is an interface that derives from IUnknown. Then a callback class derives from the client callback and implements the AddRef/Release interfaces. When the client creates the callback the client passes a pointer to itself so the callback can call the client.
MessageComObject->Register(callback pointer)
Inside MessageCOMObject:
MessageComObject adds the callback to the GIT and saves the cookie.
MessageComObject starts a thread to 'send' callbacks to the client.
Some point in time later the main application receives a 'message' via the callback pointer. The callback is initiated from within the MessageComObject.
Inside the MessageCOMObject:
MessageComObject gets the callback from the GIT using the cookie
MessageComObject calls a function on the callback interface
Inside the callback interface's derived class:
Calls the client callback function
Inside the CDialog derived class' callback handler:
Calls LoadLibrary on a DLL (what gets loaded is data driven)
Calls exported function from DLL.
Inside DLL's exported function:
Create CWnd object
CoCreateInstance (Messaging COM Object)
Allocate callback pointer (same as above)
MessageCOMObject->Register
Inside MessageCOMObject:
MessageComObject adds the callback to the GIT and saves the cookie.
MessageComObject starts a thread to 'send' callbacks to the client.
Some point in time later the DLL receives a message:
MessageComObject gets the callback from the GIT using the cookie, GIT returns 'Catastrophic Failure'
Code
Declaring the callback in the IDL file:
[
object,
uuid(...),
pointer_default(unique)
]
interface IMessageRouterCallback : IUnknown
{
...
};
[
object,
uuid(...),
pointer_default(unique)
]
interface IMessageRouter : IUnknown
{
...
};
[
uuid(....),
version(1.0),
]
library MessageRouterCOMLib
{
importlib("stdole2.tlb");
[
uuid(....)
]
coclass MessageRouter
{
[default] interface IMessageRouter;
};
};
The in-process COM DLL
class ATL_NO_VTABLE CMessageRouter :
public CComObjectRootEx<CComMultiThreadModel>,
public CComCoClass<CMessageRouter, &CLSID_MessageRouter>,
public IMessageRouter
{
public:
DECLARE_GET_CONTROLLING_UNKNOWN()
BEGIN_COM_MAP(CMessageRouter)
COM_INTERFACE_ENTRY(IMessageRouter)
COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.p)
END_COM_MAP()
CComPtr<IUnknown> m_pUnkMarshaler;
DECLARE_PROTECT_FINAL_CONSTRUCT()
DWORD callbackRegistrationId;
HRESULT FinalConstruct()
{
//- TODO: Add error checking
IUnknown *unknown;
DllGetClassObject(IID_IMessageRouterCallback, IID_IUnknown, (void**)&unknown);
CoRegisterClassObject(IID_IMessageRouterCallback, unknown, CLSCTX_INPROC_SERVER, REGCLS_MULTIPLEUSE, &callbackRegistrationId);
CoRegisterPSClsid(IID_IMessageRouterCallback, IID_IMessageRouterCallback);
return CoCreateFreeThreadedMarshaler(GetControllingUnknown(),
}
void FinalRelease()
{
if (callbackRegistrationId)
CoRevokeClassObject(callbackRegistrationId);
callbackRegistrationId = 0;
if (m_pUnkMarshaler)
m_pUnkMarshaler.Release();
}
}
Where callback is registered:
boost::lock_guard<boost::mutex> guard(callbacksMutex);
//- callback is the raw interface pointer from the client
//- The class Callback contains a pointer to the raw client callback
//- and the global process ID. The raw pointer is AddRef/Release in
//- the Callback class' constructor and destructor.
ptr = Callback::Pointer(new Callback(callback));
DWORD callbackId = 0;
HRESULT result = globalInterfaceTable->RegisterInterfaceInGlobal(callback, IID_IMessageRouterCallback, &callbackId);
if (SUCCEEDED(result))
{
ptr->globalCallbackId = callbackId;
callbackMap[callback] = ptr;
//- NOTE: uses raw pointer as key into map. This key is only
//- ever used during un-register.
//- callbackMap is a std::map of Callback instances indexed by the raw pointer.
}
Callback thread:
CoInitialize(NULL);
while (processCallbackThreadRunning)
{
QueueMessage message = messageQueue.Pop();
if (!processCallbackThreadRunning)
break;
//- Make a copy because callbacks may be added/removed during
//- this call.
CallbackMap callbacks;
{
boost::lock_guard<boost::mutex> guard(callbacksMutex);
callbacks = callbackMap;
}
for (CallbackMap::iterator callback = callbacks.begin(); callback != callbacks.end(); ++callback)
{
try
{
IMessageRouterCallback *mrCallback = NULL;
HRESULT result = globalInterfaceTable->GetInterfaceFromGlobal(callback->second->globalCallbackId,IID_IMessageRouterCallback,(void **) &mrCallback);
if (SUCCEEDED(result))
{
result = mrCallback->MessageHandler((unsigned char*)message.messageBuffer->Data(), message.messageBuffer->Length(), message.metaData.id, message.metaData.fromId, CComBSTR(message.metaData.fromAddress.c_str()));
if (FAILED(result))
{
... log debug
}
}
else
{
... log debug
}
}
catch (...)
{
... log debug
}
}
MessagePool::Push(message.messageBuffer);
}
CoUninitialize();
Client's implementation of the callback:
template <class CALLBACKCLASS>
class CMessageRouterCallback :
public CComBase<IMessageRouterCallback>
{
CMessageRouterCallback( CALLBACKCLASS *pCallbackClass = NULL) :
m_pCallbackClass(pCallbackClass)
{
AddRef(); //- Require by CComBase. This makes it so that this
//- class does not get automatically deleted when
//- Message Router is done with the class.
}
STDMETHODIMP MessageHandler( UCHAR *szBuffer, int nSize, DWORD dwTransCode, DWORD dwSenderID, BSTR bstrFromIP )
{
if ( m_pCallbackClass != NULL )
{
m_pCallbackClass->MessageHandler( szBuffer, nSize, dwTransCode, dwSenderID, bstrFromIP );
}
return S_OK;
}
}
CComBase implements the IUnknown interface:
template < class BASE_INTERFACE, const IID* piid = &__uuidof(BASE_INTERFACE) >
class CComBase :
public BASE_INTERFACE
{
protected:
ULONG m_nRefCount;
public:
CComBase() : m_nRefCount(0) {}
STDMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_IUnknown || riid == *piid )
*ppv=this;
else
return *ppv=0,E_NOINTERFACE;
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) AddRef()
{
return ++m_nRefCount;
}
STDMETHODIMP_(ULONG) Release()
{
if (!--m_nRefCount)
{
delete this;
return 0;
}
return m_nRefCount;
}
virtual ~CComBase() {}
};
The client then uses it:
class Client
{
CMessageRouterCallback<Client> *callback;
Client(IMessageRouter *messageRouter)
{
callback = new CMessageRouterCallback<this>();
messageRouter->Register(..., callback);
}
void MessageHandler(...) { ... }
}
There's something wrong with how those callbacks are being registered. Possible causes may be:
A direct pointer to the callback manager's interface in the GUI thread, so you're providing direct pointers to STA objects to the callback manager too.
Your Callback instance in the code you added seems to be doing exactly this, it can't blindly call the raw pointer's Release when destroyed.
Your server objects are marshaled with CoCreateFreeThreadedMarshaler (not much different).
With the FTM, you must never use raw pointers, and you must always marshal interface pointers you intend to keep and unmarshal interface pointers you previously kept, preferrably using the GIT. And I mean always, if you intend to keep things safe.
I recommend you keep your server objects in MTA (ThreadingModel="Free") or NA (ThreadingModel="Neutral"), make sure you're accessing them somehow in the GUI thread through CoCreateInstance[Ex] or CoGetClassObject and IClassFactory::CreateInstance (or any other object activation API), and let the "magic" happen. This is as transparent as it can get without using the GIT or manually marshaling things between threads.

Using singleton with proxy pattern together and losing both?

I am passing a callback function to a library. What the callback essentially does is receive updates from the dll and send it to GUI to display. The problem is that since the callback is global or static function, it doesn't know about the GUI and who to pass which in my case will be dialog. The approach I have used to accomplish this is to use singleton and a proxy (sort of).
class CDispatcher
{
public:
CDispatcher(void);
~CDispatcher(void);
protected:
static HWND m_hWnd;
public:
static void SetWindow( HWND hWnd );
static void Dispatch(int code, char * msg);
};
Later in the code
BOOL CTestDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// I need to set this before I set callback
CDispatcher::SetWindow( m_hWnd );
//now I can set the callback
LibRegisterCallback( CDispatcher::Dispatch );
return TRUE; // return TRUE unless you set the focus to a control
}
While this works but first I don't know if my CDispatcher class is good module. It doesn't seem like a good singleton neither it looks like a good proxy. Maybe I could pass the handle of the window in constructor which would make it better but I don't if that's even possible since I am never instantiating the singleton. Another thing is I never how to instantiate CDispatcher because again its just a all global members.
Is this a case where proxy design pattern can be applied in a better way (I am guessing in conjuction with singleton)? Maybe another pattern solves this problem more elegantly? Or is my implementation fine?

MSDN CommonFileDialogModes, altering for my purposes

I hope this falls within the realm of this forum:
I want to use the windows shell(?) to allow users to select a number of files before allowing my programme to do a few things to them. For this I found the MSDN sample "CommonFileDialogModes" - http://msdn.microsoft.com/en-us/library/windows/desktop/dd940350%28v=vs.85%29.aspx
In the sample under this class:
class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents
they have this function:
// IFileDialogEvents
IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
// if this button is in the "Add" mode then do this, otherwise return S_OK
IFileOpenDialog *pfod;
HRESULT hr = pfd->QueryInterface(IID_PPV_ARGS(&pfod));
if (SUCCEEDED(hr))
{
IShellItemArray *psia;
hr = pfod->GetSelectedItems(&psia);
if (SUCCEEDED(hr))
{
ReportSelectedItems(pfd, psia);
psia->Release();
}
pfod->Release();
}
return S_FALSE; // S_FALSE keeps the dialog up; return S_OK to allow it to dismiss.
}
which calls:
void ReportSelectedItems(IUnknown *punkSite, IShellItemArray *psia)
{
DWORD cItems;
HRESULT hr = psia->GetCount(&cItems);
for (DWORD i = 0; SUCCEEDED(hr) && (i < cItems); i++)
{
IShellItem *psi;
hr = psia->GetItemAt(i, &psi);
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = GetIDListName(psi, &pszName);
// .. I've cut some of this out for the example
CoTaskMemFree(pszName);
}
psi->Release();
}
}
}
Now I know pszName contains the names of the files selected. So I can add some extra code in to write this to disk. That works fine. But I dont want to write it to disk. I want to pass it back to the original functions that called this. The arguments for ReportSelectedItems can be altered, but IFACEMETHODIMP OnFileOk(IFileDialog *pfd) cannot as it is inherited. Adding a vector& file_names to the argument will stop it compiling.
So how should I deal with this? I could use a global variable for file_names, but everything I am learning about programming is telling me not to. It would be a quick fix, but I worry that would encourage me to be lazy in the future. I find it difficult to read the windows code and I don't really want to delve too much into the details of it. I can't even find what is calling the OnFileOk function, even though I know it is from one of the two base classes.
Do I really need to work at understanding all the library code just to get this one function doing what I'd like? Is there an faster way of going about this?
So to summarize, how would I get information from this inherited function without using a global variable or writing to disk? As I mentined before, I don't have much of a grasp of the code I am working with. And for future reference, how should I deal with this type of situation? I use c++ and would like to avoid c# and c as much as possible.
Thanks as always.
It seems a fairly big omission for Microsoft to have left out any sort of user data associated with the IFileDialog callbacks, but that does seem to be the case.
I'm assuming that simply calling GetSelectedItems() once the dialog returns is something you don't want to do for some reason - because that would obviously be the simplest solution.
From a quick look at the docs one way you may be able to pass data back from the event callback is using the owner window that you pass to IFileDialog::Show() (which is actually IModalWindow::Show()).
In the event handler, you get given the IFileDialog* pointer. From this, you can QI the address of the IOleWindow interface which will give you the dialog's window:
IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
{
CComPtr<IOleWindow> pWindow;
if (SUCCEEDED(pfd->QueryInterface(IID_IOleWindow, reinterpret_cast<void**>(&pWindow))))
{
HWND hwndDlg;
if (SUCCEEDED(pWindow->GetWindow(&hwndDlg)))
{
HWND hwndOwner;
if (hwndOwner = GetWindow(hwndDlg, GW_OWNER))
{
// hwndOwner is the owner window of the dialog
}
}
}
// more code
}
Now assuming that hwndOwner is your own window, you can associate any data you like with it using SetProp()/GetProp() - so you could use this as a mechanism to pass data back from within the callback.
A simple solution was to add member data inside the inherited class and link it from the constructor:
class CFileOpenBasketPickerCallback : public IFileDialogEvents, public IFileDialogControlEvents
{
public:
CFileOpenBasketPickerCallback(vector<wstring>& files) : files_(files)
{
}
// functions
private:
vector<wstring>& files_;
};
When constructing the object
vector<std::wstring> files
CFileOpenBasketPickerCallback foacb(files);
And in IFACEMETHODIMP OnFileOk(IFileDialog *pfd)
ReportSelectedItems(pfd, psia, files_);
ReportSelectedItems is not a member so you can alter the arguments.

How to handle IHttpSecurity::OnSecurityProblem having a IWebBrowser2 object

I can't seem to understand how i give my implementation of the IHttpSecurity::OnSecurityProblem to my IWebBrowser2 object.
I know that i need to implement a class something like this:
class CServiceProvider : public IServiceProvider
{
public:
CServiceProvider();
~CServiceProvider();
// IUnknown
ULONG STDMETHODCALLTYPE AddRef();
ULONG STDMETHODCALLTYPE Release();
STDMETHODIMP QueryInterface(REFIID iid, void ** ppvObject);
//QueryService
STDMETHODIMP QueryService(REFGUID guidService,REFIID riid,void **ppv);
private:
ULONG m_ulRefCnt;
};
And in the QueryService function when it requests the IID_IHttpSecurity i return my implementation of the IHttpSecurity interface.
But my problem is how i set the my service provider implementation on the IWebBrowser2 object and when?
My code is something like this:
IWebBrowser2 *_Browser;
IServiceProvider* pServiceProvider = NULL;
_Browser->QueryInterface(
IID_IServiceProvider,
(void**)&pServiceProvider);
IHttpSecurity* pi;
pServiceProvider->QueryService(IID_IHttpSecurity, &pi);
_Browser->Navigate(url.AllocSysString(),
&flags,
&target_frame_name,
&post_data,
&headers);
The question this works like i'm thinking if yes how i do this then, and if not can you explain how this works and can be setted?
PS: i only whant to implement the IID_IHttpSecurity interface, all other interfaces requested on the QueryService should do the default implementation provided by the system...
Thanks
I already figure out how this is done.
Using MFC we only need to implement CCustomOccManager that implements the COccManager in witch the implementation of CreateSite function returns an implementation of our COleControlSite (example CCustomControlSite). In this class you will need to override at least the QueryService function of IServiceProvider interface and in this implementation supply yours IHttpSecurity implementation (when required by the interface).
In the end the we register all this in the App InitInstance using the MFC function AfxEnableControlContainer.
Code:
// declare our custom control site to serve as the client site
class CCustomControlSite:public COleControlSite
{
public:
// constructor associates this site with the container
CCustomControlSite(COleControlContainer *pCnt):COleControlSite(pCnt){}
protected:
DECLARE_INTERFACE_MAP();
BEGIN_INTERFACE_PART(ServiceProvider, IServiceProvider)
// declare the interface method(s)
STDMETHOD(QueryService) (
/* [in] */ REFGUID guidService,
/* [in] */ REFIID riid,
/* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject);
END_INTERFACE_PART(ServiceProvider)
};
// declare our control container manager
class CCustomOccManager :public COccManager
{
public:
CCustomOccManager(){}
// creates an instance of our custom control site and associates it with the container
COleControlSite* CreateSite(COleControlContainer* pCtrlCont)
{
CCustomControlSite *pSite = new CCustomControlSite(pCtrlCont);
return pSite;
}
};
In the App InitInstance simple call AfxEnableControlContainer on our implementation:
// Create a custom control container manager class so we can overide the client site
CCustomOccManager *pMgr = new CCustomOccManager;
// Set our control containment up but using our control container
// management class instead of MFC's default
AfxEnableControlContainer(pMgr);
If someone has the knowledge on how this is done without using MFC please let me know.
Thanks
Judging by the remarks in the documentation for IServiceProvider, it seems like your IOleClientSite object needs to implement IServiceProvider.