IGlobalInterfaceTable::RegisterInterfaceInGlobal returns E_NOTIMPL - c++

Does anyone know why IGlobalInterfaceTable::RegisterInterfaceInGlobal might return E_NOTIMPL?
I'm using ATL's CComGITPtr and simply assigning it to a COM pointer. The assignment, in turn, calls the following ATL code:
HRESULT Attach(_In_ T* p) throw()
{
if (p)
{
CComPtr<IGlobalInterfaceTable> spGIT;
HRESULT hr = E_FAIL;
hr = AtlGetGITPtr(&spGIT);
ATLASSERT(spGIT != NULL);
ATLASSERT(SUCCEEDED(hr));
if (FAILED(hr))
return hr;
if (m_dwCookie != 0)
hr = spGIT->RevokeInterfaceFromGlobal(m_dwCookie);
if (FAILED(hr))
return hr;
return spGIT->RegisterInterfaceInGlobal(p, __uuidof(T), &m_dwCookie);
}
else
{
return Revoke();
}
}
Apparently that last call to spGIT->RegisterInterfaceInGlobal results in E_NOTIMPL.
I'm performing the assignment in the context of an in-proc Explorer extension, and I'm doing it on the main UI thread.
Also, I'm not sure if this is related, but I get the same error when trying to use RoGetAgileReference (on Windows 8.1), doing something like this (with an IShellItemArray):
HRESULT hr;
CComPtr<IAgileReference> par;
hr = RoGetAgileReference(AGILEREFERENCE_DEFAULT, IID_IShellItemArray, psia, &par);
Any ideas what might be going wrong?

Related

Determining whether all items in an IShellItemArray come from the same directory

