Capturing AutoDiscoverComplete event - c++

I'm using the following code to capture Account AutoDiscoverComplete event.
When running the thread that captures this event, I get an access violation error, QueryInterface method fails.
Where could be the problem?
DWORD WINAPI CaptureAccountDiscovery(LPVOID param)
{
CoInitialize(NULL);
CComPtr<Outlook::_Application> spApplication;
HRESULT hr = spApplication.CoCreateInstance(__uuidof(Outlook::Application), 0, CLSCTX_LOCAL_SERVER );
if(SUCCEEDED(hr) && spApplication)
{
CComPtr<Outlook::_NameSpace> spSession;
hr = spApplication->get_Session(reinterpret_cast<Outlook::_NameSpace **>(&spSession));
if (SUCCEEDED(hr) && spSession)
{
CComPtr<Outlook::_Accounts> spAccounts;
hr = spSession->get_Accounts(reinterpret_cast<Outlook::_Accounts **>(&spAccounts));
if (SUCCEEDED(hr) && spAccounts)
{
VARIANT index;
index.intVal = 1;
index.vt = VT_INT;
CComPtr<Outlook::_Accounts> spAccounts;
hr = spAccounts->Item(index, reinterpret_cast<Outlook::_Account **>(&spAccounts));
if (SUCCEEDED(hr) && spAccounts)
{
CComPtr<IConnectionPointContainer> spContainer;
HRESULT hr = spAccounts->QueryInterface(__uuidof(IConnectionPointContainer),reinterpret_cast<void **>(&spContainer));
if (SUCCEEDED(hr) && spContainer)
{
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
CComPtr<CAccountDiscover> spSink = new CAccountDiscover(hEvent);
CComPtr<IConnectionPoint> spConnectionPoint;
hr = spContainer->FindConnectionPoint(Outlook::CLSID_Accounts, &spConnectionPoint);
if (SUCCEEDED(hr) && spConnectionPoint)
{
DWORD dwCookie = 0;
CComPtr<IUnknown> spUnknown;
hr = spConnectionPoint->QueryInterface(IID_IUnknown, reinterpret_cast<void **>(&spUnknown));
if (SUCCEEDED(hr) && spUnknown)
{
hr = spConnectionPoint->Advise(spSink, &dwCookie);
if (SUCCEEDED(hr))
{
while(true)
{
MSG Message;
while(PeekMessage(&Message, NULL, WM_NULL, WM_NULL, PM_REMOVE))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
DWORD dwStatus = WaitForSingleObject(hEvent, 0);
Sleep(1);
}
spConnectionPoint->Unadvise(dwCookie);
}
}
}
}
}
}
}
spApplication.Release();
}
CoUninitialize();
return 0;
}

The code
CComPtr<Outlook::_Accounts> spAccounts;
hr = spAccounts->Item(index, reinterpret_cast<Outlook::_Account **>(&spAccounts));
really needs to be
CComPtr<Outlook::_Account> spAccount;
hr = spAccounts->Item(index, &spAccount);
Note the singular instead of plural.

Related

Getting bytes from GraphFilter SampleGrabber to Audio raw data WinApi

