1236 lines
30 KiB
C++
1236 lines
30 KiB
C++
//==============================================================;
|
|
//
|
|
// This source code is only intended as a supplement to
|
|
// existing Microsoft documentation.
|
|
//
|
|
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
// PURPOSE.
|
|
//
|
|
// Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
//
|
|
//==============================================================;
|
|
#include <stdio.h>
|
|
#include "People.h"
|
|
#include <commctrl.h>
|
|
#include <comdef.h>
|
|
#include <windowsx.h>
|
|
|
|
const GUID CPeoplePoweredVehicle::thisGuid = { 0x2974380d, 0x4c4b, 0x11d2, { 0x89, 0xd8, 0x0, 0x0, 0x21, 0x47, 0x31, 0x28 } };
|
|
|
|
const GUID CBicycleFolder::thisGuid = { 0xef163732, 0x9353, 0x11d2, { 0x99, 0x67, 0x0, 0x80, 0xc7, 0xdc, 0xb3, 0xdc } };
|
|
const GUID CSkateboardFolder::thisGuid = { 0xef163733, 0x9353, 0x11d2, { 0x99, 0x67, 0x0, 0x80, 0xc7, 0xdc, 0xb3, 0xdc } };
|
|
const GUID CIceSkateFolder::thisGuid = { 0xf6c660b0, 0x9353, 0x11d2, { 0x99, 0x67, 0x0, 0x80, 0xc7, 0xdc, 0xb3, 0xdc } };
|
|
|
|
const GUID CBicycle::thisGuid = { 0xef163734, 0x9353, 0x11d2, { 0x99, 0x67, 0x0, 0x80, 0xc7, 0xdc, 0xb3, 0xdc } };
|
|
const GUID CSkateboard::thisGuid = { 0xef163735, 0x9353, 0x11d2, { 0x99, 0x67, 0x0, 0x80, 0xc7, 0xdc, 0xb3, 0xdc } };
|
|
const GUID CIceSkate::thisGuid = { 0xf6c660b1, 0x9353, 0x11d2, { 0x99, 0x67, 0x0, 0x80, 0xc7, 0xdc, 0xb3, 0xdc } };
|
|
|
|
|
|
#define WM_WMI_CONNECTED WM_APP // only sent to CBicycleFolder::m_connectHwnd
|
|
#define WM_REFRESH_EVENT WM_APP+1 // only sent to CBicycleFolder::m_connectHwnd
|
|
|
|
//==============================================================
|
|
//
|
|
// CEventSink implementation
|
|
//
|
|
class CEventSink : public IWbemObjectSink
|
|
{
|
|
public:
|
|
CEventSink(HWND hwnd) : m_hwnd(hwnd){}
|
|
~CEventSink(){};
|
|
|
|
STDMETHOD_(SCODE, Indicate)(long lObjectCount,
|
|
IWbemClassObject **pObjArray)
|
|
{
|
|
// Not actually using the pObjArray. Just need a trigger for the
|
|
// refresh.
|
|
::SendMessage(m_hwnd, WM_REFRESH_EVENT, 0, 0);
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHOD_(SCODE, SetStatus)(long lFlags,
|
|
HRESULT hResult,
|
|
BSTR strParam,
|
|
IWbemClassObject *pObjParam)
|
|
{
|
|
// SetStatus() may be called to indicate that your query becomes
|
|
// invalid or valid again ussually caused by multithreading 'situations'.
|
|
return S_OK;
|
|
}
|
|
|
|
// IUnknown members
|
|
STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
|
|
{
|
|
if(riid == IID_IUnknown || riid == IID_IWbemObjectSink)
|
|
{
|
|
*ppv = this;
|
|
|
|
// you're handing out a copy of yourself so account for it.
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
}
|
|
STDMETHODIMP_(ULONG) AddRef(void)
|
|
{
|
|
return InterlockedIncrement(&m_lRef);
|
|
}
|
|
STDMETHODIMP_(ULONG) Release(void)
|
|
{
|
|
// InterlockedDecrement() helps with thread safety.
|
|
int lNewRef = InterlockedDecrement(&m_lRef);
|
|
// when all the copies are released...
|
|
if(lNewRef == 0)
|
|
{
|
|
// kill thyself.
|
|
delete this;
|
|
}
|
|
|
|
return lNewRef;
|
|
}
|
|
|
|
private:
|
|
long m_lRef;
|
|
HWND m_hwnd;
|
|
};
|
|
|
|
//==============================================================
|
|
//
|
|
// CPeoplePoweredVehicle implementation
|
|
//
|
|
//
|
|
//----------------------------------------------------------
|
|
#define TEMP_BUF 255
|
|
|
|
bool CBicycleFolder::ErrorString(HRESULT hr,
|
|
TCHAR *errMsg, UINT errSize)
|
|
{
|
|
TCHAR szError[TEMP_BUF] = {0};
|
|
TCHAR szFacility[TEMP_BUF] = {0};
|
|
IWbemStatusCodeText * pStatus = NULL;
|
|
|
|
// initialize buffers.
|
|
memset(errMsg, 0, errSize * sizeof(TCHAR));
|
|
|
|
HRESULT hr1 = CoInitialize(NULL);
|
|
SCODE sc1 = CoCreateInstance(CLSID_WbemStatusCodeText,
|
|
0, CLSCTX_INPROC_SERVER,
|
|
IID_IWbemStatusCodeText,
|
|
(LPVOID *) &pStatus);
|
|
|
|
// loaded OK?
|
|
if(sc1 == S_OK)
|
|
{
|
|
BSTR bstr;
|
|
sc1 = pStatus->GetErrorCodeText(hr, 0, 0, &bstr);
|
|
if(sc1 == S_OK)
|
|
{
|
|
#ifdef UNICODE
|
|
wcsncpy(szError, bstr, TEMP_BUF-1);
|
|
#else
|
|
wcstombs(szError, bstr, TEMP_BUF-1);
|
|
#endif UNICODE
|
|
SysFreeString(bstr);
|
|
bstr = 0;
|
|
}
|
|
|
|
sc1 = pStatus->GetFacilityCodeText(hr, 0, 0, &bstr);
|
|
if(sc1 == S_OK)
|
|
{
|
|
#ifdef UNICODE
|
|
wcsncpy(szFacility, bstr, TEMP_BUF-1);
|
|
#else
|
|
wcstombs(szFacility, bstr, TEMP_BUF-1);
|
|
#endif UNICODE
|
|
SysFreeString(bstr);
|
|
bstr = 0;
|
|
}
|
|
|
|
// RELEASE
|
|
pStatus->Release();
|
|
pStatus = NULL;
|
|
}
|
|
else
|
|
{
|
|
::MessageBox(NULL, _T("WBEM error features not available. Upgrade WMI to a newer build."),
|
|
_T("Internal Error"), MB_ICONSTOP|MB_OK);
|
|
}
|
|
|
|
// if not msgs returned....
|
|
if(_tcslen(szFacility) == 0 || _tcslen(szError) == 0)
|
|
{
|
|
// format the error nbr as a reasonable default.
|
|
_stprintf(errMsg, _T("Error code: 0x%08X"), hr);
|
|
}
|
|
else
|
|
{
|
|
// format a readable msg.
|
|
_stprintf(errMsg, _T("%s: %s"), szFacility, szError);
|
|
}
|
|
|
|
if(hr1 == S_OK)
|
|
CoUninitialize();
|
|
|
|
return (SUCCEEDED(sc1) && SUCCEEDED(hr1));
|
|
}
|
|
|
|
CPeoplePoweredVehicle::CPeoplePoweredVehicle()
|
|
{
|
|
children[0] = new CBicycleFolder;
|
|
children[1] = new CSkateboardFolder;
|
|
children[2] = new CIceSkateFolder;
|
|
}
|
|
|
|
CPeoplePoweredVehicle::~CPeoplePoweredVehicle()
|
|
{
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++)
|
|
delete children[n];
|
|
}
|
|
|
|
HRESULT CPeoplePoweredVehicle::OnExpand(IConsoleNameSpace *pConsoleNameSpace, IConsole *pConsole, HSCOPEITEM parent)
|
|
{
|
|
SCOPEDATAITEM sdi;
|
|
|
|
if (!bExpanded) {
|
|
// create the child nodes, then expand them
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++) {
|
|
ZeroMemory(&sdi, sizeof(SCOPEDATAITEM) );
|
|
sdi.mask = SDI_STR | // Displayname is valid
|
|
SDI_PARAM | // lParam is valid
|
|
SDI_IMAGE | // nImage is valid
|
|
SDI_OPENIMAGE | // nOpenImage is valid
|
|
SDI_PARENT | // relativeID is valid
|
|
SDI_CHILDREN; // cChildren is valid
|
|
|
|
sdi.relativeID = (HSCOPEITEM)parent;
|
|
sdi.nImage = children[n]->GetBitmapIndex();
|
|
sdi.nOpenImage = INDEX_OPENFOLDER;
|
|
sdi.displayname = MMC_CALLBACK;
|
|
sdi.lParam = (LPARAM)children[n]; // The cookie
|
|
sdi.cChildren = 0;
|
|
|
|
HRESULT hr = pConsoleNameSpace->InsertItem( &sdi );
|
|
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
CBicycleFolder::CBicycleFolder() :
|
|
m_connectHwnd(0),
|
|
m_threadId(0), m_thread(0),
|
|
m_doWork(0), m_threadCmd(CT_CONNECT),
|
|
m_running(false), m_ptrReady(0),
|
|
m_pStream(0), m_realWMI(0),
|
|
m_pResultData(0), m_pStubSink(0),
|
|
m_pUnsecApp(0)
|
|
{
|
|
WNDCLASS wndClass;
|
|
|
|
ZeroMemory(&wndClass, sizeof(WNDCLASS));
|
|
|
|
wndClass.lpfnWndProc = WindowProc;
|
|
wndClass.lpszClassName = _T("connectthreadwindow");
|
|
wndClass.hInstance = g_hinst;
|
|
|
|
ATOM atom = RegisterClass(&wndClass);
|
|
m_connectHwnd = CreateWindow(
|
|
_T("connectthreadwindow"), // pointer to registered class name
|
|
NULL, // pointer to window name
|
|
0, // window style
|
|
0, // horizontal position of window
|
|
0, // vertical position of window
|
|
0, // window width
|
|
0, // window height
|
|
NULL, // handle to parent or owner window
|
|
NULL, // handle to menu or child-window identifier
|
|
g_hinst, // handle to application instance
|
|
(void *)this); // pointer to window-creation data
|
|
|
|
if (m_connectHwnd)
|
|
SetWindowLong(m_connectHwnd, GWL_USERDATA, (LONG)this);
|
|
|
|
InitializeCriticalSection(&m_critSect);
|
|
m_doWork = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
m_ptrReady = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
EnterCriticalSection(&m_critSect);
|
|
|
|
// NOTE: I'm connecting real early. You may want to connect from some other place.
|
|
m_threadCmd = CT_CONNECT;
|
|
SetEvent(m_doWork);
|
|
m_thread = CreateThread(NULL, 0, ThreadProc, (void *)this, 0, &m_threadId);
|
|
|
|
LeaveCriticalSection(&m_critSect);
|
|
}
|
|
|
|
CBicycleFolder::~CBicycleFolder()
|
|
{
|
|
EmptyChildren();
|
|
if(m_pResultData)
|
|
{
|
|
m_pResultData->Release();
|
|
m_pResultData = 0;
|
|
}
|
|
|
|
if(m_pStubSink)
|
|
{
|
|
IWbemServices *service = 0;
|
|
HRESULT hr = GetPtr(&service);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
service->CancelAsyncCall(m_pStubSink);
|
|
service->Release();
|
|
service = 0;
|
|
}
|
|
m_pStubSink->Release();
|
|
m_pStubSink = NULL;
|
|
}
|
|
|
|
if(m_pUnsecApp)
|
|
{
|
|
m_pUnsecApp->Release();
|
|
m_pUnsecApp = 0;
|
|
}
|
|
|
|
StopThread();
|
|
|
|
if(m_connectHwnd != NULL)
|
|
DestroyWindow(m_connectHwnd);
|
|
|
|
UnregisterClass(_T("connectthreadwindow"), NULL);
|
|
DeleteCriticalSection(&m_critSect);
|
|
}
|
|
|
|
void CBicycleFolder::StopThread()
|
|
{
|
|
EnterCriticalSection(&m_critSect);
|
|
m_running = false;
|
|
|
|
if (m_thread != NULL)
|
|
{
|
|
m_threadCmd = CT_EXIT;
|
|
SetEvent(m_doWork);
|
|
WaitForSingleObject(m_ptrReady, 10000);
|
|
|
|
CloseHandle(m_thread);
|
|
|
|
m_thread = NULL;
|
|
}
|
|
LeaveCriticalSection(&m_critSect);
|
|
}
|
|
|
|
LRESULT CALLBACK CBicycleFolder::WindowProc(
|
|
HWND hwnd, // handle to window
|
|
UINT uMsg, // message identifier
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam) // second message parameter
|
|
{
|
|
CBicycleFolder *pThis = (CBicycleFolder *)GetWindowLong(hwnd, GWL_USERDATA);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_WMI_CONNECTED:
|
|
if(pThis != NULL)
|
|
{
|
|
IWbemServices *service = 0;
|
|
HRESULT hr = pThis->GetPtr(&service);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
pThis->RegisterEventSink(service);
|
|
pThis->EnumChildren(service);
|
|
|
|
// m_pResultData gets set when an onShow has happened. If set, the user already wants
|
|
// to see equipment but the connection was slower than the UI. Catchup now.
|
|
if(pThis->m_pResultData)
|
|
pThis->DisplayChildren();
|
|
|
|
// done with the marshaled service ptr.
|
|
service->Release();
|
|
service = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TCHAR errMsg[255] = {0};
|
|
pThis->ErrorString((HRESULT)wParam, errMsg, 255);
|
|
|
|
MessageBox(hwnd, errMsg, _T("WMI Snapin Sample"), MB_OK|MB_ICONSTOP);
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_REFRESH_EVENT:
|
|
if(pThis != NULL)
|
|
{
|
|
IWbemServices *service = 0;
|
|
HRESULT hr = pThis->GetPtr(&service);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
pThis->EmptyChildren();
|
|
pThis->EnumChildren(service);
|
|
pThis->DisplayChildren();
|
|
|
|
// done with the marshaled service ptr.
|
|
service->Release();
|
|
service = 0;
|
|
}
|
|
}
|
|
break;
|
|
|
|
} //endswitch
|
|
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
void CBicycleFolder::RegisterEventSink(IWbemServices *service)
|
|
{
|
|
//NOTE: this logic is from the Wmi documentation,
|
|
// "Security Considerations with Asynchronous Calls" so you can
|
|
// follow along.
|
|
|
|
// allocate the sink if its not already allocated.
|
|
if(m_pStubSink == 0)
|
|
{
|
|
CEventSink *pEventSink = 0;
|
|
IUnknown* pStubUnk = 0;
|
|
|
|
// create the 'real' sink.
|
|
pEventSink = new CEventSink(m_connectHwnd);
|
|
pEventSink->AddRef();
|
|
|
|
// create an unsecapp object.
|
|
CoCreateInstance(CLSID_UnsecuredApartment, NULL,
|
|
CLSCTX_LOCAL_SERVER, IID_IUnsecuredApartment,
|
|
(void**)&m_pUnsecApp);
|
|
|
|
// give the 'real' sink to the unsecapp to manage. Get a 'pStubUnk' in return.
|
|
m_pUnsecApp->CreateObjectStub(pEventSink, &pStubUnk);
|
|
|
|
// from that pUnk, get a wrapper to your original sink.
|
|
pStubUnk->QueryInterface(IID_IWbemObjectSink, (void **)&m_pStubSink);
|
|
pStubUnk->Release();
|
|
|
|
// release the 'real' sink cuz m_pStubSink "owns" it now.
|
|
long ref = pEventSink->Release();
|
|
}
|
|
|
|
HRESULT hRes = S_OK;
|
|
BSTR qLang = SysAllocString(L"WQL");
|
|
BSTR query = SysAllocString(L"select * from __InstanceCreationEvent where TargetInstance isa \"Bicycle\"");
|
|
|
|
// execute the query. For *Async, the last parm is a sink object
|
|
// that will be sent the resultset instead of returning the normal
|
|
// enumerator object.
|
|
if(SUCCEEDED(hRes = service->ExecNotificationQueryAsync(qLang, query,
|
|
0L, NULL,
|
|
m_pStubSink)))
|
|
{
|
|
OutputDebugString(_T("Executed filter query\n"));
|
|
}
|
|
else
|
|
{
|
|
OutputDebugString(_T("ExecQuery() failed\n"));
|
|
|
|
} //endif ExecQuery()
|
|
|
|
SysFreeString(qLang);
|
|
SysFreeString(query);
|
|
}
|
|
|
|
void CBicycleFolder::EmptyChildren(void)
|
|
{
|
|
if(m_pResultData)
|
|
{
|
|
HRESULT hr = m_pResultData->DeleteAllRsltItems();
|
|
|
|
int last = m_children.GetSize();
|
|
for (int n = 0; n < last; n++)
|
|
{
|
|
if (m_children[n] != NULL)
|
|
delete m_children[n];
|
|
}
|
|
m_children.RemoveAll();
|
|
}
|
|
}
|
|
|
|
bool CBicycleFolder::EnumChildren(IWbemServices *service)
|
|
{
|
|
IEnumWbemClassObject *pEnumBikes = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
// get the list of bicycles...
|
|
if(SUCCEEDED(hr = service->CreateInstanceEnum((bstr_t)L"Bicycle",
|
|
WBEM_FLAG_SHALLOW,
|
|
NULL, &pEnumBikes)))
|
|
{
|
|
// NOTE: pBike MUST be set to NULL for Next().
|
|
IWbemClassObject *pBike = NULL;
|
|
CBicycle *pBikeInst = 0;
|
|
|
|
ULONG uReturned = 1;
|
|
|
|
while((SUCCEEDED(hr = pEnumBikes->Next(-1, 1, &pBike, &uReturned))) &&
|
|
(uReturned != 0))
|
|
{
|
|
// Add the bike...
|
|
pBikeInst = new CBicycle(this, pBike);
|
|
|
|
m_children.Add(pBikeInst);
|
|
|
|
// Done with this object. pBikeInst "owns" it now.
|
|
if(pBike)
|
|
{
|
|
pBike->Release();
|
|
|
|
// NOTE: pBike MUST be reset to NULL for Next().
|
|
pBike = NULL;
|
|
}
|
|
|
|
} // endwhile
|
|
|
|
// Done with this enumerator.
|
|
if (pEnumBikes)
|
|
{
|
|
pEnumBikes->Release();
|
|
pEnumBikes = NULL;
|
|
}
|
|
} // endif CreateInstanceEnum()
|
|
|
|
return SUCCEEDED(hr);
|
|
}
|
|
|
|
HRESULT CBicycleFolder::GetPtr(IWbemServices **ptr)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
m_threadCmd = CT_GET_PTR;
|
|
SetEvent(m_doWork);
|
|
WaitForSingleObject(m_ptrReady, 10000);
|
|
|
|
if(ptr && m_pStream)
|
|
{
|
|
*ptr = 0;
|
|
hr = CoGetInterfaceAndReleaseStream(m_pStream,
|
|
IID_IWbemServices,
|
|
(void**)ptr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
DWORD WINAPI CBicycleFolder::ThreadProc(LPVOID lpParameter)
|
|
{
|
|
CBicycleFolder *pThis = (CBicycleFolder *)lpParameter;
|
|
HRESULT hr = S_OK;
|
|
|
|
CoInitialize(NULL);
|
|
|
|
while(true)
|
|
{
|
|
WaitForSingleObject(pThis->m_doWork, -1);
|
|
|
|
switch(pThis->m_threadCmd)
|
|
{
|
|
case CT_CONNECT:
|
|
{
|
|
IWbemLocator *pLocator = 0;
|
|
HRESULT hr;
|
|
|
|
// Create an instance of the WbemLocator interface.
|
|
hr = CoCreateInstance(CLSID_WbemLocator,
|
|
NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IWbemLocator, (LPVOID *)&pLocator);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = pLocator->ConnectServer(L"root\\Vehicles",// Network
|
|
NULL, // User
|
|
NULL, // Password
|
|
NULL, // Locale
|
|
0, // Security Flags
|
|
NULL, // Authority
|
|
NULL, // Context
|
|
&pThis->m_realWMI); // Namespace
|
|
|
|
// tell the callback the result of the connection.
|
|
if(pThis->m_connectHwnd)
|
|
PostMessage(pThis->m_connectHwnd, WM_WMI_CONNECTED, hr, 0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CT_GET_PTR:
|
|
if(pThis->m_realWMI != NULL)
|
|
{
|
|
hr = CoMarshalInterThreadInterfaceInStream(IID_IWbemServices,
|
|
pThis->m_realWMI,
|
|
&(pThis->m_pStream));
|
|
}
|
|
|
|
SetEvent(pThis->m_ptrReady);
|
|
break;
|
|
|
|
case CT_EXIT:
|
|
if(pThis->m_realWMI != NULL)
|
|
{
|
|
pThis->m_realWMI->Release();
|
|
pThis->m_realWMI = 0;
|
|
}
|
|
SetEvent(pThis->m_ptrReady);
|
|
return 0;
|
|
break;
|
|
|
|
} //endswitch
|
|
|
|
} //endwhile(true)
|
|
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CBicycleFolder::DisplayChildren(void)
|
|
{
|
|
// insert items here
|
|
RESULTDATAITEM rdi;
|
|
HRESULT hr = S_OK;
|
|
int last = m_children.GetSize();
|
|
CBicycle *pBike = 0;
|
|
|
|
// create the child nodes, then expand them
|
|
for (int n = 0; n < last; n++)
|
|
{
|
|
pBike = (CBicycle *)m_children[n];
|
|
|
|
ZeroMemory(&rdi, sizeof(RESULTDATAITEM) );
|
|
rdi.mask = RDI_STR | // Displayname is valid
|
|
RDI_IMAGE | // nImage is valid
|
|
RDI_PARAM;
|
|
|
|
rdi.nImage = pBike->GetBitmapIndex();
|
|
rdi.str = MMC_CALLBACK;
|
|
rdi.nCol = 0;
|
|
rdi.lParam = (LPARAM)pBike;
|
|
|
|
if(m_pResultData)
|
|
hr = m_pResultData->InsertItem( &rdi );
|
|
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBicycleFolder::OnShow(IConsole *pConsole, BOOL bShow, HSCOPEITEM scopeitem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IHeaderCtrl *pHeaderCtrl = NULL;
|
|
|
|
if (bShow)
|
|
{
|
|
hr = pConsole->QueryInterface(IID_IHeaderCtrl, (void **)&pHeaderCtrl);
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
hr = pConsole->QueryInterface(IID_IResultData, (void **)&m_pResultData);
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
// Set the column headers in the results pane
|
|
hr = pHeaderCtrl->InsertColumn(0, L"Name", LVCFMT_LEFT, 150);
|
|
_ASSERT( S_OK == hr );
|
|
|
|
hr = pHeaderCtrl->InsertColumn(1, L"Owner", LVCFMT_LEFT, 200);
|
|
_ASSERT( S_OK == hr );
|
|
|
|
if(m_pResultData)
|
|
{
|
|
hr = m_pResultData->DeleteAllRsltItems();
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
if(!bExpanded)
|
|
{
|
|
hr = DisplayChildren();
|
|
}
|
|
|
|
pHeaderCtrl->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
CIceSkateFolder::CIceSkateFolder()
|
|
{
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++) {
|
|
children[n] = new CIceSkate(n + 1);
|
|
}
|
|
}
|
|
|
|
CIceSkateFolder::~CIceSkateFolder()
|
|
{
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++)
|
|
if (children[n]) {
|
|
delete children[n];
|
|
}
|
|
}
|
|
|
|
HRESULT CIceSkateFolder::OnShow(IConsole *pConsole, BOOL bShow, HSCOPEITEM scopeitem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IHeaderCtrl *pHeaderCtrl = NULL;
|
|
IResultData *pResultData = NULL;
|
|
|
|
if (bShow) {
|
|
hr = pConsole->QueryInterface(IID_IHeaderCtrl, (void **)&pHeaderCtrl);
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
hr = pConsole->QueryInterface(IID_IResultData, (void **)&pResultData);
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
// Set the column headers in the results pane
|
|
hr = pHeaderCtrl->InsertColumn( 0, L"Name ", 0, MMCLV_AUTO );
|
|
_ASSERT( S_OK == hr );
|
|
|
|
// insert items here
|
|
RESULTDATAITEM rdi;
|
|
|
|
hr = pResultData->DeleteAllRsltItems();
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
if (!bExpanded)
|
|
{
|
|
// create the child nodes, then expand them
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++)
|
|
{
|
|
ZeroMemory(&rdi, sizeof(RESULTDATAITEM) );
|
|
rdi.mask = RDI_STR | // Displayname is valid
|
|
RDI_IMAGE | // nImage is valid
|
|
RDI_PARAM;
|
|
|
|
rdi.nImage = children[n]->GetBitmapIndex();
|
|
rdi.str = MMC_CALLBACK;
|
|
rdi.nCol = 0;
|
|
rdi.lParam = (LPARAM)children[n];
|
|
|
|
hr = pResultData->InsertItem( &rdi );
|
|
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
}
|
|
}
|
|
|
|
pHeaderCtrl->Release();
|
|
pResultData->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//================================================
|
|
CSkateboardFolder::CSkateboardFolder()
|
|
{
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++) {
|
|
children[n] = new CSkateboard(n + 1);
|
|
}
|
|
}
|
|
|
|
CSkateboardFolder::~CSkateboardFolder()
|
|
{
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++)
|
|
if (children[n]) {
|
|
delete children[n];
|
|
}
|
|
}
|
|
|
|
HRESULT CSkateboardFolder::OnShow(IConsole *pConsole, BOOL bShow, HSCOPEITEM scopeitem)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
IHeaderCtrl *pHeaderCtrl = NULL;
|
|
IResultData *pResultData = NULL;
|
|
|
|
if (bShow) {
|
|
hr = pConsole->QueryInterface(IID_IHeaderCtrl, (void **)&pHeaderCtrl);
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
hr = pConsole->QueryInterface(IID_IResultData, (void **)&pResultData);
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
// Set the column headers in the results pane
|
|
hr = pHeaderCtrl->InsertColumn( 0, L"Name ", 0, MMCLV_AUTO );
|
|
_ASSERT( S_OK == hr );
|
|
|
|
// insert items here
|
|
RESULTDATAITEM rdi;
|
|
|
|
hr = pResultData->DeleteAllRsltItems();
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
|
|
if (!bExpanded) {
|
|
// create the child nodes, then expand them
|
|
for (int n = 0; n < NUMBER_OF_CHILDREN; n++) {
|
|
ZeroMemory(&rdi, sizeof(RESULTDATAITEM) );
|
|
rdi.mask = RDI_STR | // Displayname is valid
|
|
RDI_IMAGE | // nImage is valid
|
|
RDI_PARAM;
|
|
|
|
rdi.nImage = children[n]->GetBitmapIndex();
|
|
rdi.str = MMC_CALLBACK;
|
|
rdi.nCol = 0;
|
|
rdi.lParam = (LPARAM)children[n];
|
|
|
|
hr = pResultData->InsertItem( &rdi );
|
|
|
|
_ASSERT( SUCCEEDED(hr) );
|
|
}
|
|
}
|
|
|
|
pHeaderCtrl->Release();
|
|
pResultData->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//=====================================================
|
|
const _TCHAR *CSkateboard::GetDisplayName(int nCol)
|
|
{
|
|
static _TCHAR buf[128];
|
|
|
|
_stprintf(buf, _T("Skateboard #%d"), id);
|
|
|
|
return buf;
|
|
}
|
|
|
|
//=====================================================
|
|
const _TCHAR *CIceSkate::GetDisplayName(int nCol)
|
|
{
|
|
static _TCHAR buf[128];
|
|
|
|
_stprintf(buf, _T("Ice Skate #%d"), id);
|
|
|
|
return buf;
|
|
}
|
|
|
|
//========================================
|
|
CBicycle::CBicycle(CBicycleFolder *parent, IWbemClassObject *inst) :
|
|
m_parent(parent),
|
|
m_inst(inst)
|
|
{
|
|
if(m_inst)
|
|
m_inst->AddRef();
|
|
}
|
|
|
|
// helper values for calling GetDisplayName().
|
|
#define NAME_COL 0
|
|
#define OWNER_COL 1
|
|
#define COLOR_COL 2
|
|
#define MATERIAL_COL 3
|
|
|
|
const _TCHAR *CBicycle::GetDisplayName(int nCol)
|
|
{
|
|
static _TCHAR buf[128];
|
|
|
|
// Get the corresponding property for nCol. This is in-proc local copy
|
|
// so its pretty fast even if IWbemServices is a remote connection.
|
|
if(m_inst)
|
|
{
|
|
VARIANT pVal;
|
|
WCHAR propName[10] = {0};
|
|
|
|
VariantInit(&pVal);
|
|
|
|
switch(nCol)
|
|
{
|
|
case 0:
|
|
wcscpy(propName, L"Name");
|
|
break;
|
|
|
|
case 1:
|
|
wcscpy(propName, L"Owner");
|
|
break;
|
|
|
|
// these wont be needed by MMC but its makes this routine more useful
|
|
// internal to the class.
|
|
case 2:
|
|
wcscpy(propName, L"Color");
|
|
break;
|
|
|
|
case 3:
|
|
wcscpy(propName, L"Material");
|
|
break;
|
|
|
|
} //endswitch
|
|
|
|
if(m_inst->Get(propName, 0L, &pVal, NULL, NULL) == S_OK)
|
|
{
|
|
bstr_t temp(pVal);
|
|
_tcscpy(buf, (LPTSTR)temp);
|
|
}
|
|
|
|
VariantClear(&pVal);
|
|
} //endif (m_inst)
|
|
|
|
return buf;
|
|
}
|
|
|
|
bool CBicycle::GetGirls(void)
|
|
{
|
|
VARIANT_BOOL retval = VARIANT_FALSE;
|
|
// Here's how to get/interpret a VT_BOOL property.
|
|
if(m_inst)
|
|
{
|
|
VARIANT pVal;
|
|
if(m_inst->Get(L"Girls", 0L, &pVal, NULL, NULL) == S_OK)
|
|
{
|
|
retval = V_BOOL(&pVal);
|
|
}
|
|
|
|
VariantClear(&pVal);
|
|
} //endif (m_inst)
|
|
|
|
return (retval == VARIANT_TRUE);
|
|
}
|
|
|
|
void CBicycle::LoadSurfaces(HWND hwndDlg, BYTE iSurface)
|
|
{
|
|
HWND hCombo = GetDlgItem(hwndDlg, IDC_PEOPLE_SURFACE);
|
|
HRESULT hr = E_FAIL;
|
|
IWbemQualifierSet *qualSet = 0;
|
|
int selected = 0;
|
|
|
|
// qualifiers only exist on the class definition. m_inst is a instance.
|
|
IWbemClassObject *pClass = 0;
|
|
IWbemServices *service = 0;
|
|
|
|
if(SUCCEEDED(m_parent->GetPtr(&service)))
|
|
{
|
|
hr = service->GetObject((bstr_t)L"Bicycle", 0,0, &pClass, 0);
|
|
|
|
if(SUCCEEDED(hr = pClass->GetPropertyQualifierSet((bstr_t)L"Surface",
|
|
&qualSet)))
|
|
{
|
|
VARIANT vList;
|
|
VariantInit(&vList);
|
|
if(SUCCEEDED(hr = qualSet->Get((bstr_t)L"Values", 0, &vList, 0)))
|
|
{
|
|
SAFEARRAY *pma = V_ARRAY(&vList);
|
|
long lLowerBound = 0, lUpperBound = 0 ;
|
|
UINT idx = 0;
|
|
|
|
SafeArrayGetLBound(pma, 1, &lLowerBound);
|
|
SafeArrayGetUBound(pma, 1, &lUpperBound);
|
|
|
|
for(long x = lLowerBound; x <= lUpperBound; x++)
|
|
{
|
|
BSTR vSurface;
|
|
|
|
SafeArrayGetElement(pma, &x, &vSurface);
|
|
|
|
// NOTE: taking advantage of the bstr_t's conversion operators.
|
|
// really cleans up the code.
|
|
bstr_t temp(vSurface);
|
|
|
|
UINT idx = ComboBox_AddString(hCombo, (LPTSTR)temp);
|
|
ComboBox_SetItemData(hCombo, idx, x);
|
|
|
|
// is this the one we want to select?
|
|
if(iSurface == x)
|
|
{
|
|
selected = x;
|
|
}
|
|
|
|
} //endfor
|
|
VariantClear(&vList);
|
|
ComboBox_SetCurSel(hCombo, selected);
|
|
}
|
|
|
|
qualSet->Release();
|
|
qualSet = 0;
|
|
} //endif GetPropertyQualifierSet()
|
|
|
|
service->Release();
|
|
|
|
} //endif GetPtr()
|
|
|
|
}
|
|
|
|
const TCHAR *CBicycle::ConvertSurfaceValue(BYTE val)
|
|
{
|
|
// Convert a enum to a string using the Value{} array.
|
|
static TCHAR temp[128] = {0};
|
|
|
|
return temp;
|
|
}
|
|
|
|
HRESULT CBicycle::PutProperty(LPWSTR propName, LPTSTR str)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if(m_inst)
|
|
{
|
|
VARIANT pVal;
|
|
bstr_t temp(str);
|
|
|
|
VariantInit(&pVal);
|
|
V_BSTR(&pVal) = temp;
|
|
V_VT(&pVal) = VT_BSTR;
|
|
|
|
hr = m_inst->Put(propName, 0L, &pVal, 0);
|
|
|
|
VariantClear(&pVal);
|
|
} //endif (m_inst)
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBicycle::PutProperty(LPWSTR propName, BYTE val)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if(m_inst)
|
|
{
|
|
VARIANT pVal;
|
|
|
|
VariantInit(&pVal);
|
|
V_UI1(&pVal) = val;
|
|
V_VT(&pVal) = VT_UI1;
|
|
|
|
hr = m_inst->Put(propName, 0L, &pVal, 0);
|
|
|
|
VariantClear(&pVal);
|
|
} //endif (m_inst)
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CBicycle::PutProperty(LPWSTR propName, bool val)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if(m_inst)
|
|
{
|
|
VARIANT pVal;
|
|
|
|
VariantInit(&pVal);
|
|
V_BOOL(&pVal) = (val?VARIANT_TRUE: VARIANT_FALSE);
|
|
V_VT(&pVal) = VT_BOOL;
|
|
|
|
hr = m_inst->Put(propName, 0L, &pVal, 0);
|
|
|
|
VariantClear(&pVal);
|
|
|
|
} //endif (m_inst)
|
|
|
|
return hr;
|
|
}
|
|
|
|
// handle anything special when the user clicks Apply or Ok
|
|
// on the property sheet. This sample directly accesses the
|
|
// operated-on object, so there's nothing special do to...
|
|
HRESULT CBicycle::OnPropertyChange()
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBicycle::OnSelect(IConsole *pConsole, BOOL bScope, BOOL bSelect)
|
|
{
|
|
IConsoleVerb *pConsoleVerb;
|
|
|
|
HRESULT hr = pConsole->QueryConsoleVerb(&pConsoleVerb);
|
|
_ASSERT(SUCCEEDED(hr));
|
|
|
|
// can't get to properties (via the standard methods) unless
|
|
// we tell MMC to display the Properties menu item and
|
|
// toolbar button, this wil give the user a visual cue that
|
|
// there's "something" to do
|
|
hr = pConsoleVerb->SetVerbState(MMC_VERB_PROPERTIES, ENABLED, TRUE);
|
|
|
|
pConsoleVerb->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// Implement the dialog proc
|
|
BOOL CALLBACK CBicycle::DialogProc(
|
|
HWND hwndDlg, // handle to dialog box
|
|
UINT uMsg, // message
|
|
WPARAM wParam, // first message parameter
|
|
LPARAM lParam // second message parameter
|
|
)
|
|
{
|
|
static CBicycle *pBike = NULL;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
// catch the "this" pointer so we can actually operate on the object
|
|
pBike = reinterpret_cast<CBicycle *>(reinterpret_cast<PROPSHEETPAGE *>(lParam)->lParam);
|
|
|
|
SetDlgItemText(hwndDlg, IDC_PEOPLE_NAME, pBike->GetDisplayName(NAME_COL));
|
|
SetDlgItemText(hwndDlg, IDC_PEOPLE_COLOR, pBike->GetDisplayName(COLOR_COL));
|
|
SetDlgItemText(hwndDlg, IDC_PEOPLE_MATERIAL, pBike->GetDisplayName(MATERIAL_COL));
|
|
SetDlgItemText(hwndDlg, IDC_PEOPLE_OWNER, pBike->GetDisplayName(OWNER_COL));
|
|
|
|
Button_SetCheck(GetDlgItem(hwndDlg, IDC_PEOPLE_GIRLS),
|
|
(pBike->GetGirls()? BST_CHECKED: BST_UNCHECKED));
|
|
|
|
VARIANT pVal;
|
|
VariantInit(&pVal);
|
|
if(SUCCEEDED(pBike->m_inst->Get((bstr_t)L"Surface", 0L, &pVal, NULL, NULL)))
|
|
{
|
|
pBike->m_iSurface = V_UI1(&pVal);
|
|
pBike->LoadSurfaces(hwndDlg, pBike->m_iSurface);
|
|
|
|
VariantClear(&pVal);
|
|
}
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
// turn the Apply button on
|
|
if (HIWORD(wParam) == EN_CHANGE ||
|
|
HIWORD(wParam) == CBN_SELCHANGE)
|
|
SendMessage(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0);
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
// tell MMC that we're done with the property sheet (we got this
|
|
// handle in CreatePropertyPages
|
|
MMCFreeNotifyHandle(pBike->m_ppHandle);
|
|
break;
|
|
|
|
case WM_NOTIFY:
|
|
|
|
switch(((NMHDR *)lParam)->code)
|
|
{
|
|
case PSN_APPLY:
|
|
{
|
|
bool changed = false;
|
|
TCHAR temp[50] = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
HWND hWnd = GetDlgItem(hwndDlg, IDC_PEOPLE_NAME);
|
|
if(hWnd && Edit_GetModify(hWnd))
|
|
{
|
|
GetWindowText(hWnd, temp, 50);
|
|
changed |= SUCCEEDED(pBike->PutProperty(L"Name", temp));
|
|
}
|
|
|
|
hWnd = GetDlgItem(hwndDlg, IDC_PEOPLE_COLOR);
|
|
if(hWnd && Edit_GetModify(hWnd))
|
|
{
|
|
GetWindowText(hWnd, temp, 50);
|
|
changed |= SUCCEEDED(pBike->PutProperty(L"Color", temp));
|
|
}
|
|
|
|
hWnd = GetDlgItem(hwndDlg, IDC_PEOPLE_MATERIAL);
|
|
if(hWnd && Edit_GetModify(hWnd))
|
|
{
|
|
GetWindowText(hWnd, temp, 50);
|
|
changed |= SUCCEEDED(pBike->PutProperty(L"Material", temp));
|
|
}
|
|
|
|
hWnd = GetDlgItem(hwndDlg, IDC_PEOPLE_OWNER);
|
|
if(hWnd && Edit_GetModify(hWnd))
|
|
{
|
|
GetWindowText(hWnd, temp, 50);
|
|
changed |= SUCCEEDED(hr = pBike->PutProperty(L"Owner", temp));
|
|
}
|
|
|
|
hWnd = GetDlgItem(hwndDlg, IDC_PEOPLE_SURFACE);
|
|
|
|
if(hWnd)
|
|
{
|
|
BYTE newValue = ComboBox_GetCurSel(hWnd);
|
|
if(newValue != pBike->m_iSurface)
|
|
{
|
|
changed |= SUCCEEDED(pBike->PutProperty(L"Surface", newValue));
|
|
}
|
|
}
|
|
|
|
hWnd = GetDlgItem(hwndDlg, IDC_PEOPLE_GIRLS);
|
|
if(hWnd)
|
|
{
|
|
bool checked = (Button_GetState(hWnd) & BST_CHECKED);
|
|
bool wasChecked = pBike->GetGirls();
|
|
// did it change?
|
|
if(checked != wasChecked)
|
|
{
|
|
changed |= SUCCEEDED(pBike->PutProperty(L"Girls", checked));
|
|
}
|
|
}
|
|
|
|
// if any property changed, write it back to WMI.
|
|
if(changed)
|
|
{
|
|
IWbemServices *service = 0;
|
|
// dialogs run in their own thread so use the marshaling helper
|
|
// get a useable IWbemServices ptr.
|
|
// NOTE: IWbemClassObjects are in-proc so they DONT need to be
|
|
// marshaled.
|
|
if(SUCCEEDED(pBike->m_parent->GetPtr(&service)))
|
|
{
|
|
service->PutInstance(pBike->m_inst, WBEM_FLAG_CREATE_OR_UPDATE, 0, 0);
|
|
service->Release();
|
|
HRESULT hr = MMCPropertyChangeNotify(pBike->m_ppHandle, (long)pBike);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
} // endswitch (((NMHDR *)lParam)->code)
|
|
|
|
break;
|
|
|
|
} // endswitch (uMsg)
|
|
|
|
return DefWindowProc(hwndDlg, uMsg, wParam, lParam);
|
|
}
|
|
|
|
|
|
HRESULT CBicycle::HasPropertySheets()
|
|
{
|
|
// say "yes" when MMC asks if we have pages
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CBicycle::CreatePropertyPages(IPropertySheetCallback *lpProvider, LONG_PTR handle)
|
|
{
|
|
PROPSHEETPAGE psp;
|
|
HPROPSHEETPAGE hPage = NULL;
|
|
|
|
// cache this handle so we can call MMCPropertyChangeNotify
|
|
m_ppHandle = handle;
|
|
|
|
// create the property page for this node.
|
|
// NOTE: if your node has multiple pages, put the following
|
|
// in a loop and create multiple pages calling
|
|
// lpProvider->AddPage() for each page.
|
|
psp.dwSize = sizeof(PROPSHEETPAGE);
|
|
psp.dwFlags = PSP_DEFAULT | PSP_USETITLE;
|
|
psp.hInstance = g_hinst;
|
|
psp.pszTemplate = MAKEINTRESOURCE(IDD_PROPPAGE_PEOPLE);
|
|
psp.pfnDlgProc = DialogProc;
|
|
psp.lParam = reinterpret_cast<LPARAM>(this);
|
|
psp.pszTitle = MAKEINTRESOURCE(IDS_BIKE_TITLE);
|
|
|
|
|
|
hPage = CreatePropertySheetPage(&psp);
|
|
_ASSERT(hPage);
|
|
|
|
return lpProvider->AddPage(hPage);
|
|
}
|
|
|
|
HRESULT CBicycle::GetWatermarks(HBITMAP *lphWatermark,
|
|
HBITMAP *lphHeader,
|
|
HPALETTE *lphPalette,
|
|
BOOL *bStretch)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|