In Which Library IFileDialog is Located - c++

Now I'm Writing This Code
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&pfd));
after Compiling an Error Appeared; "IFileDialog Not Declared in this Scope"
What is The Library of that Class ??

You don't need to know which library implements it. This is a COM interface that you invoke with a call to CoCreateInstance. The system does the rest. It looks up the implementing COM server in the COM registry and instantiates your object.
In order to compile you just need to include Shobjidl.h, and define the version macros appropriately. You need
#define _WINNT_WIN32 0600

Related

When is calling CoInitialize required for a Windows console application

The code below, derived from https://learn.microsoft.com/en-us/windows/desktop/shell/folder-info#determining-an-objects-parent-folder, works as expected when compiled and run via Visual Studios 2017:
#include "stdafx.h"
#include <shlobj.h>
#include <shlwapi.h>
#include <objbase.h>
#pragma comment(lib, "shlwapi")
int main()
{
IShellFolder *psfParent = NULL;
LPITEMIDLIST pidlSystem = NULL;
LPCITEMIDLIST pidlRelative = NULL;
STRRET strDispName;
TCHAR szDisplayName[MAX_PATH];
HRESULT hr;
hr = SHGetFolderLocation(NULL, CSIDL_SYSTEM, NULL, NULL, &pidlSystem);
hr = SHBindToParent(pidlSystem, IID_IShellFolder, (void **)&psfParent, &pidlRelative);
if (SUCCEEDED(hr))
{
hr = psfParent->GetDisplayNameOf(pidlRelative, SHGDN_NORMAL, &strDispName);
hr = StrRetToBuf(&strDispName, pidlSystem, szDisplayName, sizeof(szDisplayName));
_tprintf(_T("%s\n"), szDisplayName);
}
psfParent->Release();
CoTaskMemFree(pidlSystem);
Sleep(5000);
return 0;
}
If I replace CSIDL_SYSTEM with CSIDL_MYDOCUMENTS, though, the GetDisplayNameOf method call fails with:
onecore\com\combase\objact\objact.cxx(812)\combase.dll!74EA3270: (caller: 74EA201B) ReturnHr(1) tid(d4c) 800401F0 CoInitialize has not been called.
onecoreuap\shell\windows.storage\regfldr.cpp(1260)\windows.storage.dll!76FE4FA3: (caller: 76E9F7EE) ReturnHr(1) tid(d4c) 80040111 ClassFactory cannot supply requested class
Adding CoInitialize(NULL); before the call to SHGetFolderLocation fixes the issue.
Why is calling CoInitialize required in one case but not the other?
Also, it seems like CoInitialize should always be called, but it's interesting that the sample code doesn't call it. I'm curious why this is the case. I couldn't get the sample code compiling as is - <iostream.h> couldn't be found, which is why I replaced the cout printing code with a call to _tprintf... Maybe that's an indication of the problem? Does the C++ runtime call CoInitialize for you, and maybe VS is trying to build a C application for me or something (like how on Linux, compiling with gcc and g++ has different implications).
As a rule, you should initialize COM/OLE before creating shell COM objects that inherit from IUnknown, use drag & drop etc. This also applies to functions that might use COM internally which could in theory be most of the SH* functions in shell32 and shlwapi.
Why did it work with CSIDL_SYSTEM?
The Windows 95 shell could run without loading COM/OLE. To do this it provided its own mini-COM implementation. Shell extensions could mark themselves as not requiring real COM and things implemented inside shell32 would call a special CoCreateInstance that tries to load things directly from shell32. This was to avoid loading ole32.dll because it is a very big file to load on a Intel 386 machine with 4 MiB of RAM (Windows 95 minimum requirements).
The IShellFolder implementation that deals with the filesystem is implemented in shell32 and does not require COM and is therefore able to handle a path like c:\Windows\system32.
CSIDL_MYDOCUMENTS however, is not a normal folder, it is a namespace extension and parts of its implementation is in mydocs.dll. And as you found out, parts of it does require COM.
All of this is of course a implementation detail and you should never assume that any of this is going to work without initializing COM.
SHGetFolderLocation may delegate execution to an extension that requires COM initialization. Although the documentation does not explicitly say so, you can find a remark about that for ShellExecute which is part of the same module (shell32.dll).
Because ShellExecute can delegate execution to Shell extensions (data
sources, context menu handlers, verb implementations) that are
activated using Component Object Model (COM), COM should be
initialized before ShellExecute is called. Some Shell extensions
require the COM single-threaded apartment (STA) type. In that case,
COM should be initialized as shown here:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
There are certainly instances where ShellExecute does not use one of
these types of Shell extension and those instances would not require
COM to be initialized at all. Nonetheless, it is good practice to
always initalize COM before using this function.
You can use the following helper class to automatically initialize the COM library on the current thread.
class COMRuntime
{
public:
COMRuntime() {
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
}
~COMRuntime() {
::CoUninitialize();
}
};
Then just declare one instance of that class:
int main()
{
COMRuntime com;
// the rest of your code
}