I am having a big issue getting raw audio data from the SampleGrabber GraphFilter and writing it to an audio file in the proper format.
What i'm trying to achieve is:
grabbing the data from: Microphone->SampleGrabber->Null Rendrer
Write the raw data captured to a proper audio file (MP3\AVI\WAVE)
Some questions:
Am I setting the proper MediaType for the SampleGrabber?
How can you compress the audio data to a proper audio file?
My current code below:
struct sRecortdingData {
std::vector<BYTE> recordingData;
double totalRecordingTime;
};
class CFakeCallback : public ISampleGrabberCB
{
public:
sRecortdingData sRecording;
STDMETHODIMP_(ULONG) AddRef() { return 2; }
STDMETHODIMP_(ULONG) Release() { return 1; }
STDMETHODIMP_(HRESULT __stdcall) QueryInterface(REFIID riid, void** ppv)
{
if (riid == IID_ISampleGrabberCB || riid == IID_IUnknown)
{
*ppv = (void*) static_cast<ISampleGrabberCB*>(this);
return NOERROR;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(HRESULT __stdcall) SampleCB(double SampleTime, IMediaSample* pSample)
{
return S_OK;
}
STDMETHODIMP_(HRESULT __stdcall) BufferCB(double SampleTime, BYTE* pBuffer, long BufferLen)
{
sRecording.recordingData.push_back(*pBuffer);
sRecording.totalRecordingTime = SampleTime;
return S_OK;
}
};
void Microphone::RecordAudio()
{
HRESULT hr;
AM_MEDIA_TYPE mt;
long recordingEventCode;
IMediaControl* pMediaControl = NULL;
ISampleGrabber* pISampleGrabber = NULL;
IBaseFilter* pSampleGrabberFilter;
IGraphBuilder* pGraphBuilder = NULL;
IMediaEventEx* pEvent = NULL;
IBaseFilter* pMicrophoneFilter = NULL;
ISampleGrabber* pSampleGrabber = NULL;
ICreateDevEnum* pDeviceEnum = NULL;
IBaseFilter* pNullRender = NULL;
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Filter graph
hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pGraphBuilder));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Device enum
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDeviceEnum));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Null renderer
hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pNullRender));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Get the even control
hr = pGraphBuilder->QueryInterface(IID_PPV_ARGS(&pEvent));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Sample Grabber
hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pSampleGrabberFilter));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Get Media Control
hr = pGraphBuilder->QueryInterface(IID_PPV_ARGS(&pMediaControl));
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
// Setup input device filter
pMicrophoneFilter = Microphone::SetupDeviceFilter(CLSID_AudioInputDeviceCategory, pGraphBuilder, pDeviceEnum,
Utils::s2ws("My Microphone");
if (pMicrophoneFilter == NULL) {
HR_Failed(hr);
return;
}
// Setup sample grabber filter
pSampleGrabberFilter = Microphone::SetupDeviceFilter(CLSID_LegacyAmFilterCategory,
pGraphBuilder, pDeviceEnum, L"SampleGrabber");
if (pSampleGrabberFilter == NULL) {
HR_Failed(hr);
return;
}
// Connect both pins together
Device_Connect(pMicrophoneFilter, pSampleGrabberFilter);
// Setup null renderer filter
pNullRender = Microphone::SetupDeviceFilter(CLSID_LegacyAmFilterCategory,
pGraphBuilder, pDeviceEnum, L"Null Renderer");
if (pNullRender == NULL) {
HR_Failed(hr);
return;
}
// Connect both pins together
Device_Connect(pSampleGrabberFilter, pNullRender);
// Get the ISampleGranner interface
hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (LPVOID*)&pISampleGrabber);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
hr = pISampleGrabber->SetCallback(&CB, 1);
ZeroMemory(&mt, sizeof(mt));
mt.majortype = MEDIATYPE_Audio;
mt.subtype = MEDIASUBTYPE_PCM;
// Set the media type
hr = pISampleGrabber->SetMediaType(&mt);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
pISampleGrabber->SetBufferSamples(FALSE);
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
hr = pMediaControl->Run();
if (FAILED(hr))
{
HR_Failed(hr);
return;
}
DWORD recordingTime = 20000;
hr = pEvent->WaitForCompletion(recordingTime, &recordingEventCode);
std::string str(CB.sRecording.recordingData.begin(), CB.sRecording.recordingData.end());
// Do something with the data from callback and write it to audio file
}

Changing sample rate for microphone/speakers in Windows 7

I need to change sample rate for microphone and speakers.
I know how to change volume levels for speakers and microphone using IMMDevice, but how can I set sample rate?
bool SoundDeviceControler::setVolume(EDataFlow dataFlow,float volume )
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr))
return false
IMMDeviceEnumerator *deviceEnumerator = NULL;
hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&deviceEnumerator) );
if (FAILED(hr))
return false;
IMMDeviceCollection *deviceCollection = NULL;
hr = deviceEnumerator->EnumAudioEndpoints( dataFlow, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &deviceCollection );
if (FAILED(hr))
return false;
UINT deviceCount;
hr = deviceCollection->GetCount(&deviceCount);
if (FAILED(hr))
return false;
IMMDevice *device = NULL;
for (UINT i = 0 ; i < deviceCount ; i += 1)
{
device = NULL;
hr = deviceCollection->Item(i, &device);
if (FAILED(hr))
return false;
IAudioEndpointVolume *endpointVolume = NULL;
hr = device->Activate( __uuidof(IAudioEndpointVolume), CLSCTX_INPROC_SERVER, NULL, reinterpret_cast<void **>(&endpointVolume) );
if (FAILED(hr))
retur false;
hr=endpointVolume->SetMasterVolumeLevelScalar(volume,NULL);
if (FAILED(hr))
return false;
hr=endpointVolume->SetMute(false,NULL);
if (FAILED(hr))
return false;
}
return true;
}

