If I create a window and pass the HWND to D3D11CreateDeviceAndSwapChain, it works. However, after I release the device, context, swapchain, etc and try to repeat the process using the same HWND, D3D11CreateDeviceAndSwapChain fails with E_ACCESSDENIED. This tells me something must be holding onto the HWND, but what? I release all my global variables in the destructor of the class. Anyone have an idea what the problem is?
~decoder()
{
m_VertexShader->Release();
m_VertexShader = nullptr;
m_PixelShader->Release();
m_PixelShader = nullptr;
m_InputLayout->Release();
m_InputLayout = nullptr;
device->Release();
device = nullptr;
context->Release();
context = nullptr;
swapchain->Release();
swapchain = nullptr;
rendertargetview->Release();
rendertargetview = nullptr;
m_SamplerLinear->Release();
m_SamplerLinear = nullptr;
HRESULT hr = S_OK;
hr = decoder_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_OF_STREAM, NULL);
hr = decoder_transform->ProcessMessage(MFT_MESSAGE_NOTIFY_END_STREAMING, NULL);
hr = decoder_transform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, NULL);
decoder_transform.Release();
color_transform.Release();
hr = MFShutdown();
}
While D3D11CreateDeviceAbdSwapChain does not mention why this is happening in the documentation, it is essentially just a wrapper around creating a D3D11Device and swap chain. The documentation for IDXGIFactory2::CreateSwapChainForHwnd does go into detail on why this is happening.
Because you can associate only one flip presentation model swap chain at a time with an HWND, the Microsoft Direct3D 11 policy of deferring the destruction of objects can cause problems if you attempt to destroy a flip presentation model swap chain and replace it with another swap chain. For more info about this situation, see Deferred Destruction Issues with Flip Presentation Swap Chains.
The documentation regarding Deferred Destruction Issues with Flip Presentation Swap Chains advises calling ID3D11DeviceContext::ClearState followed by ID3D11DeviceContext::Flush.
However, if an application must actually destroy an old swap chain and create a new swap chain, the application must force the destruction of all objects that the application freed. To force the destruction, call ID3D11DeviceContext::ClearState (or otherwise ensure no views are bound to pipeline state), and then call Flush on the immediate context. You must force destruction before you call IDXGIFactory2::CreateSwapChainForHwnd, IDXGIFactory2::CreateSwapChainForCoreWindow, or IDXGIFactory2::CreateSwapChainForComposition again to create a new swap chain.
Related
I'm creating a "mixer" application that controls volumes of audio sessions and endpoints. My question is, does calling Release method on IMMDevice invalidate previously acquired IAudioEndpointVolume reference? In my program it looks something like this:
IMMDevice *device;
IMMDeviceEnumerator *deviceEnumerator;
IMMDeviceCollection *deviceCollection;
IAudioEndpointVolume *audioEndpoint;
... // Initializing device, deviceEnumerator and deviceCollection
// for cycle {
...
deviceCollection->Item(i, &device);
device->Activate(IID_IAudioEndpointVolume, CLSCTX_ALL, NULL, (void**)&audioEndpoint);
device->Release(); // Will this cause UB in the next line?
audioEndpoint->GetMasterVolume(&volume);
...
//}
Should I use Release method like this or only after Releasing audioEndpoint?
I have MainScreen.cpp
void MainScreen::Show(D2DResources* pD2DResources)
{
HRESULT hr = S_OK;
ID2D1Bitmap* pBitmap=pD2DResources->GetpCurrentScreen();
hr = pD2DResources->LoadBitmapFromFile(
pD2DResources->GetpRT(),
pD2DResources->GetpIWICIF(),
L".\\Images\\MainScreen.jpg",
0,
0,
&pBitmap
);
if(SUCCEEDED(hr))pD2DResources->DrawScreen();
}
and at some point the MainScreen::Show() function is called like this
MainScreen->Show(&d2DResources);
However, it seems that as pD2DResources calls DrawScreen() in the MainScreen::Show() function, whatever was stored into &pBitmap didn't save. Actually, I get an unhandled exception and as it happens, pCurrentScreen, which should have received pBitmap's value, is 0x00000000.
What should I do?
As you know, LoadBitmapFromFile function failed
So you should check LoadBitmapFromFile function's arguments
However why did you assign pD2DResources->GetpCurrentScreen() to pBitmap??
Next line, pBitmap will be assigned with LoadBitmapFromFile function again.
Additionally, If MainScreen's Show function called every frames, that is not efficient.
Because you don't have to load bitmap every frames.
And d2d bitmap should be released.
The MSDN documentation for the CCmdTarget::OnFinalRelease method is pretty brief:
Called by the framework when the last OLE reference to or from the
object is released.
I have created a sub-class of CCmdTarget
class CMyEventHandler : public CCmdTarget { ... }
I'm trying to figure out under what conditions the OnFinalRelease method will be called. I have some code that looks something like this:
CMyEventHandler* myEventHandler = new CMyEventHandler();
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionAdvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
// Application continues...events arrive...eventually the event sink is shutdown
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
Using this code, I observe that the OnFinalRelease method is never called. This means I have a memory leak. So I modified the wrap-up code as follows:
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(FALSE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, FALSE, myCookie);
delete myEventHandler;
myEventHandler = NULL;
This section of code is triggered off periodically throughout the day. What I notice now is that, while the destructor for the wrapped up instance of myEventHandler is called as expected, the OnFinalRelease function is getting called now! What's worse, it is being called not on the instance that has been wrapped up, but instead on a newly created instance of CMyEventHandler! Thinking that this might be due to a reference counting issue, I modified my wire-up and wrap-up code:
CMyEventHandler* myEventHandler = new CMyEventHandler();
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(TRUE);
AfxConnectionAdvise(myEventSource, DIID_IMyEventInterface, pUnk, TRUE, myCookie);
pUnk->Release();
// Application continues...events arrive...eventually the event sink is shutdown
LPUNKNOWN pUnk = myEventHandler->GetIDispatch(TRUE);
AfxConnectionUnadvise(myEventSource, DIID_IMyEventInterface, pUnk, TRUE, myCookie);
pUnk->Release();
delete myEventHandler;
myEventHandler = NULL;
I let this run all day and now observe that OnFinalRelease is never called. The destructor for the wrapped up instance is called as I would expect, but I'm left feeling uneasy as I clearly don't understand the circumstances under which OnFinalRelease is called. Is OnFinalRelease called on some delay, or is there a way to force it to fire? What will trigger OnFinalRelease to be called?
If it matters, the event source is a .NET assembly exposing events via COM interop.
With COM you should always use the CoCreateInstance() AddRef() and Release() paradigm to manage lifetime of your objects, and let COM do the destruction of your objects based on reference counts. Avoid new and delete because using them breaks this paradigm and causes interesting side effects. You probably have a bug in the management of the reference counts.
The way to debug why the reference counts are not being managed correctly is to override CCmdTarget::InternalRelease() copy the source from oleunk.cpp and put some trace output or break points.
DWORD CMyEventHandler::InternalRelease()
{
ASSERT(GetInterfaceMap() != NULL);
if (m_dwRef == 0)
return 0;
LONG lResult = InterlockedDecrement(&m_dwRef);
if (lResult == 0)
{
AFX_MANAGE_STATE(m_pModuleState);
OnFinalRelease();
}
return lResult;
}
There are lots of times when passing IDispatch interfaces that code will bump reference counts and you have to decrement the reference count using Release(). Pay attention to where your code may be passing this interface because there is aconvention in COM that when Interfaces are passed using [in] or [out] where the caller or callee has to release the interface.
When the reference count issue is corrected you shoudl see the objects OnFinalRelease code being called and the object destoryed by hte MFC framework:
For CCmdTarget the destruction should happen as a result of the final
release in the parent class CWnd:
void CWnd::OnFinalRelease()
{
if (m_hWnd != NULL)
DestroyWindow(); // will call PostNcDestroy
else
PostNcDestroy();
}
FYI: Passing interfaces across threads without marshalling the interface pointers is another common reason to get errors in COM.
It doesn't appear that you ever call myEventHandler->Release(). Therefore, the last reference is never released, and OnFinalRelease is never called.
Program works fine (with random crashes) and Memory Validator reports Uninitialized read problem in pD3D = Direct3DCreate9.
What could be the problem ?
init3D.h
class CD3DWindow
{
public:
CD3DWindow();
~CD3DWindow();
LPDIRECT3D9 pD3D;
HRESULT PreInitD3D();
HWND hWnd;
bool killed;
VOID KillD3DWindow();
};
init3D.cpp
CD3DWindow::CD3DWindow()
{
pD3D=NULL;
}
CD3DWindow::~CD3DWindow()
{
if (!killed) KillD3DWindow();
}
HRESULT CD3DWindow::PreInitD3D()
{
pD3D = Direct3DCreate9( D3D_SDK_VERSION ); // Here it reports a problem
if( pD3D == NULL ) return E_FAIL;
// Other not related code
VOID CD3DWindow::KillD3DWindow()
{
if (killed) return;
diwrap::input.UnCreate();
if (hWnd) DestroyWindow(hWnd);
UnregisterClass( "D3D Window", wc.hInstance );
killed = true;
}
Inside main app .h
CD3DWindow *d3dWin;
Inside main app .cpp
d3dWin = new CD3DWindow;
d3dWin->PreInitD3D();
And here is the error report:
Error: UNINITIALIZED READ: reading register ebx
#0:00:02.969 in thread 4092
0x7c912a1f <ntdll.dll+0x12a1f> ntdll.dll!RtlUnicodeToMultiByteN
0x7e42d4c4 <USER32.dll+0x1d4c4> USER32.dll!WCSToMBEx
0x7e428b79 <USER32.dll+0x18b79> USER32.dll!EnumDisplayDevicesA
0x4fdfc8c7 <d3d9.dll+0x2c8c7> d3d9.dll!DebugSetLevel
0x4fdfa701 <d3d9.dll+0x2a701> d3d9.dll!D3DPERF_GetStatus
0x4fdfafad <d3d9.dll+0x2afad> d3d9.dll!Direct3DCreate9
0x00644c59 <Temp.exe+0x244c59> Temp.exe!CD3DWindow::PreInitD3D
c:\_work\Temp\initd3d.cpp:32
Edit: Your stack trace is very, very strange- inside the USER32.dll? That's part of Windows.
What I might suggest is that you're linking the multi-byte Direct3D against the Unicode D3D libraries, or something like that. You shouldn't be able to cause Windows functions to trigger an error.
Your Memory Validator application is reporting false positives to you. I would ignore this error and move on.
There is no copy constructor in your class CD3DWindow. This might not be the cause, but it is the very first thing that comes to mind.
If, by any chance, anywhere in your code a temporary copy is made of a CD3DWindow instance, the destructor of that copy will destroy the window handle. Afterwards, your original will try to use that same, now invalid, handle.
The same holds for the assignment operator.
This might even work, if the memory is not overwritten yet, for some time. Then suddenly, the memory is reused and your code crashes.
So start by adding this to your class:
private:
CD3DWindow(const CD3DWindow&); // left unimplemented intentionally
CD3DWindow& operator=(const CD3DWindow&); // left unimplemented intentionally
If the compiler complains, check the code it refers to.
Update: Of course, this problem might apply to all your other classes. Please read up on the "Rule of Three".
I am writing a Win32 C++ DLL that uses the COM to query WMI. How can I programmatically determine if COM has already been initialized? Thanks.
Mark Ransom is right
the straightforward, clean and simple solution is to require COM initialization by the caller.
Ugly hack
You can try your first call - likely CoCreateInstance, and if it returns CO_E_NOTINITIALIZED, run CoInitialize yourself (and don't forget to uninit in that case)
However, it is still problematic to "inject" a CoInitialize into a caller thread from a DLL. So there's a
Clean Solution
Let the DLL create a worker thread (which means the DLL needs Init and Teardown calls), CoInitializeEx in this thread yourself, and move all the COM calls to that separate thread.
The easiest way is not to bother, just make it a requirement of anybody using your DLL that they initialize COM first. Otherwise you run the risk of messing up their own initialization if they perform it after yours.
On the other hand if your flags to CoInitializeEx match those of the application, you should be fine. From the CoInitializeEx documentation:
Multiple calls to CoInitializeEx by
the same thread are allowed as long as
they pass the same concurrency flag,
but subsequent valid calls return
S_FALSE.
It follows #peterchen clean solution as I coded it for a thread-safe COM logger component that I wanted to wrap:
IComLoggerPtr _logger;
_bstr_t _name;
HANDLE _thread;
HANDLE _completed;
Logger::Logger(_bstr_t name)
{
_name = name;
_completed = ::CreateEvent(NULL, false, false, NULL);
if (_completed == NULL)
::AtlThrowLastWin32();
// Launch the thread for COM interation
DWORD threadId;
_thread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)(this->threadRun),
(LPVOID)this, 0, &threadId);
// Wait object initialization
HRESULT hr = ::WaitForSingleObject(_completed, INFINITE);
if (FAILED(hr))
AtlThrow(hr);
}
Logger::~Logger()
{
::SetEvent(_completed);
CloseHandle(_thread);
CloseHandle(_completed);
}
DWORD WINAPI Logger::threadRun(LPVOID opaque)
{
Logger *obj = (Logger *)opaque;
// Init Free-Threaded COM subsystem
HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr))
::AtlThrow(hr);
hr = obj->_logger.CreateInstance(__uuidof(ComLogger));
if (FAILED(hr))
::AtlThrow(hr);
obj->_logger->Init(obj->_name);
// Initialization completed
bool success = ::SetEvent(obj->_completed);
if (!success)
::AtlThrowLastWin32();
// Wait release event
hr = ::WaitForSingleObject(obj->_completed, INFINITE);
if (FAILED(hr))
AtlThrow(hr);
obj->_logger.Release();
// Release COM subsystem
::CoUninitialize();
}
HRESULT Logger::Log(_bstr_t description)
{
return _logger->Log(description);
}
CoInitializeEx\CoUninitialize should only be called by threads (not by Dll-calls).
BTW ,you should not Use CoInitializeEx\CoUninitialize in DllMain !