/***************************************************************************** * * (C) COPYRIGHT MICROSOFT CORPORATION, 1999 - 2000 * * TITLE: vstiusd.cpp * * VERSION: 1.1 * * AUTHOR: WilliamH (created) * RickTu (ported to WIA) * * DATE: 9/7/99 * * DESCRIPTION: This module implements the CVideoStiUsd object & * supported classes. * *****************************************************************************/ #include #pragma hdrstop DEFINE_GUID(CLSID_VIDEO_STIUSD,0x0527d1d0, 0x88c2, 0x11d2, 0x82, 0xc7, 0x00, 0xc0, 0x4f, 0x8e, 0xc1, 0x83); /***************************************************************************** CVideoUsdClassFactory constructor / desctructor *****************************************************************************/ ULONG g_cDllRef = 0; CVideoUsdClassFactory::CVideoUsdClassFactory() { } /***************************************************************************** CVideoUsdClassFactory::QueryInterface Add our info to the base class QI code. *****************************************************************************/ STDMETHODIMP CVideoUsdClassFactory::QueryInterface( REFIID riid, void **ppvObject) { DBG_FN("CVideoUsdClassFactory::QueryInterface"); *ppvObject = NULL; if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) { *ppvObject = (LPVOID)this; AddRef(); return NOERROR; } return ResultFromScode(E_NOINTERFACE); } /***************************************************************************** CVideoUsdClassFactory::AddRef *****************************************************************************/ STDMETHODIMP_(ULONG) CVideoUsdClassFactory::AddRef(void) { DBG_FN("CVideoUsdClassFactory::AddRef"); InterlockedIncrement((LONG *)&g_cDllRef); InterlockedIncrement((LONG *)&m_cRef); return m_cRef; } /***************************************************************************** CVideoUsdClassFactory::Release *****************************************************************************/ STDMETHODIMP_(ULONG) CVideoUsdClassFactory::Release(void) { DBG_FN("CVideoUsdClassFactory::Release"); InterlockedDecrement((LONG *)&g_cDllRef); InterlockedDecrement((LONG *)&m_cRef); if (m_cRef == 0) { delete this; return 0; } return m_cRef; } /***************************************************************************** CVideoUsdClassFactory::CreateInstance Instantiate one of the objects we are responsible for. *****************************************************************************/ STDMETHODIMP CVideoUsdClassFactory::CreateInstance(IUnknown *pOuterUnk, REFIID riid, void **ppv) { DBG_FN("CVideoUsdClassFactory::CreateInstance"); // // Check for bad args // if (!ppv) { DBG_ERR(("ppv is NULL. returning E_INVALIDARG")); return E_INVALIDARG; } *ppv = NULL; // // If it's not an interface we support, bail early // if (!IsEqualIID(riid, IID_IStiUSD) && !IsEqualIID(riid, IID_IWiaMiniDrv) && !IsEqualIID(riid, IID_IUnknown)) { return E_NOINTERFACE; } // // When created for aggregation, only IUnknown can be requested. // if (pOuterUnk && !IsEqualIID(riid, IID_IUnknown)) { return CLASS_E_NOAGGREGATION; } // // Create our Usd/Wia mini driver // CVideoStiUsd *pUsd = NULL; HRESULT hr; pUsd = new CVideoStiUsd(pOuterUnk); if (!pUsd) { DBG_ERR(("Couldn't create new CVideoStiUsd class, " "returning E_OUTOFMEMORY")); return E_OUTOFMEMORY; } hr = pUsd->PrivateInitialize(); if (hr != S_OK) { CHECK_S_OK2( hr, ("pUsd->PrivateInitialize" )); delete pUsd; return hr; } // Move to the requested interface if we aren't aggregated. // Don't do this if aggregated, or we will lose the private // IUnknown and then the caller will be hosed. hr = pUsd->NonDelegatingQueryInterface(riid, ppv); CHECK_S_OK2( hr, ("pUsd->NonDelegatingQueryInterface" )); pUsd->NonDelegatingRelease(); return hr; } /***************************************************************************** CVideoUsdClassFactory::LockServer *****************************************************************************/ STDMETHODIMP CVideoUsdClassFactory::LockServer(BOOL fLock) { DBG_FN("CVideoUsdClassFactory::LockServer"); if (fLock) { InterlockedIncrement((LONG*)&g_cDllRef); } else { InterlockedDecrement((LONG*)&g_cDllRef); } return S_OK; } /***************************************************************************** CVideoUsdClassFactory::GetClassObject *****************************************************************************/ HRESULT CVideoUsdClassFactory::GetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) { DBG_FN("CVideoUsdClassFactory::GetClassObject"); if (!ppv) { return E_INVALIDARG; } if (rclsid == CLSID_VIDEO_STIUSD && (riid == IID_IUnknown || riid == IID_IClassFactory)) { CVideoUsdClassFactory *pFactory = NULL; pFactory = new CVideoUsdClassFactory(); if (pFactory) { *ppv = pFactory; pFactory->AddRef(); return S_OK; } else { *ppv = NULL; return E_OUTOFMEMORY; } } else { *ppv = NULL; return E_NOINTERFACE; } } /***************************************************************************** CVideoUsdClassFactory::CanUnloadNow Lets the outside world know if we can be unloaded. *****************************************************************************/ HRESULT CVideoUsdClassFactory::CanUnloadNow() { DBG_FN("CVideoUsdClassFactory::CanUnloadNow"); return (0 == g_cDllRef) ? S_OK : S_FALSE; } /***************************************************************************** CVideoStiUsd constructor/destructor *****************************************************************************/ CVideoStiUsd::CVideoStiUsd(IUnknown * pUnkOuter) : m_bDeviceIsOpened(FALSE), m_lPicsTaken(0), m_hTakePictureEvent(NULL), m_hPictureReadyEvent(NULL), m_pTakePictureOwner(NULL), m_pLastItemCreated(NULL), m_dwConnectedApps(0), m_cRef(1) { // // See if we are aggregated. If we are (almost always the case) save // pointer to the controlling Unknown , so subsequent calls will be // delegated. If not, set the same pointer to "this". // if (pUnkOuter) { m_pUnkOuter = pUnkOuter; } else { // // Cast below is needed in order to point to right virtual table // m_pUnkOuter = reinterpret_cast (static_cast (this)); } } HRESULT CVideoStiUsd::PrivateInitialize() { HRESULT hr = S_OK; HANDLE hThread = NULL; DWORD dwId = 0; // // Set up some global info // m_wfi = (WIA_FORMAT_INFO*) CoTaskMemAlloc(sizeof(WIA_FORMAT_INFO) * NUM_WIA_FORMAT_INFO); if (m_wfi) { // // Set up the format/tymed pairs // m_wfi[0].guidFormatID = WiaImgFmt_JPEG; m_wfi[0].lTymed = TYMED_CALLBACK; m_wfi[1].guidFormatID = WiaImgFmt_JPEG; m_wfi[1].lTymed = TYMED_FILE; m_wfi[2].guidFormatID = WiaImgFmt_MEMORYBMP; m_wfi[2].lTymed = TYMED_CALLBACK; m_wfi[3].guidFormatID = WiaImgFmt_BMP; m_wfi[3].lTymed = TYMED_CALLBACK; m_wfi[4].guidFormatID = WiaImgFmt_BMP; m_wfi[4].lTymed = TYMED_FILE; } // // Initialize critical sections // __try { if (!InitializeCriticalSectionAndSpinCount(&m_csItemTree, MINLONG)) { hr = HRESULT_FROM_WIN32(::GetLastError()); DBG_ERR(("ERROR: Failed to initialize one of critsections " "(0x%08X)", hr)); } } __except(EXCEPTION_EXECUTE_HANDLER) { hr = E_OUTOFMEMORY; } // // Initialize GDI+ // Gdiplus::Status StatusResult = Gdiplus::Ok; Gdiplus::GdiplusStartupInput StartupInput; m_ulGdiPlusToken = NULL; if (hr == S_OK) { StatusResult = Gdiplus::GdiplusStartup(&m_ulGdiPlusToken, &StartupInput, NULL); if (StatusResult != Gdiplus::Ok) { DBG_ERR(("ERROR: Failed to start up GDI+, Status code returned " "by GDI+ = '%d'", StatusResult)); hr = HRESULT_FROM_WIN32(StatusResult); } } return hr; } CVideoStiUsd::~CVideoStiUsd() { if (m_pRootItem) { HRESULT hr = S_OK; DBG_TRC(("CVideoStiUsd::~CVideoStiUsd, driver is being destroyed, " "and for some reason the tree still exists, deleting tree...")); hr = m_pRootItem->UnlinkItemTree(WiaItemTypeDisconnected); // Clear the root item m_pRootItem = NULL; } // // Disable Take Picture command. // DisableTakePicture(NULL, TRUE); // // Shutdown GDI+ // Gdiplus::GdiplusShutdown(m_ulGdiPlusToken); CloseDevice(); if (m_wfi) { CoTaskMemFree( (LPVOID)m_wfi ); m_wfi = NULL; } DeleteCriticalSection(&m_csItemTree); } /***************************************************************************** CVideoStiUsd::NonDelegatingQueryInterface This is the inner object QI -- in other words, handle QI's for the interfaces our object supports. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::NonDelegatingQueryInterface(REFIID riid, LPVOID *ppvObj ) { HRESULT hr = S_OK; DBG_FN("CVideoStiUsd::NonDelegatingQueryInterface"); // // Check for invalid args // if (!ppvObj) { DBG_ERR(("ppvObj is NULL, returning E_INVALIDARG")); return E_INVALIDARG; } *ppvObj = NULL; if (IsEqualIID(riid, IID_IUnknown)) { *ppvObj = static_cast(this); } else if (IsEqualIID(riid, IID_IStiUSD)) { *ppvObj = static_cast(this); } else if (IsEqualIID(riid, IID_IWiaMiniDrv)) { *ppvObj = static_cast(this); } else { hr = E_NOINTERFACE; DBG_ERR(("CVideoStiUsd::NonDelegatingQueryInterface requested " "interface we don't support, returning hr = 0x%08lx", hr)); } if (SUCCEEDED(hr)) { (reinterpret_cast(*ppvObj))->AddRef(); } CHECK_S_OK(hr); return hr; } /***************************************************************************** CVideoStiUsd::NonDelegatingAddRef This is the inner object AddRef -- actually inc the ref count for our interfaces. *****************************************************************************/ STDMETHODIMP_(ULONG) CVideoStiUsd::NonDelegatingAddRef(void) { DBG_FN("CVideoStiUsd::NonDelegatingAddRef"); return InterlockedIncrement((LPLONG)&m_cRef); } /***************************************************************************** CVideoStiUsd::NonDelegatingRelease This is the inner object Release -- actually dec the ref count for our interfaces. *****************************************************************************/ STDMETHODIMP_(ULONG) CVideoStiUsd::NonDelegatingRelease(void) { DBG_FN("CVideoStiUsd::NonDelegatingRelease"); ULONG ulRef = 0; ulRef = InterlockedDecrement((LPLONG)&m_cRef); if (!ulRef) { delete this; } return ulRef; } /***************************************************************************** CVideoStiUsd::QueryInterface Outer QI -- used for aggregation *****************************************************************************/ STDMETHODIMP CVideoStiUsd::QueryInterface(REFIID riid, LPVOID *ppvObj) { DBG_FN("CVideoStiUsd::QueryInterface"); return m_pUnkOuter->QueryInterface( riid, ppvObj ); } /***************************************************************************** CVideoStiUsd::AddRef Outer AddRef -- used for aggregation *****************************************************************************/ STDMETHODIMP_(ULONG) CVideoStiUsd::AddRef(void) { DBG_FN("CVideoStiUsd::AddRef"); return m_pUnkOuter->AddRef(); } /***************************************************************************** CVideoStiUsd::Release Outer Release -- used for aggregation *****************************************************************************/ STDMETHODIMP_(ULONG) CVideoStiUsd::Release(void) { DBG_FN("CVideoStiUsd::Release"); return m_pUnkOuter->Release(); } /***************************************************************************** CVideoStiUsd::Initialize [IStillUsd] *****************************************************************************/ STDMETHODIMP CVideoStiUsd::Initialize(PSTIDEVICECONTROL pDcb, DWORD dwStiVersion, HKEY hParameterKey) { DBG_FN("CVideoStiUsd::Initialize"); HRESULT hr = S_OK; WCHAR DeviceName[MAX_PATH] = {0}; if ((pDcb == NULL) || (hParameterKey == NULL)) { hr = E_POINTER; CHECK_S_OK2(hr, ("CVideoStiUsd::Initialize, received a " "NULL pointer, either 'pDcb = 0x%08lx' is NULL " "or 'hParameterKey = 0x%08lx' is NULL", pDcb, hParameterKey)); } if (hr == S_OK) { // // get the device symbolic name. We use the name to get an IMoniker to // the device filter proxy. // hr = pDcb->GetMyDevicePortName(DeviceName, sizeof(DeviceName)/sizeof(WCHAR)); if (SUCCEEDED(hr)) { hr = OpenDevice(DeviceName); } } if (hr == S_OK) { HKEY hKey = NULL; DWORD dwType = 0; LRESULT lResult = ERROR_SUCCESS; TCHAR szValue[MAX_PATH + 1] = {0}; DWORD dwSize = sizeof(szValue) - sizeof(szValue[0]); lResult = RegOpenKeyEx(hParameterKey, TEXT("DeviceData"), 0, KEY_READ, &hKey); if (lResult == ERROR_SUCCESS) { // // Read DShow device ID from DeviceData registry // lResult = RegQueryValueEx(hKey, TEXT("DShowDeviceId"), NULL, &dwType, (BYTE*) szValue, &dwSize); szValue[sizeof(szValue) / sizeof(szValue[0]) - 1] = '\0'; } if (hKey) { RegCloseKey(hKey); hKey = NULL; } if (lResult == ERROR_SUCCESS) { m_strDShowDeviceId = szValue; } else { hr = E_FAIL; CHECK_S_OK2(hr, ("CVideoStiUsd::Initialize, failed to retrieve the " "DShow Device ID.")); } } return hr; } /***************************************************************************** CVideoStiUsd::CloseDevice [IStillUsd] *****************************************************************************/ HRESULT CVideoStiUsd::CloseDevice() { DBG_FN("CVideoStiUsd::CloseDevice"); m_bDeviceIsOpened = FALSE; return S_OK; } /***************************************************************************** CVideoStiUsd::OpenDevice [IStillUsd] *****************************************************************************/ HRESULT CVideoStiUsd::OpenDevice(LPCWSTR DeviceName) { DBG_FN("CVideoStiUsd::OpenDevice"); // // Check for bad args // if (!DeviceName || (0 == *DeviceName)) { return E_INVALIDARG; } m_bDeviceIsOpened = TRUE; return S_OK; } /***************************************************************************** CVideoStiUsd::GetCapabilities [IStillUsd] *****************************************************************************/ STDMETHODIMP CVideoStiUsd::GetCapabilities(PSTI_USD_CAPS pDevCaps) { DBG_FN("CVideoStiUsd::GetCapabilities"); // // Check for bad args // if (!pDevCaps) { return E_INVALIDARG; } memset(pDevCaps, 0, sizeof(STI_USD_CAPS)); pDevCaps->dwVersion = STI_VERSION; pDevCaps->dwGenericCaps = STI_USD_GENCAP_NATIVE_PUSHSUPPORT; return S_OK; } /***************************************************************************** CVideoStiUsd::GetStatus [IStillUsd] *****************************************************************************/ STDMETHODIMP CVideoStiUsd::GetStatus(PSTI_DEVICE_STATUS pDevStatus) { DBG_FN("CVideoStiUsd::GetStatus"); if (!pDevStatus) { return E_INVALIDARG; } if (pDevStatus->StatusMask & STI_DEVSTATUS_ONLINE_STATE ) { if (m_bDeviceIsOpened) { pDevStatus->dwOnlineState |= STI_ONLINESTATE_OPERATIONAL; } else { pDevStatus->dwOnlineState &= ~STI_ONLINESTATE_OPERATIONAL; } } return S_OK; } /***************************************************************************** CVideoStiUsd::DeviceReset [IStillUsd] *****************************************************************************/ STDMETHODIMP CVideoStiUsd::DeviceReset() { DBG_FN("CVideoStiUsd::DeviceReset"); return S_OK; } /***************************************************************************** CVideoStiUsd::Diagnostic [IStillUsd] *****************************************************************************/ STDMETHODIMP CVideoStiUsd::Diagnostic(LPSTI_DIAG pStiDiag) { DBG_FN("CVideoStiUsd::Diagnostic"); // // Check for bad args // if (!pStiDiag) { return E_INVALIDARG; } // // Return diag info // pStiDiag->dwStatusMask = 0; memset(&pStiDiag->sErrorInfo, 0, sizeof(pStiDiag->sErrorInfo)); if (m_bDeviceIsOpened) { pStiDiag->sErrorInfo.dwGenericError = NOERROR; } else { pStiDiag->sErrorInfo.dwGenericError = STI_NOTCONNECTED; } pStiDiag->sErrorInfo.dwVendorError = 0; return S_OK; } /***************************************************************************** CVideoStiUsd::Escape [IStillUsd] *****************************************************************************/ STDMETHODIMP CVideoStiUsd::Escape(STI_RAW_CONTROL_CODE Function, LPVOID DataIn, DWORD DataInSize, LPVOID DataOut, DWORD DataOutSize, DWORD *pActualSize) { DBG_FN("CVideoStiUsd::Escape"); return S_OK; } /***************************************************************************** CVideoStiUsd::GetLastError [IStillUsd] Not implemented yet. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::GetLastError(LPDWORD pLastError) { DBG_FN("CVideoStiUsd::GetLastError( NOT_IMPL )"); return E_NOTIMPL; } /***************************************************************************** CVideoStiUsd::LockDevice [IStillUsd] No actual locking of the device has to happen, so just return success. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::LockDevice() { DBG_FN("CVideoStiUsd::LockDevice"); return S_OK; } /***************************************************************************** CVideoStiUsd::UnLockDevice [IStillUsd] No actual locking/unlocking of the device has to happen, so just return success. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::UnLockDevice() { DBG_FN("CVideoStiUsd::UnlockDevice"); return S_OK; } /***************************************************************************** CVideoStiUsd::RawReadData [IStillUsd] Not implemented yet. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::RawReadData(LPVOID Buffer, LPDWORD BufferSize, LPOVERLAPPED lpOverlapped) { DBG_FN("CVideoStiUsd::RawReadData( NOT_IMPL )"); return E_NOTIMPL; } /***************************************************************************** CVideoStiUsd::RawWriteData [IStillUsd] Not implemented yet. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::RawWriteData(LPVOID Buffer, DWORD BufferSize, LPOVERLAPPED lpOverlapped) { DBG_FN("CVideoStiUsd::RawWriteData( NOT_IMPL )"); return E_NOTIMPL; } /***************************************************************************** CVideoStiUsd::RawReadCommand [IStillUsd] Not implemented yet. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::RawReadCommand(LPVOID Buffer, LPDWORD BufferSize, LPOVERLAPPED lpOverlapped) { DBG_FN("CVideoStiUsd::RawReadCommand( NOT_IMPL )"); return E_NOTIMPL; } /***************************************************************************** CVideoStiUsd::RawWriteCommand [IStillUsd] Not implemented yet. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::RawWriteCommand(LPVOID Buffer, DWORD BufferSize, LPOVERLAPPED lpOverlapped) { DBG_FN("CVideoStiUsd::RawWriteCommand( NOT_IMPL )"); return E_NOTIMPL; } /***************************************************************************** CVideoStiUsd::SetNotificationHandle [IStillUsd] Sets the event notification handle. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::SetNotificationHandle(HANDLE hEvent) { DBG_FN("CVideoStiUsd::SetNotificationHandle"); return S_OK; } /***************************************************************************** CVideoStiUsd::GetNotificationData [IStillUsd] Returns the current event notification handle. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::GetNotificationData(LPSTINOTIFY lpNotify) { DBG_FN("CVideoStiUsd::GetNotificationData"); HRESULT hr = STIERR_NOEVENTS; DBG_ERR(("We were called, but no events are present -- why?")); CHECK_S_OK(hr); return hr; } /***************************************************************************** CVideoStiUsd::GetLastErrorInfo [IStillUsd] Not implemented yet. *****************************************************************************/ STDMETHODIMP CVideoStiUsd::GetLastErrorInfo(STI_ERROR_INFO *pLastErrorInfo) { DBG_FN("CVideoStiUsd::GetLastErrorInfo( NOT_IMPL )"); return E_NOTIMPL; }