COM Error: Class not registered (I'm sure it is)

I have a large complex program that has a COM problem.
I'm attempting to write a much smaller SSCCE program to reduce the problem.
However, no matter what I try, the CoCreateInstance in my SSCCE keeps coming back with
hr 0x80040154 (Class Not Registered) (For CoCreateInstance)
0x800706B5: The interface is unknown. (for ICalendarPtr constructor)
I'm using the same GUIDs and other parameters from the bigger program.
(turns out I wasn't using the same guids. Just similar ones)
I'm linking to the same libraries, and have the same DLLs available (both locally and properly registered in Program Files).
I'm not a Registry expert, but looking through the registry, I do find the Interface and Class GUID look to be properly registered, with a TypeLib-key that refers to a DLL that is present and accessible.
Can you think of something I might be missing that would cause one program to create a COM object successfully, but another to say the class isn't registered?
Code:
_COM_SMARTPTR_TYPEDEF(ICalendar, __uuidof(ICalendar));
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);
CLSID classID = __uuidof(ICalendar);
REFIID iid = __uuidof(IUnknown);
LPVOID pRet;
HRESULT hr = CoCreateInstance(classID, NULL, CLSCTX_INPROC_SERVER, iid, &pRet);
// Result: 0x80040154 Class not registered
GUID guid = __uuidof(ICalendar);
ICalendarPtr pDtTm(guid);
// Result: First-chance exception at 0x773dc41f in COMTest.exe: 0x800706B5: The interface is unknown.
return 0;
}
CLSID classID = __uuidof(ICalendar);
This is wrong. __uuidof() retrieves an interface's IID, not its CLSID. When calling CoCreateInstance(), you need to use the CLSID in the 1st parameter and the IID in the 4th parameter, eg:
ICalendar *pRet;
HRESULT hr = CoCreateInstance(CLSID_Calendar, NULL, CLSCTX_INPROC_SERVER, __uuidof(ICalendar), (void**)&pRet);
When using the constructor of an interface smart wrapper, you need to use the CLSID, eg:
ICalendarPtr pDtTm(CLSID_Calendar);
There is no compiler syntax for retrieving an interface's CLSID. You have to import the interface's TypeLibrary and then use the generated .h file to get the definitions, or else do a lookup of the Registry at runtime, such as with CLSIDFromProgID().

How to use CoCreateInstance in c++

I am a COM beginner. I have a DLL file that I register using the regsvr32 command. In the COM client, I try to use the CoCreateInstance function, but it doesn't work. This is my code:
IMessageBox *pBox;
hr = CoCreateInstance(
__uuidof(IMessageBox),
NULL,
CLSCTX_INPROC_SERVER,
IID_IUnknown,
(void **)&pBox
);
IMessageBox is the interface which is defined in my DLL file. It implements the IDispatch interface. The result of hr displays the error REGDB_E_CLASSNOTREG. How do I use the CoCreateInstance function?
Instead of __uuidof(IMessageBox) you have to pass the UUID of the class you want to instantiate - i.e. the class you registered previously using regsrv32.
Well, the error tells you what is the problem. The class you are requesting is not registered in the COM registry. It could be that the IID of class that you registered is not the one that you are requesting. Another common failure mode is that you registered a 32 bit DLL and your calling process is 64 bit. Or vice versa.

