 1529605337
			
		
	
	
		1529605337
		
	
	
	
	
		
			
			When the command "guest-fsfreeze-freeze" is executed it causes the VSS service to log the error below in the Event Viewer. This error is caused by an issue in the function "CommitSnapshots" in provider.cpp: * When VSS_TIMEOUT_MSEC expires the funtion returns E_ABORT. This causes the error #12293. |event id| error | * 12293 : Volume Shadow Copy Service error: Error calling a routine on a Shadow Copy Provider {00000000-0000-0000-0000-000000000000}. Routine details CommitSnapshots [hr = 0x80004004, Operation aborted. Signed-off-by: Sameeh Jubran <sameeh@daynix.com> Signed-off-by: Michael Roth <mdroth@linux.vnet.ibm.com>
		
			
				
	
	
		
			534 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			534 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * QEMU Guest Agent win32 VSS Provider implementations
 | |
|  *
 | |
|  * Copyright Hitachi Data Systems Corp. 2013
 | |
|  *
 | |
|  * Authors:
 | |
|  *  Tomoki Sekiyama   <tomoki.sekiyama@hds.com>
 | |
|  *
 | |
|  * This work is licensed under the terms of the GNU GPL, version 2 or later.
 | |
|  * See the COPYING file in the top-level directory.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "vss-common.h"
 | |
| #include <inc/win2003/vscoordint.h>
 | |
| #include <inc/win2003/vsprov.h>
 | |
| 
 | |
| #define VSS_TIMEOUT_MSEC (60*1000)
 | |
| 
 | |
| static long g_nComObjsInUse;
 | |
| HINSTANCE g_hinstDll;
 | |
| 
 | |
| /* VSS common GUID's */
 | |
| 
 | |
| const CLSID CLSID_VSSCoordinator = { 0xE579AB5F, 0x1CC4, 0x44b4,
 | |
|     {0xBE, 0xD9, 0xDE, 0x09, 0x91, 0xFF, 0x06, 0x23} };
 | |
