COM and DirectSound - c++

I am trying to initialize Direct Sound the following way:
// Somewhere in my header...
#define DIRECT_SOUND_CREATE(name) HRESULT WINAPI name(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter)
typedef DIRECT_SOUND_CREATE(direct_sound_create);
// Initialization code...
HMODULE DSoundLib = LoadLibraryA("dsound.dll");
if(DSoundLib)
{
direct_sound_create *DirectSoundCreate = (direct_sound_create*)
GetProcAddress(DSoundLib, "DirectSoundCreate");
LPDIRECTSOUND DirectSound;
if(DirectSoundCreate && SUCCEEDED(DirectSoundCreate(0, &DirectSound,0)))
{
The issue is that I am getting this error(?)
onecore\com\combase\objact\objact.cxx(812)\combase.dll!75521B90: (caller: 7552093B) ReturnHr(1) tid(2444) 800401F0 CoInitialize has not been called.
Could anyone tell what this is/is related to? Do I need to call CoInitialize when using DirectSound or can I bypass COM stuff?
Here are my linker options:
set CommonLinkerFlags= -incremental:no -nodefaultlib -stack:0x100000,0x100000 ^
kernel32.lib ^
user32.lib ^
gdi32.lib ^
winmm.lib

There is no way to use Direct Sound without COM because it is COM-based. The very first call you make DirectSoundCreate, creates an instance of object implementing IDirectSound COM interface. Documentation mentions that explicit COM initialization is not required in some situations. But it is a good idea to manually perform it anyway to be on the safe side. CoInitialize is mandatory when dealing with COM stuff. Typically this should be on of the first things to do at application initialization, before creating windows / renders. And don't forget to call CoUninitialize when you done working with COM.

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
}

Confirmed Registered COM Object CoCreateInstance returning REGDB_E_CLASSNOTREG

I have an ATL DLL that I am trying to consume from an ATL Exe. When I try to create an instance of the object in the DLL it fails with an error REGDB_E_CLASSNOTREG. I have checked the registry and I can see the object is registered, both the ProgID as well as the CLSID.
CComPtr<IMyInterface> ptrMyInterface;
ptrMyInterface.CoCreateInstance(L"ProgID", nullptr, CLSCTX_ALL); // Fails
CLSID myClsid;
CLSIDFromString(L"{MyCLSID}"), &myClsid); // Correctly looks up the CLSID
ptrMyInterface.CoCreateInstance(myClsid, nullptr, CLSCTX_ALL); // Also fails
Any suggestions
Ok, I feel really kind of silly. The problem was the fact that CComPtr was trying to call QueryInterface on the object I was CoCreating. The Interface that I was using was not implemented by the class that I was creating. It seems that there would be a better error than REGDB_E_CLASSNOTREG. Something like E_NOINTERFACE would be a better return code IMHO.
CComPtr<IMyCorrectInterface> ptrMyInterface;
ptrMyInterface.CoCreateInstance(L"ProgID", nullptr, CLSCTX_ALL); // WORKS!!!

Using GetProcAddress and EasyHook to hook class methods and constructors

I've had plenty of success using EasyHook to hook system API routines (in C++) out of libraries. These libraries have always been flat and basically filled with globally callable routines. Here is a small sample using MessageBeep() out of the User32.dll library (minus setup code):
HMODULE hUser32 = GetModuleHandle ( L"User32" );
FARPROC TrampolineMethod; // This would have been set to my new replacement trampoline method.
TRACED_HOOK_HANDLE hHook = new HOOK_TRACE_INFO();
NTSTATUS status;
status = LhInstallHook(
GetProcAddress(hUser32, "MessageBeep"),
TrampolineMethod,
(PVOID)0x12345678,
hHook);
This is all works great. The problem is, I now have a need to hook methods out of a class, not just a global function. I don't really care about the object itself, I'm really more interested in examining the parameters of the method and that's it. I don't know how to syntactically identify the routine in the function name parameter for GetProcAddress(), and I'm not even sure if GetProcAddress() supports it. For example, I'd like to hook the Pen::SetColor() method out of the gdiplus.dll library:
HMODULE hGDIPlus = GetModuleHandle ( L"Gdiplus" );
FARPROC TrampolineMethod; // This would have been set to my new replacement trampoline method.
TRACED_HOOK_HANDLE hHook = new HOOK_TRACE_INFO();
NTSTATUS status;
status = LhInstallHook(
GetProcAddress(hGDIPlus, "Pen.SetColor"), // this is probably wrong or not possible here
TrampolineMethod,
(PVOID)0x12345678,
hHook);
This doesn't work of course and I don't think the GetProcAddress(hGDIPlus, "Pen.SetColor") is correct. How do I specify a member function of a class to GetProcAddress()? Is this even possible? Also, how would this look if I wanted to hook a constructor such as Pen::Pen()?
The Portable Executable (PE) format that Windows uses doesn't really supports exporting or importing objects or their methods, so that's not what GdiPlus (or any other DLL) uses internally. The object notation is probably an abstraction implemented in the import library for the DLL.
If you take a look at GdiPlus's export table with the Dependency Walker tool (Windows SDK), or similar, you will see
GdipGetPenColor
GdipSetPenColor
etc.
So it is basically no different than the legacy exports, like MessageBeep.

C++ Builder XE2: Initializing a Data Module in a dll