Let's say you want to determine whether all items in an IShellItemArray belong to the same parent directory. How might you do that?
This is what I've come up with, but I was wondering if there's a more efficient or otherwise more acceptable way (it's based on the behaviors described here):
HRESULT ItemsInSameDirectory(IShellItemArray* psia, BOOL* fSameDir)
{
HRESULT hr;
CComPtr<IPropertyStore> pPropStore;
hr = psia->GetPropertyStore(GPS_FASTPROPERTIESONLY, IID_PPV_ARGS(&pPropStore));
if (FAILED(hr))
{
return hr;
}
wil::unique_prop_variant pv;
hr = pPropStore->GetValue(PKEY_ItemFolderPathDisplay, &pv);
if (FAILED(hr))
{
return hr;
}
*fSameDir = (pv.vt == VT_LPWSTR) ? TRUE : FALSE;
return S_OK;
}

How to initialise CComSafeArray which a SAFEARRAY returned from a Scripting.Dictionary Keys method

I'm using a Scripting.Dictionary from the COM Library "Microsoft Scripting Runtime" (c:\windows\System32\scrrun.dll). I'm using import to get my wrappers and these are working. After adding some items, I'm trying to get the list of keys but I am stuck.
I have got some way. I can get the SAFEARRAY of keys out of the Dictionary but I want to use the CComSafeArray class defined in <atlsafe.h> but I cannot figure out a good construction. Currently, the constructor throws an ATL assertion complaining about vartype mismatch. Admittedly, the newly constructed CComSafeArray has a vartype of 52428 of CCCC in hex and this looks potentially like uninitialised memory. But the type inferred doesn't look right either, I am expecting the vartype of the safearray to be strings but the assertion code gives vartype 12 which is UI2. Very strange.
Anyway, this is probably easy for someone familiar with this. Here is a console program Minimal, Complete, Verifiable, Example (MCVE) as per SO standards.
#include "stdafx.h"
#include <atlbase.h>
#include <atlsafe.h>
#import "c:\windows\System32\scrrun.dll" raw_interfaces_only,raw_native_types, named_guids, rename("DeleteFile", "_DeleteFile"), rename("MoveFile","_MoveFile"), rename("CopyFile", "_CopyFile"), rename("GetFreeSpace", "_GetFreeSpace")
int main()
{
HRESULT hr = S_OK;
CoInitialize(0);
CComQIPtr<Scripting::IDictionary> dictColours;
hr = dictColours.CoCreateInstance(__uuidof(Scripting::Dictionary));
if (!SUCCEEDED(hr)) { return hr; }
CComVariant varZero(0); //dummy value, only interested in keys
hr = dictColours->Add(&variant_t(L"red"), &varZero);
if (!SUCCEEDED(hr)) { return hr; }
hr = dictColours->Add(&variant_t(L"green"), &varZero);
if (!SUCCEEDED(hr)) { return hr; }
hr = dictColours->Add(&variant_t(L"blue"), &varZero);
if (!SUCCEEDED(hr)) { return hr; }
long lColourCount(0);
hr = dictColours->get_Count(&lColourCount);
if (!SUCCEEDED(hr)) { return hr; }
CComVariant varColoursKeys;
hr = dictColours->Keys(&varColoursKeys);
if (!SUCCEEDED(hr)) { return hr; }
SAFEARRAY keys(*(varColoursKeys.parray));
//fine up to this point
CComSafeArray<VARIANT> varColours;
/* The following code throws an error for the next (line) live, vt=52428 or hex CCCC which looks like uninitialised memory
whilst GetType() returns 12 which is UI2 (and somehow I expected string type 8!)
VARTYPE vt;
HRESULT hRes = ::ATL::AtlSafeArrayGetActualVartype(const_cast<LPSAFEARRAY>(psaSrc), &vt);
ATLENSURE_SUCCEEDED(hRes);
ATLENSURE_THROW(vt == GetType(), E_INVALIDARG);
*/
varColours = (keys);
CoUninitialize();
return 0;
}
Change this:
SAFEARRAY keys(*(varColoursKeys.parray));
To this:
SAFEARRAY* keys = varColoursKeys.parray;
(optionally, first make sure that varColoursKeys.vt has the VT_ARRAY flag)
And then you can query keys for its vt:
hr = ::ATL::AtlSafeArrayGetActualVartype(keys, &vt);
Note that varColours = keys is going to make a copy of the array data. If that is not what you really want, then you should Detach() the array from the CComVariant (or just use VARIANT directly and not CComVariant) and Attach() it to the CComSafeArray (then you can use the CComSafeArray::GetType() method).
Also, you need to make sure all of your COM wrappers go out of scope and release resources before you call CoUninitialize().
Try this:
#include "stdafx.h"
#include <atlbase.h>
#include <atlsafe.h>
#import "c:\windows\System32\scrrun.dll" raw_interfaces_only,raw_native_types, named_guids, rename("DeleteFile", "_DeleteFile"), rename("MoveFile","_MoveFile"), rename("CopyFile", "_CopyFile"), rename("GetFreeSpace", "_GetFreeSpace")
HRESULT DoIt()
{
CComQIPtr<Scripting::IDictionary> dictColours;
hr = dictColours.CoCreateInstance(__uuidof(Scripting::Dictionary));
if (FAILED(hr)) { return hr; }
CComVariant varZero(0); //dummy value, only interested in keys
hr = dictColours->Add(&variant_t(L"red"), &varZero);
if (FAILED(hr)) { return hr; }
hr = dictColours->Add(&variant_t(L"green"), &varZero);
if (FAILED(hr)) { return hr; }
hr = dictColours->Add(&variant_t(L"blue"), &varZero);
if (FAILED(hr)) { return hr; }
long lColourCount(0);
hr = dictColours->get_Count(&lColourCount);
if (FAILED(hr)) { return hr; }
VARIANT varColoursKeys;
hr = dictColours->Keys(&varColoursKeys);
if (FAILED(hr)) { return hr; }
CComSafeArray<VARIANT> varColours;
varColours.Attach(varColoursKeys.parray);
// use varColours as needed...
VARTYPE vt = varColours.GetType();
LONG lLower = varColours.GetLowerBound();
LONG lUpper = varColours.GetUpperBound();
for (LONG i = lLower; i <= lUpper; ++i)
{
VARIANT &v = varColours.GetAt(i);
// use v.bstrVal as needed ...
}
//...
return S_OK;
}
int main()
{
HRESULT hr = CoInitialize(0);
if (FAILED(hr)) { return hr; }
hr = DoIt();
CoUninitialize();
return hr;
}

COM using CoMarshalInterThreadInterfaceInStream to marshal interface

I have a method that register functions and interface in C++, and I am using CoMarshalInterThreadInterfaceInStream to marshal an interface pointer to a method in another thread.
In RegisterFunction:
HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_Function, function, &stream);
Releasing it in GetFunction:
HRESULT hr = CoGetInterfaceAndReleaseStream(stream, IID_Function, reinterpret_cast<void**>(&function));
Since I will be calling GetFunction more than once and CoGetInterfaceAndReleaseStream only releases a stream once, how do I save the stream to use it again?
I tried using IGlobalInterfaceTable, but I can't get it to work.
I registered the Inteface successfully in RegisterFunction:
DWORD dwCookie = 0;
int a = 0;
if (pGIT == NULL) {
HRESULT hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&pGIT);
if (hr != S_OK) {
throw _com_error(hr);
}
}
if (dwCookie == 0) {
HRESULT hr = pGIT->RegisterInterfaceInGlobal(function, IID_Function, &dwCookie);
if (hr != S_OK) {
throw _com_error(hr);
}
}
But when I tried to retrieve it in GetFunction:
IGlobalInterfaceTable* GIT = NULL;
if (GIT == NULL) {
hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable,
NULL,
CLSCTX_INPROC_SERVER,
IID_IGlobalInterfaceTable,
(void **)&GIT);
if (FAILED(hr)) {
exit(0);
}
}
hr = pGIT->GetInterfaceFromGlobal(dwCookie, IID_Function, (void**)&function);
if (FAILED(hr)) {
exit(0);
}
I get an HR error of invalidArg when I tried to RegisterInterfaceInGlobal (even though it uses the same parameter as CoMarshalInterThreadInterfaceInStream except for the cookie)
As SoronelHaetir stated, you can't do this with CoMarshalInterThreadInterfaceInStream. It can only (un)marshal an interface one time.
Look at using IGlobalInterfaceTable instead. You can register your COM object in the table one time, and then retrieve the object from the table in any thread/apartment as many times as you want, until you revoke the object from the table.
You can't do it with CoMarshalInterThreadInterfaceInStream, it is a convenience method for interface pointers that will be extracted just once.
Instead use CoMarshalInterface/CoUnmarshalInterface and use a stream created with ShCreateMemStream.

