2

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.

Community
  • 1
  • 1
EEberhart
  • 31
  • 6
  • 2
    You cannot write software to solve operating system install corruption. This is not your problem, recommend the user to replace the hard disk. – Hans Passant Jan 14 '16 at 18:05
  • `XAudio2Create` has no access to the factory you created so naturally it can't use it. (In fact, you overwrite your factory with the instance you created when you called `ClassFactory->CreateInstance(..., &ClassFactory)`. Fix that, and then you have your XAudio2 object, and then you can `Initialize` it. Mind you, this is just the tip of the iceberg. Other things may not work because the OS is corrupted. The correct solution is to have the users repair their OS, not to try to drive a broken car. – Raymond Chen Jan 14 '16 at 19:21
  • seems like an awful lot of work just to avoid a one time registering with admin rights. – AndersK Jan 14 '16 at 21:55

1 Answers1

2

Have you tried registration-free activation?

Joel Lucsy
  • 8,520
  • 1
  • 29
  • 35
  • It's a bit dated. I'm currently working with Windows 7. I'll try it out just in case it still works for 7. – EEberhart Jan 14 '16 at 18:14
  • 1
    technique should still work, was never removed that i know off – Joel Lucsy Jan 14 '16 at 18:15
  • 1
    @EEberhart: *"It's a bit dated."* - Then so is [CreateWindow](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632679.aspx). Are you worried about calling `CreateWindow`, just because it is more than 30 years old? – IInspectable Jan 14 '16 at 18:22
  • @JoelLucsy I struggle to follow the walkthrough. I've made a few changes to be able to compile it (such as adding a parameter to ptr->Version()), but the changes doesn't deviate far from the walkthrough. It crashes at this line (Access violation): ptr->Version(&versionNum); The CreateInstance function does seem to assign the ISideBySideClass::m_pInterface variable to a memory address, but I speculate the pointer I have does not evaluate to a SideBySide object. – EEberhart Jan 18 '16 at 20:35
  • you do realize that ISideBySideClass is just a demo thing, and that the key to the whole thing is the manifest, right? You setup the manifest with the info from the xaudio2.dll and put the dll in the same folder as your program. – Joel Lucsy Jan 19 '16 at 04:23
  • @JoelLucsy I understand it's a demo. My plan was to get a simple case to minimize variables, and to help further my understanding with the system. I've managed to get the demo running, and I'd like to apply this method to the actual project. I'm currently investigating how I can obtain the XAudio's IDD and LibID for the manifest file. I haven't identified why OleView can't load the dll (Error type: CantLoadLibrary). – EEberhart Jan 19 '16 at 22:05
  • Managed to get it working thanks to the direction the walkthrough provided. I couldn't find the LibID for XAudio2.dll. Although I have concern about incompatible apartments, those options are optional. The other IDs (CLSID and IDD) were found using XAudio's header file. I also picked up some new tools in the process: OleView & Dependency Walker (good for viewing dll details). Sigcheck (good for viewing exes' assembly data) EventViewer (good for reading more descriptive messages from OS errors (such as a manifest file preventing an application from running)). Thanks for the help! – EEberhart Jan 21 '16 at 15:53