// DeviceConsole.cpp : Implementation of CDeviceConsole #include "stdafx.h" #include "DevCon2.h" #include "DeviceConsole.h" #include "Devices.h" #include "SetupClasses.h" #include "xStrings.h" #include "utils.h" #include ///////////////////////////////////////////////////////////////////////////// // CDeviceConsole // // a window is used for events to ensure that we dispatch the events // within the same apartment as the DeviceConsole object // LRESULT CDevConNotifyWindow::OnDeviceChange(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT ret = TRUE; bHandled = TRUE; switch(wParam) { case DBT_DEVNODES_CHANGED: // // post this so we don't block sender // PostMessage(UM_POSTGLOBALEVENT,wParam); return TRUE; default: ; } return ret; } LRESULT CDevConNotifyWindow::OnPostGlobalEvent(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT ret = TRUE; bHandled = TRUE; // // defer this global event to CDeviceConsole // if(m_pDevCon) { m_pDevCon->FireGlobalEvent(wParam); } return ret; } LRESULT CDevConNotifyWindow::OnPostEvents(UINT nMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { LRESULT ret = TRUE; bHandled = TRUE; // // handle all the pending events // return ret; } STDMETHODIMP CDeviceConsole::AllDevices(VARIANT flags, VARIANT machine, LPDISPATCH *pDevices) { CComVariant m; DWORD diflags = 0; HRESULT hr; LPCWSTR pMachine; HDEVINFO hDevInfo; *pDevices = NULL; hr = TranslateDeviceFlags(&flags,&diflags); if(FAILED(hr)) { return hr; } diflags |= DIGCF_ALLCLASSES; hr = GetOptionalString(&machine,m,&pMachine); if(FAILED(hr)) { return hr; } hDevInfo = SetupDiGetClassDevsEx(NULL,NULL,NULL,diflags,NULL,pMachine,NULL); if(hDevInfo == INVALID_HANDLE_VALUE) { DWORD Err = GetLastError(); return HRESULT_FROM_SETUPAPI(Err); } return BuildDeviceList(hDevInfo,pDevices); } HRESULT CDeviceConsole::BuildDeviceList(HDEVINFO hDevInfo,LPDISPATCH *pDevices) { *pDevices = NULL; HRESULT hr; CComObject *d; hr = CComObject::CreateInstance(&d); if(FAILED(hr)) { SetupDiDestroyDeviceInfoList(hDevInfo); return hr; } CComPtr dPtr = d; hr = d->Init(hDevInfo,this); if(FAILED(hr)) { return hr; } *pDevices = dPtr.Detach(); return S_OK; } STDMETHODIMP CDeviceConsole::CreateEmptyDeviceList(VARIANT machine, LPDISPATCH *pDevices) { CComVariant machine_v; LPCWSTR pMachine; HRESULT hr; HDEVINFO hDevInfo; DWORD Err; hr = GetOptionalString(&machine,machine_v,&pMachine); if(FAILED(hr)) { return hr; } hDevInfo = SetupDiCreateDeviceInfoListEx(NULL, NULL, pMachine, NULL); if(hDevInfo != INVALID_HANDLE_VALUE) { return BuildDeviceList(hDevInfo,pDevices); } Err = GetLastError(); return HRESULT_FROM_SETUPAPI(Err); } typedef BOOL (WINAPI *UpdateDriverForPlugAndPlayDevicesProtoW)(HWND hwndParent, LPCWSTR HardwareId, LPCWSTR FullInfPath, DWORD InstallFlags, PBOOL bRebootRequired OPTIONAL ); #define UPDATEDRIVERFORPLUGANDPLAYDEVICESW "UpdateDriverForPlugAndPlayDevicesW" STDMETHODIMP CDeviceConsole::UpdateDriver(BSTR infname, BSTR hwid, VARIANT op_flags) { HMODULE newdevMod = NULL; UpdateDriverForPlugAndPlayDevicesProtoW UpdateFnW; BOOL reboot = FALSE; DWORD flags = 0; WCHAR InfPath[MAX_PATH]; HRESULT hr; CComVariant flags_v; // // op_flags are optional // if(V_VT(&op_flags)!=VT_ERROR) { hr = flags_v.ChangeType(VT_BSTR,&op_flags); if(FAILED(hr)) { return hr; } flags = V_I4(&flags_v); } else if (V_ERROR(&op_flags) != DISP_E_PARAMNOTFOUND) { hr = V_ERROR(&op_flags); return hr; } // // Inf must be a full pathname // if(GetFullPathName(infname,MAX_PATH,InfPath,NULL) >= MAX_PATH) { // // inf pathname too long // return E_INVALIDARG; } // // make use of UpdateDriverForPlugAndPlayDevices // newdevMod = LoadLibrary(TEXT("newdev.dll")); if(!newdevMod) { goto final; } UpdateFnW = (UpdateDriverForPlugAndPlayDevicesProtoW)GetProcAddress(newdevMod,UPDATEDRIVERFORPLUGANDPLAYDEVICESW); if(!UpdateFnW) { goto final; } if(!UpdateFnW(NULL,hwid,InfPath,flags,&reboot)) { DWORD Err = GetLastError(); hr = HRESULT_FROM_SETUPAPI(Err); goto final; } if(reboot) { RebootRequired = VARIANT_TRUE; hr = S_FALSE; } else { hr = S_OK; } final: if(newdevMod) { FreeLibrary(newdevMod); } return hr; } STDMETHODIMP CDeviceConsole::CheckReboot() { WCHAR RebootText[MAX_PATH]; WCHAR RebootCaption[MAX_PATH]; if(!RebootRequired) { return S_OK; } int str = LoadString(GetModuleHandle(NULL),IDS_REBOOTREQ,RebootText,MAX_PATH); if(!str) { return E_UNEXPECTED; } str = LoadString(GetModuleHandle(NULL),IDS_REBOOTCAP,RebootCaption,MAX_PATH); if(!str) { return E_UNEXPECTED; } MessageBeep(MB_ICONSTOP); int mb = MessageBox(NULL,RebootText,RebootCaption,MB_YESNO|MB_ICONSTOP); if(mb == IDOK) { return RebootReasonHardware(); } return S_FALSE; } STDMETHODIMP CDeviceConsole::RebootReasonHardware() { HANDLE Token; TOKEN_PRIVILEGES NewPrivileges; LUID Luid; // // On WinNT, need to "turn on" reboot privilege // if any of this fails, try reboot anyway // if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&Token)) { goto final; } if(!LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&Luid)) { CloseHandle(Token); goto final; } NewPrivileges.PrivilegeCount = 1; NewPrivileges.Privileges[0].Luid = Luid; NewPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges( Token, FALSE, &NewPrivileges, 0, NULL, NULL ); CloseHandle(Token); final: // // attempt reboot - inform system that this is planned hardware install // return ExitWindowsEx(EWX_REBOOT, REASON_PLANNED_FLAG|REASON_HWINSTALL) ? S_OK : E_UNEXPECTED; } STDMETHODIMP CDeviceConsole::get_RebootRequired(VARIANT_BOOL *pVal) { *pVal = RebootRequired ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } STDMETHODIMP CDeviceConsole::put_RebootRequired(VARIANT_BOOL newVal) { RebootRequired = newVal ? VARIANT_TRUE : VARIANT_FALSE; return S_OK; } STDMETHODIMP CDeviceConsole::SetupClasses(VARIANT classList, VARIANT machine, LPDISPATCH *pDevices) { *pDevices = NULL; CComVariant m; HRESULT hr; LPCWSTR pMachine; hr = GetOptionalString(&machine,m,&pMachine); if(FAILED(hr)) { return hr; } CComObject *d; hr = CComObject::CreateInstance(&d); if(FAILED(hr)) { return hr; } CComPtr dPtr = d; hr = d->Init(pMachine,this); if(FAILED(hr)) { return hr; } if(IsBlankString(&classList)) { hr = d->AllClasses(); } else { hr = d->Add(classList); } if(FAILED(hr)) { return hr; } *pDevices = dPtr.Detach(); return S_OK; } STDMETHODIMP CDeviceConsole::CreateEmptySetupClassList(VARIANT machine, LPDISPATCH *pResult) { *pResult = NULL; CComVariant m; HRESULT hr; LPCWSTR pMachine; hr = GetOptionalString(&machine,m,&pMachine); if(FAILED(hr)) { return hr; } CComObject *d; hr = CComObject::CreateInstance(&d); if(FAILED(hr)) { return hr; } CComPtr dPtr = d; hr = d->Init(pMachine,this); if(FAILED(hr)) { return hr; } *pResult = dPtr.Detach(); return S_OK; } STDMETHODIMP CDeviceConsole::DevicesBySetupClasses(VARIANT SetupClasses, VARIANT flags, VARIANT machine, LPDISPATCH *pDevices) { *pDevices = NULL; CComObject *pClasses = NULL; CComVariant m; HRESULT hr; LPCWSTR pMachine; // // shorthand for initializing class collection // to get devices // hr = GetOptionalString(&machine,m,&pMachine); if(FAILED(hr)) { return hr; } hr = CComObject::CreateInstance(&pClasses); if(FAILED(hr)) { return hr; } CComPtr pClassesPtr = pClasses; hr = pClasses->Init(pMachine,this); if(FAILED(hr)) { return hr; } hr = pClasses->Add(SetupClasses); if(FAILED(hr)) { return hr; } hr = pClasses->Devices(flags,pDevices); return hr; } STDMETHODIMP CDeviceConsole::DevicesByInterfaceClasses(VARIANT InterfaceClasses, VARIANT machine, LPDISPATCH *pDevicesOut) { *pDevicesOut = NULL; // // similar to above, but for interface classes // CComObject *pStrings = NULL; CComObject *pDevices = NULL; CComVariant m; HRESULT hr; LPCWSTR pMachine; HDEVINFO hDevInfo = INVALID_HANDLE_VALUE; HDEVINFO hPrevDevInfo = NULL; DWORD Err; DWORD c; BSTR str; hr = GetOptionalString(&machine,m,&pMachine); if(FAILED(hr)) { return hr; } hr = CComObject::CreateInstance(&pStrings); if(FAILED(hr)) { return hr; } CComPtr pStringsPtr = pStrings; hr = pStrings->Add(InterfaceClasses); if(FAILED(hr)) { return hr; } for(c=0;pStrings->InternalEnum(c,&str);c++) { // // convert string to interface // GUID guid; hr = CLSIDFromString(str,&guid); if(FAILED(hr)) { return hr; } // // query present devices of interface // hDevInfo = SetupDiGetClassDevsEx(&guid,NULL,NULL,DIGCF_DEVICEINTERFACE|DIGCF_PRESENT,hPrevDevInfo,pMachine,NULL); if(hDevInfo == INVALID_HANDLE_VALUE) { Err = GetLastError(); if(hPrevDevInfo) { SetupDiDestroyDeviceInfoList(hPrevDevInfo); } return HRESULT_FROM_SETUPAPI(Err); } hPrevDevInfo = hDevInfo; } if(hDevInfo == INVALID_HANDLE_VALUE) { return E_INVALIDARG; } // // now build resultant list // hr = CComObject::CreateInstance(&pDevices); if(FAILED(hr)) { SetupDiDestroyDeviceInfoList(hDevInfo); return hr; } CComPtr pDevicesPtr = pDevices; hr = pDevices->Init(hDevInfo,this); if(FAILED(hr)) { return hr; } *pDevicesOut = pDevicesPtr.Detach(); return S_OK; } STDMETHODIMP CDeviceConsole::DevicesByInstanceIds(VARIANT InstanceIdList, VARIANT machine, LPDISPATCH *pDevices) { // // shorthand for CreateEmptyDeviceList followed by Add // HRESULT hr; LPDISPATCH Devices = NULL; CComQIPtr pIf; hr = CreateEmptyDeviceList(machine,&Devices); if(FAILED(hr)) { return hr; } pIf = Devices; if(!pIf) { return E_UNEXPECTED; } hr = pIf->Add(InstanceIdList); if(FAILED(hr)) { Devices->Release(); return hr; } *pDevices = Devices; return S_OK; } STDMETHODIMP CDeviceConsole::StringList(VARIANT from, LPDISPATCH *pDest) { // // convinience only // *pDest = NULL; HRESULT hr; CComObject *pStrings = NULL; hr = CComObject::CreateInstance(&pStrings); if(FAILED(hr)) { return hr; } CComPtr pStringsPtr = pStrings; if(!IsNoArg(&from)) { hr = pStrings->Add(from); if(FAILED(hr)) { return hr; } } *pDest = pStringsPtr.Detach(); return S_OK; } STDMETHODIMP CDeviceConsole::AttachEvent(/*[in]*/ BSTR eventName,/*[in]*/ LPDISPATCH handler,/*[out, retval]*/ VARIANT_BOOL *pOk) { *pOk = VARIANT_FALSE; if(!NotifyWindow()) { return E_OUTOFMEMORY; } return m_Events.AttachEvent(eventName,handler,pOk); } STDMETHODIMP CDeviceConsole::DetachEvent(/*[in]*/ BSTR eventName,/*[in]*/ LPDISPATCH handler,/*[out, retval]*/ VARIANT_BOOL *pOk) { return m_Events.DetachEvent(eventName,handler,pOk); } CDevConNotifyWindow *CDeviceConsole::NotifyWindow() { if(m_pNotifyWindow) { return m_pNotifyWindow; } m_pNotifyWindow = new CDevConNotifyWindow; if(!m_pNotifyWindow) { return NULL; } m_pNotifyWindow->m_pDevCon = this; RECT nil; nil.top = 0; nil.left = 0; nil.bottom = 8; nil.right = 8; if(!m_pNotifyWindow->Create(NULL,nil,NULL)) { delete m_pNotifyWindow; m_pNotifyWindow = NULL; return NULL; } return m_pNotifyWindow; } void CDeviceConsole::FireGlobalEvent(WPARAM wParam) { switch(wParam) { case DBT_DEVNODES_CHANGED: m_Events.Invoke(L"OnDeviceNodesChanged",0,NULL); break; } } HRESULT CEventsDispEntry::AttachEvent(LPDISPATCH pDisp,VARIANT_BOOL *pStatus) { try { *pStatus = VARIANT_FALSE; push_back(pDisp); *pStatus = VARIANT_TRUE; } catch(std::bad_alloc) { return E_OUTOFMEMORY; } catch(...) { return E_INVALIDARG; } return S_OK; } HRESULT CEventsDispEntry::DetachEvent(LPDISPATCH pDisp,VARIANT_BOOL *pStatus) { try { *pStatus = VARIANT_FALSE; remove(pDisp); *pStatus = VARIANT_TRUE; } catch(std::bad_alloc) { return E_OUTOFMEMORY; } catch(...) { return E_INVALIDARG; } return S_OK; } HRESULT CEventsDispEntry::Invoke(UINT argc,VARIANT *argv) { CEventsDispEntry::iterator i; for(i = begin();i != end();i++) { i->Invoke(argc,argv); } return S_OK; } CEventsDispEntry & CEventsMap::LookupNc(LPWSTR Name) throw(std::bad_alloc) { HRESULT hr; size_t len = wcslen(Name)+1; wchar_t *pBuffer = new wchar_t[len+1]; if(!pBuffer) { throw std::bad_alloc(); } wcscpy(pBuffer,Name); _wcsupr(pBuffer); std::wstring ind = pBuffer; delete [] pBuffer; return (*this)[ind]; } HRESULT CEventsMap::AttachEvent(LPWSTR Name,LPDISPATCH pDisp,VARIANT_BOOL *pStatus) { HRESULT hr; try { *pStatus = VARIANT_FALSE; CEventsDispEntry &Ent = LookupNc(Name); hr = Ent.AttachEvent(pDisp,pStatus); } catch(std::bad_alloc) { return E_OUTOFMEMORY; } catch(...) { return E_INVALIDARG; } return hr; } HRESULT CEventsMap::DetachEvent(LPWSTR Name,LPDISPATCH pDisp,VARIANT_BOOL *pStatus) { HRESULT hr; try { *pStatus = VARIANT_FALSE; CEventsDispEntry &Ent = LookupNc(Name); hr = Ent.DetachEvent(pDisp,pStatus); } catch(std::bad_alloc) { return E_OUTOFMEMORY; } catch(...) { return E_INVALIDARG; } return hr; } HRESULT CEventsMap::Invoke(LPWSTR Name,UINT argc,VARIANT *argv) { HRESULT hr; try { CEventsDispEntry &Ent = LookupNc(Name); hr = Ent.Invoke(argc,argv); } catch(std::bad_alloc) { return E_OUTOFMEMORY; } catch(...) { return E_INVALIDARG; } return hr; }