How to create shortcut for virtual folder in C++ on windows 7?

The platform I am using is windows 7. I need to create shortcut for the virtual folder on windows 7.
I use windows 7 SDK sample to create a virtual folder under Computer:
The sample project name is called ExplorerDataProvider, it defines the CLSID for the IShellFolder class:
// add classes supported by this module here
const CLASS_OBJECT_INIT c_rgClassObjectInit[] =
{
{ &CLSID_FolderViewImpl, CFolderViewImplFolder_CreateInstance },
{ &CLSID_FolderViewImplContextMenu,CFolderViewImplContextMenu_CreateInstance },
};
The definition for CFolderViewImplFolder_CreateInstance is:
HRESULT CFolderViewImplFolder_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CFolderViewImplFolder* pFolderViewImplShellFolder = new (std::nothrow) CFolderViewImplFolder(0);
HRESULT hr = pFolderViewImplShellFolder ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pFolderViewImplShellFolder->QueryInterface(riid, ppv);
pFolderViewImplShellFolder->Release();
}
return hr;
}
And the CFolderViewImplFolder implement IShellFolder2 amd IPersistFolder2.
I found a similar code which is used to create shortcut for printer here:
https://www.codeproject.com/Articles/596642/Creating-a-shortcut-programmatically-in-Cplusplus
and in
https://msdn.microsoft.com/en-us/library/aa969393.aspx#Shellink_Item_Identifiers
Once you have the class identifier for IShellFolder, you can call the CoCreateInstance function to retrieve the address of the interface. Then you can call the interface to enumerate the objects in the folder and retrieve the address of the item identifier for the object that you are searching for. Finally, you can use the address in a call to the IShellLink::SetIDList member function to create a shortcut to the object.
I revised
hr = SHGetMalloc(&pMalloc);
hr = SHGetDesktopFolder( &pDesktopFolder );
hr = SHGetSpecialFolderLocation( NULL, CSIDL_PRINTERS, &netItemIdLst );
hr = pDesktopFolder->BindToObject( netItemIdLst, NULL, IID_IShellFolder, (void **)&pPrinterFolder );
to
// testFolder is the CLSID for the virtual folder implementation
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&pVirtualFolder);
or
hr = CoCreateInstance(testFolder, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder2, (LPVOID*)&pVirtualFolder);
But the pVirtualFolder is still NULL, and it prints that "cannot find the corresponding interface".
Is there anything wrong with CoCreateInstance when I use it ? Or I shouldn't use this solution ? Any sample code for it ?
To create a shortcut, you can use the official documentation. Here is a sample code that creates a shortcut for a children of "This PC" (aka: ComputerFolder)
int main()
{
CoInitialize(NULL);
// I've used my Apple's iCloud as an example (name is in french)
// it's a virtual folder, a shell namespace extension
HRESULT hr = CreateComputerChildShortCut(L"Photos iCloud", L"c:\\temp\\my icloud");
printf("hr:0x%08X\n", hr);
CoUninitialize();
return 0;
}
HRESULT CreateComputerChildShortCut(LPWSTR childDisplayName, LPWSTR path)
{
// get My Computer folder's ShellItem (there are other ways for this...)
CComPtr<IShellItem> folder;
HRESULT hr = SHCreateItemInKnownFolder(FOLDERID_ComputerFolder, 0, NULL, IID_PPV_ARGS(&folder));
if (FAILED(hr)) return hr;
// enumerate children
CComPtr<IEnumShellItems> items;
hr = folder->BindToHandler(NULL, BHID_EnumItems, IID_PPV_ARGS(&items));
if (FAILED(hr)) return hr;
for (CComPtr<IShellItem> item; items->Next(1, &item, NULL) == S_OK; item.Release())
{
// get parsing path (if's often useful)
CComHeapPtr<wchar_t> parsingPath;
item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingPath);
wprintf(L"Path: %s\n", parsingPath);
// get display name
CComHeapPtr<wchar_t> displayName;
item->GetDisplayName(SIGDN_NORMALDISPLAY, &displayName);
wprintf(L" Name: %s\n", displayName);
if (!lstrcmpi(childDisplayName, displayName))
{
// get pidl
// it's the unambiguous way of referencing a shell thing
CComHeapPtr<ITEMIDLIST> pidl;
hr = SHGetIDListFromObject(item, &pidl);
if (FAILED(hr)) return hr;
// create an instance of the standard Shell's folder shortcut creator
CComPtr<IShellLink> link;
hr = link.CoCreateInstance(CLSID_FolderShortcut);
if (FAILED(hr)) return hr;
// just use the pidl
hr = link->SetIDList(pidl);
if (FAILED(hr)) return hr;
CComPtr<IPersistFile> file;
hr = link->QueryInterface(&file);
if (FAILED(hr)) return hr;
// save the shortcut (we could also change other IShellLink parameters)
hr = file->Save(path, FALSE);
if (FAILED(hr)) return hr;
break;
}
}
return S_OK;
}
Of course, if you know an absolute parsing path or an absolute pidl, you don't have to enumerate anything, this was just for demonstration purposes.

