How to get Windows installed updates - c++

I need to get all installed updates on Windows machine. I tried WUA API, but the result is differs from what I see in Control Panel - Installed Updates. My code returns 321 update, while in control panel I see 508. Here is my code:
IUpdateSearcher* updateSearcher = NULL;
IUpdateSession* updateSession = NULL;
IUpdateCollection* updateList = NULL;
ISearchResult* results = NULL;
IUpdate* updateItem = NULL;
BSTR criteria = NULL;
LONG updateSize = 0;
HRESULT hr;
if ((hr = CoInitialize(NULL)) != S_OK)
{
return -1;
}
if ((hr = CoCreateInstance(CLSID_UpdateSession, NULL, CLSCTX_INPROC_SERVER, IID_IUpdateSession, (LPVOID*)&updateSession)) != S_OK)
{
return -1;
}
if ((hr = updateSession->CreateUpdateSearcher(&updateSearcher)) != S_OK)
{
return -1;
}
if ((hr = updateSearcher->put_ServerSelection(ssWindowsUpdate)) != S_OK)
{
return -1;
}
criteria = SysAllocString(L"IsInstalled=1 or IsInstalled=0 or IsHidden=1 or IsPresent=1");
if ((hr = updateSearcher->Search(criteria, &results)) == S_OK)
{
std::wcout << L"[*]Successfully completed search for updates on this host" << std::endl;
}
else
{
std::wcout << L"[-]Failed to search for updates" << std::endl;
return -1;
}
results->get_Updates(&updateList);
updateList->get_Count(&updateSize);
if (updateSize == 0)
{
std::wcout << L"[-]No updates available for this host" << std::endl;
CoUninitialize();
return 0;
}
std::set<std::wstring> KBs;
for (LONG i = 0; i < updateSize; i++)
{
IStringCollection *KBCollection;
LONG KBsSize = 0;
updateList->get_Item(i, &updateItem);
updateItem->get_KBArticleIDs(&KBCollection);
KBCollection->get_Count(&KBsSize);
for (LONG i = 0; i < KBsSize; i++)
{
BSTR KBValue;
KBCollection->get_Item(i, &KBValue);
std::wstring ws(KBValue, SysStringLen(KBValue));
KBs.insert(ws);
}
}
if ((hr = updateSearcher->put_ServerSelection(ssOthers)) != S_OK)
{
return -1;
}
BSTR serviceID = SysAllocString(L"7971f918-a847-4430-9279-4a52d1efe18d");
if ((hr = updateSearcher->put_ServiceID(serviceID)) != S_OK)
{
return -1;
}
hr = updateSearcher->Search(criteria, &results);
if ((hr = updateSearcher->Search(criteria, &results)) == S_OK)
{
std::wcout << L"[*]Successfully completed search for updates on this host" << std::endl;
}
else
{
std::wcout << L"[-]Failed to search for updates" << std::endl;
}
results->get_Updates(&updateList);
updateList->get_Count(&updateSize);
if (updateSize == 0)
{
std::wcout << L"[-]No updates available for this host" << std::endl;
CoUninitialize();
return 0;
}
for (LONG i = 0; i < updateSize; i++)
{
IStringCollection *KBCollection;
LONG KBsSize = 0;
updateList->get_Item(i, &updateItem);
updateItem->get_KBArticleIDs(&KBCollection);
KBCollection->get_Count(&KBsSize);
for (LONG i = 0; i < KBsSize; i++)
{
BSTR KBValue;
KBCollection->get_Item(i, &KBValue);
KBs.insert(KBValue);
}
}
SysFreeString(criteria);
SysFreeString(serviceID);
CoUninitialize();
std::wcout << KBs.size() << std::endl;
Can anyone please explain how can I get all the updates?

WUA API only lists updates installed through Windows Updates Services.
To list other kind of updates you may use Windows Installer API, specifically:
MsiEnumPatchesEx()
MsiGetPatchInfoEx()

Related

OPC DA client ItemMgt Incorrect function error

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.

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;
}
}

Using Joystick in Windows Forms application

