windows-nt/Source/XPSP1/NT/base/pnp/tools/devcon2/deviceconsole.cpp

660 lines
16 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
// 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 <dbt.h>
/////////////////////////////////////////////////////////////////////////////
// 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<CDevices> *d;
hr = CComObject<CDevices>::CreateInstance(&d);
if(FAILED(hr)) {
SetupDiDestroyDeviceInfoList(hDevInfo);
return hr;
}
CComPtr<IDevices> 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<CSetupClasses> *d;
hr = CComObject<CSetupClasses>::CreateInstance(&d);
if(FAILED(hr)) {
return hr;
}
CComPtr<ISetupClasses> 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<CSetupClasses> *d;
hr = CComObject<CSetupClasses>::CreateInstance(&d);
if(FAILED(hr)) {
return hr;
}
CComPtr<ISetupClasses> 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<CSetupClasses> *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<CSetupClasses>::CreateInstance(&pClasses);
if(FAILED(hr)) {
return hr;
}
CComPtr<ISetupClasses> 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<CStrings> *pStrings = NULL;
CComObject<CDevices> *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<CStrings>::CreateInstance(&pStrings);
if(FAILED(hr)) {
return hr;
}
CComPtr<IStrings> 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<CDevices>::CreateInstance(&pDevices);
if(FAILED(hr)) {
SetupDiDestroyDeviceInfoList(hDevInfo);
return hr;
}
CComPtr<IDevices> 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<IDevices> 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<CStrings> *pStrings = NULL;
hr = CComObject<CStrings>::CreateInstance(&pStrings);
if(FAILED(hr)) {
return hr;
}
CComPtr<IStrings> 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;
}