How to display system context menu for multiple files in different folders?

This question is related to my previous question.
I need to obtain IContextMenu* interface pointer for files in different directories (or even drives).
See my code, which is not working properly (e.g. the file properties dialog shows wrong information), because I provide wrong relative PIDLs (as mentioned in this answer).
int main() {
CoInitialize(NULL);
LPOLESTR pszFile = OLESTR("c:\\Windows\\notepad.exe");
LPOLESTR pszFile2 = OLESTR("c:\\Windows\\System32\\notepad.exe");
//LPOLESTR pszDir = OLESTR("c:\\Windows\\");
LPITEMIDLIST pidl = NULL;
LPITEMIDLIST pidl2 = NULL;
LPITEMIDLIST pidlDir;
LPCITEMIDLIST pidlItem;
LPCITEMIDLIST pidlItem2;
HRESULT hr;
IShellFolder* pFolder;
//IShellFolder* pDir;
IShellFolder* pDesktop;
IContextMenu* pContextMenu;
HMENU hMenu;
CMINVOKECOMMANDINFO cmi;
TCHAR szTemp[256];
hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr)) {
CoUninitialize();
return 0;
}
HWND wnd = ::CreateWindowA("STATIC", "dummy", WS_VISIBLE, 0, 0, 100, 100, NULL, NULL, NULL, NULL);
/*hr = pDesktop->ParseDisplayName(wnd, NULL, pszDir, NULL, &pidlDir, NULL);
if (FAILED(hr)) {
goto clear;
}
hr = pDesktop->BindToObject(pidlDir, 0, IID_IShellFolder, (void**)&pDir);
if (FAILED(hr)) {
goto clear;
}
*/
hr = pDesktop->ParseDisplayName(wnd, NULL, pszFile, NULL, &pidl, NULL);
if (FAILED(hr)) {
goto clear;
}
hr = pDesktop->ParseDisplayName(wnd, NULL, pszFile2, NULL, &pidl2, NULL);
if (FAILED(hr)) {
goto clear;
}
hr = SHBindToParent(pidl, IID_IShellFolder, (void **)&pFolder, &pidlItem);
if (FAILED(hr)) {
goto clear;
}
pFolder->Release();
hr = SHBindToParent(pidl2, IID_IShellFolder, (void **)&pFolder, &pidlItem2);
if (FAILED(hr)) {
goto clear;
}
LPCITEMIDLIST list[] = {pidlItem, pidlItem2};
hr = pFolder->GetUIObjectOf(wnd, 2, (LPCITEMIDLIST *)list, IID_IContextMenu, NULL, (void **)&pContextMenu);
pFolder->Release();
if (SUCCEEDED(hr)) {
hMenu = CreatePopupMenu();
if (hMenu) {
hr = pContextMenu->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
if (SUCCEEDED(hr)) {
int idCmd = TrackPopupMenu(hMenu,
TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON,
1, 1, 0, wnd, NULL);
if (idCmd) {
cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
cmi.fMask = 0;
cmi.hwnd = wnd;
cmi.lpVerb = MAKEINTRESOURCEA(idCmd-1);
cmi.lpParameters = NULL;
cmi.lpDirectory = NULL;
cmi.nShow = SW_SHOWNORMAL;
cmi.dwHotKey = 0;
cmi.hIcon = NULL;
hr = pContextMenu->InvokeCommand(&cmi);
if (!SUCCEEDED(hr)) {
wsprintf(szTemp, _T("InvokeCommand failed. hr=%lx"), hr);
MessageBox(0, szTemp, 0, 0);
PostQuitMessage(0);
}
}
}
DestroyMenu(hMenu);
}
pContextMenu->Release();
}
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
// Handle Error
}
else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
clear:
pDesktop->Release();
SHFree(pidl);
SHFree(pidl2);
CoUninitialize();
return 0;
}
Maybe it is possible to achieve using SHCreateDefaultContextMenu or CDefFolderMenu_Create2 but I don't know how.
It is possible if you embed IExplorerBrowser UI object into your app, which is available since Windows Vista.
The you can fill it with any PIDLs through IResultsFolder.
Here is sample code, which is just slightly modified example from Win7 Platform SDK (C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\winui\shell\appplatform\ExplorerBrowserCustomContents\ExplorerBrowserCustomContents.sln)
There is definitely an another method using IShellView which works on Windows XP but I didn't find it anyway.
#define STRICT_TYPED_ITEMIDS
#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
#include <shellapi.h>
#include <shlwapi.h>
#include <propkey.h>
#include <new>
#include "resource.h"
#pragma comment(lib, "shell32.lib")
#pragma comment(lib, "shlwapi.lib")
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE g_hinst = 0;
UINT const KFD_SELCHANGE = WM_USER;
class CFillResultsOnBackgroundThread;
class CExplorerBrowserHostDialog : public IServiceProvider, public ICommDlgBrowser
{
public:
CExplorerBrowserHostDialog() : _cRef(1), _hdlg(NULL), _peb(NULL), _fEnumerated(FALSE), _prf(NULL)
{
}
HRESULT DoModal(HWND hwnd)
{
DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, s_DlgProc, (LPARAM)this);
return S_OK;
}
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] =
{
QITABENT(CExplorerBrowserHostDialog, IServiceProvider),
QITABENT(CExplorerBrowserHostDialog, ICommDlgBrowser),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IServiceProvider
STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
HRESULT hr = E_NOINTERFACE;
*ppv = NULL;
if (guidService == SID_SExplorerBrowserFrame)
{
// responding to this SID allows us to hook up our ICommDlgBrowser
// implementation so we get selection change events from the view
hr = QueryInterface(riid, ppv);
}
return hr;
}
// ICommDlgBrowser
STDMETHODIMP OnDefaultCommand(IShellView * /* psv */)
{
_OnExplore();
return S_OK;
}
STDMETHODIMP OnStateChange(IShellView * /* psv */, ULONG uChange)
{
if (uChange == CDBOSC_SELCHANGE)
{
PostMessage(_hdlg, KFD_SELCHANGE, 0, 0);
}
return S_OK;
}
STDMETHODIMP IncludeObject(IShellView * /* psv */, PCUITEMID_CHILD /* pidl */)
{
return S_OK;
}
void FillResultsOnBackgroundThread(IResultsFolder *prf);
private:
~CExplorerBrowserHostDialog()
{
}
static INT_PTR CALLBACK s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CExplorerBrowserHostDialog *pebhd = reinterpret_cast<CExplorerBrowserHostDialog *>(GetWindowLongPtr(hdlg, DWLP_USER));
if (uMsg == WM_INITDIALOG)
{
pebhd = reinterpret_cast<CExplorerBrowserHostDialog *>(lParam);
pebhd->_hdlg = hdlg;
SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(pebhd));
}
return pebhd ? pebhd->_DlgProc(uMsg, wParam, lParam) : 0;
}
INT_PTR _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT _FillViewWithKnownFolders(IResultsFolder *prf);
void _OnInitDlg();
void _OnDestroyDlg();
void _StartFolderEnum();
void _OnSelChange();
void _OnExplore();
void _OnRefresh();
long _cRef;
HWND _hdlg;
IExplorerBrowser *_peb;
IResultsFolder *_prf;
BOOL _fEnumerated;
static const UINT c_rgControlsShownOnEnum[3]; // controls that will be shown while known folder list is populated
static const UINT c_rgControlsHiddenOnEnum[4]; // controls that will be hidden while known folder list is populated
};
const UINT CExplorerBrowserHostDialog::c_rgControlsShownOnEnum[] =
{
IDC_STATUS,
IDC_ENUMNAME,
IDC_ENUMPATH
};
const UINT CExplorerBrowserHostDialog::c_rgControlsHiddenOnEnum[] =
{
IDC_FOLDERNAME,
IDC_FOLDERPATH,
IDC_LBLFOLDER,
IDC_LBLPATH
};
HRESULT CExplorerBrowserHostDialog::_FillViewWithKnownFolders(IResultsFolder *prf)
{
LPOLESTR pszFile = OLESTR("c:\\Windows\\notepad.exe");
LPOLESTR pszFile2 = OLESTR("c:\\Windows\\System32\\notepad.exe");
IShellFolder* pDesktop;
PIDLIST_RELATIVE pidl = NULL;
PIDLIST_RELATIVE pidl2 = NULL;
HRESULT hr;
hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr)) {
return E_FAIL;
}
hr = pDesktop->ParseDisplayName(0, NULL, pszFile, NULL, &pidl, NULL);
if (FAILED(hr)) {
pDesktop->Release();
return E_FAIL;
}
hr = pDesktop->ParseDisplayName(0, NULL, pszFile2, NULL, &pidl2, NULL);
if (FAILED(hr)) {
pDesktop->Release();
return E_FAIL;
}
prf->AddIDList((PIDLIST_ABSOLUTE)pidl, 0);
prf->AddIDList((PIDLIST_ABSOLUTE)pidl2, 0);
pDesktop->Release();
IFolderView2 *pfv2;
IShellView *shellView;
hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
if (SUCCEEDED(hr)) {
pfv2->SelectItem(0, SVSI_SELECT);
pfv2->SelectItem(1, SVSI_SELECT);
//hr = pfv2->QueryInterface(IID_IOleWindow, (void**)&oleWnd);
hr = _peb->GetCurrentView(IID_PPV_ARGS(&shellView));
if (SUCCEEDED(hr)) {
HWND wnd = 0;
shellView->GetWindow(&wnd);
//SendMessage(wnd, WM_CONTEXTMENU, 0, MAKELPARAM(0,0)); not working
}
}
return S_OK;
}
void CExplorerBrowserHostDialog::_OnInitDlg()
{
// Hide initial folder information
for (UINT i = 0; i < ARRAYSIZE(c_rgControlsHiddenOnEnum); i++)
{
ShowWindow(GetDlgItem(_hdlg, c_rgControlsHiddenOnEnum[i]), SW_HIDE);
}
HWND hwndStatic = GetDlgItem(_hdlg, IDC_BROWSER);
if (hwndStatic)
{
RECT rc;
GetWindowRect(hwndStatic, &rc);
MapWindowRect(HWND_DESKTOP, _hdlg, &rc);
HRESULT hr = CoCreateInstance(CLSID_ExplorerBrowser, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&_peb));
if (SUCCEEDED(hr))
{
IUnknown_SetSite(_peb, static_cast<IServiceProvider *>(this));
FOLDERSETTINGS fs = {0};
fs.ViewMode = FVM_DETAILS;
fs.fFlags = FWF_AUTOARRANGE | FWF_NOWEBVIEW;
hr = _peb->Initialize(_hdlg, &rc, &fs);
if (SUCCEEDED(hr))
{
_peb->SetOptions(EBO_NAVIGATEONCE); // do not allow navigations
// Initialize the explorer browser so that we can use the results folder
// as the data source. This enables us to program the contents of
// the view via IResultsFolder
hr = _peb->FillFromObject(NULL, EBF_NODROPTARGET);
if (SUCCEEDED(hr))
{
IFolderView2 *pfv2;
hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
if (SUCCEEDED(hr))
{
IColumnManager *pcm;
hr = pfv2->QueryInterface(IID_PPV_ARGS(&pcm));
if (SUCCEEDED(hr))
{
PROPERTYKEY rgkeys[] = {PKEY_ItemNameDisplay, PKEY_ItemFolderPathDisplay};
hr = pcm->SetColumns(rgkeys, ARRAYSIZE(rgkeys));
if (SUCCEEDED(hr))
{
CM_COLUMNINFO ci = {sizeof(ci), CM_MASK_WIDTH | CM_MASK_DEFAULTWIDTH | CM_MASK_IDEALWIDTH};
hr = pcm->GetColumnInfo(PKEY_ItemFolderPathDisplay, &ci);
if (SUCCEEDED(hr))
{
ci.uWidth += 100;
ci.uDefaultWidth += 100;
ci.uIdealWidth += 100;
pcm->SetColumnInfo(PKEY_ItemFolderPathDisplay, &ci);
}
}
pcm->Release();
}
hr = pfv2->GetFolder(IID_PPV_ARGS(&_prf));
if (SUCCEEDED(hr))
{
_StartFolderEnum();
}
pfv2->Release();
}
}
}
}
// If we fail to initialize properly, close the dialog
if (FAILED(hr))
{
EndDialog(_hdlg, IDCLOSE);
}
}
}
// pass -1 for the current selected item
// returns an IShellItem type object
HRESULT GetItemFromView(IFolderView2 *pfv, int iItem, REFIID riid, void **ppv)
{
*ppv = NULL;
HRESULT hr = S_OK;
if (iItem == -1)
{
hr = pfv->GetSelectedItem(-1, &iItem); // Returns S_FALSE if none selected
}
if (S_OK == hr)
{
hr = pfv->GetItem(iItem, riid, ppv);
}
else
{
hr = E_FAIL;
}
return hr;
}
void CExplorerBrowserHostDialog::_OnSelChange()
{
if (_fEnumerated)
{
IFolderView2 *pfv2;
HRESULT hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
if (SUCCEEDED(hr))
{
IShellItem2 *psi;
hr = GetItemFromView(pfv2, -1, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, &pszName);
if (SUCCEEDED(hr))
{
SetDlgItemText(_hdlg, IDC_FOLDERNAME, pszName);
CoTaskMemFree(pszName);
}
hr = psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszName);
if (SUCCEEDED(hr))
{
SetDlgItemText(_hdlg, IDC_FOLDERPATH, pszName);
CoTaskMemFree(pszName);
}
psi->Release();
}
else if (hr == S_FALSE)
{
SetDlgItemText(_hdlg, IDC_FOLDERNAME, TEXT(""));
SetDlgItemText(_hdlg, IDC_FOLDERPATH, TEXT(""));
}
EnableWindow(GetDlgItem(_hdlg, IDC_EXPLORE), hr == S_OK);
pfv2->Release();
}
}
}
void CExplorerBrowserHostDialog::_OnDestroyDlg()
{
if (_peb)
{
IUnknown_SetSite(_peb, NULL);
_peb->Destroy();
_peb->Release();
_peb = NULL;
}
if (_prf)
{
_prf->Release();
_prf = NULL;
}
}
void CExplorerBrowserHostDialog::_OnExplore()
{
IFolderView2 *pfv2;
HRESULT hr = _peb->GetCurrentView(IID_PPV_ARGS(&pfv2));
if (SUCCEEDED(hr))
{
IShellItem *psi;
hr = GetItemFromView(pfv2, -1, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
PIDLIST_ABSOLUTE pidl;
hr = SHGetIDListFromObject(psi, &pidl);
if (SUCCEEDED(hr))
{
SHELLEXECUTEINFO ei = { sizeof(ei) };
ei.fMask = SEE_MASK_INVOKEIDLIST;
ei.hwnd = _hdlg;
ei.nShow = SW_NORMAL;
ei.lpIDList = pidl;
ShellExecuteEx(&ei);
CoTaskMemFree(pidl);
}
psi->Release();
}
pfv2->Release();
}
}
void CExplorerBrowserHostDialog::_OnRefresh()
{
_fEnumerated = FALSE;
// Update UI
EnableWindow(GetDlgItem(_hdlg, IDC_EXPLORE), FALSE);
EnableWindow(GetDlgItem(_hdlg, IDC_REFRESH), FALSE);
if (SUCCEEDED(_peb->RemoveAll()))
{
_StartFolderEnum();
}
}
void CExplorerBrowserHostDialog::_StartFolderEnum()
{
FillResultsOnBackgroundThread(_prf);
}
void CExplorerBrowserHostDialog::FillResultsOnBackgroundThread(IResultsFolder *prf)
{
_FillViewWithKnownFolders(prf);
_fEnumerated = TRUE;
/*
// Adjust dialog to show proper view info and buttons
for (UINT k = 0; k < ARRAYSIZE(c_rgControlsShownOnEnum); k++)
{
ShowWindow(GetDlgItem(_hdlg, c_rgControlsShownOnEnum[k]), SW_HIDE);
}
for (UINT l = 0; l < ARRAYSIZE(c_rgControlsHiddenOnEnum); l++)
{
ShowWindow(GetDlgItem(_hdlg, c_rgControlsHiddenOnEnum[l]), SW_SHOW);
}*/
EnableWindow(GetDlgItem(_hdlg, IDC_REFRESH), TRUE);
}
INT_PTR CExplorerBrowserHostDialog::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM /* lParam */)
{
INT_PTR iRet = 1; // default for all handled cases in switch below
switch (uMsg)
{
case WM_INITDIALOG:
_OnInitDlg();
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDOK:
case IDCANCEL:
case IDC_CANCEL:
return EndDialog(_hdlg, TRUE);
case IDC_REFRESH:
_OnRefresh();
break;
case IDC_EXPLORE:
_OnExplore();
break;
}
break;
case KFD_SELCHANGE:
_OnSelChange();
break;
case WM_DESTROY:
_OnDestroyDlg();
break;
default:
iRet = 0;
break;
}
return iRet;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int)
{
g_hinst = hInstance;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
OleInitialize(0); // for drag and drop
CExplorerBrowserHostDialog *pdlg = new (std::nothrow) CExplorerBrowserHostDialog();
if (pdlg)
{
pdlg->DoModal(NULL);
pdlg->Release();
}
OleUninitialize();
CoUninitialize();
}
return 0;
}

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 :-)

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).