#include "shellprv.h" #include "mixctnt.h" #include "clsobj.h" #include "apprmdlg.h" #include "hwcmmn.h" #include "ids.h" #include "shpriv.h" #include "mtptl.h" #include "filetbl.h" class CDeviceEventHandler : public IHWEventHandler { public: CDeviceEventHandler(); // IUnknown STDMETHODIMP QueryInterface(REFIID, void **); STDMETHODIMP_(ULONG) AddRef(void); STDMETHODIMP_(ULONG) Release(void); // IHWEventHandler STDMETHODIMP Initialize(LPCWSTR pszParams); STDMETHODIMP HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType); STDMETHODIMP HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType, LPCWSTR pszContentTypeHandler, IDataObject* pdtobj); private: ~CDeviceEventHandler(); LONG _cRef; LPWSTR _pszParams; }; CDeviceEventHandler::CDeviceEventHandler() : _cRef(1) { DllAddRef(); } CDeviceEventHandler::~CDeviceEventHandler() { CoTaskMemFree(_pszParams) DllRelease(); } STDAPI CDeviceEventHandler_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppv) { HRESULT hr = E_OUTOFMEMORY; *ppv = NULL; // aggregation checking is handled in class factory CDeviceEventHandler* pHWMixedContent = new CDeviceEventHandler(); if (pHWMixedContent) { hr = pHWMixedContent->QueryInterface(riid, ppv); pHWMixedContent->Release(); } return hr; } // IUnknown STDMETHODIMP CDeviceEventHandler::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDeviceEventHandler, IHWEventHandler), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CDeviceEventHandler::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CDeviceEventHandler::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } // IHWEventHandler STDMETHODIMP CDeviceEventHandler::Initialize(LPCWSTR pszParams) { return SHStrDup(pszParams, &_pszParams); } DWORD WINAPI _PromptThreadProc(void* pv) { CBaseContentDlg* pdlg = (CBaseContentDlg*)pv; if (IDOK == pdlg->DoModal(pdlg->_hinst, MAKEINTRESOURCE(pdlg->_iResource), pdlg->_hwndParent)) { // Try to Autoplay this type handler IHWDevice* phwd; if (SUCCEEDED(_GetHWDevice(pdlg->_pszDeviceID, &phwd))) { phwd->AutoplayHandler(TEXT("DeviceArrival"), pdlg->_szHandler); phwd->Release(); } } _RemoveFromAutoplayPromptHDPA(pdlg->_pszDeviceID); pdlg->Release(); return 0; } HRESULT _Prompt(LPCWSTR pszDeviceID, LPCWSTR pszEventType, BOOL fCheckAlwaysDoThis) { HRESULT hr; BOOL fShowDlg = _AddAutoplayPrompt(pszDeviceID); if (fShowDlg) { BOOL fDialogShown = FALSE; CBaseContentDlg* pdlg = new CNoContentDlg(); if (pdlg) { pdlg->_szContentTypeHandler[0] = 0; pdlg->_hinst = g_hinst; pdlg->_iResource = DLG_APNOCONTENT; pdlg->_hwndParent = NULL; hr = pdlg->Init(pszDeviceID, NULL, 0, fCheckAlwaysDoThis); if (SUCCEEDED(hr)) { if (SHCreateThread(_PromptThreadProc, pdlg, CTF_COINIT, NULL)) { fDialogShown = TRUE; } else { pdlg->Release(); } } else { pdlg->Release(); } } else { hr = E_OUTOFMEMORY; } if (!fDialogShown) { _RemoveFromAutoplayPromptHDPA(pszDeviceID); } } else { hr = S_FALSE; } return hr; } STDMETHODIMP CDeviceEventHandler::HandleEvent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType) { HRESULT hr = E_NOTIMPL; if (!lstrcmp(_pszParams, TEXT("PromptEachTimeNoContent"))) { hr = _Prompt(pszDeviceID, pszEventType, FALSE); } else { // The '*' means to check the AlwaysDoThis checkbox! if (!lstrcmp(_pszParams, TEXT("PromptEachTimeNoContent*"))) { hr = _Prompt(pszDeviceID, pszEventType, TRUE); } } return hr; } STDMETHODIMP CDeviceEventHandler::HandleEventWithContent(LPCWSTR pszDeviceID, LPCWSTR pszAltDeviceID, LPCWSTR pszEventType, LPCWSTR pszContentTypeHandler, IDataObject* pdtobj) { return E_FAIL; } class CDeviceAutoPlayNotification : IQueryContinue { public: CDeviceAutoPlayNotification(); void CreateNotificationThread(); // IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // IQueryContinue STDMETHODIMP QueryContinue(); // S_OK -> Continue, other HRESULT Init(LPCTSTR pszDevice, LPCWSTR pszEventType, CCrossThreadFlag* pDeviceGoneFlag); private: ~CDeviceAutoPlayNotification(); void _DoNotificationUI(); static DWORD CALLBACK s_ThreadProc(void *pv); LONG _cRef; LPTSTR _pszDevice; LPTSTR _pszEventType; CCrossThreadFlag* _pDeviceGoneFlag; BOOL _fPoppedUpDlg; }; CDeviceAutoPlayNotification::CDeviceAutoPlayNotification() : _cRef(1) {} HRESULT CDeviceAutoPlayNotification::Init(LPCTSTR pszDevice, LPCWSTR pszEventType, CCrossThreadFlag* pDeviceGoneFlag) { HRESULT hr = S_OK; _pszDevice = StrDup(pszDevice); _pszEventType = StrDup(pszEventType); pDeviceGoneFlag->AddRef(); _pDeviceGoneFlag = pDeviceGoneFlag; if (!_pszDevice || !_pszEventType) { hr = E_OUTOFMEMORY; } return hr; } CDeviceAutoPlayNotification::~CDeviceAutoPlayNotification() { LocalFree(_pszDevice); LocalFree(_pszEventType); if (_pDeviceGoneFlag) { _pDeviceGoneFlag->Release(); } } HRESULT CDeviceAutoPlayNotification::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CDeviceAutoPlayNotification, IQueryContinue), { 0 }, }; return QISearch(this, qit, riid, ppv); } STDMETHODIMP_(ULONG) CDeviceAutoPlayNotification::AddRef() { return InterlockedIncrement(&_cRef); } STDMETHODIMP_(ULONG) CDeviceAutoPlayNotification::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CDeviceAutoPlayNotification::QueryContinue() { HRESULT hr; if (_fPoppedUpDlg || _pDeviceGoneFlag->IsSignaled()) { hr = S_FALSE; } else { hr = S_OK; } return hr; } void CDeviceAutoPlayNotification::_DoNotificationUI() { DWORD dwTimeOut = 30; #ifdef DEBUG dwTimeOut = 60; #endif IUserNotification *pun; HRESULT hr = CoCreateInstance(CLSID_UserNotification, NULL, CLSCTX_ALL, IID_PPV_ARG(IUserNotification, &pun)); if (SUCCEEDED(hr)) { IHWDeviceCustomProperties* pdcp; if (SUCCEEDED(GetDeviceProperties(_pszDevice, &pdcp))) { HICON hicon = NULL; WORD_BLOB* pblob; if (SUCCEEDED(pdcp->GetMultiStringProperty(TEXT("Icons"), TRUE, &pblob))) { TCHAR szLocation[MAX_PATH + 10]; if (SUCCEEDED(StringCchCopy(szLocation, ARRAYSIZE(szLocation), pblob->asData))) { int iIcon = PathParseIconLocation(szLocation); ExtractIconEx(szLocation, iIcon, NULL, &hicon, 1); } CoTaskMemFree(pblob); } LPWSTR psz; if (SUCCEEDED(pdcp->GetStringProperty(TEXT("Label"), &psz))) { TCHAR szName[128]; SHLoadIndirectString(psz, szName, ARRAYSIZE(szName), NULL); pun->SetIconInfo(hicon, szName); CoTaskMemFree(psz); } pdcp->Release(); if (hicon) DestroyIcon(hicon); } pun->SetBalloonRetry(dwTimeOut * 1000, 0, 0); // show for 30 sec, then go away hr = pun->Show(SAFECAST(this, IQueryContinue *), 1 * 1000); // 1 sec poll for callback pun->Release(); if (S_OK == hr) { _Prompt(_pszDevice, _pszEventType, FALSE); _fPoppedUpDlg = TRUE; } } } DWORD CALLBACK CDeviceAutoPlayNotification::s_ThreadProc(void *pv) { CDeviceAutoPlayNotification *pldsui = (CDeviceAutoPlayNotification *)pv; pldsui->_DoNotificationUI(); pldsui->Release(); return 0; } void CDeviceAutoPlayNotification::CreateNotificationThread() { AddRef(); if (!SHCreateThread(s_ThreadProc, this, CTF_COINIT, NULL)) { Release(); } } HRESULT DoDeviceNotification(LPCTSTR pszDevice, LPCTSTR pszEventType, CCrossThreadFlag* pDeviceGoneFlag) { HRESULT hr; CDeviceAutoPlayNotification *pldsui = new CDeviceAutoPlayNotification(); if (pldsui) { hr = pldsui->Init(pszDevice, pszEventType, pDeviceGoneFlag); if (SUCCEEDED(hr)) { pldsui->CreateNotificationThread(); } pldsui->Release(); } else { hr = E_OUTOFMEMORY; } return hr; } template BOOL _FindAutoplayStructAndExecuteCB(LPCWSTR pszDriveOrDeviceID, TCALLBACKFCT fct, TCALLBACKARG arg, TCALLBACKRET* pRet) { BOOL fRet = FALSE; EnterCriticalSection(&g_csAutoplayPrompt); if (g_hdpaAutoplayPrompt) { int n = DPA_GetPtrCount(g_hdpaAutoplayPrompt); for (int i = 0; i < n; ++i) { AUTOPLAYPROMPT* pap = (AUTOPLAYPROMPT*)DPA_GetPtr(g_hdpaAutoplayPrompt, i); if (!lstrcmpi(pap->szDriveOrDeviceID, pszDriveOrDeviceID)) { fRet = TRUE; *pRet = fct(pap, arg); break; } } } LeaveCriticalSection(&g_csAutoplayPrompt); return fRet; } BOOL _AddAutoplayPromptEntry(LPCWSTR pszDriveOrDeviceID, BOOL fDlgWillBeShown) { BOOL fFoundEntry = FALSE; BOOL fWasRunning = FALSE; EnterCriticalSection(&g_csAutoplayPrompt); if (g_hdpaAutoplayPrompt) { int n = DPA_GetPtrCount(g_hdpaAutoplayPrompt); for (int i = 0; i < n; ++i) { AUTOPLAYPROMPT* pap = (AUTOPLAYPROMPT*)DPA_GetPtr(g_hdpaAutoplayPrompt, i); if (!lstrcmpi(pap->szDriveOrDeviceID, pszDriveOrDeviceID)) { fWasRunning = pap->fDlgWillBeShown; fFoundEntry = TRUE; if (!pap->fDlgWillBeShown) { pap->fDlgWillBeShown = fDlgWillBeShown; } TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: Found entry for %s - (dlg = %d -> %d)", pszDriveOrDeviceID, fWasRunning, pap->fDlgWillBeShown); break; } } } if (!fFoundEntry) { if (!g_hdpaAutoplayPrompt) { g_hdpaAutoplayPrompt = DPA_Create(3); } if (g_hdpaAutoplayPrompt) { AUTOPLAYPROMPT* pap; if (SUCCEEDED(SHLocalAlloc(sizeof(AUTOPLAYPROMPT), &pap))) { StringCchCopy(pap->szDriveOrDeviceID, ARRAYSIZE(pap->szDriveOrDeviceID), pszDriveOrDeviceID); pap->fDlgWillBeShown = fDlgWillBeShown; TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: Adding entry for %s - (dlg = %d)", pszDriveOrDeviceID, fDlgWillBeShown); if (-1 == DPA_AppendPtr(g_hdpaAutoplayPrompt, (void*)pap)) { LocalFree((HLOCAL)pap); } else { TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: Total # of entry: %d", DPA_GetPtrCount(g_hdpaAutoplayPrompt)); } } } } LeaveCriticalSection(&g_csAutoplayPrompt); return !fWasRunning; } BOOL _AddAutoplayPrompt(LPCWSTR pszDriveOrDeviceID) { return _AddAutoplayPromptEntry(pszDriveOrDeviceID, TRUE); } void _RemoveFromAutoplayPromptHDPA(LPCWSTR pszAltDeviceID) { EnterCriticalSection(&g_csAutoplayPrompt); if (g_hdpaAutoplayPrompt) { int n = DPA_GetPtrCount(g_hdpaAutoplayPrompt); for (int i = 0; i < n; ++i) { AUTOPLAYPROMPT* pap = (AUTOPLAYPROMPT*)DPA_GetPtr(g_hdpaAutoplayPrompt, i); if (!lstrcmpi(pap->szDriveOrDeviceID, pszAltDeviceID)) { TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: Removing %s", pap->szDriveOrDeviceID); if (pap->pDeviceGoneFlag) { pap->pDeviceGoneFlag->Release(); } LocalFree((HLOCAL)pap); DPA_DeletePtr(g_hdpaAutoplayPrompt, i); TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: Total # of entry: %d", DPA_GetPtrCount(g_hdpaAutoplayPrompt)); break; } } } LeaveCriticalSection(&g_csAutoplayPrompt); } // Set/Get HWND typedef BOOL (*PFNSETAAUTOPLAYPROMPTHWNDCB)(AUTOPLAYPROMPT* pap, HWND hwnd); BOOL _SetAutoplayPromptHWNDCB(AUTOPLAYPROMPT* pap, HWND hwnd) { pap->hwndDlg = hwnd; TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: SetHWND for %s - (dlg = %d)", pap->szDriveOrDeviceID, pap->fDlgWillBeShown); return TRUE; } void _SetAutoplayPromptHWND(LPCWSTR pszAltDeviceID, HWND hwnd) { BOOL fRet; _FindAutoplayStructAndExecuteCB (pszAltDeviceID, _SetAutoplayPromptHWNDCB, hwnd, &fRet); } typedef BOOL (*PFNGETAAUTOPLAYPROMPTHWNDCB)(AUTOPLAYPROMPT* pap, HWND* phwnd); BOOL _GetAutoplayPromptHWNDCB(AUTOPLAYPROMPT* pap, HWND* phwnd) { *phwnd = pap->hwndDlg; TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: GetHWND for %s - (dlg = %d)", pap->szDriveOrDeviceID, pap->fDlgWillBeShown); return TRUE; } BOOL _GetAutoplayPromptHWND(LPCWSTR pszAltDeviceID, HWND* phwnd) { BOOL fRet; return _FindAutoplayStructAndExecuteCB (pszAltDeviceID, _GetAutoplayPromptHWNDCB, phwnd, &fRet); } // Set/Get DeviceGoneFlag typedef BOOL (*PFNSETDEVICEGONEFLAGCB)(AUTOPLAYPROMPT* pap, CCrossThreadFlag* pDeviceGoneFlag); BOOL _SetDeviceGoneFlagCB(AUTOPLAYPROMPT* pap, CCrossThreadFlag* pDeviceGoneFlag) { pap->pDeviceGoneFlag = pDeviceGoneFlag; TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: SetDeviceGoneFlag for %s - (dlg = %d)", pap->szDriveOrDeviceID, pap->fDlgWillBeShown); return TRUE; } void AttachGoneFlagForDevice(LPCWSTR pszAltDeviceID, CCrossThreadFlag* pDeviceGoneFlag) { BOOL fRet; if (!_FindAutoplayStructAndExecuteCB (pszAltDeviceID, _SetDeviceGoneFlagCB, pDeviceGoneFlag, &fRet)) { _AddAutoplayPromptEntry(pszAltDeviceID, FALSE); if (_FindAutoplayStructAndExecuteCB (pszAltDeviceID, _SetDeviceGoneFlagCB, pDeviceGoneFlag, &fRet)) { if (fRet) { pDeviceGoneFlag->AddRef(); } } } } typedef BOOL (*PFNGETDEVICEGONEFLAGCB)(AUTOPLAYPROMPT* pap, CCrossThreadFlag** ppDeviceGoneFlag); BOOL _GetDeviceGoneFlagCB(AUTOPLAYPROMPT* pap, CCrossThreadFlag** ppDeviceGoneFlag) { *ppDeviceGoneFlag = pap->pDeviceGoneFlag; TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: GetDeviceGoneFlag for %s - (dlg = %d)", pap->szDriveOrDeviceID, pap->fDlgWillBeShown); return !!(*ppDeviceGoneFlag); } BOOL GetGoneFlagForDevice(LPCWSTR pszAltDeviceID, CCrossThreadFlag** ppDeviceGoneFlag) { BOOL fRet; if (_FindAutoplayStructAndExecuteCB (pszAltDeviceID, _GetDeviceGoneFlagCB, ppDeviceGoneFlag, &fRet)) { (*ppDeviceGoneFlag)->AddRef(); } else { TraceMsg(TF_MOUNTPOINT, "AUTOPLAY: GetDeviceGoneFlag for %s -> Did not find!", pszAltDeviceID); fRet = FALSE; } return fRet; }