527 lines
9.7 KiB
C++
527 lines
9.7 KiB
C++
// Devices.cpp : Implementation of CDevices
|
|
#include "stdafx.h"
|
|
#include "DevCon2.h"
|
|
#include "Devices.h"
|
|
#include "Device.h"
|
|
#include "DevicesEnum.h"
|
|
#include "DevInfoSet.h"
|
|
#include "xStrings.h"
|
|
#include "utils.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CDevices
|
|
|
|
CDevices::~CDevices()
|
|
{
|
|
DWORD c;
|
|
if(pDevices) {
|
|
for(c=0;c<Count;c++) {
|
|
pDevices[c]->Release();
|
|
}
|
|
delete [] pDevices;
|
|
}
|
|
}
|
|
|
|
|
|
STDMETHODIMP CDevices::get_Count(long *pVal)
|
|
{
|
|
*pVal = (long)Count;
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDevices::Item(long Index, LPDISPATCH *ppVal)
|
|
{
|
|
*ppVal = NULL;
|
|
DWORD i = (DWORD)Index;
|
|
if(i<1 || i > Count) {
|
|
return E_INVALIDARG;
|
|
}
|
|
i--;
|
|
pDevices[i]->AddRef();
|
|
*ppVal = pDevices[i];
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDevices::get__NewEnum(IUnknown **ppUnk)
|
|
{
|
|
*ppUnk = NULL;
|
|
HRESULT hr;
|
|
CComObject<CDevicesEnum> *pEnum = NULL;
|
|
hr = CComObject<CDevicesEnum>::CreateInstance(&pEnum);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
if(!pEnum) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
pEnum->AddRef();
|
|
if(!pEnum->CopyDevices(pDevices,Count)) {
|
|
pEnum->Release();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
*ppUnk = pEnum;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CDevices::InternalAdd(LPCWSTR InstanceId)
|
|
{
|
|
HRESULT hr;
|
|
DWORD c;
|
|
HDEVINFO hDevInfo = GetDevInfoSet();
|
|
if(hDevInfo == INVALID_HANDLE_VALUE) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
if(!IncreaseArraySize(1)) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
//
|
|
// create the SP_DEVINFO_DATA holder
|
|
//
|
|
CComObject<CDevice> *pDevice = NULL;
|
|
hr = CComObject<CDevice>::CreateInstance(&pDevice);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
CComPtr<IDevice> pDevicePtr = pDevice;
|
|
hr = pDevice->Init(DevInfoSet,InstanceId,DeviceConsole);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
//
|
|
// see if we have a device already in our list
|
|
// if we have, don't add another copy
|
|
//
|
|
for(c=0;c<Count;c++) {
|
|
if(pDevices[c]->SameAs(pDevice)) {
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
pDevicePtr.Detach();
|
|
pDevices[Count++] = pDevice;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDevices::Add(VARIANT InstanceIds)
|
|
{
|
|
CComObject<CStrings> *pStrings = NULL;
|
|
HRESULT hr;
|
|
BSTR str;
|
|
DWORD c;
|
|
|
|
hr = CComObject<CStrings>::CreateInstance(&pStrings);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
CComPtr<IStrings> pStringsPtr = pStrings;
|
|
|
|
hr = pStrings->Add(InstanceIds);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
for(c=0;pStrings->InternalEnum(c,&str);c++) {
|
|
//
|
|
// convert string to interface
|
|
//
|
|
hr = InternalAdd(str);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP CDevices::Remove(VARIANT Index)
|
|
{
|
|
//
|
|
// remove from logical list
|
|
// removal from HDEVINFO based on refcounting
|
|
//
|
|
DWORD i;
|
|
HRESULT hr;
|
|
hr = GetIndex(&Index,&i);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
if(i >= Count) {
|
|
return E_INVALIDARG;
|
|
}
|
|
pDevices[i]->Release();
|
|
Count--;
|
|
DWORD c;
|
|
for(c=i;c<Count;c++) {
|
|
pDevices[c] = pDevices[c+1];
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDevices::Init(HDEVINFO hDevInfo,IDeviceConsole *pDevCon)
|
|
{
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
Reset();
|
|
DWORD c;
|
|
HRESULT hr;
|
|
|
|
CComObject<CDevInfoSet> *d;
|
|
hr = CComObject<CDevInfoSet>::CreateInstance(&d);
|
|
if(FAILED(hr)) {
|
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
|
return hr;
|
|
}
|
|
DeviceConsole = pDevCon;
|
|
DevInfoSet = d; // addref's
|
|
d->Init(hDevInfo);
|
|
|
|
ZeroMemory(&DeviceInfoData,sizeof(DeviceInfoData));
|
|
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
|
|
for(c=0;SetupDiEnumDeviceInfo(hDevInfo,c,&DeviceInfoData);c++) {
|
|
if(!IncreaseArraySize(1)) {
|
|
Reset();
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
CComObject<CDevice> *pDevice = NULL;
|
|
hr = CComObject<CDevice>::CreateInstance(&pDevice);
|
|
if(FAILED(hr)) {
|
|
Reset();
|
|
return hr;
|
|
}
|
|
pDevice->AddRef();
|
|
pDevices[Count++] = pDevice;
|
|
hr = pDevice->Init(DevInfoSet,&DeviceInfoData,DeviceConsole);
|
|
if(FAILED(hr)) {
|
|
Reset();
|
|
return hr;
|
|
}
|
|
}
|
|
return Count ? S_OK : S_FALSE;
|
|
}
|
|
|
|
WINSETUPAPI BOOL WINAPI
|
|
SetupDiEnumDeviceInfo(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN DWORD MemberIndex,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData
|
|
);
|
|
|
|
void CDevices::Reset()
|
|
{
|
|
DWORD c;
|
|
for(c=0;c<Count;c++) {
|
|
pDevices[c]->Release();
|
|
}
|
|
Count = 0;
|
|
DevInfoSet = NULL;
|
|
}
|
|
|
|
BOOL CDevices::IncreaseArraySize(DWORD add)
|
|
{
|
|
CDevice** pNewDevices;
|
|
DWORD Inc;
|
|
DWORD c;
|
|
|
|
if((ArraySize-Count)>=add) {
|
|
return TRUE;
|
|
}
|
|
Inc = ArraySize + add + 32;
|
|
pNewDevices = new CDevice*[Inc];
|
|
if(!pNewDevices) {
|
|
return FALSE;
|
|
}
|
|
for(c=0;c<Count;c++) {
|
|
pNewDevices[c] = pDevices[c];
|
|
}
|
|
delete [] pDevices;
|
|
pDevices = pNewDevices;
|
|
ArraySize = Inc;
|
|
return TRUE;
|
|
}
|
|
|
|
STDMETHODIMP CDevices::CreateRootDevice(VARIANT hwidParam, LPDISPATCH *pDispatch)
|
|
{
|
|
*pDispatch = NULL;
|
|
|
|
HRESULT hr;
|
|
DWORD len;
|
|
DWORD Err;
|
|
LPWSTR hwidlist = NULL;
|
|
CComObject<CDevice> *pDevice = NULL;
|
|
HDEVINFO hDevInfo;
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
WCHAR name[33];
|
|
DWORD c,cc;
|
|
LPCWSTR lastPart;
|
|
LPCWSTR hwid;
|
|
CComVariant hwid_v;
|
|
LPCGUID pGuid = NULL;
|
|
|
|
//
|
|
// prepare list if we need to
|
|
//
|
|
hDevInfo = GetDevInfoSet();
|
|
if(hDevInfo == INVALID_HANDLE_VALUE) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
if(!IncreaseArraySize(1)) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
hr = GetOptionalString(&hwidParam,hwid_v,&hwid);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// see if this devices collection is associated with a setup class
|
|
//
|
|
SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
|
|
|
|
devInfoListDetail.cbSize = sizeof(devInfoListDetail);
|
|
if(!SetupDiGetDeviceInfoListDetail(hDevInfo,&devInfoListDetail)) {
|
|
DWORD Err = GetLastError();
|
|
return HRESULT_FROM_SETUPAPI(Err);
|
|
}
|
|
|
|
if(memcmp(&devInfoListDetail.ClassGuid,&GUID_NULL,sizeof(devInfoListDetail.ClassGuid)) != 0) {
|
|
//
|
|
// collection is locked to a class, use that class to create device
|
|
//
|
|
pGuid = &devInfoListDetail.ClassGuid;
|
|
} else {
|
|
//
|
|
// class is unknown
|
|
//
|
|
pGuid = &GUID_DEVCLASS_UNKNOWN;
|
|
}
|
|
|
|
if(hwid) {
|
|
//
|
|
// use hwid as basis of name
|
|
// this really has no significant meaning, but helps for diagnostics
|
|
// another option would be to use class name
|
|
// be we don't know classname here
|
|
//
|
|
lastPart = wcsrchr(hwid,L'\\');
|
|
if(!lastPart) {
|
|
lastPart = hwid;
|
|
}
|
|
for(c=0,cc=0;c<16;c++) {
|
|
//
|
|
// ignore troublesome characters
|
|
//
|
|
while(lastPart[cc] &&
|
|
((lastPart[cc] == L'/')
|
|
|| (lastPart[cc] == L'\\')
|
|
|| (lastPart[cc] == L'#')
|
|
|| (lastPart[cc] >= 0x7f)
|
|
|| (lastPart[cc] <= 0x20)
|
|
)) {
|
|
cc++;
|
|
}
|
|
if(!hwid[cc]) {
|
|
break;
|
|
}
|
|
name[c] = hwid[cc];
|
|
}
|
|
} else {
|
|
c = 0;
|
|
}
|
|
if(c) {
|
|
name[c] = L'0';
|
|
} else {
|
|
wcscpy(name,L"NONAME");
|
|
}
|
|
|
|
ZeroMemory(&DeviceInfoData,sizeof(DeviceInfoData));
|
|
DeviceInfoData.cbSize = sizeof(DeviceInfoData);
|
|
if (!SetupDiCreateDeviceInfo(hDevInfo,
|
|
name,
|
|
pGuid,
|
|
NULL,
|
|
0,
|
|
DICD_GENERATE_ID,
|
|
&DeviceInfoData))
|
|
{
|
|
Err = GetLastError();
|
|
hr = HRESULT_FROM_SETUPAPI(Err);
|
|
return hr;
|
|
}
|
|
|
|
if(hwid && hwid[0]) {
|
|
//
|
|
// Add the HardwareID to the Device's HardwareID property.
|
|
//
|
|
len = wcslen(hwid);
|
|
hwidlist = new WCHAR[len+2];
|
|
if(!hwidlist) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto final;
|
|
}
|
|
wcsncpy(hwidlist,hwid,len+1);
|
|
hwidlist[len] = L'\0';
|
|
hwidlist[len+1] = L'\0';
|
|
|
|
if(!SetupDiSetDeviceRegistryProperty(hDevInfo,
|
|
&DeviceInfoData,
|
|
SPDRP_HARDWAREID,
|
|
(LPBYTE)hwidlist,
|
|
(len+2)*sizeof(TCHAR)))
|
|
{
|
|
Err = GetLastError();
|
|
hr = HRESULT_FROM_SETUPAPI(Err);
|
|
goto final;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Transform the registry element into an actual devnode
|
|
// in the PnP HW tree.
|
|
//
|
|
if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE,
|
|
hDevInfo,
|
|
&DeviceInfoData))
|
|
{
|
|
Err = GetLastError();
|
|
hr = HRESULT_FROM_SETUPAPI(Err);
|
|
goto final;
|
|
}
|
|
|
|
//
|
|
// create the SP_DEVINFO_DATA holder
|
|
//
|
|
hr = CComObject<CDevice>::CreateInstance(&pDevice);
|
|
if(FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
pDevice->AddRef();
|
|
hr = pDevice->Init(DevInfoSet,&DeviceInfoData,DeviceConsole);
|
|
if(FAILED(hr)) {
|
|
pDevice->Release();
|
|
goto final;
|
|
}
|
|
//
|
|
// Add to list
|
|
//
|
|
pDevices[Count++] = pDevice;
|
|
//
|
|
// Return it
|
|
//
|
|
pDevice->AddRef();
|
|
*pDispatch = pDevice;
|
|
|
|
hr = S_OK;
|
|
|
|
final:
|
|
|
|
if(hwidlist) {
|
|
delete [] hwidlist;
|
|
}
|
|
|
|
if(FAILED(hr)) {
|
|
if(!SetupDiCallClassInstaller(DIF_REMOVE,hDevInfo,&DeviceInfoData)) {
|
|
//
|
|
// if we failed to delete including class/co installers
|
|
// force it
|
|
//
|
|
SetupDiRemoveDevice(hDevInfo,&DeviceInfoData);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HDEVINFO CDevices::GetDevInfoSet()
|
|
{
|
|
ULONGLONG h;
|
|
HRESULT hr;
|
|
|
|
if(!DevInfoSet) {
|
|
return (HDEVINFO)INVALID_HANDLE_VALUE;
|
|
}
|
|
hr = DevInfoSet->get_Handle(&h);
|
|
if(FAILED(hr)) {
|
|
return (HDEVINFO)INVALID_HANDLE_VALUE;
|
|
}
|
|
return (HDEVINFO)h;
|
|
}
|
|
|
|
HRESULT CDevices::GetIndex(LPVARIANT Index,DWORD * pAt)
|
|
{
|
|
CComVariant v;
|
|
HRESULT hr;
|
|
if(IsNumericVariant(Index)) {
|
|
hr = v.ChangeType(VT_I4,Index);
|
|
if(FAILED(hr)) {
|
|
return DISP_E_TYPEMISMATCH;
|
|
}
|
|
if(V_I4(&v)<1) {
|
|
return E_INVALIDARG;
|
|
}
|
|
*pAt = ((DWORD)V_I4(&v))-1;
|
|
return S_OK;
|
|
}
|
|
//
|
|
// user actually supplied instance id
|
|
//
|
|
hr = v.ChangeType(VT_BSTR,Index);
|
|
if(FAILED(hr)) {
|
|
return DISP_E_TYPEMISMATCH;
|
|
}
|
|
if(!Count) {
|
|
//
|
|
// cannot match anything
|
|
//
|
|
return E_INVALIDARG;
|
|
}
|
|
//
|
|
// find an existing device that matches this
|
|
//
|
|
DWORD c;
|
|
for(c=0;c<Count;c++) {
|
|
if(pDevices[c]->SameAs(V_BSTR(&v))) {
|
|
*pAt = c;
|
|
return S_OK;
|
|
}
|
|
}
|
|
//
|
|
// none found
|
|
//
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
STDMETHODIMP CDevices::get_Machine(BSTR *pVal)
|
|
{
|
|
HDEVINFO hDevInfo = GetDevInfoSet();
|
|
if(hDevInfo == INVALID_HANDLE_VALUE) {
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail;
|
|
|
|
devInfoListDetail.cbSize = sizeof(devInfoListDetail);
|
|
if(!SetupDiGetDeviceInfoListDetail(hDevInfo,&devInfoListDetail)) {
|
|
DWORD Err = GetLastError();
|
|
return HRESULT_FROM_SETUPAPI(Err);
|
|
}
|
|
|
|
if((devInfoListDetail.RemoteMachineHandle == NULL) || !devInfoListDetail.RemoteMachineName[0]) {
|
|
*pVal = SysAllocString(L"");
|
|
if(*pVal) {
|
|
return S_FALSE;
|
|
}
|
|
} else {
|
|
*pVal = SysAllocString(devInfoListDetail.RemoteMachineName);
|
|
if(*pVal) {
|
|
return S_OK;
|
|
}
|
|
}
|
|
*pVal = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|