OPC DA client ItemMgt Incorrect function error - c++

I have created a simple OPC DA client for the C++ COM API in Qt5.
The client connects to the remote server, gets an OPCServer pointer, creates a new OPC group with an ItemMgt interface, and fails when I try to add items to the group.
The error message is: Incorrect function.
As far as I can see, the IUnknown:: QueryInterface works for this pItemMgt, but the ValidateItems, CreateEnumerator and AddItems calls results in the same Incorrect function error. The OPC server is a QMS220Simulator (Quadera).
Any idea what could be the problem?
This is my first attempt to write a DCOM client, so many, many thing could be wrong with this code.
The qms220.h file contains the CLSID for the QMS220Simulator.
The shortest code to reproduce the problem is this:
#include "opcda.h"
#include "qms220.h"
#include <QApplication>
#include <QDebug>
#include <comdef.h>
static void showStatus(const QString &message,HRESULT code);
IOPCServer *pOPCServer = nullptr;
IOPCItemMgt *pItemMgt = nullptr;
OPCHANDLE serverGroupHandle;
bool initializeCOM()
{
HRESULT hr = CoInitializeEx(nullptr,COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
showStatus("COM initialization failed!",hr);
return false;
}
hr = CoInitializeSecurity(
NULL, //security descriptor
-1, //COM authentication
NULL, //authentication services
NULL, //reserved
RPC_C_AUTHN_LEVEL_DEFAULT, //default authentication
RPC_C_IMP_LEVEL_IMPERSONATE, //default impersonation
NULL, //authentication info
EOAC_NONE, //additional capabilities
NULL //reserved
);
if (hr == RPC_E_TOO_LATE) {
showStatus("RPC initalization is too late, ignoring...",hr);
} else {
if (FAILED(hr)) {
showStatus("CoInitializeSecurity",hr);
return false;
}
}
return true;
}
void deinitializeCOM()
{
CoUninitialize();
}
static const int INTERFACE_COUNT = 1;
bool connectToServer(const QString &address)
{
_bstr_t serverName = address.toStdString().c_str();
COSERVERINFO cs;
memset(&cs,0,sizeof(cs));
cs.pwszName = serverName;
MULTI_QI qi[INTERFACE_COUNT];
memset(qi,0,sizeof(qi));
qi[0].pIID = &IID_IOPCServer;
HRESULT hr = CoCreateInstanceEx(
CLSID_QMG220SIMDA,
NULL,
CLSCTX_SERVER,
&cs,
INTERFACE_COUNT,
qi
);
if (FAILED(hr)) {
showStatus("CoCreateInstanceEx",hr);
return false;
}
pOPCServer = (IOPCServer*)(qi[0].pItf);
return true;
}
void disconnectFromServer()
{
if (pOPCServer != nullptr) {
pOPCServer->Release();
pOPCServer = nullptr;
}
}
void showOPCStatus(const QString &message,HRESULT hr)
{
if (pOPCServer != nullptr) {
LPWSTR buffer = nullptr;
HRESULT hr2 = pOPCServer->GetErrorString(hr,LOCALE_SYSTEM_DEFAULT,&buffer);
if (hr2 != S_OK) {
qDebug() << message << QString(": HRESULT: 0x%1").arg(hr,8,16,QChar('0'));
} else {
qDebug() << message << QString(": ") << QString::fromWCharArray(buffer);
CoTaskMemFree(buffer);
}
} else {
qDebug() << message << QString(": HRESULT: 0x%1").arg(hr,8,16,QChar('0'));
}
}
static const LPCWSTR MIDGROUPNAME = L"mid";
bool createMIDGroup()
{
if (pOPCServer == nullptr) return false;
OPCHANDLE clientGroupHandle = 1;
DWORD revisedUpdateRate;
HRESULT hr = pOPCServer->AddGroup(
MIDGROUPNAME,
FALSE, //active
0, // requestedUpdateRate
clientGroupHandle,
NULL, //timebias
NULL, //percentDeadBand,
LOCALE_SYSTEM_DEFAULT, //lcid
&serverGroupHandle,
&revisedUpdateRate,
IID_IOPCItemMgt,
(LPUNKNOWN *)(&pItemMgt)
);
showOPCStatus("OPCServer::AddGroup",hr);
if (hr != S_OK) return false;
qDebug() << "The server group handle is: " << QString("0x%1").arg(serverGroupHandle,4,16);
qDebug() << "The revised update rate is: " << revisedUpdateRate;
#define ITEM_ID L"Hardware.Modules.Analyser.SI220.SimulationMode"
QString accessPath("");
QString itemId("Hardware.Modules.Analyser.SI220.SimulationMode");
wchar_t accessPathBuffer[1024];
wchar_t itemIdBuffer[1024];
accessPath.toWCharArray(accessPathBuffer);
itemId.toWCharArray(itemIdBuffer);
static const int ITEM_COUNT = 1;
OPCITEMDEF ItemArray[ITEM_COUNT] =
{{
/*szAccessPath*/ accessPathBuffer,
/*szItemID*/ itemIdBuffer,
/*bActive*/ FALSE,
/*hClient*/ 1,
/*dwBlobSize*/ 0,
/*pBlob*/ NULL,
/*vtRequestedDataType*/ VT_UI1,
/*wReserved*/0
}};
OPCITEMRESULT *itemResults = nullptr;
HRESULT *errors = nullptr;
hr = pItemMgt->AddItems(ITEM_COUNT,ItemArray,&itemResults,&errors);
bool failed = false;
if (hr != S_OK) {
failed = true;
}
showOPCStatus("createMidGroup/AddItems ",hr);
for(DWORD k=0;k<ITEM_COUNT;k++) {
showOPCStatus(QString("createMidGroup/AddItems[%1]").arg(k),errors[k]);
if (errors[k] != S_OK) {
failed = true;
}
CoTaskMemFree(itemResults[k].pBlob);
}
CoTaskMemFree(itemResults);
CoTaskMemFree(errors);
return !failed;
}
void removeMIDGroup()
{
if (pOPCServer != nullptr) {
if (pItemMgt != nullptr) {
pItemMgt->Release();
pItemMgt = nullptr;
}
HRESULT hr = pOPCServer->RemoveGroup(serverGroupHandle,false);
if (hr != S_OK) {
showStatus("deleteMIDGroup",hr);
}
}
}
int main(int argc, char *argv[])
{
Q_UNUSED(argc)
Q_UNUSED(argv)
if (!initializeCOM()) return -1;
if (connectToServer(QString("192.168.12.106"))) {
if (createMIDGroup()) {
removeMIDGroup();
}
disconnectFromServer();
}
deinitializeCOM();
return 0;
}
static void showStatus(const QString &message,HRESULT code)
{
_com_error error(code);
qDebug() << message + QString(": " ) + QString::fromWCharArray(error.ErrorMessage());
}

So, according to the Qt documentation: https://doc.qt.io/qt-5/qstring.html#toWCharArray
The toWCharArray creates a NOT NULL CHAR TERMINATED unicode string.
So a bit better usage would be:
int size = itemId.toWCharArray(itemIdBuffer);
itemIdBuffer[size] = L'\0';
And the same for accessPathBuffer.
Probably it's not a really good idea to send an unterminated group name to the OPC server.
The good thing is, that the CreateGroupEnumerator sends back the same unterminated group names as received.
So the problem is not COM nor OPC related, it's just not reading the fine documentation.

Related

C++ check if web browser closed

