2
votes

I am new to the com technology but what I am trying to do is to create a basic wrapper for a com , specifically the audio com (mmdevapi). It should work like this: my program calls a com(also created by me) that in the end will load and return a pointer to the audio interfaces. I tried to make everything as transparent as I could but I need more info about the calls made when loading a com. From my understanding:

  1. CoInitialize is called

  2. CoCreateInstance:

    a. Searches the dll in the registry

    b. Loads the library

    c. Get address on DllGetClassObject(I guess it doesn't check for the other function DllCanUnloadNow)

    d. Jumps to the function passing the clsid requested by the program(one clsid for very object-so there are more than one objects in one dll? Every "object" containing multiple classes?) and the interface's id.

    e. DllGetClassObject return a void pointer to the interface.

    1. Because the dll is loaded in the same memory as the program it can use this pointer to access the methods from the interface.

If everything I wrote is correct then this should suit my needs(this is part of the wrapper com,used by the main program):

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppvObj) {
    //main();
    //print iid from here..
    LPOLESTR s;
    StringFromCLSID(rclsid, &s);
    OutputDebugStringW(s);
    CoTaskMemFree(s);

    StringFromCLSID(riid, &s);
    OutputDebugStringW(s);
    CoTaskMemFree(s);

    //prints: {BCDE0395-E52F-467C-8E3D-C4579291692E}  -the audio clsid
    //        {00000001-0000-0000-C000-000000000046}  -the requested interface

    // {D3C5025B-3634-4F74-9404-942ECEFC1152}  -contains the dll for audio
    static const GUID custom_Audio_GUID =
    { 0xd3c5025b, 0x3634, 0x4f74,{ 0x94, 0x4, 0x94, 0x2e, 0xce, 0xfc, 0x11, 0x52 } };

    CoInitialize(NULL);
    CoCreateInstance(custom_Audio_GUID, NULL, CLSCTX_INPROC_SERVER, riid, ppvObj);  //The program wants to access an interface from the audio com so I have to bypass this com,load the audio and request the interface and pass it to the program

    return S_OK;
}

So,for the program there's nothing else it should do (it can take rest of the interfaces using QueryInterface using the ppvObj returned by my com,etc.). But ,of course,it doesn't work. It loads the audio dll,spawns a surrogate but the audio doesn't work. Any ideas?

(the custom clsid is defined in CURRENT_USER in CLSID. It only contains InprocServer32/Default -no apartment config; I haven't defined the DLLCanUnload but I don't think it's a problem).

1
Understanding why calling CoInitialize(NULL) in a class factory is so very, very wrong does take a book, it can't be easily summarized in an SO post. COM is useful to hide the language implementation details, one basic reason that Microsoft likes to use it since they want to create libraries that are useable in any language. It does come with a high price however, writing COM code is not very enjoyable. And sure, easy to get it wrong in a way that is quite hard to debug. There just isn't any point in using COM for a "wrapper", you know what language you are using. - Hans Passant
what to debug? There are only 10 lines of code. The code is so small that makes me think the problem is elsewhere. I know there are many things in com (even undocumented as I can see) but this is a very important chapter in my learning. I have the power/time to master it. I need an ideea for my problem. I did something to test it.I used Chrome. I replaced the dll from mmdevapi with my own. When Chrome starts( a fresh start) it loads my dll as a com,my code then is supposed to load the audio. But,sadly,youtube is ...mute. - sergiu reznicencu
ok. If you can't provide an elaborate answer here can you at least tell me why it is wrong to call CoInitiliaze in DllGetClassObject? - sergiu reznicencu
DllGetClassObject is not equivalent to CoCreateInstance. Most of the time, DllGetClassObject is just used to get an IClassFactory reference (which seems the case here). You can't CoCreateInstance an IClassFactory, that's precisely the role of DllGetClassObject. You're Chicken-an-egging. CoInitialize is supposed to be called by the developer who creates the running thread, not by components. - Simon Mourier
Drop CoInitialize call, and replace the last CoCreateInstance call with CoGetClassObject (and return what it returns - don't hard-code S_OK). I think your wrapper would work with that; though the purpose of it escapes me. - Igor Tandetnik

1 Answers

2
votes

Finally I solved the problem. What problem? I couldn't use CoGetClassObject or CoCreateInstance because those functions loaded the audio dll and called DllGetClassObject with the custom clsid under which I moved mmdevapi.dll (the original clsid was hosting now my dll). Because of the com properties a dll can host many "objects" ,every single of them being represented by a clsid. Every object contains interfaces identified by IIDs. Now, usually DllGetClassObject of mmdevapi.dll is called by Chrome with the clsid {BCDE0395-E52F-467C-8E3D-C4579291692E}. Inside its DllGetClassObject function,it tries to figure out the class by looking at the clsid passed by the program to this function(in a long if statement or switch). If it finds a matching clsid,it returns the matching interface,whose iid is also specified by Chrome.

In my case ,I was trying to get the same interface as Chrome(because I was forwarding its call to DllGetClassObject) from the mmdevapi dll ,using the code:

CoGetClassObject(custom_Audio_GUID, NULL, CLSCTX_INPROC_SERVER, riid, ppvObj);

But,custom_Audio_GUID was different than the requested clsid so mmdevapi always returned CLASS_E_CLASSNOTAVAILABLE.

My solution:

  HMODULE dll=LoadLibrary("mmdevapi.dll");    //I can load the exact dll I need,without the registry lookup
  Func load=(load)GetProcAddr(dll,"DllGetClassObject");
  load(rclsid, riid, ppvObj);   //and I can pass the exact params I want

This worked for me in my attempt to bypass a com. (I am new so there certainly are mistakes in my understanding and maybe terminology but this is how I have understood the error).