Why COM doesn't work in a new thread?

My problems started after converting my VS2003 project to VS2008. Solution contains 3 projects. Projects are DLL's. There were A LOT of compilation errors, then some linker errors... Well, I fought them off. Now it just simply doesn't work ;)
So, one of this DLL's is suppoused to communicate with Word by COM.
Word::_ApplicationPtr d_pApp;
Word::_DocumentPtr d_pDoc;
void MSWord2003::init()
{
free();
HRESULT hr;
CLSID clsid;
CLSIDFromProgID(L"Word.Application", &clsid);
// Get an interface to the running instance, if any..
IUnknown *pUnk;
hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie znaleziono działającej aplikacji MSWord.");
IDispatch* d_pDispApp;
hr = pUnk->QueryInterface(IID_IDispatch, (void**)&d_pDispApp);
if(hr!=S_OK)
throw MSWord::MSWordException("Nie udało się połączyć z aplikacją MSWord.");
pUnk->Release();
pUnk = 0;
d_pApp = d_pDispApp;
d_pDoc = d_pApp->ActiveDocument;
d_pDispApp->AddRef();
d_currIdx = -1;
paragraphsCount = d_pDoc->GetParagraphs()->Count;
footnotesCount = d_pDoc->GetFootnotes()->Count;
endnotesCount = d_pDoc->GetEndnotes()->Count;
}
void MSWord2003::free()
{
if(d_pApp!=0)
{
d_pApp->Release();
d_pApp=0;
}
}
This code works on VS2003 (and different machine, I don't have VS2003 on my computer) while in VS2008 it works only if it is called by main thread.
When called by a new thread (wich is initialized by CoInitialize) d_pApp is not initialized properly - its ptr shows 0.
While debugging I reached code in comip.h:
template<typename _InterfacePtr> HRESULT _QueryInterface(_InterfacePtr p) throw()
{
HRESULT hr;
// Can't QI NULL
//
if (p != NULL) {
// Query for this interface
//
Interface* pInterface;
hr = p->QueryInterface(GetIID(), reinterpret_cast<void**>(&pInterface));
// Save the interface without AddRef()ing.
//
Attach(SUCCEEDED(hr)? pInterface: NULL);
}
else {
operator=(static_cast<Interface*>(NULL));
hr = E_NOINTERFACE;
}
return hr;
}
In a new thread, QueryInterface returns E_NOINTERFACE, although GetIID() returns the same thing for both threads. And that is where I got stuck - I have no idea, what causes this behaviour...
IMO you should initialize COM not with CoInitialize, but with CoInitializeEx, specifying COINIT_MULTITHREADED. Otherwise you'll have separate single-threaded COM apartment for every thread.