Search code examples
winapidllcomxaudio2

How do you Initialize a Nonregistered COM DLL?


Recently, some of our clients lost their XAudio2_7.dll from their C:/Windows/System32 directory, and now they can't hear any audio. Reinstalling DirectX or registering the dll normally would be sufficient enough to fix this problem, but we're looking for a solution that does not require admin rights. This is for Windows 7. Applications are written in CPP, and some of them are 32 bit and the rest are 64 bit.

There is a local copy of XAudio2_7.dll within the same directory of the exe, but that is not loaded unless that dll is registered since it's a COM dll. Registering for the current user (using "Regsvr32.exe /n /i:user XAudio2_7.dll") does not work since the dll does not implement a required interface "DllInstall".

One approach I've tried is to statically link a .lib of XAudio instead of using a dynamic link. Microsoft does not distribute XAudio2_7.lib with DirectX SDK. "No versions of the DirectX SDK contain the xaudio2.lib import library. DirectX SDK versions use COM to create a new XAudio2 object." msdn.microsoft.com/en-us/library/windows/desktop/microsoft.directx_sdk.xaudio2.xaudio2create%28v=vs.85%29.aspx

Granted, this suggests that it may not be possible to use a static XAudio library even if I created one since it sounds like it depends on external objects defined in other libraries. It was still worth testing in case my hypothesis is incorrect.

Following the steps mentioned in this link: adrianhenke.wordpress.com/2008/12/05/create-lib-file-from-dll/

I didn't get very far with this method. Outside of the articles being dated, there were a couple problems with this. The MS link within the blog mentions this is for 32-bit dlls. Even for 32-bit dlls, dumpbin didn't work since it only exported the interface functions. Dump of file C:\XAudioTest\32Bit\XAudio2_7.dll

File Type: DLL

  Section contains the following exports for xaudio2.dll

    00000000 characteristics
    4C064181 time date stamp Wed Jun 02 07:33:21 2010
        0.00 version
           1 ordinal base
           4 number of functions
           4 number of names

    ordinal hint RVA      name

          1    0 00030AA0 DllCanUnloadNow
          2    1 00031150 DllGetClassObject
          3    2 00031470 DllRegisterServer
          4    3 000314D0 DllUnregisterServer

  Summary

        C000 .data
        1000 .no_bbt
        4000 .reloc
        1000 .rsrc
       7B000 .text

Later, I found this quote from another MSDN article. "The COM standard requires that COM DLLs export DllCanUnloadNow, DllGetClassObject, DllRegisterServer and DllUnregisterServer. Typically they will export nothing else. This means that you cannot get COM object or method information using dumpbin.exe." msdn.microsoft.com/en-us/library/aa446532.aspx

I've also tried using a third party program that claims it's able to create a .lib from a COM dll. www.binary-soft.com/dll2lib/dll2lib.htm

Although this did generate a 32-bit .lib file, compiling with the lib file generated unresolved external symbols. This utility does come with methods to handle unresolved symbols, but entering anything within Symbol Finder or Advanced Conversion Options would crash. Looking in the latest release notes (3.00), I found that they added support for Vista and no mentions for Windows 7. Also this doesn't work for 64-bit dlls.

I've tried another approach is to change the initialization sequence to use a nonregistered COM DLL described in this link: Use COM Object from DLL without register The initialization code looks similar to this:

    HMODULE audioLib = LoadLibrary(TEXT("XAudio2_7.dll"));
    if (audioLib == NULL)
    {
        debugf(TEXT("Failed to create COM object.  Unable to load XAudio2_7.dll library.  GetLastError:  %d"), GetLastError());
        return;
    }

    typedef HRESULT (WINAPI* Function_DllGCO) (REFCLSID, REFIID, LPVOID*);
    Function_DllGCO processAddress = (Function_DllGCO)GetProcAddress(audioLib, "DllGetClassObject");

    if (processAddress == NULL)
    {
        debugf(TEXT("COM DLL failed to find the process address to interface function 'DllgetClassObject' within the XAudio2_7.dll.  GetLastError:  %d"), GetLastError());
        return;
    }

    class __declspec(uuid("{5a508685-a254-4fba-9b82-9a24b00306af}")) xAudioGUID;
    REFCLSID classID = __uuidof(xAudioGUID);

    class __declspec(uuid("{00000001-0000-0000-c000-000000000046}")) classFactoryGUID;
    REFIID classFactoryID = __uuidof(classFactoryGUID);

    IClassFactory* ClassFactory = NULL;
    if (processAddress(classID, classFactoryID, reinterpret_cast<LPVOID*>(&ClassFactory)) != S_OK)
    {
        debugf(TEXT("Failed to execute function pointer to DLLGetClassObject.  GetLastError:  %d"), GetLastError());
    return;
    }

    class __declspec(uuid("{00000000-0000-0000-C000-000000000046}")) unknownGUID;
    REFIID unknownID = __uuidof(unknownGUID);
    if (ClassFactory->CreateInstance(NULL, unknownID, reinterpret_cast<void**>(&ClassFactory)) != S_OK)
    {
        debugf(TEXT("Class factory for XAudio2_7 failed to create an object instance.  GetLastError:  %d"), GetLastError());
        ClassFactory->Release();
        return;
    }

    if( XAudio2Create( &XAudio2, 0, AUDIO_THREAD) != S_OK ) //Fails here with error number:  1008 (An attempt was made to reference a token that does not exist.)
    {
        debugf( NAME_Init, TEXT( "Failed to create XAudio2 interface:  GetLastError:  %d" ), GetLastError());
        return;
    }
    //Do other things

All WinAPI function calls passed excluding the XAudio2Create function. I'm uncertain why XAudio2Create is not using the object created from the factory, and I don't know what I need to do to get it to use that object. I'm still investigating what I can do here, but it's difficult to debug closed source libraries.

Before I knew about COM DLLs, a method I've tried is to use DLL-Redirection to force an application to use a particular DLL. Using the process described here: DLL redirection using manifests XAudio2Create still failed. I don't have a strong strategy in identifying what's wrong with the manifest. I also haven't found much up to date documentation regarding manifests for dll redirection. From the sounds of this is this mostly used to load a particular DLL version. I don't think DLL redirection is the method I need since there is already a local copy of XAudio2_7 within the same directory of the exe, which means it should take precedence over the XAudio2_7 within the system directory according to: msdn.microsoft.com/en-us/library/windows/desktop/ms682586%28v=vs.85%29.aspx

Note: I removed the hyper links for some addresses since I don't have enough reputation points to post more than 2 links.


Solution

  • Have you tried registration-free activation?