Marshalling D3D11Device and D3D11DeviceContext objects - c++

This article says:
The ID3D11DeviceContext methods (except for those that exist on
ID3D11DeviceChild) are not free-threaded, that is, they require single
threading. Only one thread may safely be calling any of its methods
(Draw, Copy, Map, etc.) at a time.
I was wondering if I can make COM do the ID3D11DeviceContext synchronization for me.
Let's say I do this (in simplified code):
CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
// Create D3D11Device
CComPtr<ID3D11DeviceContext> pD3D11DeviceContext;
pD3D11Device->GetImmediateContext(&pD3D11DeviceContext);
Then I either marshall it:
IStream* pStreamD3D11DeviceContext = NULL;
CComPtr<IUnknown> pUnknownD3D11DeviceContext;
pD3D11DeviceContext->QueryInterface(IID_PPV_ARGS(&pUnknownD3D11DeviceContext));
::CoMarshalInterThreadInterfaceInStream( __uuidof(ID3D11DeviceContext), pUnknownD3D11DeviceContext, &pStreamD3D11DeviceContext );
Or us a GIT table:
CComPtr<IGlobalInterfaceTable> pIGlobalInterfaceTable;
::CoCreateInstance( CLSID_StdGlobalInterfaceTable, NULL, CLSCTX_INPROC_SERVER, IID_IGlobalInterfaceTable, (void **)&pIGlobalInterfaceTable );
CComPtr<IUnknown> pUnknownD3D11DeviceContext;
DWORD dwCookieD3D11DeviceContext = 0;
pD3D11DeviceContext->QueryInterface(IID_PPV_ARGS(&pUnknownD3D11DeviceContext));
pIGlobalInterfaceTable->RegisterInterfaceInGlobal( pUnknownD3D11DeviceContext, __uuidof(ID3D11DeviceContext), &dwCookieD3D11DeviceContext );
Unfortunately this doesn't seem to work.
CoMarshalInterThreadInterfaceInStream returns REGDB_E_IIDNOTREG (0x80040155, Interface not registered) and pStreamD3D11DeviceContext remains NULL.
GIT method goes one step further. I get the cookie, but when I try to use it on another MTA thread, the GetInterfaceFromGlobal returns E_INVALIDARG.
CComPtr<ID3D11DeviceContext> pD3D11DeviceContext;
hresult = pIGlobalInterfaceTable->GetInterfaceFromGlobal( dwCookieD3D11DeviceContext, __uuidof(ID3D11DeviceContext), (void**)&pD3D11DeviceContext );
The params for GetInterfaceFromGlobal seem okay, I tested getting the pointer back on the original thread and it worked.
The D3D11Device and D3D11DeviceContext appear to be unmarshallable. Obviously I have neither proxy DLL nor a typelib for the d3d11.
Am I missing anything?
Thank you.

Related

Should I call Release on a ID3D10Device interface after using CreateTexture2D? why?

I've this code in a directx10 project.
ID3D10Texture2D *depthStencilBuffer;
UINT a = m_device->Release();
if(FAILED(hr = m_device->CreateTexture2D( &descDepth, NULL, &depthStencilBuffer ))) {
DXGI_D3D10_ErrorExit(hr, L"CreateTexture2D");
return hr;
}
a = m_device->Release();
Now if I stop the debugger at the third line and check the value of a it says 2. And when I stop it at the line after the last one it says 3. I can't understand why. Is the CreateTexture2D function adding references to the ID3D10Device interface? And apparently it's not even adding one reference but two of them since Release() decrements one.
My problem is the documentation for ID3D10Device::CreateTexture2D doesnt specify it adds references to the ID3D10Device object. Same goes for ID3D10Device::CreateRenderTargetView for instance. How am I supposed to guess when to call Release?
I don't have the ability to have the DirectX SDK installed atm to test this, but, in general principals, when it comes to COM you should follow the COM rules and trust that other objects follow the rules too.
i.e. you shouldn't know the implementation of Texture2D but you should trust that if it needs add references to device, that it will also remove them when it is done. You shouldn't be attempting to make additional calls to Release().
Your code should read:
ID3D10Texture2D *depthStencilBuffer = NULL;
if(FAILED(hr = m_device->CreateTexture2D( &descDepth, NULL, &depthStencilBuffer ))) {
DXGI_D3D10_ErrorExit(hr, L"CreateTexture2D");
return hr;
}
depthStencilBuffer->Release();
depthStencilBuffer = NULL;
i.e. you should expect that the call returns a texture2d with a reference count of 1, this is all you need to know. When you're done, you should call release only on the depthStencilBuffer and expect it to clean up itself entirely. If during the implementation the stencil buffer needed references to the device, you should trust that it will also call release on those references correctly.