C# COM dll has REGDB_E_CLASSNOTREG error in C++ project

I have a C# dll that I properly have registered for COM Interop, and made COM visible. Using cppbuilder, I imported the type library, which generated the wrapper classes, and I am now attempting to use to create an instance of my C# class. However, I'm getting a REGDB_E_CLASSNOTREG error in my C++ code. I verified the dll is in the registry, and even re-registered it with regasm. No change. What could I be missing?
Here is my C++ code:
_MyClassPtr obj;
HRESULT hr = obj.CreateInstance(__uuidof(MyClass));
//now hr equals REGDB_E_CLASSNOTREG
I've also tried it as such:
IMyClass* obj;
HRESULT hr = CoCreateInstance(__uuidof(MyClass), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMyClass), (void**) &obj);
//same result, hr equals REGDB_E_CLASSNOTREG
I do have one additional dependency in the C# app. I registered it for COM as well with no difference, but did not import it's type library into the C++ project.
UPDATE: based on the comments below, I discovered that CreateInstance is looking up the class guid in the following places in the registry:
HKCU\Software\Classes\Wow6432Node\CLSID\{guid}
HKCR\Wow6432Node\CLSID\{guid}
HKCU\Software\Classes\CLSID\{guid}
HKCR\CLSID\{guid}
But, going through the registry, the only entry under any of the CLSID nodes that is related to my assembly is the guid for the assembly itself, which is, of course, different than the guid for the class, or the interface.
I've manually run regasm under both x86 and x64 mode to try to acheive different results. No differences.
Well, I found out what would work.
IMyClassPtr obj;
HRESULT hr = obj.CreateInstance(CLSID_MyClass);
CLSID_MyCLass was a guid constant in the generated MyClass_TLB.cpp file. Using it instead of __uuidof(...) for the class types enabled everything to start working correctly.

CoCreateInstance: Create a COM instance defined in a .NET library?

I've created a COM library defined in an IDL, it has an "entrypoint" called IFoo:
[
object,
uuid(789b4d46-4028-4196-8412-4c5c8ef86caa),
nonextensible,
pointer_default(unique)
]
interface IFoo: IUnknown
{
HRESULT HelloWorld();
};
I've implemented this in my C# library like so:
[ComVisible(true)]
[Guid("45b50f1e-d551-4be0-b52a-7ec075840114")]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IFoo))]
class Foo: IFoo
{
public void HelloWorld()
{
Console.WriteLine("Hello world");
}
}
I compiled this, and registered it with:
regasm.exe foo.dll
Everything looks good thus far.
Now, how do I create an instance of this in my C++ program? I think the right function is ::CoCreateInstance:
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
IUnknown *pUnk = 0;
HRESULT hr = ::CoCreateInstance(???,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IFoo),
(void**)&pUnk);
Is this the right way to create an instance of this class? If so, what goes in "???" above?
I think the class's UUID ("45b50f1e-d551-4be0-b52a-7ec075840114" above) should go there, but I can't figure out how to manually create an IID from this GUID string.
It turns out, I was right, the class's UUID goes there, but getting it was something I didn't expect:
struct __declspec(uuid("45b50f1e-d551-4be0-b52a-7ec075840114")) Cls;
Then:
HRESULT hr = ::CoCreateInstance(__uuidof(Cls),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IFoo),
(void**)&pUnk);
This fixed the problem and worked correctly.
Just use MIDL to compile your IDL file into set of C++ files and include them into your C++ program. Have in mind that interface is not the "entry point" and you are going to need the class object in your IDL too.
Another way to create a COM client in C++ is Microsoft specific #import directive, but I am not sure if it is compatible with .NET-based components. If you can create a .tlb file for your .NET component, you can just add the needed stuff like this:
#import "MyLibrary.tlb"
This directive has lots of options. Check this MSDN article for details.