I'm trying to create a dll that contains a VCL data module - the idea being that various applications can all load the same dll and use the same database code.
The data module itself is tested ok as part of an application - I've copied the form over to my dll project.
So in the dll entry point method, I need to initialize the data module:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
//if I don't call this, I get an exception on initializing the data module
CoInitialize(NULL);
//initialize a standard VCL form; seems to works fine
//I'm not using Application->CreateForm as I don't want the form to appear straight away
if(!MyForm) MyForm = new TMyForm(Application);
//this doesn't work - the thread seems to hang in the TDataModule base constructor?
//I've also tried Application->CreateForm; same result
if(!MyDataModule) MyDataModule = new TMyDataModule(Application);
}
I've also seen something about how I need to call Application->Initialize before creating the form but this doesn't seem to make any difference.
Any ideas?
Thanks
You really should not be doing very much work in your DllEntryPoint() at all. Certainly not calling CoInitialize(), anyway. It is not the DLL's responsibility to call that when loaded. It is the calling app's responsibility before loading the DLL.
You should either:
export an additional function to initialize your DLL and then have the app it after loading the DLL (same for uninitialing the DLL before unloading it)
don't create your TForm/TDataModule until the first time the DLL actually needs them.
move your TForm/TDataModule into their own worker thread inside the DLL. In this case, you would then call CoIniitalize().
And in all cases, don't relay on the DLL's Application object to manage the lifetime of your TForm/TDataModule. Free them yourself instead before the DLL is unloaded.

Given a COM DLL, extract all classes CLSID and corresponding interface name

My question is similar to Getting CLSID for a DLL file?, I think.
I have a directory with some DLLs, each one implementing one or more COM interfaces. I would like to get:
1) Each interface name
2) The CLSID of the class implementing the interface
For each DLL. It's important that everything can be done programatically (So I can't use some sort of COM browser and manually look up for that information).
Later I will lookup the CLSID given the interface name and call some methods using IDispatch.
One alternative seems to be scanning the registry trying to match the type, interface and class GUID and the .dll filename. But that seems to be slow and not robust.
Does someone has a clear solution to this problem?
EDIT:
With the response of Ben Voigt, I came with the following code which suit my needs:
ITypeLib *typelib;
ITypeInfo *typeinfo;
LoadTypeLibEx(_T("c:\\mydir\\mycom1"), REGKIND_NONE, &typelib);
for (UINT i = 0;i < typelib->GetTypeInfoCount();++i) {
TYPEKIND typekind;
typelib->GetTypeInfoType(i, &typekind);
if (typekind == TKIND_COCLASS) {
// class!
CComBSTR className;
TYPEATTR *typeattr;
typelib->GetTypeInfo(i, &typeinfo);
typeinfo->GetDocumentation(MEMBERID_NIL, &className, NULL, NULL, NULL);
typeinfo->GetTypeAttr(&typeattr);
GUID classGUID = typeattr->guid;
for (UINT j = 0;j < typeattr->cImplTypes;++j) {
// interface!
CComBSTR interfaceName;
HREFTYPE hreftype;
ITypeInfo *classtypeinfo;
typeinfo->GetRefTypeOfImplType(j, &hreftype);
typeinfo->GetRefTypeInfo(hreftype, &classtypeinfo);
classtypeinfo->GetDocumentation(MEMBERID_NIL, &interfaceName, NULL, NULL, NULL);
// associate interfaceName with classGUID here
}
}
}
You can't get that from the COM DLL, but you can get it from the typelib. I'm pretty sure the MIDL compiler has a switch to decompile a typelib, but parsing IDL wouldn't be as easy as using the TypeLib API.
To complicate matters, the typelib is often stored as a resource inside the DLL. So you'd extract the resource, and open it with the TypeLib API.
Start with LoadTypeLibEx which will return you an ITypeLib* interface pointer (you knew you were going to need COM in order to get information about COM libraries, right?). This will actually do the resource extraction step for you.
Then, call ITypeLib::GetTypeInfoCount to find out how many types there are. Call ITypeLib::GetTypeInfoType for each one to find the interfaces and coclasses. And call ITypeLib::GetTypeInfo followed by ITypeInfo::GetDocumentation to get the name.
It looks like you have all of this so far. Next you need the GUID of the type, which is gotten with ITypeInfo::GetTypeAttr (not ITypeLib::GetLibAttr). That gives you a TYPEATTR structure, which has a guid field.
From the same TYPEATTR structure, you'll need the cImplTypes field. That together with ITypeInfo::GetRefTypeOfImplType will let you match up each coclass to the interfaces it implements.
Note that there's not guaranteed to be a 1:1 relationship between interfaces and implementation coclasses. And the interface can be in a different library from the coclass.
Few caveats to Ben Voigt's answer: not every COM DLL has a typelib. In your case, it seems, it does; but that's not a requirement. The only rock-solid requirement is that the DLL exports the function DllGetClassObject(), where you pass a CLSID and get back an object factory.
You could load the library and call DllGetClassObject for every registered CLSID on the system (scan the registry under HKCR\CLSID for the list of those). The ones where you get a valid object back are the ones that the DLL supports. Now, in theory, it's not even a requirement that the CLSIDs the DLL supports are registered; I can envision a DLL that implements private object classes that only the intended client knows about and no one else should. But this is a very, very exotic scenario; for one thing, the regular COM mechanism of looking up the DLL path by the CLSID will break for those. The client would have to load the DLL directly.
You could also scan the registry for the CLSID's where the DLL under consideration is registered as InprocServer32; this, again, will break in case of private classes. You can do a registry search in regedit, search in data. Also, you'd have to deal with filename case issues, short vs. long names, hardlinks, environment variable substitution and so on. So I would not recommend it.
EDIT: just thought of another way. Download Regmon, run it, and call regsvr32 on the DLL. Then watch what CLSID's are touched.
You might want to look at the source code in WiX's heat utility, which does the same thing:
http://wix.sourceforge.net/manual-wix3/heat.htm