IsSearchSynchronous fails in Outlook - outlook

I am using Outlook automation to find items in the Calendar. For that I use IsSearchSynchronous() method to see if I have to wait for the AdvancedSearchComplete event. BTW, is it ever synchronous???
Anyway, if I have Outlook running, I have no problems with this call. But if it doesn't run - the call fails with
HRESULT: 0x80020009 Exception occurred
The EXCEPINFO contains:
Source: "Microsoft Outlook"
Description: "The operation failed."
scode: 0x80004005
Any suggestions?
Here is my test case:
#include <atlstr.h>
int _tmain()
{
IDispatch* pApp;
HRESULT hr;
CoInitialize(NULL);
CLSID clsid;
hr = CLSIDFromProgID(L"Outlook.Application", &clsid);
if(SUCCEEDED(hr))
{
hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pApp);
if(SUCCEEDED(hr))
{
// Get DISPID for "IsSearchSynchronous"
OLECHAR Name[] = {L"IsSearchSynchronous"};
LPOLESTR lp = Name;
DISPID dispID;
hr = pApp->GetIDsOfNames(IID_NULL, &lp, 1, LOCALE_USER_DEFAULT, &dispID);
if(SUCCEEDED(hr))
{
// The path name of the folders that the search will search through.
VARIANT path;
path.vt = VT_BSTR;
path.bstrVal = SysAllocString(L"'Calendar'");
// Build DISPPARAMS
DISPPARAMS dp = { NULL, NULL, 0, 0 };
dp.cArgs = 1;
dp.rgvarg = &path;
// get IsSearchSynchronous
VARIANT result;
VariantInit(&result);
EXCEPINFO ei = {0};
hr = pApp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &dp, &result, &ei, NULL);
VARIANT_BOOL vbIsSearchSynchronous = result.boolVal;
}
}
}
CoUninitialize();
return 0;
}

In case anybody else is interested, this is resolved with a help from Microsoft.
The “scope” parameter to a IsSearchSynchronous() call should be a complete path to the calendar folder, like:
"\\Mailbox - <user_name>\\Calendar"

Related

C++ - Windows Shell API - Wrong path while iterating through a folder

Consider the following code:
bool ListFolderContent(const std::wstring& fileName)
{
PIDLIST_ABSOLUTE pidl = nullptr;
if (FAILED(::SHILCreateFromPath(fileName.c_str(), &pidl, nullptr)))
return false;
IShellFolder* pShellfolder = nullptr;
LPCITEMIDLIST pidlRelative = nullptr;
HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, &pidlRelative);
if (FAILED(hr))
{
::CoTaskMemFree(pidl);
return false;
}
IEnumIDList* pEnumIDList = nullptr;
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnumIDList);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
while (1)
{
LPITEMIDLIST pChild = nullptr;
hr = pEnumIDList->Next(1, &pChild, nullptr);
if (FAILED(hr))
{
pShellfolder->Release();
::CoTaskMemFree(pidl);
return false;
}
if (hr == S_FALSE)
break;
wchar_t buffer[MAX_PATH + 1];
if (::SHGetPathFromIDListW(pChild, buffer))
{
::OutputDebugString(buffer);
::OutputDebugString(L"\r\n");
}
}
pShellfolder->Release();
::CoTaskMemFree(pidl);
return true;
}
This code works well and logs the content of the folder owning the given file I pass through the fileName parameter.
However I have an issues with this code: Whatever I pass as file name, the logged path is always C:\Users\Admin\Desktop, and NOT the path to the parent folder I'm iterating, as expected. On the other hand the file names are correct.
Can someone explain me what I'm doing wrong?
You are passing just the last component ("filename") here: SHGetPathFromIDListW(pChild, buffer). Since the desktop is the root you are basically asking to get the filesystem path of [Desktop] [Filename] from the namespace.
There are two solutions:
pShellfolder->GetDisplayNameOf(pChild, SHGDN_FORPARSING, ...)+StrRetToBuf. This is fast since you already have an instance of the folder interface.
Call SHGetPathFromIDList with an absolute (full) pidl. On the pidl from SHILCreateFromPath, call ILCloneFull, ILRemoveLastID and ILCombine(clone, pChild).
HRESULT Example()
{
WCHAR buf[MAX_PATH];
GetWindowsDirectory(buf, MAX_PATH);
PathAppend(buf, L"Explorer.exe");
PIDLIST_ABSOLUTE pidl, pidlFullItem;
HRESULT hr = SHILCreateFromPath(buf, &pidl, nullptr); // %windir%\Explorer.exe
if (FAILED(hr)) return hr;
LPITEMIDLIST pLeaf;
IShellFolder*pShellfolder; // %windir%
hr = SHBindToParent(pidl, IID_IShellFolder, (void**)&pShellfolder, nullptr);
if (SUCCEEDED(hr))
{
IEnumIDList*pEnum;
// Method 1:
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
STRRET sr;
hr = pShellfolder->GetDisplayNameOf(pLeaf, SHGDN_FORPARSING, &sr);
if (SUCCEEDED(hr))
{
hr = StrRetToBuf(&sr, pLeaf, buf, MAX_PATH);
if (SUCCEEDED(hr)) wprintf(L"M1: Item: %s\n", buf);
}
}
pEnum->Release();
}
// Method 2:
ILRemoveLastID(pidl); // %windir%\Explorer.exe => %windir%
hr = pShellfolder->EnumObjects(nullptr, SHCONTF_NONFOLDERS, &pEnum);
if (SUCCEEDED(hr))
{
for (; S_OK == (hr = pEnum->Next(1, &pLeaf, nullptr));)
{
pidlFullItem = ILCombine(pidl, pLeaf); // %windir% + Filename
if (pidlFullItem)
{
hr = SHGetPathFromIDListW(pidlFullItem, buf);
if (SUCCEEDED(hr)) wprintf(L"M2: Item: %s\n", buf);
ILFree(pidlFullItem);
}
}
pEnum->Release();
}
pShellfolder->Release();
}
ILFree(pidl);
return hr;
}