#include <windows.h>
#include <exdisp.h>
class CWebBrowser{
public:
HRESULT hr;
IWebBrowserApp *www;
HRESULT init(){
CLSID clsid;
const IID IID_IEApplication = {0x0002DF05,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
if(www) hr = www->put_Visible(-1);
if(hr!=0 || www==NULL){
QuitBrowser();
hr=CLSIDFromProgID(L"InternetExplorer.Application",&clsid);
if(hr==0) hr=CoCreateInstance(clsid,NULL,CLSCTX_ALL,IID_IEApplication,reinterpret_cast<void**>(&www));
if(hr==0) hr = www->put_Visible(-1);
}
return hr;
}
HRESULT browse(BSTR addr){
VARIANT vEmpty;
VariantInit(&vEmpty);
hr=www->Navigate(addr, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
VariantClear(&vEmpty);
return hr;
}
void QuitBrowser(){
if(www){
www->Quit();
www->Release();
www=NULL;
}
}
CWebBrowser(){
hr=CoInitialize(NULL);
}
~CWebBrowser(){
if(www){
www->Quit();
www->Release();
www=NULL;
}
CoUninitialize();
}
};
I am calling the init() function to check if Web Browser is still open before browse() another webpage.
When I run the app the first time there are 2 processes showing in Task Manager (iexplorer.exe) & (iexplorer.exe *32)
When I close the app sometimes processes close and sometimes they don't.
Sometimes (iexplorer.exe *32) closes and only (iexplorer.exe) is open. When I try calling init() in this case the app crashes.
Using CodeBlocks 17.12, Windows 2000 & IE 11.
This works fine for me. Maybe you are calling www->Release() prematurely.
Here is my MCVE:
#include <Windows.h>
#include <assert.h>
#include <Exdisp.h>
#include <iostream>
#pragma comment (lib, "SHDOCVW.lib")
IWebBrowserApp *www;
HRESULT init()
{
CLSID clsid;
const IID IID_IEApplication = {0x0002DF05,0x0000,0x0000,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
HRESULT hr = CLSIDFromProgID (L"InternetExplorer.Application", &clsid);
if (hr)
{
std::cout << std::hex << "CLSIDFromProgID failed, error " << hr << "\n";
return hr;
}
hr = CoCreateInstance (clsid, NULL, CLSCTX_ALL, IID_IEApplication, reinterpret_cast<void**>(&www));
if (hr)
{
std::cout << std::hex << "CoCreateInstance failed, error " << hr << "\n";
return hr;
}
hr = www->put_Visible(-1);
if (hr)
{
std::cout << std::hex << "put_Visible failed, error " << hr << "\n";
www->Release ();
www = nullptr;
return hr;
}
return S_OK;
}
HRESULT browse(BSTR addr)
{
HRESULT hr;
if (www)
{
VARIANT vEmpty;
VariantInit (&vEmpty);
hr = www->Navigate (addr, &vEmpty, &vEmpty, &vEmpty, &vEmpty);
VariantClear (&vEmpty);
}
return hr;
}
int main ()
{
HRESULT hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
assert (hr == 0);
int mb_result;
while (1)
{
hr = init ();
if (hr == 0)
{
mb_result = MessageBoxW (NULL, L"About to browse", L"Browser Test", MB_OKCANCEL);
if (mb_result == IDOK)
{
BSTR url = SysAllocString (L"https://www.google.com");
hr = browse (url);
SysFreeString (url);
if (hr)
std::cout << "browse () returned: " << std::hex << hr << "\n";
}
mb_result = MessageBoxW (NULL, L"About to quit", L"Browser Test", MB_OKCANCEL);
if (mb_result == IDOK)
www->Quit ();
www->Release ();
www = nullptr;
}
mb_result = MessageBoxW (NULL, L"Again?", L"Browser Test", MB_YESNO);
if (mb_result == IDNO)
break;
}
CoUninitialize ();
}
And if I close the browser (Edge, in my case, running on Windows 10) between the calls to init and browse I get:
browse () returned: 800706ba
which is entirely understandable, since this error means "The RPC server unavailable". It certainly doesn't crash.
Edit
Stupid Vista bugs, see the OP's recent comments. After calling www->Quit() the next call to CoCreateInstance() fails, if you're too quick about it at least.
So, two suggestions.
1 (might work):
for (int i = 0; i < 10; ++i)
{
hr = CoCreateInstance (clsid, NULL, CLSCTX_ALL, IID_IEApplication, reinterpret_cast<void**>(&www));
if (hr != ERROR_SHUTDOWN_IS_SCHEDULED) // not really
break;
Sleep (1000);
}
2 (a bit more drastic, error handling omitted for brevity):
// www->Quit ();
HWND hWnd;
hr = www->get_HWND ((SHANDLE_PTR *) &hWnd);
if (hr == S_OK)
{
DWORD processID;
if (GetWindowThreadProcessId (hWnd, &processID))
{
HANDLE hProcess = OpenProcess (PROCESS_TERMINATE, FALSE, processID);
if (hProcess)
{
TerminateProcess (hProcess, (DWORD) -1);
CloseHandle (hProcess);
}
}
}
Want to give it a try, OP, and report back?

IPortableDeviceEventCallback doesn't work properly (strange behaviour)

I want to receive a callback in my application when a photo on a connected mobile phone was shot (WPD_EVENT_OBJECT_ADDED).
I implemented a WPD client and the IPortableDeviceEventCallback like shown in the Windows Dev Center.
Now my problem is that the IPortableDeviceEventCallback::OnEvent() methode get only invoked if I have opened the DCIM/Camera folder on the phone via Windows Explorer and the explorer starts to creating thumbnails for the images. After I did this once, I receive every event from the phone till I dis- and reconnect it. But if I do not, onEvent() never gets called except I disconnect the phone.
Tested with different Android and iOS phones.
Does anyone have an idea whats going on?
DeviceEventsCallback::DeviceEventsCallback(MyPortableDevice* parent) : IPortableDeviceEventCallback(), cRef(1)
{
parentDevice = parent;
}
DeviceEventsCallback::~DeviceEventsCallback()
{
}
HRESULT __stdcall DeviceEventsCallback::QueryInterface(const IID& riid, LPVOID* ppvObj)
{
static const QITAB qitab[] = {
QITABENT(DeviceEventsCallback, IPortableDeviceEventCallback),
{ },
};
return QISearch(this, qitab, riid, ppvObj);
// HRESULT hr = S_OK;
// if (ppvObj == NULL) {
// hr = E_INVALIDARG;
// return hr;
// }
// if ((riid == IID_IUnknown) ||
// (riid == IID_IPortableDeviceEventCallback)) {
// AddRef();
// *ppvObj = this;
// }
// else {
// *ppvObj = NULL;
// hr = E_NOINTERFACE;
// }
// return hr;
}
ULONG __stdcall DeviceEventsCallback::AddRef()
{
InterlockedIncrement((long*) &cRef);
return cRef;
}
ULONG __stdcall DeviceEventsCallback::Release()
{
ULONG refCount = cRef - 1;
long ref = InterlockedDecrement(&cRef);
if (ref == 0) {
delete this;
return 0;
}
return refCount;
}
HRESULT __stdcall DeviceEventsCallback::OnEvent(IPortableDeviceValues* pEventParameters)
{
HRESULT hr = S_OK;
if (pEventParameters == NULL) {
hr = E_POINTER;
return hr;
}
// The pEventParameters collection contains information about the event that was
// fired. We'll at least need the EVENT_ID to figure out which event was fired
// and based on that retrieve additional values from the collection
// Display the event that was fired
GUID EventId;
if (EventId == WPD_EVENT_DEVICE_CAPABILITIES_UPDATED) {
return S_OK;
}
if (hr == S_OK) {
hr = pEventParameters->GetGuidValue(WPD_EVENT_PARAMETER_EVENT_ID, &EventId);
}
if (EventId == WPD_EVENT_DEVICE_REMOVED) {
return S_OK;
}
LPWSTR pwszEventId = NULL;
if (hr == S_OK) {
hr = StringFromCLSID(EventId, &pwszEventId);
}
if (pwszEventId != NULL) {
CoTaskMemFree(pwszEventId);
}
// Display the ID of the object that was affected
// We can also obtain WPD_OBJECT_NAME, WPD_OBJECT_PERSISTENT_UNIQUE_ID,
// WPD_OBJECT_PARENT_ID, etc.
LPWSTR pwszObjectId = NULL;
if (hr == S_OK) {
hr = pEventParameters->GetStringValue(WPD_OBJECT_ID, &pwszObjectId);
}
if (parentDevice != nullptr && pwszObjectId != nullptr && EventId == WPD_EVENT_OBJECT_ADDED) {
qDebug() << "invoked method";
QMetaObject::invokeMethod(parentDevice, "onNewFileOnDevice", Qt::DirectConnection, Q_ARG(QString, QString::fromStdWString(pwszObjectId)));
}
if (pwszObjectId != NULL) {
CoTaskMemFree(pwszObjectId);
}
// Note that we intentionally do not call Release on pEventParameters since we
// do not own it
return hr;
}
And in my MTP implementation I register my DeviceEventsCallback() eventNotifier
void MyPortableDevice::registerEventNotification(ComPtr<IPortableDevice> pDevice)
{
HRESULT hr = S_OK;
PWSTR tempEventCookie = nullptr;
if (pwszEventCookie != nullptr || pDevice == nullptr) {
return;
}
eventNotifier = new(std::nothrow) DeviceEventsCallback(this);
if (eventNotifier == nullptr) {
hr = E_OUTOFMEMORY;
}
if (hr == S_OK) {
hr = pDevice->Advise(0, eventNotifier, nullptr, &tempEventCookie);
}
if (hr == S_OK) {
pwszEventCookie = tempEventCookie;
tempEventCookie = nullptr; // relinquish memory to the caller
}
else {
// Free the event registration cookie because some error occurred
CoTaskMemFree(tempEventCookie);
tempEventCookie = nullptr;
}
}
void MyPortableDevice::unregisterEventsNotification(ComPtr<IPortableDevice> pDevice)
{
if (pDevice == nullptr || pwszEventCookie == nullptr) {
return;
}
HRESULT hr = pDevice->Unadvise(pwszEventCookie);
CoTaskMemFree(pwszEventCookie);
pwszEventCookie = nullptr;
}
My open() function looks like this:
bool MyPortableDevice::open(IPortableDevice** ppDevice)
{
retrieveClientInformation(&clientInformation);
IPortableDevice* pDevice = nullptr;
HRESULT hr = CoCreateInstance(CLSID_PortableDeviceFTM, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevice));
if (SUCCEEDED(hr)) {
wchar_t* wID = new wchar_t[ID.length() + 1];
ID.toWCharArray(wID);
wID[ID.length()] = '\0';
hr = pDevice->Open(wID, clientInformation.Get());
if (hr == E_ACCESSDENIED) {
qDebug() << "Failed to Open the device for Read Write access, will open it for Read-only access instead" << hr;
clientInformation->SetUnsignedIntegerValue(WPD_CLIENT_DESIRED_ACCESS, GENERIC_READ);
hr = pDevice->Open(wID, clientInformation.Get());
readOnly = true;
}
if (SUCCEEDED(hr)) {
// The device successfully opened, obtain an instance of the Device into
// ppDevice so the caller can be returned an opened IPortableDevice.
hr = pDevice->QueryInterface(IID_IPortableDevice, (VOID**)ppDevice);
if (FAILED(hr)) {
qDebug() << "Failed to QueryInterface the opened IPortableDevice";
return false;
}
}
if (pDevice != nullptr) {
pDevice->Release();
pDevice = nullptr;
}
delete [] wID;
wID = nullptr;
if (clientInformation != nullptr) {
clientInformation.Reset();
clientInformation = nullptr;
}
return true;
}
else {
qDebug() << "! Failed to CoCreateInstance CLSID_PortableDeviceFTM, hr = 0x%lx\n" << hr;
return false;
}
}

Handling InternetCloseHandle failure

INTRODUCTION AND RELEVANT INFORMATION:
I am learning WinInet on my own. I have written (in my humble opinion) "typical" piece of code, that needs to perform cleanup in the end:
DWORD CSomeClass::MVCE4StackOverflow()
{
DWORD errorCode = ERROR_SUCCESS;
URL_COMPONENTS urlComp;
::ZeroMemory(&urlComp, sizeof(URL_COMPONENTS));
urlComp.dwStructSize = sizeof(URL_COMPONENTS);
urlComp.dwHostNameLength = -1;
urlComp.dwSchemeLength = -1;
urlComp.dwUrlPathLength = -1;
if (!::InternetCrackUrl(m_URL.c_str(), m_URL.length(), 0, &urlComp))
{
errorCode = ::GetLastError();
return errorCode;
}
HINTERNET hInternetSession = ::InternetOpen("WinInet",
INTERNET_OPEN_TYPE_DIRECT,
NULL, NULL, 0);
if (NULL == hInternetSession)
{
errorCode = ::GetLastError();
return errorCode;
}
std::string hostname(urlComp.dwHostNameLength, 0);
::memcpy(&hostname[0], urlComp.lpszHostName, urlComp.dwHostNameLength);
HINTERNET hHttpSession = ::InternetConnect(hInternetSession,
hostname.c_str(),
INTERNET_DEFAULT_HTTP_PORT, 0, 0,
INTERNET_SERVICE_HTTP, 0, NULL);
if (NULL == hHttpSession)
{
errorCode = ::GetLastError();
return errorCode;
}
HINTERNET hHttpRequest = ::HttpOpenRequest(hHttpSession, "POST",
urlComp.lpszUrlPath, 0, 0, 0,
INTERNET_FLAG_RELOAD, 0);
if (NULL == hHttpRequest)
{
errorCode = ::GetLastError();
return errorCode;
}
const char header[] = "Content-Type: application/x-www-form-urlencoded";
std::string data = "input=1234";
if (!::HttpSendRequest(hHttpRequest, header, strlen(header),
&data[0], data.length()))
{
errorCode = ::GetLastError();
return errorCode;
}
DWORD dwBytesRead = 0;
BOOL result = false;
char szBuffer[1025] = "";
char *temp = szBuffer;
const DWORD dwBytes2Read = sizeof(szBuffer) - 1;
do{
result = ::InternetReadFile(hHttpRequest, szBuffer, dwBytes2Read, &dwBytesRead);
if (FALSE == result)
{
errorCode = ::GetLastError();
}
temp += dwBytesRead;
} while (result && dwBytesRead > 0);
// need error handling for below 3
result = ::InternetCloseHandle(hHttpRequest);
result = ::InternetCloseHandle(hHttpSession);
result = ::InternetCloseHandle(hInternetSession);
return errorCode;
}
PROBLEM:
In the provided code example, I need to call InternetCloseHandle 3 times consecutively.
I do not know how to structure that part of code to perform proper error handling.
My idea would be to do do the following:
result = ::InternetCloseHandle(hHttpRequest);
if(result)
{
result = ::InternetCloseHandle(hHttpSession);
if (result)
{
result = ::InternetCloseHandle(hInternetSession);
if(!result) return ::GetLastError();
}
else return ::GetLastError();
}
else return ::GetLastError();
However, being new to WinInet, I am not sure if my solution is correct.
QUESTION:
Can you please instruct me on how to handle the scenario in the provided code example?
I understand that my question might be confusing, but please take into consideration that English is not my native. Please leave a comment seeking further clarifications.
UPDATE #1:
I have tried to apply RAII:
#include <Windows.h>
#include <iostream>
#include <WinInet.h>
#pragma comment(lib, "Wininet.lib")
class CInternetOpenRAII
{
HINTERNET hIntSession;
public:
CInternetOpenRAII(){}
HINTERNET get() const { return hIntSession; }
DWORD init()
{
hIntSession = ::InternetOpen("WinInet", INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0);
return hIntSession ? ERROR_SUCCESS : ::GetLastError();
}
~CInternetOpenRAII()
{
if(hIntSession)
{
if(!::InternetCloseHandle(hIntSession))
{
std::cerr << "InternetOpen failed with GetLastErrorCode: " << ::GetLastError();
}
}
}
};
class CInternetConnectRAII
{
HINTERNET hHttpSession;
public:
CInternetConnectRAII() {}
HINTERNET get() const { return hHttpSession; }
DWORD init(const HINTERNET &hIntSession, const char *url)
{
hHttpSession = ::InternetConnect(hIntSession, url, INTERNET_DEFAULT_HTTP_PORT, 0, 0, INTERNET_SERVICE_HTTP, 0, NULL);
return hHttpSession ? ERROR_SUCCESS : ::GetLastError();
}
~CInternetConnectRAII()
{
if(hHttpSession)
{
if(!::InternetCloseHandle(hHttpSession))
{
std::cerr << "InternetConnect failed with GetLastErrorCode: " << ::GetLastError();
}
}
}
};
class CHttpOpenRequestRAII
{
HINTERNET hHttpRequest;
public:
CHttpOpenRequestRAII() {}
HINTERNET get() const { return hHttpRequest; }
DWORD init(const HINTERNET &hHttpSession, const char *request)
{
hHttpRequest = ::HttpOpenRequest(hHttpSession, "POST", request, 0, 0, 0, INTERNET_FLAG_RELOAD, 0);
return hHttpRequest ? ERROR_SUCCESS : ::GetLastError();
}
DWORD doRequest(const char *data, size_t dataLength, const char *header, size_t headerLength)
{
if (!::HttpSendRequest(hHttpRequest, header, headerLength, (void *)data, dataLength))
return ::GetLastError();
CHAR szBuffer[10] = "";
DWORD dwRead = 0;
const int dwBytes2Read = sizeof(szBuffer) - 1;
while (::InternetReadFile(hHttpRequest, szBuffer, dwBytes2Read, &dwRead) && dwRead)
{
std::cout << szBuffer;
}
return ERROR_SUCCESS;
}
~CHttpOpenRequestRAII()
{
if(hHttpRequest)
{
if(!::InternetCloseHandle(hHttpRequest))
{
std::cerr << "HttpOpenRequest failed with GetLastErrorCode: " << ::GetLastError();
}
}
}
};
int main()
{
DWORD error = ERROR_SUCCESS;
CInternetOpenRAII session;
error = session.init();
if(error) return error;
CInternetConnectRAII conn;
error = conn.init(session.get(), "www.test.com");
if(error) return error;
CHttpOpenRequestRAII req;
error = req.init(conn.get(), "/home/something");
if(error) return error;
error = req.doRequest("parameter=1234", strlen("parameter=1234"),
"Content-Type: application/x-www-form-urlencoded", strlen("Content-Type: application/x-www-form-urlencoded"));
if(error) return error;
return 0;
}
Now I do not know how to handle error in destructor. Can somebody please look at the code and provide some advice on that?

How to get a instance of Win32_SecurityDescriptor in WMI?

i'm writting a C++ WMI utility class which wraps the WMI stuff to help my further WMI query. i encountered a problem when accessing a Win32_SecurityDescriptor instance.
i used the WQL query "Select * from Win32_LogicalShareSecuritySetting" to get all the Win32_LogicalShareSecuritySetting instances. then called the GetSecurityDescriptor method of Win32_LogicalShareSecuritySetting instance to get a Win32_SecurityDescriptor instance. But when i attempted to retrieve some property like "__PATH" from the Win32_SecurityDescriptor instance, i got a 0xC0000005 Access Violation messagebox.
i wrote my code in C++, but i think the problem should be common.
I also used the wbemtest tool by Microsoft for test but didn't find a way to display a Win32_SecurityDescriptor instance. Why is Win32_SecurityDescriptor so special?
here's all my code, you can try it in your VC, mine is 6. Windows SDK maybe needed.
Access Violation found in the caller code:
CString strSDInstancePath = wmiSearcher->getStringFromObject(pSDInstance, "__PATH");
ADWMISearch.cpp
#include "StdAfx.h"
#include "ADWMISearch.h"
#include <comdef.h>
# pragma comment(lib, "wbemuuid.lib")
# pragma comment(lib, "credui.lib")
# pragma comment(lib, "comsupp.lib")
#include <wincred.h>
#include <strsafe.h>
#include <iostream>
using namespace std;
DWORD ADWMISearch::WIN32_FROM_HRESULT_alternate(HRESULT hr)
{
DWORD dwResult;
if (hr < 0)
{
dwResult = (DWORD) hr;
return dwResult;
}
else
{
MyMessageBox_Error(_T("WIN32_FROM_HRESULT_alternate Error."), _T("Error"));
return -1;
}
}
IWbemServices* ADWMISearch::connect(CString strServerName, CString strServerIP, CString strDomainName, CString strUsername,
CString strPassword)
{
CString strComputerName = strServerName;
CString strFullUsername; //_T("adtest\\Administrator")
if (strDomainName == _T(""))
{
strUsername = _T("");
strFullUsername = _T("");
strPassword = _T("");
}
else
{
strFullUsername = strDomainName + _T("\\") + strUsername;
}
HRESULT hres;
// Step 1: --------------------------------------------------
// Initialize COM. ------------------------------------------
hres = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hres))
{
//cout << "Failed to initialize COM library. Error code = 0x"
// << hex << hres << endl;
MyMessageBox_Error(_T("connect"));
return NULL; // Program has failed.
}
// Step 2: --------------------------------------------------
// Set general COM security levels --------------------------
hres = CoInitializeSecurity(
NULL,
-1, // COM authentication
NULL, // Authentication services
NULL, // Reserved
RPC_C_AUTHN_LEVEL_DEFAULT, // Default authentication
RPC_C_IMP_LEVEL_IDENTIFY, // Default Impersonation
NULL, // Authentication info
EOAC_NONE, // Additional capabilities
NULL // Reserved
);
//char aaa[100];
//sprintf(aaa, "%x", hres);
if (FAILED(hres) && hres != RPC_E_TOO_LATE)
{
//cout << "Failed to initialize security. Error code = 0x"
// << hex << hres << endl;
CoUninitialize();
MyMessageBox_Error(_T("connect"));
return NULL; // Program has failed.
}
// Step 3: ---------------------------------------------------
// Obtain the initial locator to WMI -------------------------
IWbemLocator *pLoc = NULL;
hres = CoCreateInstance(
CLSID_WbemLocator,
0,
CLSCTX_INPROC_SERVER,
IID_IWbemLocator, (LPVOID *) &pLoc);
if (FAILED(hres))
{
CoUninitialize();
MyMessageBox_Error(_T("connect"));
return NULL; // Program has failed.
}
// Step 4: -----------------------------------------------------
// Connect to WMI through the IWbemLocator::ConnectServer method
IWbemServices *pSvc = NULL;
bool useToken = TRUE;
bool useNTLM = FALSE;
// Get the user name and password for the remote computer
/*
CREDUI_INFO cui;
bool useToken = TRUE;
bool useNTLM = FALSE;
TCHAR pszName[CREDUI_MAX_USERNAME_LENGTH+1] = {0};
TCHAR pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1] = {0};
TCHAR pszDomain[CREDUI_MAX_USERNAME_LENGTH+1];
TCHAR pszUserName[CREDUI_MAX_USERNAME_LENGTH+1];
TCHAR pszAuthority[CREDUI_MAX_USERNAME_LENGTH+1];
BOOL fSave;
DWORD dwErr;
memset(&cui,0,sizeof(CREDUI_INFO));
cui.cbSize = sizeof(CREDUI_INFO);
cui.hwndParent = NULL;
// Ensure that MessageText and CaptionText identify
// what credentials to use and which application requires them.
cui.pszMessageText = _T("Press cancel to use process token");
cui.pszCaptionText = _T("Enter Account Information");
cui.hbmBanner = NULL;
fSave = FALSE;
dwErr = CredUIPromptForCredentials(
&cui, // CREDUI_INFO structure
_T(""), // Target for credentials
NULL, // Reserved
0, // Reason
pszName, // User name
CREDUI_MAX_USERNAME_LENGTH+1, // Max number for user name
pszPwd, // Password
CREDUI_MAX_PASSWORD_LENGTH+1, // Max number for password
&fSave, // State of save check box
CREDUI_FLAGS_GENERIC_CREDENTIALS |// flags
CREDUI_FLAGS_ALWAYS_SHOW_UI |
CREDUI_FLAGS_DO_NOT_PERSIST);
if(dwErr == ERROR_CANCELLED)
{
useToken = true;
}
else if (dwErr)
{
cout << "Did not get credentials " << dwErr << endl;
pLoc->Release();
CoUninitialize();
return 1;
}
*/
// change the computerName strings below to the full computer name
// of the remote computer
CString strAuthority;
if(!useNTLM)
{
//StringCchPrintf(pszAuthority, CREDUI_MAX_USERNAME_LENGTH+1, _T("kERBEROS:%s"), strComputerName);
strAuthority = _T("kERBEROS:") + strComputerName;
}
// Connect to the remote root\cimv2 namespace
// and obtain pointer pSvc to make IWbemServices calls.
//---------------------------------------------------------
CString strNetworkResource = _T("\\\\") + strComputerName + _T("\\root\\cimv2");
if (strFullUsername == _T(""))
{
hres = pLoc->ConnectServer(
_bstr_t(strNetworkResource),
NULL, // User name
NULL, // User password
NULL, // Locale
WBEM_FLAG_CONNECT_USE_MAX_WAIT, // Security flags
_bstr_t(strAuthority), // Authority
NULL, // Context object
&pSvc // IWbemServices proxy
);
}
else
{
hres = pLoc->ConnectServer(
_bstr_t(strNetworkResource),
_bstr_t(strFullUsername), // User name
_bstr_t(strPassword), // User password
NULL, // Locale
WBEM_FLAG_CONNECT_USE_MAX_WAIT, // Security flags
_bstr_t(strAuthority), // Authority
NULL, // Context object
&pSvc // IWbemServices proxy
);
}
if (FAILED(hres))
{
DWORD aaa = WIN32_FROM_HRESULT_alternate(hres);
//cout << "Could not connect. Error code = 0x"
// << hex << hres << endl;
pLoc->Release();
CoUninitialize();
MyMessageBox_Error(_T("connect"));
return NULL; // Program has failed.
}
//cout << "Connected to ROOT\\CIMV2 WMI namespace" << endl;
// step 5: --------------------------------------------------
// Create COAUTHIDENTITY that can be used for setting security on proxy
COAUTHIDENTITY *userAcct = NULL;
COAUTHIDENTITY authIdent;
if(!useToken)
{
memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
authIdent.PasswordLength = strPassword.GetLength();
authIdent.Password = (USHORT*) _bstr_t(strPassword);
authIdent.Domain = (USHORT*) _bstr_t(strDomainName);
authIdent.DomainLength = strDomainName.GetLength();
authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
userAcct = &authIdent;
}
// Step 6: --------------------------------------------------
// Set security levels on a WMI connection ------------------
hres = CoSetProxyBlanket(
pSvc, // Indicates the proxy to set
RPC_C_AUTHN_DEFAULT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_DEFAULT, // RPC_C_AUTHZ_xxx
COLE_DEFAULT_PRINCIPAL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
userAcct, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hres))
{
//cout << "Could not set proxy blanket. Error code = 0x"
// << hex << hres << endl;
pSvc->Release();
pLoc->Release();
CoUninitialize();
MyMessageBox_Error(_T("connect"));
return NULL; // Program has failed.
}
// Cleanup
// ========
pLoc->Release();
return pSvc;
}
vector<IWbemClassObject*> ADWMISearch::query(IWbemServices *pSvc, CString strDomainName, CString strPassword, CString strWQL)
{
vector<IWbemClassObject*> npResultObjects;
BOOL useToken = TRUE;
// step 5: --------------------------------------------------
// Create COAUTHIDENTITY that can be used for setting security on proxy
COAUTHIDENTITY *userAcct = NULL;
COAUTHIDENTITY authIdent;
if(!useToken)
{
memset(&authIdent, 0, sizeof(COAUTHIDENTITY));
authIdent.PasswordLength = strPassword.GetLength();
authIdent.Password = (USHORT*) _bstr_t(strPassword);
/*
LPTSTR slash = _tcschr(pszName, _T('\\'));
if( slash == NULL )
{
//cout << _T("Could not create Auth identity. No domain specified\n") ;
pSvc->Release();
pLoc->Release();
CoUninitialize();
return 1; // Program has failed.
}
StringCchCopy(pszUserName, CREDUI_MAX_USERNAME_LENGTH+1, slash+1);
authIdent.User = (USHORT*)pszUserName;
authIdent.UserLength = _tcslen(pszUserName);
StringCchCopyN(pszDomain, CREDUI_MAX_USERNAME_LENGTH+1, pszName, slash - pszName);
*/
authIdent.Domain = (USHORT*) _bstr_t(strDomainName);
authIdent.DomainLength = strDomainName.GetLength();
authIdent.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
userAcct = &authIdent;
}
// Step 7: --------------------------------------------------
// Use the IWbemServices pointer to make requests of WMI ----
// For example, get the name of the operating system
IEnumWbemClassObject* pEnumerator = NULL;
HRESULT hResult;
hResult = pSvc->ExecQuery(
_bstr_t(_T("WQL")),
_bstr_t(strWQL), //_T("Select * from Win32_Share")
WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
NULL,
&pEnumerator);
if (FAILED(hResult))
{
//cout << "Query for operating system name failed."
// << " Error code = 0x"
// << hex << hres << endl;
MyMessageBox_Error(_T("query"));
return npResultObjects; // Program has failed.
}
// Step 8: -------------------------------------------------
// Secure the enumerator proxy
hResult = CoSetProxyBlanket(
pEnumerator, // Indicates the proxy to set
RPC_C_AUTHN_DEFAULT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_DEFAULT, // RPC_C_AUTHZ_xxx
COLE_DEFAULT_PRINCIPAL, // Server principal name
RPC_C_AUTHN_LEVEL_PKT_PRIVACY, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
userAcct, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hResult))
{
//cout << "Could not set proxy blanket on enumerator. Error code = 0x"
// << hex << hres << endl;
pEnumerator->Release();
MyMessageBox_Error(_T("query"));
return npResultObjects; // Program has failed.
}
// When you have finished using the credentials,
// erase them from memory.
// Step 9: -------------------------------------------------
// Get the data from the query in step 7 -------------------
IWbemClassObject *pclsObj = NULL;
ULONG uReturn = 0;
while (pEnumerator)
{
HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,
&pclsObj, &uReturn);
if(0 == uReturn)
{
break;
}
npResultObjects.push_back(pclsObj);
/*
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0);
cout << _T("OS Name: ") << vtProp.bstrVal << endl;
// Get the value of the FreePhysicalMemory property
hr = pclsObj->Get(L"FreePhysicalMemory", 0, &vtProp, 0, 0);
cout << "Free physical memory(in kilobytes): " << vtProp.uintVal << endl;
VariantClear(&vtProp);
pclsObj->Release();
pclsObj = NULL;
*/
}
// Cleanup
// ========
pEnumerator->Release();
return npResultObjects;
}
CString ADWMISearch::getStringFromObject(IWbemClassObject* pObject, CString strPropertyName)
{
_bstr_t bstrPropertyName = strPropertyName;
VARIANT vtProperty;
CString strProperty;
// Get the value of the property
VariantInit(&vtProperty);
HRESULT hResult = pObject->Get(bstrPropertyName, 0, &vtProperty, 0, 0);
if (SUCCEEDED(hResult) && V_VT(&vtProperty) == VT_BSTR)
{
strProperty = vtProperty.bstrVal;
VariantClear(&vtProperty);
return strProperty;
}
else
{
VariantClear(&vtProperty);
MyMessageBox_Error(_T("getStringsFromObjects"));
return _T("");
}
}
vector<CString> ADWMISearch::getStringsFromObjects(vector<IWbemClassObject*> npObjects, CString strPropertyName)
{
_bstr_t bstrPropertyName = strPropertyName;
vector<CString> nstrProperties;
for (int i = 0; i < npObjects.size(); i ++)
{
VARIANT vtProperty;
CString strProperty;
// Get the value of the property
VariantInit(&vtProperty);
HRESULT hResult = npObjects[i]->Get(bstrPropertyName, 0, &vtProperty, 0, 0);
if (SUCCEEDED(hResult) && V_VT(&vtProperty) == VT_BSTR)
{
CString strProperty = vtProperty.bstrVal;
nstrProperties.push_back(strProperty);
VariantClear(&vtProperty);
}
else
{
VariantClear(&vtProperty);
MyMessageBox_Error(_T("getStringsFromObjects"));
return nstrProperties;
}
}
return nstrProperties;
}
void ADWMISearch::clearObjects(vector<IWbemClassObject*> npObjects)
{
for (int i = 0; i < npObjects.size(); i ++)
{
npObjects[i]->Release();
npObjects[i] = NULL;
}
npObjects.clear();
}
IWbemClassObject* ADWMISearch::getWMIClass(IWbemServices *pSvc, CString strClassName)
{
HRESULT hResult;
IWbemClassObject* pClass = NULL;
hResult = pSvc->GetObject(_bstr_t(strClassName), 0, NULL, &pClass, NULL); //_T("Win32_Share")
if (!SUCCEEDED(hResult))
{
MyMessageBox_Error(_T("getWMIClass"));
return NULL;
}
else
{
return pClass;
}
}
IWbemClassObject* ADWMISearch::getObjectFromObject(IWbemClassObject *pObject, CString strArgumentName)
{
if (!pObject)
{
MyMessageBox_Error(_T("getObjectFromObject"));
return NULL;
}
HRESULT hResult;
VARIANT varArgument;
hResult = pObject->Get(_bstr_t(strArgumentName), 0, &varArgument, NULL, 0);
if (!SUCCEEDED(hResult))
{
VariantClear(&varArgument);
MyMessageBox_Error(_T("getObjectFromObject"));
return NULL;
}
//IWbemClassObject **ppResultObject;
IWbemClassObject *pResultObject2;
if (V_VT(&varArgument) == VT_UNKNOWN)
{
//ppResultObject = (IWbemClassObject **)varArgument.ppunkVal;
pResultObject2 = (IWbemClassObject *)varArgument.punkVal;
}
else if (V_VT(&varArgument) == VT_DISPATCH)
{
//ppResultObject = (IWbemClassObject **)varArgument.ppdispVal;
pResultObject2 = (IWbemClassObject *)varArgument.pdispVal;
}
else
{
VariantClear(&varArgument);
MyMessageBox_Error(_T("getObjectFromObject"));
return NULL;
}
VariantClear(&varArgument);
//IWbemClassObject *pResultObject = *ppResultObject;
//return pResultObject;
return pResultObject2;
}
IWbemClassObject* ADWMISearch::getObjectFromObjectWithCheck(IWbemClassObject *pOutParams, CString strArgumentName)
{
if (!pOutParams)
{
MyMessageBox_Error(_T("getObjectFromObjectWithCheck"));
return NULL;
}
HRESULT hResult;
VARIANT varReturnValue;
VARIANT varArgument;
hResult = pOutParams->Get(_bstr_t(L"ReturnValue"), 0, &varReturnValue, NULL, 0);
if (!SUCCEEDED(hResult))
{
VariantClear(&varReturnValue);
VariantClear(&varArgument);
MyMessageBox_Error(_T("getObjectFromObjectWithCheck"));
return NULL;
}
DWORD dwResult = varReturnValue.lVal;
hResult = pOutParams->Get(_bstr_t(strArgumentName), 0, &varArgument, NULL, 0);
if (!SUCCEEDED(hResult))
{
VariantClear(&varReturnValue);
VariantClear(&varArgument);
MyMessageBox_Error(_T("getObjectFromObjectWithCheck"));
return NULL;
}
//IWbemClassObject **ppResultObject;
IWbemClassObject *pResultObject2;
if (V_VT(&varArgument) == VT_UNKNOWN)
{
//ppResultObject = (IWbemClassObject **)varArgument.ppunkVal;
pResultObject2 = (IWbemClassObject *)varArgument.punkVal;
}
else if (V_VT(&varArgument) == VT_DISPATCH)
{
//ppResultObject = (IWbemClassObject **)varArgument.ppdispVal;
pResultObject2 = (IWbemClassObject *)varArgument.pdispVal;
}
else
{
VariantClear(&varReturnValue);
VariantClear(&varArgument);
MyMessageBox_Error(_T("getObjectFromObjectWithCheck"));
return NULL;
}
VariantClear(&varReturnValue);
VariantClear(&varArgument);
//IWbemClassObject *pResultObject = *ppResultObject;
//return pResultObject;
return pResultObject2;
}
vector<ADWMIParam> ADWMISearch::genObjectArguments(CString strArgName, IWbemClassObject *pObject)
{
vector<ADWMIParam> resultArgs;
resultArgs.push_back(ADWMIParam(strArgName, pObject));
return resultArgs;
}
IWbemClassObject* ADWMISearch::callStaticMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strMethodName)
{
return callStaticMethod(pSvc, pClass, strClassName, strMethodName, vector<ADWMIParam>());
}
IWbemClassObject* ADWMISearch::callStaticMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strMethodName, vector<ADWMIParam> &arguments)
{
_bstr_t bstrClassName = _bstr_t(strClassName);
_bstr_t bstrMethodName = _bstr_t(strMethodName);
BOOL bInParams;
BOOL bOutParams;
HRESULT hResult;
IWbemClassObject* pClass2 = getWMIClass(pSvc, strClassName);
IWbemClassObject* pInParamsDefinition = NULL;
IWbemClassObject* pOutParamsDefinition = NULL;
hResult = pClass2->GetMethod(bstrMethodName, 0, &pInParamsDefinition, &pOutParamsDefinition);
if (hResult != WBEM_S_NO_ERROR)
{
//pClass->Release();
MyMessageBox_Error(_T("callStaticMethod"));
return NULL;
}
if (pInParamsDefinition == NULL)
{
bInParams = FALSE;
}
else
{
bInParams = TRUE;
}
if (pOutParamsDefinition == NULL)
{
bOutParams = FALSE;
}
else
{
bOutParams = TRUE;
}
IWbemClassObject* pInParamsInstance = NULL;
if (bInParams)
{
hResult = pInParamsDefinition->SpawnInstance(0, &pInParamsInstance);
for (int i = 0; i < arguments.size(); i ++)
{
_variant_t varArg;
VariantCopy(&varArg, &(arguments[i].value));
hResult = pInParamsInstance->Put(_bstr_t(arguments[i].key), 0, &varArg, 0);
//hResult = pInParamsInstance->Put(_bstr_t(arguments[i].key), 0, &(arguments[i].value), 0);
}
}
IWbemClassObject* pOutParams = NULL;
hResult = pSvc->ExecMethod(bstrClassName, bstrMethodName, 0, NULL, pInParamsInstance, &pOutParams, NULL);
if (hResult != WBEM_S_NO_ERROR)
{
//pClass->Release();
if (pInParamsDefinition != NULL)
{
pInParamsDefinition->Release();
}
if (pOutParamsDefinition != NULL)
{
pOutParamsDefinition->Release();
}
if (pInParamsInstance != NULL)
{
pInParamsInstance->Release();
}
MyMessageBox_Error(_T("callStaticMethod"));
return NULL;
}
if (pInParamsDefinition != NULL)
{
pInParamsDefinition->Release();
}
if (pOutParamsDefinition != NULL)
{
pOutParamsDefinition->Release();
}
if (pInParamsInstance != NULL)
{
pInParamsInstance->Release();
}
if (bOutParams)
{
return pOutParams;
}
else
{
return NULL;
}
}
IWbemClassObject* ADWMISearch::callMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strInstancePath, CString strMethodName)
{
return callMethod(pSvc, pClass, strClassName, strInstancePath, strMethodName, vector<ADWMIParam>());
}
IWbemClassObject* ADWMISearch::callMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strInstancePath, CString strMethodName, vector<ADWMIParam> &arguments)
{
_bstr_t bstrClassName = _bstr_t(strClassName);
_bstr_t bstrMethodName = _bstr_t(strMethodName);
BOOL bInParams;
BOOL bOutParams;
HRESULT hResult;
//IWbemClassObject* pClass = getWMIClass(pSvc, strClassName);
IWbemClassObject* pInParamsDefinition = NULL;
IWbemClassObject* pOutParamsDefinition = NULL;
hResult = pClass->GetMethod(bstrMethodName, 0, &pInParamsDefinition, &pOutParamsDefinition);
if (hResult != WBEM_S_NO_ERROR)
{
//pClass->Release();
MyMessageBox_Error(_T("callMethod"));
return NULL;
}
if (pInParamsDefinition == NULL)
{
bInParams = FALSE;
}
else
{
bInParams = TRUE;
}
if (pOutParamsDefinition == NULL)
{
bOutParams = FALSE;
}
else
{
bOutParams = TRUE;
}
IWbemClassObject* pInParamsInstance = NULL;
if (bInParams)
{
hResult = pInParamsDefinition->SpawnInstance(0, &pInParamsInstance);
for (int i = 0; i < arguments.size(); i ++)
{
_variant_t varArg;
VariantCopy(&varArg, &(arguments[i].value));
hResult = pInParamsInstance->Put(_bstr_t(arguments[i].key), 0, &varArg, 0);
//hResult = pInParamsInstance->Put(_bstr_t(arguments[i].key), 0, &(arguments[i].value), 0);
}
}
IWbemClassObject* pOutParams = NULL;
hResult = pSvc->ExecMethod(_bstr_t(strInstancePath), bstrMethodName, 0, NULL, pInParamsInstance, &pOutParams, NULL);
if (hResult != WBEM_S_NO_ERROR)
{
//pClass->Release();
if (pInParamsDefinition != NULL)
{
pInParamsDefinition->Release();
}
if (pOutParamsDefinition != NULL)
{
pOutParamsDefinition->Release();
}
if (pInParamsInstance != NULL)
{
pInParamsInstance->Release();
}
MyMessageBox_Error(_T("callMethod"));
return NULL;
}
if (pInParamsDefinition != NULL)
{
pInParamsDefinition->Release();
}
if (pOutParamsDefinition != NULL)
{
pOutParamsDefinition->Release();
}
if (pInParamsInstance != NULL)
{
pInParamsInstance->Release();
}
if (bOutParams)
{
return pOutParams;
}
else
{
return NULL;
}
}
void ADWMISearch::disconnect(IWbemServices *pSvc)
{
pSvc->Release();
CoUninitialize();
}
ADWMISearch.h
#include <Wbemidl.h>
#include <vector>
#include <map>
using namespace std;
class ADWMIParam
{
public:
CString key;
_variant_t value;
public:
ADWMIParam(CString strKey, CString strValue)
{
key = strKey;
VariantInit(&value);
value.vt = VT_BSTR;
value.bstrVal = _bstr_t(strValue);
}
ADWMIParam(CString strKey, IWbemClassObject *pValue)
{
key = strKey;
VariantInit(&value);
value.vt = VT_UNKNOWN;
value.punkVal = pValue;
}
};
class ADWMISearch
{
public:
DWORD WIN32_FROM_HRESULT_alternate(HRESULT hr);
IWbemServices* connect(CString strServerName, CString strServerIP, CString strDomainName,
CString strUsername, CString strPassword);
vector<IWbemClassObject*> query(IWbemServices *pSvc, CString strDomainName, CString strPassword, CString strWQL);
CString getStringFromObject(IWbemClassObject* pObject, CString strPropertyName);
vector<CString> getStringsFromObjects(vector<IWbemClassObject*> npObjects, CString strPropertyName);
void clearObjects(vector<IWbemClassObject*> npObjects);
IWbemClassObject* getWMIClass(IWbemServices *pSvc, CString strClassName);
IWbemClassObject* getObjectFromObject(IWbemClassObject *pObject, CString strArgumentName);
IWbemClassObject* getObjectFromObjectWithCheck(IWbemClassObject *pOutParams, CString strArgumentName);
vector<ADWMIParam> genObjectArguments(CString strArgName, IWbemClassObject *pObject);
IWbemClassObject* callStaticMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strMethodName);
IWbemClassObject* callStaticMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strMethodName, vector<ADWMIParam> &arguments);
IWbemClassObject* callMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strInstancePath, CString strMethodName);
IWbemClassObject* callMethod(IWbemServices *pSvc, IWbemClassObject *pClass, CString strClassName,
CString strInstancePath, CString strMethodName, vector<ADWMIParam> &arguments);
void disconnect(IWbemServices *pSvc);
};
the call code(a place like main is ok)
ADWMISearch *wmiSearcher = new ADWMISearch();
IWbemServices *pSvc = wmiSearcher->connect("luo-pc", "127.0.0.1", "", "", "");
IWbemClassObject *pLSSSClass = wmiSearcher->getWMIClass(pSvc, "Win32_LogicalShareSecuritySetting");
vector<IWbemClassObject*> npObjects = wmiSearcher->query(pSvc, "", "", "Select * from Win32_Log

Getting volume change notifications on Vista/7 (C++)

I'm trying to get notifications whenever the master volume changes on Windows Vista/7. This is the code I'm using:
#include <audiopolicy.h>
#include <audioclient.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <windows.h>
#include <shlwapi.h>
#include <iostream>
#include <Tchar.h>
static const GUID AudioSessionVolumeCtx = { 0x2715279f, 0x4139, 0x4ba0, { 0x9c, 0xb1, 0xb3, 0x51, 0xf1, 0xb5, 0x8a, 0x4a } };
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
class CAudioSessionVolume : public IAudioSessionEvents
{
public:
static HRESULT CreateInstance( UINT uNotificationMessage, HWND hwndNotification, CAudioSessionVolume **ppAudioSessionVolume )
{
CAudioSessionVolume *pAudioSessionVolume = new (std::nothrow)
CAudioSessionVolume(uNotificationMessage, hwndNotification);
if (pAudioSessionVolume == NULL)
{
return E_OUTOFMEMORY;
}
HRESULT hr = pAudioSessionVolume->Initialize();
if (SUCCEEDED(hr))
{
*ppAudioSessionVolume = pAudioSessionVolume;
}
else
{
pAudioSessionVolume->Release();
}
return hr;
}
// IUnknown methods.
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CAudioSessionVolume, IAudioSessionEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement( &m_cRef );
if (cRef == 0)
{
delete this;
}
return cRef;
}
STDMETHODIMP OnSimpleVolumeChanged( float NewVolume, BOOL NewMute, LPCGUID EventContext )
{
MessageBox(NULL, _T("vol changed"), _T("test"), MB_OK);
return S_OK;
}
// The remaining audio session events do not require any action.
STDMETHODIMP OnDisplayNameChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnIconPathChanged(LPCWSTR,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnChannelVolumeChanged(DWORD,float[],DWORD,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnGroupingParamChanged(LPCGUID,LPCGUID)
{
return S_OK;
}
STDMETHODIMP OnStateChanged(AudioSessionState)
{
return S_OK;
}
STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason)
{
return S_OK;
}
// Other methods
HRESULT EnableNotifications(BOOL bEnable)
{
HRESULT hr = S_OK;
if (bEnable)
{
hr = m_pAudioSession->RegisterAudioSessionNotification(this);
}
else
{
hr = m_pAudioSession->UnregisterAudioSessionNotification(this);
}
return hr;
}
HRESULT SetDisplayName(const WCHAR *wszName)
{
if (m_pAudioSession == NULL)
{
return E_FAIL;
}
else
{
return m_pAudioSession->SetDisplayName(wszName, NULL);
}
}
protected:
CAudioSessionVolume( UINT uNotificationMessage, HWND hwndNotification ) :
m_cRef(1), m_pAudioSession(NULL), m_pSimpleAudioVolume(NULL)
{
}
~CAudioSessionVolume()
{
EnableNotifications(FALSE);
SafeRelease(&m_pAudioSession);
SafeRelease(&m_pSimpleAudioVolume);
}
HRESULT Initialize()
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pDeviceEnumerator = NULL;
IMMDevice *pDevice = NULL;
IAudioSessionManager *pAudioSessionManager = NULL;
// Get the enumerator for the audio endpoint devices.
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnumerator));
if (FAILED(hr))
{
goto done;
}
// Get the default audio endpoint that the SAR will use.
hr = pDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole,
&pDevice);
if (FAILED(hr))
{
goto done;
}
// Get the session manager for this device.
hr = pDevice->Activate(__uuidof(IAudioSessionManager),
CLSCTX_INPROC_SERVER, NULL, (void**) &pAudioSessionManager);
if (FAILED(hr))
{
goto done;
}
// Get the audio session.
hr = pAudioSessionManager->GetAudioSessionControl(
&GUID_NULL, // Get the default audio session.
FALSE, // The session is not cross-process.
&m_pAudioSession);
if (FAILED(hr))
{
goto done;
}
hr = pAudioSessionManager->GetSimpleAudioVolume(&GUID_NULL, 0,
&m_pSimpleAudioVolume);
done:
SafeRelease(&pDeviceEnumerator);
SafeRelease(&pDevice);
SafeRelease(&pAudioSessionManager);
return hr;
}
private:
LONG m_cRef;
IAudioSessionControl *m_pAudioSession;
ISimpleAudioVolume *m_pSimpleAudioVolume;
};
int main()
{
CoInitialize(NULL);
CAudioSessionVolume *asv;
CAudioSessionVolume::CreateInstance(0, NULL, &asv);
asv->EnableNotifications(true);
char s;
gets(&s);
}
I was expecting to get a message box saying "vol changed" when the system volume changes, but I never get a message box.
I got most of the code from http://msdn.microsoft.com/en-us/library/dd374921(VS.85).aspx
What am I doing wrong?
EDIT: I do get notifications when changing the volume for my application (thanks #nobugz). But I want notification when changing the master volume. (Listed as "Device" in Win7's Volume Mixers dialog)
EDIT 2: Larry Osterman has a series of blog posts about the volume in Vista/7. This one in particular is interesting: http://blogs.msdn.com/larryosterman/archive/2007/03/22/fun-with-the-endpoint-volume-interfaces-closing-the-loop.aspx I'll try the code out tomorrow to see if I can get what I want. Now it's time for bed.
EDIT 3: This is the complete code from Larry's blog posts up to and including the link posted above. It does what I need and then some. I'll try to trim the features I don't need.
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <Tchar.h>
#include <strsafe.h>
class CVolumeNotification : public IAudioEndpointVolumeCallback
{
LONG m_RefCount;
~CVolumeNotification(void) {};
public:
CVolumeNotification(void) : m_RefCount(1)
{
}
STDMETHODIMP_(ULONG)AddRef() { return InterlockedIncrement(&m_RefCount); }
STDMETHODIMP_(ULONG)Release()
{
LONG ref = InterlockedDecrement(&m_RefCount);
if (ref == 0)
delete this;
return ref;
}
STDMETHODIMP QueryInterface(REFIID IID, void **ReturnValue)
{
if (IID == IID_IUnknown || IID== __uuidof(IAudioEndpointVolumeCallback))
{
*ReturnValue = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ReturnValue = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData)
{
wchar_t outputString[256];
DWORD written;
COORD writeCoord;
StringCbPrintf(outputString, sizeof(outputString), L"Volume Changed: %f", NotificationData->fMasterVolume);
writeCoord.X = 0;
writeCoord.Y = 3;
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), outputString, (DWORD)wcslen(outputString), writeCoord, &written);
return S_OK;
}
};
struct TimerContext
{
IAudioMeterInformation *_Meter;
};
const int TimerPeriodicityMS = 100;
void CALLBACK TimerMeterCallback(PTP_CALLBACK_INSTANCE CallbackInstance, PVOID Context, PTP_TIMER Timer)
{
TimerContext *timerContext = (TimerContext *)Context;
wchar_t outputString[256];
float peakValue;
DWORD written;
COORD writeCoord;
StringCbCopy(outputString, sizeof(outputString), L"Meter: ");
timerContext->_Meter->GetPeakValue(&peakValue);
for (size_t i = 0 ; i < peakValue*100; i += 1)
{
StringCbCat(outputString, sizeof(outputString), L"*");
}
for (size_t i = (size_t)(peakValue*100) ; i < 100; i += 1)
{
StringCbCat(outputString, sizeof(outputString), L".");
}
writeCoord.X = 0;
writeCoord.Y = 1;
WriteConsoleOutputCharacter(GetStdHandle(STD_OUTPUT_HANDLE), outputString, (DWORD)wcslen(outputString), writeCoord, &written);
}
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = NULL;;
HANDLE handle;
TP_CALLBACK_ENVIRON callbackEnvironment;
PTP_CLEANUP_GROUP cleanupGroup;
PTP_TIMER timer;
TimerContext context;
InitializeThreadpoolEnvironment(&callbackEnvironment);
cleanupGroup = CreateThreadpoolCleanupGroup();
SetThreadpoolCallbackCleanupGroup(&callbackEnvironment, cleanupGroup, NULL);
timer = CreateThreadpoolTimer(TimerMeterCallback, &context, &callbackEnvironment);
//
// Clear the screen. Code stolen from: http://support.microsoft.com/kb/319257.
//
handle = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD writtenChars = 0;
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
COORD home;
home.X = home.Y = 0;
GetConsoleScreenBufferInfo(handle, &consoleInfo);
FillConsoleOutputCharacter(handle, L' ', consoleInfo.dwSize.X * consoleInfo.dwSize.Y, home, &writtenChars);
SetConsoleCursorPosition(handle, home);
//
// Set the console to raw mode.
//
DWORD oldMode;
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &oldMode);
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));
//
// Instantiate an endpoint volume object.
//
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
IMMDevice *defaultDevice = NULL;
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
deviceEnumerator = NULL;
IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
CVolumeNotification *volumeNotification = new CVolumeNotification();
hr = endpointVolume->RegisterControlChangeNotify(volumeNotification);
hr = defaultDevice->Activate(__uuidof(IAudioMeterInformation), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&context._Meter);
defaultDevice->Release();
defaultDevice = NULL;
// Set a 100 millisecond timer.
LARGE_INTEGER timerPeriodicityLI;
FILETIME timerPeriodicity;
timerPeriodicityLI.QuadPart = -1*(TimerPeriodicityMS* 10000 );
timerPeriodicity.dwLowDateTime = timerPeriodicityLI.LowPart;
timerPeriodicity.dwHighDateTime = timerPeriodicityLI.HighPart;
SetThreadpoolTimer(timer, &timerPeriodicity, TimerPeriodicityMS, 10);
wchar_t inputChar = '\0';
while (inputChar != '\r')
{
UINT currentStep, stepCount;
DWORD read, written;
COORD writeCoord;
wchar_t outputString[256];
StringCbCopy(outputString, sizeof(outputString), L"Volume: ");
//
// Calculate the cheesy OSD.
//
endpointVolume->GetVolumeStepInfo(&currentStep, &stepCount);
for (size_t i = 0 ; i < stepCount ; i += 1)
{
if (i <= currentStep)
{
StringCbCat(outputString, sizeof(outputString), L"=");
}
else
{
StringCbCat(outputString, sizeof(outputString), L"-");
}
}
writeCoord.X = 0;
writeCoord.Y = 0;
WriteConsoleOutputCharacter(handle, outputString, (DWORD)wcslen(outputString), writeCoord, &written);
ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
if (inputChar == '+')
{
endpointVolume->VolumeStepUp(NULL);
}
else if (inputChar == '-')
{
endpointVolume->VolumeStepDown(NULL);
}
}
//
// Remove our notification.
//
endpointVolume->UnregisterControlChangeNotify(volumeNotification);
endpointVolume->Release();
volumeNotification->Release();
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), oldMode);
CloseThreadpoolCleanupGroupMembers(cleanupGroup, FALSE, NULL);
CloseThreadpoolCleanupGroup(cleanupGroup);
cleanupGroup = NULL;
DestroyThreadpoolEnvironment(&callbackEnvironment);
context._Meter->Release();
CoUninitialize();
return 0;
}
EDIT 4: This is a stripped down version of Larry's code.
#include <windows.h>
#include <mmdeviceapi.h>
#include <endpointvolume.h>
#include <iostream>
#include <Tchar.h>
class CVolumeNotification : public IAudioEndpointVolumeCallback
{
LONG m_RefCount;
~CVolumeNotification(void) {};
public:
CVolumeNotification(void) : m_RefCount(1)
{
}
STDMETHODIMP_(ULONG)AddRef() { return InterlockedIncrement(&m_RefCount); }
STDMETHODIMP_(ULONG)Release()
{
LONG ref = InterlockedDecrement(&m_RefCount);
if (ref == 0)
delete this;
return ref;
}
STDMETHODIMP QueryInterface(REFIID IID, void **ReturnValue)
{
if (IID == IID_IUnknown || IID== __uuidof(IAudioEndpointVolumeCallback))
{
*ReturnValue = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ReturnValue = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP OnNotify(PAUDIO_VOLUME_NOTIFICATION_DATA NotificationData)
{
std::cout << NotificationData->fMasterVolume << _T(" ");
return S_OK;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
HRESULT hr;
IMMDeviceEnumerator *deviceEnumerator = NULL;;
//
// Instantiate an endpoint volume object.
//
hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (LPVOID *)&deviceEnumerator);
IMMDevice *defaultDevice = NULL;
hr = deviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &defaultDevice);
deviceEnumerator->Release();
deviceEnumerator = NULL;
IAudioEndpointVolume *endpointVolume = NULL;
hr = defaultDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, (LPVOID *)&endpointVolume);
CVolumeNotification *volumeNotification = new CVolumeNotification();
hr = endpointVolume->RegisterControlChangeNotify(volumeNotification);
defaultDevice->Release();
defaultDevice = NULL;
wchar_t inputChar = '\0';
while (inputChar != '\r')
{
DWORD read;
ReadConsole(GetStdHandle(STD_INPUT_HANDLE), &inputChar, 1, &read, NULL);
}
//
// Remove our notification.
//
endpointVolume->UnregisterControlChangeNotify(volumeNotification);
endpointVolume->Release();
volumeNotification->Release();
CoUninitialize();
return 0;
}
I still haven't looked at the SDK example. I now know how this stuff works well enough to add what I need to my application. Thanks for everyones help!
It works fine when I try it. Be sure to click on the volume control tray icon, click Mixer and modify the volume for your app.
The Windows SDK "OSD" sample is a much simpler example of monitoring the hardware volume than the one I posted on the blog (you don't need all the metering junk in that post).