I've got a problem with my application. I wrote a code that works fine in console, but I need to make a GUI. This application let me control a mouse with joystick. I'm using Direcinput library. It is reading position of joystick and its name.
Here is my code:
#ifndef D_INPUT
#define D_INPUT
#include <dinput.h>
#include <iostream>
#endif
LPDIRECTINPUT8 di;
HRESULT hr;
LPDIRECTINPUTDEVICE8 joystick;
DIDEVICEINSTANCE pdidi;
DIDEVICEINSTANCE info;
BOOL CALLBACK
enumCallback(const DIDEVICEINSTANCE* instance, VOID* context)
{
HRESULT hr;
hr = di->CreateDevice(instance->guidInstance, &joystick, NULL);
if (FAILED(hr)) {
return DIENUM_CONTINUE;
}
return DIENUM_STOP;
}
BOOL CALLBACK
enumAxesCallback(const DIDEVICEOBJECTINSTANCE* instance, VOID* context)
{
HWND hDlg = (HWND)context;
DIPROPRANGE propRange;
propRange.diph.dwSize = sizeof(DIPROPRANGE);
propRange.diph.dwHeaderSize = sizeof(DIPROPHEADER);
propRange.diph.dwHow = DIPH_BYID;
propRange.diph.dwObj = instance->dwType;
propRange.lMin = -50;
propRange.lMax = +50;
if (FAILED(joystick->SetProperty(DIPROP_RANGE, &propRange.diph))) {
return DIENUM_STOP;
}
return DIENUM_CONTINUE;
}
class Joy
{
public:
HRESULT
poll(DIJOYSTATE *js)
{
HRESULT hr;
if (joystick == NULL)
{
return S_OK;
}
// Poll the device to read the current state
hr = joystick->Poll();
if (FAILED(hr)) {
// DInput is telling us that the input stream has been
// interrupted. We aren't tracking any state between polls, so
// we don't have any special reset that needs to be done. We
// just re-acquire and try again.
hr = joystick->Acquire();
while (hr == DIERR_INPUTLOST) {
hr = joystick->Acquire();
}
// If we encounter a fatal error, return failure.
if ((hr == DIERR_INVALIDPARAM) || (hr == DIERR_NOTINITIALIZED)) {
return E_FAIL;
}
// If another application has control of this device, return successfully.
// We'll just have to wait our turn to use the joystick.
if (hr == DIERR_OTHERAPPHASPRIO) {
return S_OK;
}
}
// Get the input's device state
if (FAILED(hr = joystick->GetDeviceState(sizeof(DIJOYSTATE), js))) {
return hr; // The device should have been acquired during the Poll()
}
return S_OK;
}
void GetDesktopResolution(int& horizontal, int& vertical)
{
RECT desktop;
// Get a handle to the desktop window
const HWND hDesktop = GetDesktopWindow();
// Get the size of screen to the variable desktop
GetWindowRect(hDesktop, &desktop);
horizontal = desktop.right;
vertical = desktop.bottom;
}
void moveMouse(int dx, int dy)
{
POINT pt;
int horizontal = 0;
int vertical = 0;
GetDesktopResolution(horizontal, vertical);
GetCursorPos(&pt);
pt.x += dx;
pt.y += dy;
if (pt.x < 0)
{
pt.x = 0;
}
if (pt.x > horizontal)
{
pt.x = horizontal;
}
if (pt.y < 0)
{
pt.y = 0;
}
if (pt.y > vertical)
{
pt.y = vertical;
}
SetCursorPos(pt.x, pt.y);
}
void clickMouse()
{
if (GetKeyState(VK_LBUTTON) >= 0)
{
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
}
}
void unclickMouse()
{
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
void close()
{
if (joystick)
{
joystick->Unacquire();
}
}
int start()
{
DIJOYSTATE js; // struktura stanu joysticka
// Create a DirectInput device
if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
IID_IDirectInput8, (VOID**)&di, NULL))) {
return hr;
}
if (FAILED(hr = di->EnumDevices(DI8DEVCLASS_GAMECTRL, enumCallback,
NULL, DIEDFL_ATTACHEDONLY))) {
return hr;
}
// sprawdzenie czy jest joystick
if (joystick == NULL) {
std::cout << "Joystick not found.\n";
system("pause");
return E_FAIL;
}
// ustawienia
DIDEVCAPS capabilities;
// zdefiniowanie formatu danych urzadzenia
if (FAILED(hr = joystick->SetDataFormat(&c_dfDIJoystick)))
{
return hr;
}
// Powiazanie urzadzenia z oknem aplikacji
if (FAILED(hr = joystick->SetCooperativeLevel(GetConsoleWindow(), DISCL_NONEXCLUSIVE | DISCL_FOREGROUND))) {
return hr;
}
// wczytanie ustawien joysticka
capabilities.dwSize = sizeof(DIDEVCAPS);
if (FAILED(hr = joystick->GetCapabilities(&capabilities))) {
return hr;
}
// wyliczanie
if (FAILED(hr = joystick->EnumObjects(enumAxesCallback, NULL, DIDFT_AXIS))) {
return hr;
}
info.dwSize = sizeof(DIDEVICEINSTANCE);
if (FAILED(hr = joystick->GetDeviceInfo(&info)))
{
return hr;
}
int i = 0;
while (i < MAX_PATH && info.tszProductName[i] != 0)
{
std::cout << (char)info.tszProductName[i];
i++;
}
std::cout << std::endl;
system("pause");
while (1)
{
poll(&js);
if (js.rgbButtons[0] != 0)
clickMouse();
else
unclickMouse();
//clickMouse(js.rgbButtons[0]);
for (int i = 0; i < 11; i++)
{
if (js.rgbButtons[i] != 0) std::cout << "Przycisk " << i + 1 << std::endl;
}
std::cout << "X: " << js.lX << std::endl;
std::cout << "Y: " << js.lY << std::endl;
std::cout << "Z: " << js.lZ << std::endl;
moveMouse(js.lX, js.lY);
//Sleep(400);
std::cout << std::endl;
system("cls");
}
close();
system("pause");
}
};
This is how I'm using it in MyForm.h:
#pragma once
#include "Joy.cpp"
(...)
public ref class MyForm : public System::Windows::Forms::Form
{
public:
MyForm(void)
{
InitializeComponent();
Joy joy;
joy.start();
}
Here is the errors that I get.
When I was testing it, I had an main() function instead of start(). I also tried to put enumCallback and enumAxesCallback function into class, but it occured other errors.
Does anybody sees what am I doing wrong?
Thanks for all your answers.

IShellDisptach: Why does FolderItemVerbs::Release() + CoUninitialize() crash?

I'm having a very bizarre problem with the IShellDispatch COM interface, more specifically with the FolderItemVerbs object, that drives me nuts!
Calling FolderItemVerbs::Release() followed by CoUninitialze() will result in crash. It's clearly reproducible, but only happens 1 out of 10 times.
The crash is an "0xC0000005: Access Violation" error. Running the problematic code in a loop 100% reproduces the crash sooner or later :-(
Please see the example program:
static int TestProc(const TCHAR *pcDirectoryName, const TCHAR *pcFileName)
{
int iSuccess = 0;
IShellDispatch *pShellDispatch = NULL;
Folder *pFolder = NULL; FolderItem *pItem = NULL;
FolderItemVerbs *pVerbs = NULL;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pShellDispatch);
if(FAILED(hr) || (pShellDispatch == NULL))
{
iSuccess = -3;
return iSuccess;
}
variant_t vaDirectory(pcDirectoryName);
hr = pShellDispatch->NameSpace(vaDirectory, &pFolder);
if(FAILED(hr) || (pFolder == NULL))
{
iSuccess = -4;
pShellDispatch->Release();
return iSuccess;
}
variant_t vaFileName(pcFileName);
hr = pFolder->ParseName(vaFileName, &pItem);
if(FAILED(hr) || (pItem == NULL))
{
iSuccess = -5;
pFolder->Release();
pShellDispatch->Release();
return iSuccess;
}
hr = pItem->Verbs(&pVerbs);
if(FAILED(hr) || (pVerbs == NULL))
{
iSuccess = -6;
pItem->Release();
pFolder->Release();
pShellDispatch->Release();
return iSuccess;
}
/* Here we would do something with the FolderItemVerbs */
pVerbs->Release(); pVerbs = NULL; //If this line is commented out, we don't get a crash, but a massive memory leak!
pItem->Release(); pItem = NULL;
pFolder->Release(); pFolder = NULL;
pShellDispatch->Release(); pShellDispatch = NULL;
iSuccess = 1;
return iSuccess;
}
//-----------------------------------------------------------------------------
static unsigned __stdcall ThreadProc(void* pArguments)
{
HRESULT hr = CoInitialize(NULL);
if((hr == S_OK) || (hr == S_FALSE))
{
threadParam_t *params = (threadParam_t*) pArguments;
params->returnValue = TestProc(params->pcDirectoryName, params->pcFileName);
CoUninitialize();
}
else
{
if(threadParam_t *params = (threadParam_t*) pArguments)
{
params->returnValue = -10;
}
}
return EXIT_SUCCESS;
}
Please download the complete example code is here:
http://pastie.org/private/0xsnajpia9lsmgnlf2afa
Please also note that I unambiguously tracked down to crash to FolderItemVerbs, because if I never create the FolderItemVerbs object, the crash is gone immediately.
Also if I never call "pVerbs->Release()" before CoUninitialize() the crash is gone too, but this will result in a massive memleak, obviously.
Another strange thing is that the crash will NOT happen, if I run the program under the Debugger! But I can run the program, wait for the crash and then let the Debugger handle the crash.
Unfortunately the Stack Trace that I get then doesn't help much:
http://pastie.org/private/cuwunlun2t5dc5lembpw
I don't think I'm doing anything wrong here. I have checked the code over and over again in the last two days. So this all seems to be a bug in FolderItemVerbs!
Has anybody encountered this before and/or can confirm that this is a bug in FolderItemVerbs? Also, is there any workaround for the problem?
Thanks in advance !!!
Thanks everybody!
Here is the "corrected" code which performs explicit message dispatching:
void DispatchPendingMessages(void)
{
const DWORD uiTimeout = GetTickCount() + 10000;
const HANDLE hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
unsigned int counter = 0;
if(hEvent)
{
for(;;)
{
MSG Message;
while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
const DWORD nWaitResult = MsgWaitForMultipleObjects(1, &hEvent, FALSE, 250, QS_ALLINPUT | QS_ALLPOSTMESSAGE);
if((nWaitResult == WAIT_TIMEOUT) || (nWaitResult == WAIT_FAILED) || (GetTickCount() >= uiTimeout)) break;
}
CloseHandle(hEvent);
}
}
//-----------------------------------------------------------------------------
static int TestProc(const TCHAR *pcDirectoryName, const TCHAR *pcFileName)
{
int iSuccess = 0;
IShellDispatch *pShellDispatch = NULL;
Folder *pFolder = NULL; FolderItem *pItem = NULL;
FolderItemVerbs *pVerbs = NULL;
HRESULT hr = CoCreateInstance(CLSID_Shell, NULL, CLSCTX_INPROC_SERVER, IID_IShellDispatch, (void**)&pShellDispatch);
if(FAILED(hr) || (pShellDispatch == NULL))
{
iSuccess = -3;
return iSuccess;
}
variant_t vaDirectory(pcDirectoryName);
hr = pShellDispatch->NameSpace(vaDirectory, &pFolder);
if(FAILED(hr) || (pFolder == NULL))
{
iSuccess = -4;
pShellDispatch->Release();
return iSuccess;
}
variant_t vaFileName(pcFileName);
hr = pFolder->ParseName(vaFileName, &pItem);
if(FAILED(hr) || (pItem == NULL))
{
iSuccess = -5;
pFolder->Release();
pShellDispatch->Release();
return iSuccess;
}
hr = pItem->Verbs(&pVerbs);
if(FAILED(hr) || (pVerbs == NULL))
{
iSuccess = -6;
pItem->Release();
pFolder->Release();
pShellDispatch->Release();
return iSuccess;
}
/* Here we would do something with the FolderItemVerbs */
pVerbs->Release(); pVerbs = NULL;
pItem->Release(); pItem = NULL;
pFolder->Release(); pFolder = NULL;
pShellDispatch->Release(); pShellDispatch = NULL;
iSuccess = 1;
return iSuccess;
}
//-----------------------------------------------------------------------------
static unsigned __stdcall ThreadProc(void* pArguments)
{
HRESULT hr = CoInitialize(NULL);
if((hr == S_OK) || (hr == S_FALSE))
{
threadParam_t *params = (threadParam_t*) pArguments;
params->returnValue = TestProc(params->pcDirectoryName, params->pcFileName);
DispatchPendingMessages(); //This is required before CoUninitialize() to avoid crash with certain Shell Extensions !!!
CoUninitialize();
}
else
{
if(threadParam_t *params = (threadParam_t*) pArguments)
{
params->returnValue = -10;
}
}
return EXIT_SUCCESS;
}
Couldn't reproduce the crash with that code so far :-)

Windows: how to get cameras supported resolutions?

So to get cameras list and let user select one (C++, Boost, dshow, windows) I use such code:
#include "StdAfx.h"
#include "list.h"
#include <windows.h>
#include <dshow.h>
#include <boost/lexical_cast.hpp>
HRESULT CamerasList::EnumerateDevices( REFGUID category, IEnumMoniker **ppEnum )
{
// Create the System Device Enumerator.
ICreateDevEnum *pDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum));
if (SUCCEEDED(hr))
{
// Create an enumerator for the category.
hr = pDevEnum->CreateClassEnumerator(category, ppEnum, 0);
if (hr == S_FALSE)
{
hr = VFW_E_NOT_FOUND; // The category is empty. Treat as an error.
}
pDevEnum->Release();
}
return hr;
}
int CamerasList::SelectFromList()
{ int i = 0;
int SelectedIndex;
IEnumMoniker *pEnum;
printf("\nLet us select video device\n");
printf("Available Capture Devices are:\n");
HRESULT hr;
hr = EnumerateDevices(CLSID_VideoInputDeviceCategory, &pEnum);
if (SUCCEEDED(hr))
{
IMoniker *pMoniker = NULL;
while (pEnum->Next(1, &pMoniker, NULL) == S_OK)
{
IPropertyBag *pPropBag;
HRESULT hr = pMoniker->BindToStorage(0, 0, IID_PPV_ARGS(&pPropBag));
if (FAILED(hr))
{
pMoniker->Release();
continue;
}
VARIANT var;
VariantInit(&var);
// Get description or friendly name.
hr = pPropBag->Read(L"Description", &var, 0);
if (FAILED(hr))
{
hr = pPropBag->Read(L"FriendlyName", &var, 0);
}
if (SUCCEEDED(hr))
{
std::cout << i;
printf(") %S\n", var.bstrVal);
i++;
VariantClear(&var);
}
hr = pPropBag->Write(L"FriendlyName", &var);
pPropBag->Release();
pMoniker->Release();
}
SelectedIndex = 999;
if (i <= 0)
{
cout <<"No devices found. \n " << endl;
//cout <<"Please restart application." << endl;
//cin.get();
//Sleep(999999);
return 999;
}else if(i == 1){
cout <<"Default device will be used\n" << std::endl;
SelectedIndex = 0;
}else{
while(SelectedIndex > i-1 || SelectedIndex < 0)
{
try{
std::string s;
std::getline( cin, s, '\n' );
SelectedIndex = boost::lexical_cast<int>(s);
}
catch(std::exception& e){
std::cout <<"please input index from 0 to " << i-1 << std::endl;
SelectedIndex = 999;
}
}}
pEnum->Release();
}else
{
printf("no Video Devices found. \n") ;
//cout <<"Please restart application." << endl;
//cin.get();
//Sleep(999999);
return 999;
}
return SelectedIndex;
}
I need to somehow get list of camera supported resolutions for selected camera. How to do such thing?
Assuming that you've added the capture source filter to the graph:
One method is to get the IAMStreamConfig interface of the capture filter's output pin and then call the IAMStreamConfig::GetNumberOfCapabilities to get the number of format capabilities supported by the device. You can iterate over all formats by calling the IAMStreamConfig::GetStreamCaps with the appropriate indices.
You can get supported resolutions without adding the capture source to a filter graph. You need to:
bind the device moniker to a base filter
get an output pin from that filter
enumerate over media types of that output pin
Here is how to enumerate the media types given a media type enumerator:
AM_MEDIA_TYPE* mediaType = NULL;
VIDEOINFOHEADER* videoInfoHeader = NULL;
while (S_OK == mediaTypesEnumerator->Next(1, &mediaType, NULL))
{
if ((mediaType->formattype == FORMAT_VideoInfo) &&
(mediaType->cbFormat >= sizeof(VIDEOINFOHEADER)) &&
(mediaType->pbFormat != NULL))
{
videoInfoHeader = (VIDEOINFOHEADER*)mediaType->pbFormat;
videoInfoHeader->bmiHeader.biWidth; // Supported width
videoInfoHeader->bmiHeader.biHeight; // Supported height
}
FreeMediaType(*mediaType);
}