unresolved problems with C++ EDITTEXT and DIRECTSHOW PAUSE()

i have a simple winform that writes to an EDITTEXT , as the program goes on the printing process executing perfectly . but once i click the STOP BUTTON which firstly calls the PAUSE()
function my program gets stuck inside the
SetWindowText(m_hWatermarksEditBox, &m_watermarkLog[0]);
all values are initialized and proper data gets in.
my guess is that i have to declare a METHOD WORKER , like in C#.NET but i dont know how.
STDMETHODIMP CNaveFilter::Pause()
{
ATLTRACE(L"(%0.5d)CNaveFilter::Pause() (this:0x%.8x)\r\n", GetCurrentThreadId(), (DWORD)this);
HRESULT hr = S_OK;
CAutoLock __lock(&m_cs);
hr = CBaseFilter::Pause();
return hr;
}
STDMETHODIMP CNaveFilter::Stop()
{
ATLTRACE(L"(%0.5d)CNaveFilter::Stop() (this:0x%.8x)\r\n", GetCurrentThreadId(), (DWORD)this);
HRESULT hr = S_OK;
CAutoLock __lock(&m_cs);
hr = CBaseFilter::Stop();
ATLASSERT(SUCCEEDED(hr));
return hr;
}
You don't show where you are doing SetWindowText but as you have the custom filter the most likely problem is that with this call you block your streaming/worker thread execution and the involved threads lock dead.
SetWindowText is only safe to be called from your UI thread (well, technically not only it, but definitely not a streaming thread). So if you want to update the control text or send any message to it, you have to do it in a different way, so that your caller thread could keep running.
Typically, you would store some relevant information in member variable (don't forget critical section lock) then PostMessage, receive the message on your window/control and handle it there in the right thread, calling SetWindowText there.
See controlling frame/rate and exposure time through sampleCB. It covers a bit different topic, but useful in terms of sending/posting messages in a DirectShow filter.

Visual C++: InvokeHelper() function

I am deciphering a huge project that uses COM, which I am completely new to. It's quite confusing and I can't figure out how everything interacts. All I see is InvokeHelper(...) where I would expect to see a big amount of code. What is InvokeHelper()? What does it do?
Thank you for any help.
Even though it's a late answer, I'd like to post it here as I have spent a couple of days to figure out how it's working. It may be interesting for someone else.
Below is the path how to get to the real code from InvokeHelper() call:
InvokeHelper() should be called for an object of a class, inherited from CWnd with DISPID specified, where DISPID is something like 0x00000261
The class should have inside a call to a method CreateControl() with a GUID of a COM class
The COM class with the GUID should be COM coclass with at least one IDL interface
The IDL interface should implement a method with the attribute [id(DISPID)]. This is the same DISPID as in item 1
Look for implementation of the interface and find the method with this id attribute
VoilĂ !
Sure, if you don't have a source code of the COM class with the CLSID you cannot take a look inside the method, but at least, you can find its name as follows:
DISPID dispidCommand = 0x1; /// This is the dispid, you're looking for
COleDispatchDriver driver;
BOOL bRes = driver.CreateDispatch(GetClsid());
ASSERT(bRes);
HRESULT hr;
CComPtr<ITypeInfo> pti;
hr = driver.m_lpDispatch->GetTypeInfo(0, GetUserDefaultLCID(), &pti);
ASSERT(SUCCEEDED(hr));
UINT nCount = 0;
CComBSTR bstrName; // Name of the method, which is called via DISPID
hr = pti->GetNames(dispidCommand, &bstrName, 1, &nCount);
ASSERT(SUCCEEDED(hr));
I hope it helps someone.
Take care.

How to share COM objects between 2 processes?

I want Application1.exe to instantiate an instance of its Item class.
I want Application2.exe to call GetPrice() on this object.
I have followed steps 1-7 on the following website:
http://www.codeguru.com/Cpp/COM-Tech/activex/tutorials/article.php/c5567/
This is what I have so far.
Application1's main looks like this:
CoInitialize( NULL );
DWORD dwRegister;
ItemFactory *pFactory = new ItemFactory;
CoRegisterClassObject( CLSID_Item, pFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister );
_getch();
return 0;
Application2's main looks like this:
CoInitialize( NULL );
CoGetClassObject( CLSID_Item, CLSCTX_LOCAL_SERVER, NULL, IID_IItem, (LPVOID *)&pFactory );
My Issue (hopefully my only issue) is that I have no idea how to associate my Item class (or its interface, IItem) with CLSID_Item; this is just some random GUID I defined in another file. I've tried
CoRegisterPSClsid( IID_IItem, CLSID_Item );
After this line, I tried
Item *pItem;
CoCreateInstance( CLSID_Item, NULL, CLSCTX_LOCAL_SERVER, IID_IItem, (LPVOID *)&pItem );
I get an E_NOINTERFACE error.
Should I be creating a factory with CoCreateInstance? Ugh, so confused...
In order to use COM across process or thread boundraries, you must tell COM about your interfaces so it can marshal your function arguments/return values between processes. The easiest way to do this is to use an interface predefined in the system, such as IDispatch, but if you want to use your own, you must either register a proxy/stub DLL, or a type library. If you don't do this, then calls to QueryInterface for your custom interface across COM domains will fail with E_NOINTERFACE, as you are seeing.

How to prevent crashing if com dll isnt registered

From some old c++ code im trying to use a com dll, it works fine when the dll is registered, but it crahses if the dll isnt registered.
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
IGetTestPtr ptest(__uuidof(tester));
"Use method from the dll"
// Uninitialize COM.
CoUninitialize();
Is it anyway to check if the dll has been registered, before calling IGetTestPtr ptest(__uuidof(tester))?
Or what is the correct way to prevent the crash?
Calling CreateInstance on your object will return an HRESULT that can be tested for success:
IGetTestPtr p = null;
HRESULT hRes = p.CreateInstance( __uuidof(tester) );
bool bSuccess = SUCCEEDED(hRes);
This assumes you've created an interface wrapper around your type library using Visual Studio, where COM Smart Pointers are used in the interface (this gives you the CreateInstance method).
If the DLL is registered, there is a record in HKCR/CLSID/{uuidof(tester)} (curly brackets do matter).
Actually, if it's not, then CoCreateInstance will return an error. Check for this error before using the pointer.
If the COM class is not registered then CoCreateInstance will return REGDB_E_CLASSNOTREG. You should check for general success or failure by using the SUCCEEDED() or FAILED() macros. You also need to create the object properly - see MDSN for an introduction to COM. For example:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
IGetTestPtr ptr = NULL;
hr = CoCreateInstance(CLSID_MyObjectNULL, CLSCTX_INPROC_SERVER, IID_IMyInterface, ptr)
if (SUCCEEDED(hr))
{
Do something with your COM object
...
// Don't forget to release your interface pointer or the object will leak
ptr->Release();
}
hr = CoUninitialize();
}
return hr;
If I recall correctly this "#import" styled COM framework throws exceptions. Add a try-catch block around the code. Google tells me the exceptions are of type _com_error.
Unless you twiddle the params on #import to prevent this, all failing COM calls are going to throw exceptions. You're going to have to turn this off (#import "raw" interfaces I think does it - look at the #import docs) or get real familiar with these exceptions.