How do you keep Properties Dialog open while Closing the App that opened it?

I am working on something that pulls up a context menu. I want to be able to close the app while leaving open things like "properties". What happens is if you open the context menu, select "properties", it opens the properties dialog and closes the context menu just fine. But when I close my app, the properties window also closes.
The question is: How do you keep that window open? If you can't, how could the app tell if it has a window/dialog dependent on it open? EnumWindows doesn't show it; Spy++ shows the Properties Dialog stands on its own as well (under desktop).
CComPtr<IContextMenu> pMenu;
CComPtr<IShellFolder> pFolder;
PCUITEMID_CHILD pidl = NULL;
SHBindToParent(item.pidl, IID_IShellFolder, (void**)&pFolder, &pidl);
pFolder->GetUIObjectOf(NULL, 1, &pidl, IID_IContextMenu, NULL, (void**)&pMenu);
UINT flags = CMF_NORMAL | CMF_CANRENAME;
pMenu->QueryContextMenu(menu, CID_FIRST, CID_LAST, 0x7fff, flags);
.
.
.
// command invocation
CMINVOKECOMMANDINFOEX info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_NOASYNC | CMIC_MASK_PTINVOKE; // I've played around with flags
info.hwnd = toolbar.m_hWnd; // I've played around with this value
info.lpVerb = MAKEINTRESOURCEA(cid - CID_LAST);
info.nShow = SW_SHOWNORMAL;
info.ptInvoke = point;
pMenu->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
TIA!!
The properties dialog is in-process.
You need to call SHSetInstanceExplorer and keep your process alive until that COM object is released:
int main()
{
struct CoInit { HRESULT m_hr; CoInit() { m_hr = CoInitialize(0); } ~CoInit() { if (SUCCEEDED(m_hr)) CoUninitialize(); } } coinit;
ProcessReference ref;
IShellItem*pSI;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_Windows, KF_FLAG_DEFAULT, L"Explorer.exe", IID_IShellItem, (void**) &pSI);
if (hr) return hr;
IContextMenu*pCM;
hr = pSI->BindToHandler(NULL, BHID_SFUIObject, IID_IContextMenu, (void**) &pCM);
pSI->Release();
if (hr) return hr;
//HMENU menu = CreatePopupMenu();
//pCM->QueryContextMenu(menu, 0, 1, 0x7fff, CMF_NORMAL);
CMINVOKECOMMANDINFO info;
memset(&info, 0, sizeof(info));
info.cbSize = sizeof(info);
info.fMask = CMIC_MASK_NOASYNC; // I've played around with flags
info.hwnd = NULL; // I've played around with this value
info.lpVerb = "Properties";
info.nShow = SW_SHOWNORMAL;
//info.ptInvoke = point;
hr = pCM->InvokeCommand((CMINVOKECOMMANDINFO*)&info);
pCM->Release();
return hr;
}
EnumWindows doesn't show it
If I misunderstood something, please tell me.
This is my test:
The second screenshot is what I got with EnumWindows, in my opinion, For some APP Properties Dialog , you can get it with EnumWindows.
At the same time, as #Jonathan Potter said that, the shell properties dialog runs in-process, it is true that you can open the properties dialog independently with ShellExecuteEx.
Only as a supplement, if you just want to open the properties dialog independently, you can use the api above.This way, when you close the app, you can reopen the properties dialog if you are not bothered.
Minimal code example:
#include <windows.h>
#include <shlobj_core.h>
#pragma comment(lib,"Shell32.lib")
class ProcessReference : public IUnknown {
public:
STDMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_IUnknown) {
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
*ppv = NULL; return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
LONG lRef = InterlockedDecrement(&m_cRef);
if (lRef == 0) PostThreadMessage(m_dwThread, WM_NULL, 0, 0);
return lRef;
}
ProcessReference()
: m_cRef(1), m_dwThread(GetCurrentThreadId())
{
SHSetInstanceExplorer(this);
}
~ProcessReference()
{
SHSetInstanceExplorer(NULL);
Release();
MSG msg;
while (m_cRef && GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
private:
LONG m_cRef;
DWORD m_dwThread;
};
int main(void)
{
struct CoInit { HRESULT m_hr; CoInit() { m_hr = CoInitialize(0); } ~CoInit() { if (SUCCEEDED(m_hr)) CoUninitialize(); } } coinit;
ProcessReference ref;
IShellItem*pSI;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_Windows, KF_FLAG_DEFAULT, L"Explorer.exe", IID_IShellItem, (void**)&pSI);
if (hr) return hr;
IContextMenu*pCM;
hr = pSI->BindToHandler(NULL, BHID_SFUIObject, IID_IContextMenu, (void**)&pCM);
pSI->Release();
if (hr) return hr;
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof info;
info.lpFile = "C:\\Users\\strives\\Desktop\\print.txt";
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
info.lpVerb = "properties";
ShellExecuteEx(&info);
}
Updated:
SHELLEXECUTEINFO info = { 0 };
info.cbSize = sizeof info;
info.lpFile = "C:\\Users\\strives\\Desktop\\Test.txt";
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
info.lpVerb = "openas";
ShellExecuteEx(&info);
Debug:

CreateBitmapFromWicBitmap access violation

Here's my problem:
I'm working on a test project, that initializes Direct2D to draw some stuff.
For now, I am in need of creating BMP background so I looked some tutorials of loading bmp to use it with Direct2D on MSDN site. After some coding and debugging I came to the only problem I can't fully comprehend and, well, I'm stuck here. The problem is simple: I get ACCESS VIOLATION at this line:
pRenderTarget->CreateBitmapFromWicBitmap( pConverter, NULL, ppBitmap);
I fixed all issues I could find out and tested every HRESULT.
here's full code of background.cpp:
`
#include "Background.h"
using namespace D2D1;
Background::Background()
{
pIWICFactory = nullptr;
pDecoder = nullptr;
pSource = nullptr;
pStream = nullptr;
pConverter = nullptr;
ppBitmap = nullptr;
destinationWidth = 0;
destinationHeight = 0;
file_path = L"Background.bmp";
}
bool Background::init(HWND hwnd)
{
CoInitializeEx(0, COINIT_MULTITHREADED);
CoCreateInstance( CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pIWICFactory));
pIWICFactory->CreateDecoderFromFilename(file_path, NULL, GENERIC_READ, WICDecodeMetadataCacheOnLoad, &pDecoder);
pIWICFactory->CreateStream(&pStream);
pStream->InitializeFromFilename(file_path, GENERIC_READ);
pDecoder->Initialize(pStream,WICDecodeMetadataCacheOnLoad);
pDecoder->GetFrame(0, &pSource);
pIWICFactory->CreateFormatConverter(&pConverter);
pConverter->Initialize(pSource, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.f, WICBitmapPaletteTypeMedianCut);
pRenderTarget->CreateBitmapFromWicBitmap( pConverter, NULL, ppBitmap);
return true;
}
Background::~Background()
{
CoUninitialize();
pIWICFactory->Release();
pDecoder->Release();
pSource->Release();
pStream->Release();
pConverter->Release();
CoUninitialize();
}
`
And here's parent class "Render.cpp"
(there's no connection between how I managed class inheritance and the problem - I tried to create new project that contained only one class including both render and background)
`
#include "Render.h"
using namespace D2D1;
Render::Render()
{
pD2DFactory = nullptr;
pRenderTarget = nullptr;
pGreenBrush = nullptr;
}
bool Render::Init(HWND hwnd)
{
HRESULT hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory );
RECT rc;
GetClientRect(hwnd, &rc);
hr = pD2DFactory->CreateHwndRenderTarget(RenderTargetProperties(), HwndRenderTargetProperties(hwnd, SizeU( rc.right - rc.left, rc.bottom - rc.top)),&pRenderTarget);
if (SUCCEEDED(hr))
pRenderTarget->CreateSolidColorBrush(ColorF(ColorF::Green), &pGreenBrush );
else
return false;
return true;
}
bool Render::Draw(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
pRenderTarget->BeginDraw();
pRenderTarget->FillRectangle(
RectF(
rc.left + 500.0f,
rc.top + 250.0f,
rc.right - 500.0f,
rc.bottom - 500.0f),
pGreenBrush);
HRESULT hr = pRenderTarget->EndDraw();
if (SUCCEEDED(hr))
return true;
else
return false;
}
void Render::ShutDown()
{
if (pD2DFactory)
pD2DFactory->Release();
if (pRenderTarget)
pRenderTarget->Release();
if (pGreenBrush)
pGreenBrush->Release();
}
`
From the double 'pp' I asume you declared ppBitmap; as D2D1Bitmap**, also because you pass it as-value to CreateBitmapFromWicBitmap.
If correct your solution is simple: Declare a ptrP*, not a ptrptrPP**
// in class declaration or c'tor(?)
D2D1Bitmap* pBitmap = nullptr; // instead of D2D1Bitmap**
/// at the point of creation
D2D1wndTarget->CreateBitmapFromWicBitmap(<yourConverter>, NULL, &pBitmap);
In your example you declared it as null-ptr and passed it to a function that takes an address to pointer, resulting in the function reading at 0x000000 resulting in an access violation.

Getting the Windows Control Panel's virtual folder

Why would GetPath always return E_FAIL when querying FOLDERID_ControlPanelFolder? Other FOLDERIDs actually do work:
HRESULT hr = S_OK;
*path = '\0';
LPWSTR pwcPath = NULL;
CoInitialize(NULL);
IKnownFolderManager *pFolderManager = NULL;
if ((hr = CoCreateInstance(__uuidof(KnownFolderManager), NULL, CLSCTX_INPROC_SERVER, __uuidof(IKnownFolderManager), (LPVOID *)&pFolderManager)) == S_OK)
{
IKnownFolder *pControlPanelFolder = NULL;
if ((hr = pFolderManager->GetFolder(FOLDERID_ControlPanelFolder, &pControlPanelFolder)) == S_OK)
{
hr = pControlPanelFolder->GetPath(0, &pwcPath);
if (hr == S_OK && pwcPath)
{
int nSize = wcslen(pwcPath);
WideCharToMultiByte(CP_ACP, 0, pwcPath, nSize, path, nSize+2, NULL, NULL);
path[nSize] = '\0';
CoTaskMemFree(pwcPath);
}
pControlPanelFolder->Release();
pControlPanelFolder = NULL;
}
pFolderManager->Release();
pFolderManager = NULL;
}
CoUninitialize();
(Yes, I stumbled upon this question but I don't have need for all that enumeration stuff.)
The Control Panel has no directory path because it does not exist on the disc. You can get its PIDL, and even a Desktop Absolute Parsing "display name" (via GetShellItem and GetDisplayName), but not a directory path.
Reason why I needed the path was that I wanted to open the controp panel with a ShellExecute "open". I now execute the control panel program directly, with the benefit of being able to select the desired applet right away (in this case "Sound"). I hope it's not too pretentious that I post this as answer:
char controlpanelpath[2000];
UINT controlpanelpathbuffersize = sizeof(controlpanelpath);
int actualcontrolpanelpathsize;
if (actualcontrolpanelpathsize = GetSystemDirectory(controlpanelpath, controlpanelpathbuffersize))
{
char *parameters = "\\control.exe mmsys.cpl,,0";
if (actualcontrolpanelpathsize + strlen(parameters) < controlpanelpathbuffersize)
{
strcat(controlpanelpath, parameters);
WinExec(controlpanelpath, SW_NORMAL);
}
}

Windows VC++ Get Machine Model Name

Can anyone Please tell me how to get Model name of Windows Machine.
I am new to Windows VC++.
For Example i have an IBM ThinkCenter M50 running on Windows. Here the Model name is "Think Center M50". I want to get this from the System using some API.
Thanks in Advance,
Shashi Kiran G M
Alternatively, you could use the registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SystemInformation
also: HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\BIOS (Win7 or later only)
The SystemManufacturer and SystemProductName entries should do it. Saves using WMI, which i try to avoid at all costs for performance reasons.
As Ben suggests, you'll need to use WMI for this.
The class you're looking for is Win32_ComputerSystem, which contains a read-only Model property of type string that returns the product name that a manufacturer gives to a computer.
I'll leave writing the C++ code to make this WMI call as an exercise for the reader.
Do note Ben's caveat as well: not all manufacturers publish this information in the BIOS. It's very likely that IBM does, so your test case should work out fine, but this is not a universal assumption that you are justified in making. Applications should not rely on this property containing a particular value.
With the help of the Microsoft example code, I was able to create this method.
#include <Wbemidl.h>
#pragma comment(lib, "wbemuuid.lib")
std::pair<CString,CString> getComputerManufacturerAndModel() {
// Obtain the initial locator to Windows Management on a particular host computer.
IWbemLocator *locator = nullptr;
IWbemServices *services = nullptr;
auto hResult = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&locator);
auto hasFailed = [&hResult]() {
if (FAILED(hResult)) {
auto error = _com_error(hResult);
TRACE(error.ErrorMessage());
TRACE(error.Description().Detach());
return true;
}
return false;
};
auto getValue = [&hResult, &hasFailed](IWbemClassObject *classObject, LPCWSTR property) {
CString propertyValueText = "Not set";
VARIANT propertyValue;
hResult = classObject->Get(property, 0, &propertyValue, 0, 0);
if (!hasFailed()) {
if ((propertyValue.vt == VT_NULL) || (propertyValue.vt == VT_EMPTY)) {
} else if (propertyValue.vt & VT_ARRAY) {
propertyValueText = "Unknown"; //Array types not supported
} else {
propertyValueText = propertyValue.bstrVal;
}
}
VariantClear(&propertyValue);
return propertyValueText;
};
CString manufacturer = "Not set";
CString model = "Not set";
if (!hasFailed()) {
// Connect to the root\cimv2 namespace with the current user and obtain pointer pSvc to make IWbemServices calls.
hResult = locator->ConnectServer(L"ROOT\\CIMV2", nullptr, nullptr, 0, NULL, 0, 0, &services);
if (!hasFailed()) {
// Set the IWbemServices proxy so that impersonation of the user (client) occurs.
hResult = CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL,
RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, EOAC_NONE);
if (!hasFailed()) {
IEnumWbemClassObject* classObjectEnumerator = nullptr;
hResult = services->ExecQuery(L"WQL", L"SELECT * FROM Win32_ComputerSystem", WBEM_FLAG_FORWARD_ONLY |
WBEM_FLAG_RETURN_IMMEDIATELY, nullptr, &classObjectEnumerator);
if (!hasFailed()) {
IWbemClassObject *classObject;
ULONG uReturn = 0;
hResult = classObjectEnumerator->Next(WBEM_INFINITE, 1, &classObject, &uReturn);
if (uReturn != 0) {
manufacturer = getValue(classObject, (LPCWSTR)L"Manufacturer");
model = getValue(classObject, (LPCWSTR)L"Model");
}
classObject->Release();
}
classObjectEnumerator->Release();
}
}
}
if (locator) {
locator->Release();
}
if (services) {
services->Release();
}
CoUninitialize();
return { manufacturer, model };
}

Resources