| /* |
| * Copyright 2009 Maarten Lankhorst |
| * Copyright 2011 Andrew Eikum for CodeWeavers |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include "config.h" |
| #include "wine/port.h" |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wingdi.h" |
| #include "wine/library.h" |
| |
| #include "ole2.h" |
| #include "olectl.h" |
| #include "rpcproxy.h" |
| #include "propsys.h" |
| #include "propkeydef.h" |
| #include "mmdeviceapi.h" |
| #include "mmsystem.h" |
| #include "dsound.h" |
| #include "audioclient.h" |
| #include "endpointvolume.h" |
| #include "audiopolicy.h" |
| #include "devpkey.h" |
| #include "winreg.h" |
| |
| #include "mmdevapi.h" |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi); |
| |
| static HINSTANCE instance; |
| |
| DriverFuncs drvs; |
| |
| const WCHAR drv_keyW[] = {'S','o','f','t','w','a','r','e','\\', |
| 'W','i','n','e','\\','D','r','i','v','e','r','s',0}; |
| |
| static const char *get_priority_string(int prio) |
| { |
| switch(prio){ |
| case Priority_Unavailable: |
| return "Unavailable"; |
| case Priority_Low: |
| return "Low"; |
| case Priority_Neutral: |
| return "Neutral"; |
| case Priority_Preferred: |
| return "Preferred"; |
| } |
| return "Invalid"; |
| } |
| |
| static BOOL load_driver(const WCHAR *name, DriverFuncs *driver) |
| { |
| WCHAR driver_module[264]; |
| static const WCHAR wineW[] = {'w','i','n','e',0}; |
| static const WCHAR dotdrvW[] = {'.','d','r','v',0}; |
| |
| lstrcpyW(driver_module, wineW); |
| lstrcatW(driver_module, name); |
| lstrcatW(driver_module, dotdrvW); |
| |
| TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module)); |
| |
| driver->module = LoadLibraryW(driver_module); |
| if(!driver->module){ |
| TRACE("Unable to load %s: %u\n", wine_dbgstr_w(driver_module), |
| GetLastError()); |
| return FALSE; |
| } |
| |
| #define LDFC(n) do { driver->p##n = (void*)GetProcAddress(driver->module, #n);\ |
| if(!driver->p##n) { FreeLibrary(driver->module); return FALSE; } } while(0) |
| LDFC(GetPriority); |
| LDFC(GetEndpointIDs); |
| LDFC(GetAudioEndpoint); |
| LDFC(GetAudioSessionManager); |
| #undef LDFC |
| |
| /* optional - do not fail if not found */ |
| driver->pGetPropValue = (void*)GetProcAddress(driver->module, "GetPropValue"); |
| |
| driver->priority = driver->pGetPriority(); |
| lstrcpyW(driver->module_name, driver_module); |
| |
| TRACE("Successfully loaded %s with priority %s\n", |
| wine_dbgstr_w(driver_module), get_priority_string(driver->priority)); |
| |
| return TRUE; |
| } |
| |
| static BOOL init_driver(void) |
| { |
| static const WCHAR drv_value[] = {'A','u','d','i','o',0}; |
| |
| static WCHAR default_list[] = {'p','u','l','s','e',',','a','l','s','a',',','o','s','s',',', |
| 'c','o','r','e','a','u','d','i','o',0}; |
| |
| DriverFuncs driver; |
| HKEY key; |
| WCHAR reg_list[256], *p, *next, *driver_list = default_list; |
| |
| if(drvs.module) |
| return TRUE; |
| |
| if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){ |
| DWORD size = sizeof(reg_list); |
| |
| if(RegQueryValueExW(key, drv_value, 0, NULL, (BYTE*)reg_list, |
| &size) == ERROR_SUCCESS){ |
| if(reg_list[0] == '\0'){ |
| TRACE("User explicitly chose no driver\n"); |
| RegCloseKey(key); |
| return TRUE; |
| } |
| |
| driver_list = reg_list; |
| } |
| |
| RegCloseKey(key); |
| } |
| |
| TRACE("Loading driver list %s\n", wine_dbgstr_w(driver_list)); |
| for(next = p = driver_list; next; p = next + 1){ |
| next = strchrW(p, ','); |
| if(next) |
| *next = '\0'; |
| |
| driver.priority = Priority_Unavailable; |
| if(load_driver(p, &driver)){ |
| if(driver.priority == Priority_Unavailable) |
| FreeLibrary(driver.module); |
| else if(!drvs.module || driver.priority > drvs.priority){ |
| TRACE("Selecting driver %s with priority %s\n", |
| wine_dbgstr_w(p), get_priority_string(driver.priority)); |
| if(drvs.module) |
| FreeLibrary(drvs.module); |
| drvs = driver; |
| }else |
| FreeLibrary(driver.module); |
| }else |
| TRACE("Failed to load driver %s\n", wine_dbgstr_w(p)); |
| |
| if(next) |
| *next = ','; |
| } |
| |
| return drvs.module != 0; |
| } |
| |
| BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) |
| { |
| TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); |
| |
| switch (fdwReason) |
| { |
| case DLL_PROCESS_ATTACH: |
| instance = hinstDLL; |
| DisableThreadLibraryCalls(hinstDLL); |
| break; |
| case DLL_PROCESS_DETACH: |
| if(lpvReserved) |
| break; |
| MMDevEnum_Free(); |
| break; |
| } |
| |
| return TRUE; |
| } |
| |
| HRESULT WINAPI DllCanUnloadNow(void) |
| { |
| return S_FALSE; |
| } |
| |
| typedef HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj); |
| |
| typedef struct { |
| IClassFactory IClassFactory_iface; |
| REFCLSID rclsid; |
| FnCreateInstance pfnCreateInstance; |
| } IClassFactoryImpl; |
| |
| static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface) |
| { |
| return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface); |
| } |
| |
| static HRESULT WINAPI |
| MMCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj) |
| { |
| IClassFactoryImpl *This = impl_from_IClassFactory(iface); |
| TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); |
| if (ppobj == NULL) |
| return E_POINTER; |
| if (IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IClassFactory)) |
| { |
| *ppobj = iface; |
| IClassFactory_AddRef(iface); |
| return S_OK; |
| } |
| *ppobj = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI MMCF_AddRef(LPCLASSFACTORY iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI MMCF_Release(LPCLASSFACTORY iface) |
| { |
| /* static class, won't be freed */ |
| return 1; |
| } |
| |
| static HRESULT WINAPI MMCF_CreateInstance( |
| LPCLASSFACTORY iface, |
| LPUNKNOWN pOuter, |
| REFIID riid, |
| LPVOID *ppobj) |
| { |
| IClassFactoryImpl *This = impl_from_IClassFactory(iface); |
| TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj); |
| |
| if (pOuter) |
| return CLASS_E_NOAGGREGATION; |
| |
| if (ppobj == NULL) { |
| WARN("invalid parameter\n"); |
| return E_POINTER; |
| } |
| *ppobj = NULL; |
| return This->pfnCreateInstance(riid, ppobj); |
| } |
| |
| static HRESULT WINAPI MMCF_LockServer(LPCLASSFACTORY iface, BOOL dolock) |
| { |
| IClassFactoryImpl *This = impl_from_IClassFactory(iface); |
| FIXME("(%p, %d) stub!\n", This, dolock); |
| return S_OK; |
| } |
| |
| static const IClassFactoryVtbl MMCF_Vtbl = { |
| MMCF_QueryInterface, |
| MMCF_AddRef, |
| MMCF_Release, |
| MMCF_CreateInstance, |
| MMCF_LockServer |
| }; |
| |
| static IClassFactoryImpl MMDEVAPI_CF[] = { |
| { { &MMCF_Vtbl }, &CLSID_MMDeviceEnumerator, (FnCreateInstance)MMDevEnum_Create } |
| }; |
| |
| HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) |
| { |
| unsigned int i = 0; |
| TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); |
| |
| if(!init_driver()){ |
| ERR("Driver initialization failed\n"); |
| return E_FAIL; |
| } |
| |
| if (ppv == NULL) { |
| WARN("invalid parameter\n"); |
| return E_INVALIDARG; |
| } |
| |
| *ppv = NULL; |
| |
| if (!IsEqualIID(riid, &IID_IClassFactory) && |
| !IsEqualIID(riid, &IID_IUnknown)) { |
| WARN("no interface for %s\n", debugstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| for (i = 0; i < sizeof(MMDEVAPI_CF)/sizeof(MMDEVAPI_CF[0]); ++i) |
| { |
| if (IsEqualGUID(rclsid, MMDEVAPI_CF[i].rclsid)) { |
| IClassFactory_AddRef(&MMDEVAPI_CF[i].IClassFactory_iface); |
| *ppv = &MMDEVAPI_CF[i]; |
| return S_OK; |
| } |
| } |
| |
| WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid), |
| debugstr_guid(riid), ppv); |
| return CLASS_E_CLASSNOTAVAILABLE; |
| } |
| |
| /*********************************************************************** |
| * DllRegisterServer (MMDEVAPI.@) |
| */ |
| HRESULT WINAPI DllRegisterServer(void) |
| { |
| return __wine_register_resources( instance ); |
| } |
| |
| /*********************************************************************** |
| * DllUnregisterServer (MMDEVAPI.@) |
| */ |
| HRESULT WINAPI DllUnregisterServer(void) |
| { |
| return __wine_unregister_resources( instance ); |
| } |