| const IID IID_IVssAdmin = { 0x77ED5996, 0x2F63, 0x11d3,
 | |
|     {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
 | |
| 
 | |
| const IID IID_IVssHardwareSnapshotProvider = { 0x9593A157, 0x44E9, 0x4344,
 | |
|     {0xBB, 0xEB, 0x44, 0xFB, 0xF9, 0xB0, 0x6B, 0x10} };
 | |
| const IID IID_IVssSoftwareSnapshotProvider = { 0x609e123e, 0x2c5a, 0x44d3,
 | |
|     {0x8f, 0x01, 0x0b, 0x1d, 0x9a, 0x47, 0xd1, 0xff} };
 | |
| const IID IID_IVssProviderCreateSnapshotSet = { 0x5F894E5B, 0x1E39, 0x4778,
 | |
|     {0x8E, 0x23, 0x9A, 0xBA, 0xD9, 0xF0, 0xE0, 0x8C} };
 | |
| const IID IID_IVssProviderNotifications = { 0xE561901F, 0x03A5, 0x4afe,
 | |
|     {0x86, 0xD0, 0x72, 0xBA, 0xEE, 0xCE, 0x70, 0x04} };
 | |
| 
 | |
| const IID IID_IVssEnumObject = { 0xAE1C7110, 0x2F60, 0x11d3,
 | |
|     {0x8A, 0x39, 0x00, 0xC0, 0x4F, 0x72, 0xD8, 0xE3} };
 | |
| 
 | |
| 
 | |
| void LockModule(BOOL lock)
 | |
| {
 | |
|     if (lock) {
 | |
|         InterlockedIncrement(&g_nComObjsInUse);
 | |
|     } else {
 | |
|         InterlockedDecrement(&g_nComObjsInUse);
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Empty enumerator for VssObject */
 | |
| 
 | |
| class CQGAVSSEnumObject : public IVssEnumObject
 | |
| {
 | |
| public:
 | |
|     STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
 | |
|     STDMETHODIMP_(ULONG) AddRef();
 | |
|     STDMETHODIMP_(ULONG) Release();
 | |
| 
 | |
|     /* IVssEnumObject Methods */
 | |
|     STDMETHODIMP Next(
 | |
|         ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched);
 | |
|     STDMETHODIMP Skip(ULONG celt);
 | |
|     STDMETHODIMP Reset(void);
 | |
|     STDMETHODIMP Clone(IVssEnumObject **ppenum);
 | |
| 
 | |
|     /* CQGAVSSEnumObject Methods */
 | |
|     CQGAVSSEnumObject();
 | |
|     ~CQGAVSSEnumObject();
 | |
| 
 | |
| private:
 | |
|     long m_nRefCount;
 | |
| };
 | |
| 
 | |
| CQGAVSSEnumObject::CQGAVSSEnumObject()
 | |
| {
 | |
|     m_nRefCount = 0;
 | |
|     LockModule(TRUE);
 | |
| }
 | |
| 
 | |
| CQGAVSSEnumObject::~CQGAVSSEnumObject()
 | |
| {
 | |
|     LockModule(FALSE);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVSSEnumObject::QueryInterface(REFIID riid, void **ppObj)
 | |
| {
 | |
|     if (riid == IID_IUnknown || riid == IID_IVssEnumObject) {
 | |
|         *ppObj = static_cast<void*>(static_cast<IVssEnumObject*>(this));
 | |
|         AddRef();
 | |
|         return S_OK;
 | |
|     }
 | |
|     *ppObj = NULL;
 | |
|     return E_NOINTERFACE;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CQGAVSSEnumObject::AddRef()
 | |
| {
 | |
|     return InterlockedIncrement(&m_nRefCount);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CQGAVSSEnumObject::Release()
 | |
| {
 | |
|     long nRefCount = InterlockedDecrement(&m_nRefCount);
 | |
|     if (m_nRefCount == 0) {
 | |
|         delete this;
 | |
|     }
 | |
|     return nRefCount;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVSSEnumObject::Next(
 | |
|     ULONG celt, VSS_OBJECT_PROP *rgelt, ULONG *pceltFetched)
 | |
| {
 | |
|     *pceltFetched = 0;
 | |
|     return S_FALSE;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVSSEnumObject::Skip(ULONG celt)
 | |
| {
 | |
|     return S_FALSE;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVSSEnumObject::Reset(void)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVSSEnumObject::Clone(IVssEnumObject **ppenum)
 | |
| {
 | |
|     return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* QGAVssProvider */
 | |
| 
 | |
| class CQGAVssProvider :
 | |
|     public IVssSoftwareSnapshotProvider,
 | |
|     public IVssProviderCreateSnapshotSet,
 | |
|     public IVssProviderNotifications
 | |
| {
 | |
| public:
 | |
|     STDMETHODIMP QueryInterface(REFIID riid, void **ppObj);
 | |
|     STDMETHODIMP_(ULONG) AddRef();
 | |
|     STDMETHODIMP_(ULONG) Release();
 | |
| 
 | |
|     /* IVssSoftwareSnapshotProvider Methods */
 | |
|     STDMETHODIMP SetContext(LONG lContext);
 | |
|     STDMETHODIMP GetSnapshotProperties(
 | |
|         VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp);
 | |
|     STDMETHODIMP Query(
 | |
|         VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
 | |
|         VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum);
 | |
|     STDMETHODIMP DeleteSnapshots(
 | |
|         VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
 | |
|         BOOL bForceDelete, LONG *plDeletedSnapshots,
 | |
|         VSS_ID *pNondeletedSnapshotID);
 | |
|     STDMETHODIMP BeginPrepareSnapshot(
 | |
|         VSS_ID SnapshotSetId, VSS_ID SnapshotId,
 | |
|         VSS_PWSZ pwszVolumeName, LONG lNewContext);
 | |
|     STDMETHODIMP IsVolumeSupported(
 | |
|         VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider);
 | |
|     STDMETHODIMP IsVolumeSnapshotted(
 | |
|         VSS_PWSZ pwszVolumeName, BOOL *pbSnapshotsPresent,
 | |
|         LONG *plSnapshotCompatibility);
 | |
|     STDMETHODIMP SetSnapshotProperty(
 | |
|         VSS_ID SnapshotId, VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId,
 | |
|         VARIANT vProperty);
 | |
|     STDMETHODIMP RevertToSnapshot(VSS_ID SnapshotId);
 | |
|     STDMETHODIMP QueryRevertStatus(VSS_PWSZ pwszVolume, IVssAsync **ppAsync);
 | |
| 
 | |
|     /* IVssProviderCreateSnapshotSet Methods */
 | |
|     STDMETHODIMP EndPrepareSnapshots(VSS_ID SnapshotSetId);
 | |
|     STDMETHODIMP PreCommitSnapshots(VSS_ID SnapshotSetId);
 | |
|     STDMETHODIMP CommitSnapshots(VSS_ID SnapshotSetId);
 | |
|     STDMETHODIMP PostCommitSnapshots(
 | |
|         VSS_ID SnapshotSetId, LONG lSnapshotsCount);
 | |
|     STDMETHODIMP PreFinalCommitSnapshots(VSS_ID SnapshotSetId);
 | |
|     STDMETHODIMP PostFinalCommitSnapshots(VSS_ID SnapshotSetId);
 | |
|     STDMETHODIMP AbortSnapshots(VSS_ID SnapshotSetId);
 | |
| 
 | |
|     /* IVssProviderNotifications Methods */
 | |
|     STDMETHODIMP OnLoad(IUnknown *pCallback);
 | |
|     STDMETHODIMP OnUnload(BOOL bForceUnload);
 | |
| 
 | |
|     /* CQGAVssProvider Methods */
 | |
|     CQGAVssProvider();
 | |
|     ~CQGAVssProvider();
 | |
| 
 | |
| private:
 | |
|     long m_nRefCount;
 | |
| };
 | |
| 
 | |
| CQGAVssProvider::CQGAVssProvider()
 | |
| {
 | |
|     m_nRefCount = 0;
 | |
|     LockModule(TRUE);
 | |
| }
 | |
| 
 | |
| CQGAVssProvider::~CQGAVssProvider()
 | |
| {
 | |
|     LockModule(FALSE);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::QueryInterface(REFIID riid, void **ppObj)
 | |
| {
 | |
|     if (riid == IID_IUnknown) {
 | |
|         *ppObj = static_cast<void*>(this);
 | |
|         AddRef();
 | |
|         return S_OK;
 | |
|     }
 | |
|     if (riid == IID_IVssSoftwareSnapshotProvider) {
 | |
|         *ppObj = static_cast<void*>(
 | |
|             static_cast<IVssSoftwareSnapshotProvider*>(this));
 | |
|         AddRef();
 | |
|         return S_OK;
 | |
|     }
 | |
|     if (riid == IID_IVssProviderCreateSnapshotSet) {
 | |
|         *ppObj = static_cast<void*>(
 | |
|             static_cast<IVssProviderCreateSnapshotSet*>(this));
 | |
|         AddRef();
 | |
|         return S_OK;
 | |
|     }
 | |
|     if (riid == IID_IVssProviderNotifications) {
 | |
|         *ppObj = static_cast<void*>(
 | |
|             static_cast<IVssProviderNotifications*>(this));
 | |
|         AddRef();
 | |
|         return S_OK;
 | |
|     }
 | |
|     *ppObj = NULL;
 | |
|     return E_NOINTERFACE;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CQGAVssProvider::AddRef()
 | |
| {
 | |
|     return InterlockedIncrement(&m_nRefCount);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CQGAVssProvider::Release()
 | |
| {
 | |
|     long nRefCount = InterlockedDecrement(&m_nRefCount);
 | |
|     if (m_nRefCount == 0) {
 | |
|         delete this;
 | |
|     }
 | |
|     return nRefCount;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * IVssSoftwareSnapshotProvider methods
 | |
|  */
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::SetContext(LONG lContext)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::GetSnapshotProperties(
 | |
|     VSS_ID SnapshotId, VSS_SNAPSHOT_PROP *pProp)
 | |
| {
 | |
|     return VSS_E_OBJECT_NOT_FOUND;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::Query(
 | |
|     VSS_ID QueriedObjectId, VSS_OBJECT_TYPE eQueriedObjectType,
 | |
|     VSS_OBJECT_TYPE eReturnedObjectsType, IVssEnumObject **ppEnum)
 | |
| {
 | |
|     try {
 | |
|         *ppEnum = new CQGAVSSEnumObject;
 | |
|     } catch (...) {
 | |
|         return E_OUTOFMEMORY;
 | |
|     }
 | |
|     (*ppEnum)->AddRef();
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::DeleteSnapshots(
 | |
|     VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType,
 | |
|     BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID)
 | |
| {
 | |
|     *plDeletedSnapshots = 0;
 | |
|     *pNondeletedSnapshotID = SourceObjectId;
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot(
 | |
|     VSS_ID SnapshotSetId, VSS_ID SnapshotId,
 | |
|     VSS_PWSZ pwszVolumeName, LONG lNewContext)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::IsVolumeSupported(
 | |
|     VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider)
 | |
| {
 | |
|     HANDLE hEventFrozen;
 | |
| 
 | |
|     /* Check if a requester is qemu-ga by whether an event is created */
 | |
|     hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
 | |
|     if (!hEventFrozen) {
 | |
|         *pbSupportedByThisProvider = FALSE;
 | |
|         return S_OK;
 | |
|     }
 | |
|     CloseHandle(hEventFrozen);
 | |
| 
 | |
|     *pbSupportedByThisProvider = TRUE;
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::IsVolumeSnapshotted(VSS_PWSZ pwszVolumeName,
 | |
|     BOOL *pbSnapshotsPresent, LONG *plSnapshotCompatibility)
 | |
| {
 | |
|     *pbSnapshotsPresent = FALSE;
 | |
|     *plSnapshotCompatibility = 0;
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::SetSnapshotProperty(VSS_ID SnapshotId,
 | |
|     VSS_SNAPSHOT_PROPERTY_ID eSnapshotPropertyId, VARIANT vProperty)
 | |
| {
 | |
|     return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::RevertToSnapshot(VSS_ID SnapshotId)
 | |
| {
 | |
|     return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::QueryRevertStatus(
 | |
|     VSS_PWSZ pwszVolume, IVssAsync **ppAsync)
 | |
| {
 | |
|     return E_NOTIMPL;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * IVssProviderCreateSnapshotSet methods
 | |
|  */
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::EndPrepareSnapshots(VSS_ID SnapshotSetId)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::PreCommitSnapshots(VSS_ID SnapshotSetId)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId)
 | |
| {
 | |
|     HRESULT hr = S_OK;
 | |
|     HANDLE hEventFrozen, hEventThaw, hEventTimeout;
 | |
| 
 | |
|     hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN);
 | |
|     if (!hEventFrozen) {
 | |
|         return E_FAIL;
 | |
|     }
 | |
| 
 | |
|     hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW);
 | |
|     if (!hEventThaw) {
 | |
|         CloseHandle(hEventFrozen);
 | |
|         return E_FAIL;
 | |
|     }
 | |
| 
 | |
|     hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT);
 | |
|     if (!hEventTimeout) {
 | |
|         CloseHandle(hEventFrozen);
 | |
|         CloseHandle(hEventThaw);
 | |
|         return E_FAIL;
 | |
|     }
 | |
| 
 | |
|     /* Send event to qemu-ga to notify filesystem is frozen */
 | |
|     SetEvent(hEventFrozen);
 | |
| 
 | |
|     /* Wait until the snapshot is taken by the host. */
 | |
|     if (WaitForSingleObject(hEventThaw, VSS_TIMEOUT_MSEC) != WAIT_OBJECT_0) {
 | |
|         /* Send event to qemu-ga to notify the provider is timed out */
 | |
|         SetEvent(hEventTimeout);
 | |
|     }
 | |
| 
 | |
|     CloseHandle(hEventThaw);
 | |
|     CloseHandle(hEventFrozen);
 | |
|     CloseHandle(hEventTimeout);
 | |
|     return hr;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::PostCommitSnapshots(
 | |
|     VSS_ID SnapshotSetId, LONG lSnapshotsCount)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::PreFinalCommitSnapshots(VSS_ID SnapshotSetId)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::PostFinalCommitSnapshots(VSS_ID SnapshotSetId)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::AbortSnapshots(VSS_ID SnapshotSetId)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * IVssProviderNotifications methods
 | |
|  */
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::OnLoad(IUnknown *pCallback)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProvider::OnUnload(BOOL bForceUnload)
 | |
| {
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * CQGAVssProviderFactory class
 | |
|  */
 | |
| 
 | |
| class CQGAVssProviderFactory : public IClassFactory
 | |
| {
 | |
| public:
 | |
|     STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
 | |
|     STDMETHODIMP_(ULONG) AddRef();
 | |
|     STDMETHODIMP_(ULONG) Release();
 | |
|     STDMETHODIMP CreateInstance(
 | |
|         IUnknown *pUnknownOuter, REFIID iid, void **ppv);
 | |
|     STDMETHODIMP LockServer(BOOL lock) { return E_NOTIMPL; }
 | |
| 
 | |
|     CQGAVssProviderFactory();
 | |
|     ~CQGAVssProviderFactory();
 | |
| 
 | |
| private:
 | |
|     long m_nRefCount;
 | |
| };
 | |
| 
 | |
| CQGAVssProviderFactory::CQGAVssProviderFactory()
 | |
| {
 | |
|     m_nRefCount = 0;
 | |
|     LockModule(TRUE);
 | |
| }
 | |
| 
 | |
| CQGAVssProviderFactory::~CQGAVssProviderFactory()
 | |
| {
 | |
|     LockModule(FALSE);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProviderFactory::QueryInterface(REFIID riid, void **ppv)
 | |
| {
 | |
|     if (riid == IID_IUnknown || riid == IID_IClassFactory) {
 | |
|         *ppv = static_cast<void*>(this);
 | |
|         AddRef();
 | |
|         return S_OK;
 | |
|     }
 | |
|     *ppv = NULL;
 | |
|     return E_NOINTERFACE;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CQGAVssProviderFactory::AddRef()
 | |
| {
 | |
|     return InterlockedIncrement(&m_nRefCount);
 | |
| }
 | |
| 
 | |
| STDMETHODIMP_(ULONG) CQGAVssProviderFactory::Release()
 | |
| {
 | |
|     long nRefCount = InterlockedDecrement(&m_nRefCount);
 | |
|     if (m_nRefCount == 0) {
 | |
|         delete this;
 | |
|     }
 | |
|     return nRefCount;
 | |
| }
 | |
| 
 | |
| STDMETHODIMP CQGAVssProviderFactory::CreateInstance(
 | |
|     IUnknown *pUnknownOuter, REFIID iid, void **ppv)
 | |
| {
 | |
|     CQGAVssProvider *pObj;
 | |
| 
 | |
|     if (pUnknownOuter) {
 | |
|         return CLASS_E_NOAGGREGATION;
 | |
|     }
 | |
|     try {
 | |
|         pObj = new CQGAVssProvider;
 | |
|     } catch (...) {
 | |
|         return E_OUTOFMEMORY;
 | |
|     }
 | |
|     HRESULT hr = pObj->QueryInterface(iid, ppv);
 | |
|     if (FAILED(hr)) {
 | |
|         delete pObj;
 | |
|     }
 | |
|     return hr;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * DLL functions
 | |
|  */
 | |
| 
 | |
| STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
 | |
| {
 | |
|     CQGAVssProviderFactory *factory;
 | |
|     try {
 | |
|         factory = new CQGAVssProviderFactory;
 | |
|     } catch (...) {
 | |
|         return E_OUTOFMEMORY;
 | |
|     }
 | |
|     factory->AddRef();
 | |
|     HRESULT hr = factory->QueryInterface(riid, ppv);
 | |
|     factory->Release();
 | |
|     return hr;
 | |
| }
 | |
| 
 | |
| STDAPI DllCanUnloadNow()
 | |
| {
 | |
|     return g_nComObjsInUse == 0 ? S_OK : S_FALSE;
 | |
| }
 | |
| 
 | |
| EXTERN_C
 | |
| BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpReserved)
 | |
| {
 | |
|     if (dwReason == DLL_PROCESS_ATTACH) {
 | |
|         g_hinstDll = hinstDll;
 | |
|         DisableThreadLibraryCalls(hinstDll);
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 |