|  | /* IDirectMusicLoader8 Implementation | 
|  | * | 
|  | * Copyright (C) 2003 Rok Mandeljc | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License as published by | 
|  | * the Free Software Foundation; either version 2 of the License, or | 
|  | * (at your option) any later version. | 
|  | * | 
|  | * This program 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 Library General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU General Public License | 
|  | * along with this program; if not, write to the Free Software | 
|  | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winuser.h" | 
|  | #include "wingdi.h" | 
|  | #include "wine/debug.h" | 
|  | #include "wine/unicode.h" | 
|  | #include "winreg.h" | 
|  |  | 
|  | #include "dmloader_private.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(dmloader); | 
|  |  | 
|  | HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]); | 
|  |  | 
|  | /* IDirectMusicLoader8 IUnknown part: */ | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_QueryInterface (LPDIRECTMUSICLOADER8 iface, REFIID riid, LPVOID *ppobj) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | if (IsEqualIID (riid, &IID_IUnknown) || | 
|  | IsEqualIID (riid, &IID_IDirectMusicLoader) || | 
|  | IsEqualIID (riid, &IID_IDirectMusicLoader8)) { | 
|  | IDirectMusicLoader8Impl_AddRef(iface); | 
|  | *ppobj = This; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppobj); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | ULONG WINAPI IDirectMusicLoader8Impl_AddRef (LPDIRECTMUSICLOADER8 iface) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  | TRACE("(%p) : AddRef from %ld\n", This, This->ref); | 
|  | return ++(This->ref); | 
|  | } | 
|  |  | 
|  | ULONG WINAPI IDirectMusicLoader8Impl_Release (LPDIRECTMUSICLOADER8 iface) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  | ULONG ref = --This->ref; | 
|  | TRACE("(%p) : ReleaseRef to %ld\n", This, This->ref); | 
|  | if (ref == 0) { | 
|  | HeapFree(GetProcessHeap(), 0, This); | 
|  | } | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | /* IDirectMusicLoader8 IDirectMusicLoader part: */ | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_GetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc, REFIID riid, LPVOID* ppv) | 
|  | { | 
|  | IDirectMusicObject* pObject; | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  | HRESULT result; | 
|  | struct list *listEntry; | 
|  | DMUS_PRIVATE_CACHE_ENTRY *cacheEntry; | 
|  | LPDMUS_PRIVATE_CACHE_ENTRY newEntry; | 
|  |  | 
|  | TRACE("(%p, %p(dwValidData:0x%08lx), %s, %p)\n", This, pDesc, pDesc->dwValidData, debugstr_guid(riid), ppv); | 
|  |  | 
|  | TRACE("looking up cache...\n"); | 
|  |  | 
|  | LIST_FOR_EACH (listEntry, &This->CacheList) { | 
|  | cacheEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry ); | 
|  |  | 
|  | if ((pDesc->dwValidData & DMUS_OBJ_OBJECT) || (pDesc->dwValidData & DMUS_OBJ_FILENAME)) { | 
|  | if (pDesc->dwValidData & DMUS_OBJ_OBJECT) { | 
|  | if (IsEqualGUID (&cacheEntry->guidObject, &pDesc->guidObject)) { | 
|  | TRACE(": found it by GUID\n"); | 
|  | if (cacheEntry->pObject) | 
|  | return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT) cacheEntry->pObject, riid, ppv); | 
|  | } | 
|  | } | 
|  | if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { | 
|  | if (cacheEntry->wzFileName && !strncmpW (pDesc->wszFileName, cacheEntry->wzFileName, MAX_PATH)) { | 
|  | TRACE(": found it by FileName\n"); | 
|  | if (cacheEntry->pObject) | 
|  | return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT) cacheEntry->pObject, riid, ppv); | 
|  | } | 
|  | } | 
|  | } else if (pDesc->dwValidData & DMUS_OBJ_NAME) { | 
|  | /** | 
|  | * Usually search by name (for example main procedure name for scripts) after containers loading | 
|  | * TODO: container loading code | 
|  | */ | 
|  | TRACE(" comparing %s with cached %s (file:%s)\n", debugstr_w (pDesc->wszName), debugstr_w (cacheEntry->wzName), debugstr_w(cacheEntry->wzFileName)); | 
|  | if (cacheEntry->wzName && !strncmpW (pDesc->wszName, cacheEntry->wzName, 256)) { | 
|  | TRACE(": found it by Name\n"); | 
|  | if (NULL != cacheEntry->pObject) | 
|  | return IDirectMusicObject_QueryInterface ((LPDIRECTMUSICOBJECT) cacheEntry->pObject, riid, ppv); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* object doesn't exist in cache... guess we'll have to load it */ | 
|  | TRACE(": object does not exist in cache\n"); | 
|  | if (pDesc->dwValidData & DMUS_OBJ_LOADED) { | 
|  | ERR("Wanted a on-memory (cached) entry, but not found. Active Hack (waiting for Load code)\n"); | 
|  | /* ugly hack waiting for Load impl */ | 
|  | result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*) &pObject); | 
|  | if (SUCCEEDED(result)) { | 
|  | /* add object to cache */ | 
|  | result = IDirectMusicObject_QueryInterface (pObject, riid, ppv); | 
|  | if (SUCCEEDED(result)) { | 
|  | newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY)); | 
|  | if (pDesc->dwValidData & DMUS_OBJ_NAME) | 
|  | strncpyW (newEntry->wzName, pDesc->wszName, 256); | 
|  | newEntry->pObject = pObject; | 
|  | list_add_tail (&This->CacheList, &newEntry->entry); | 
|  | TRACE(": filled in cache entry\n"); | 
|  | } else { | 
|  | IDirectMusicObject_Release(pObject); | 
|  | } | 
|  | } | 
|  | return result; | 
|  | /* | 
|  | * Normal code | 
|  | *ppv = NULL; | 
|  | return E_FAIL; | 
|  | */ | 
|  | } | 
|  | if (!(pDesc->dwValidData & DMUS_OBJ_CLASS)) { | 
|  | WARN("guidClass not valid but needed. What they want to do ?\n"); | 
|  | *ppv = NULL; | 
|  | return DMUS_E_LOADER_NOCLASSID; | 
|  | } | 
|  | result = CoCreateInstance (&pDesc->guidClass, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicObject, (LPVOID*) &pObject); | 
|  | if (FAILED(result)) return result; | 
|  | if (pDesc->dwValidData & DMUS_OBJ_FILENAME) { | 
|  | /* load object from file */ | 
|  | WCHAR wzFileName[MAX_PATH]; | 
|  | ILoaderStream* pStream; | 
|  | IPersistStream* pPersistStream = NULL; | 
|  | /* if it's full path, don't add search directory path, otherwise do */ | 
|  | if (pDesc->dwValidData & DMUS_OBJ_FULLPATH) { | 
|  | lstrcpyW(wzFileName, pDesc->wszFileName); | 
|  | } else { | 
|  | WCHAR *p; | 
|  | lstrcpyW(wzFileName, This->wzSearchPath); | 
|  | p = wzFileName + lstrlenW(wzFileName); | 
|  | if (p > wzFileName && p[-1] != '\\') *p++ = '\\'; | 
|  | strcpyW(p, pDesc->wszFileName); | 
|  | } | 
|  | TRACE(": loading from file (%s)\n", debugstr_w(wzFileName)); | 
|  |  | 
|  | result = DMUSIC_CreateLoaderStream ((LPSTREAM*) &pStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER) iface); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = IPersistStream_Load (pPersistStream, (LPSTREAM) pStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | ILoaderStream_IStream_Release ((LPSTREAM) pStream); | 
|  | IPersistStream_Release (pPersistStream); | 
|  | } else if (pDesc->dwValidData & DMUS_OBJ_STREAM) { | 
|  | /* load object from stream */ | 
|  | IStream* pClonedStream = NULL; | 
|  | IPersistStream* pPersistStream = NULL; | 
|  |  | 
|  | TRACE(": loading from stream\n"); | 
|  | result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = IStream_Clone (pDesc->pStream, &pClonedStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = IPersistStream_Load (pPersistStream, pClonedStream); | 
|  | if (FAILED(result))	return result; | 
|  |  | 
|  | IPersistStream_Release (pPersistStream); | 
|  | IStream_Release (pClonedStream); | 
|  | } else if (pDesc->dwValidData & DMUS_OBJ_OBJECT) { | 
|  | /* load object by GUID */ | 
|  | TRACE(": loading by GUID (only default DLS supported)\n"); | 
|  | if (IsEqualGUID (&pDesc->guidObject, &GUID_DefaultGMCollection)) { | 
|  | WCHAR wzFileName[MAX_PATH]; | 
|  | IPersistStream *pPersistStream = NULL; | 
|  | ILoaderStream* pStream; | 
|  | if (FAILED(DMUSIC_GetDefaultGMPath (wzFileName))) | 
|  | return E_FAIL; | 
|  | /* load object from file */ | 
|  | result = DMUSIC_CreateLoaderStream ((LPSTREAM*) &pStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = ILoaderStream_Attach (pStream, wzFileName, (LPDIRECTMUSICLOADER) iface); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = IDirectMusicObject_QueryInterface (pObject, &IID_IPersistStream, (LPVOID*) &pPersistStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | result = IPersistStream_Load (pPersistStream, (LPSTREAM) pStream); | 
|  | if (FAILED(result)) return result; | 
|  |  | 
|  | ILoaderStream_IStream_Release ((LPSTREAM) pStream); | 
|  | IPersistStream_Release (pPersistStream); | 
|  | } else { | 
|  | return E_FAIL; | 
|  | } | 
|  | } else { | 
|  | /* nowhere to load from */ | 
|  | FIXME(": unknown/unsupported way of loading\n"); | 
|  | return E_FAIL; | 
|  | } | 
|  | /* add object to cache */ | 
|  | newEntry = (LPDMUS_PRIVATE_CACHE_ENTRY) HeapAlloc (GetProcessHeap (), HEAP_ZERO_MEMORY, sizeof(DMUS_PRIVATE_CACHE_ENTRY)); | 
|  | if (pDesc->dwValidData & DMUS_OBJ_OBJECT) | 
|  | memcpy (&newEntry->guidObject, &pDesc->guidObject, sizeof (pDesc->guidObject)); | 
|  | if (pDesc->dwValidData & DMUS_OBJ_FILENAME) | 
|  | strncpyW (newEntry->wzFileName, pDesc->wszFileName, MAX_PATH); | 
|  | if (pObject) | 
|  | newEntry->pObject = pObject; | 
|  | list_add_tail (&This->CacheList, &newEntry->entry); | 
|  | TRACE(": filled in cache entry\n"); | 
|  |  | 
|  | /* for debug purposes (e.g. to check if all files are cached) */ | 
|  | #if 0 | 
|  | int i = 0; | 
|  | LIST_FOR_EACH (listEntry, &This->CacheList) { | 
|  | i++; | 
|  | cacheEntry = LIST_ENTRY( listEntry, DMUS_PRIVATE_CACHE_ENTRY, entry ); | 
|  | TRACE("Entry nr. %i: GUID = %s, FileName = %s\n", i, debugstr_guid (&cacheEntry->guidObject), debugstr_w (cacheEntry->wzFileName)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | return IDirectMusicObject_QueryInterface (pObject, riid, ppv); | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_SetObject (LPDIRECTMUSICLOADER8 iface, LPDMUS_OBJECTDESC pDesc) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %p): stub\n", This, pDesc); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_SetSearchDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzPath, BOOL fClear) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | TRACE("(%p, %s, %s, %d)\n", This, debugstr_guid(rguidClass), debugstr_w(pwzPath), fClear); | 
|  | if (0 == strncmpW(This->wzSearchPath, pwzPath, MAX_PATH)) { | 
|  | return S_FALSE; | 
|  | } | 
|  | strncpyW(This->wzSearchPath, pwzPath, MAX_PATH); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_ScanDirectory (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, WCHAR* pwzFileExtension, WCHAR* pwzScanFileName) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %s, %p, %p): stub\n", This, debugstr_guid(rguidClass), pwzFileExtension, pwzScanFileName); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_CacheObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %p): stub\n", This, pObject); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObject (LPDIRECTMUSICLOADER8 iface, IDirectMusicObject* pObject) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %p): stub\n", This, pObject); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_ClearCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %s): stub\n", This, debugstr_guid(rguidClass)); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_EnableCache (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, BOOL fEnable) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %s, %d): stub\n", This, debugstr_guid(rguidClass), fEnable); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_EnumObject (LPDIRECTMUSICLOADER8 iface, REFGUID rguidClass, DWORD dwIndex, LPDMUS_OBJECTDESC pDesc) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %s, %ld, %p): stub\n", This, debugstr_guid(rguidClass), dwIndex, pDesc); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /* IDirectMusicLoader8 Interface part follow: */ | 
|  | void WINAPI IDirectMusicLoader8Impl_CollectGarbage (LPDIRECTMUSICLOADER8 iface) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p): stub\n", This); | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_ReleaseObjectByUnknown (LPDIRECTMUSICLOADER8 iface, IUnknown* pObject) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  |  | 
|  | FIXME("(%p, %p): stub\n", This, pObject); | 
|  |  | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | HRESULT WINAPI IDirectMusicLoader8Impl_LoadObjectFromFile (LPDIRECTMUSICLOADER8 iface, | 
|  | REFGUID rguidClassID, | 
|  | REFIID iidInterfaceID, | 
|  | WCHAR* pwzFilePath, | 
|  | void** ppObject) | 
|  | { | 
|  | ICOM_THIS(IDirectMusicLoader8Impl,iface); | 
|  | DMUS_OBJECTDESC ObjDesc; | 
|  |  | 
|  | TRACE("(%p, %s, %s, %s, %p): wrapping to IDirectMusicLoader8Impl_GetObject\n", This, debugstr_guid(rguidClassID), debugstr_guid(iidInterfaceID), debugstr_w(pwzFilePath), ppObject); | 
|  |  | 
|  | ObjDesc.dwSize = sizeof(DMUS_OBJECTDESC); | 
|  | ObjDesc.dwValidData = DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH | DMUS_OBJ_CLASS; /* I believe I've read somewhere in MSDN that this function requires either full path or relative path */ | 
|  | ObjDesc.guidClass = *rguidClassID; | 
|  | /* OK, MSDN says that search order is the following: | 
|  | - current directory (DONE) | 
|  | - windows search path (FIXME: how do I get that?) | 
|  | - loader's search path (DONE) | 
|  | */ | 
|  | /* search in current directory */ | 
|  | if (!SearchPathW (NULL, pwzFilePath, NULL, | 
|  | sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL) && | 
|  | /* search in loader's search path */ | 
|  | !SearchPathW (This->wzSearchPath, pwzFilePath, NULL, | 
|  | sizeof(ObjDesc.wszFileName)/sizeof(WCHAR), ObjDesc.wszFileName, NULL)) | 
|  | { | 
|  | /* cannot find file */ | 
|  | TRACE("cannot find file\n"); | 
|  | return DMUS_E_LOADER_FAILEDOPEN; | 
|  | } | 
|  |  | 
|  | TRACE("full file path = %s\n", debugstr_w (ObjDesc.wszFileName)); | 
|  |  | 
|  | return IDirectMusicLoader8Impl_GetObject (iface, &ObjDesc, iidInterfaceID, ppObject); | 
|  | } | 
|  |  | 
|  | ICOM_VTABLE(IDirectMusicLoader8) DirectMusicLoader8_Vtbl = | 
|  | { | 
|  | ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE | 
|  | IDirectMusicLoader8Impl_QueryInterface, | 
|  | IDirectMusicLoader8Impl_AddRef, | 
|  | IDirectMusicLoader8Impl_Release, | 
|  | IDirectMusicLoader8Impl_GetObject, | 
|  | IDirectMusicLoader8Impl_SetObject, | 
|  | IDirectMusicLoader8Impl_SetSearchDirectory, | 
|  | IDirectMusicLoader8Impl_ScanDirectory, | 
|  | IDirectMusicLoader8Impl_CacheObject, | 
|  | IDirectMusicLoader8Impl_ReleaseObject, | 
|  | IDirectMusicLoader8Impl_ClearCache, | 
|  | IDirectMusicLoader8Impl_EnableCache, | 
|  | IDirectMusicLoader8Impl_EnumObject, | 
|  | IDirectMusicLoader8Impl_CollectGarbage, | 
|  | IDirectMusicLoader8Impl_ReleaseObjectByUnknown, | 
|  | IDirectMusicLoader8Impl_LoadObjectFromFile | 
|  | }; | 
|  |  | 
|  | /* for ClassFactory */ | 
|  | HRESULT WINAPI DMUSIC_CreateDirectMusicLoader (LPCGUID lpcGUID, LPDIRECTMUSICLOADER8 *ppDMLoad, LPUNKNOWN pUnkOuter) | 
|  | { | 
|  | IDirectMusicLoader8Impl *dmloader; | 
|  |  | 
|  | TRACE("(%p,%p,%p)\n",lpcGUID, ppDMLoad, pUnkOuter); | 
|  | if (IsEqualIID (lpcGUID, &IID_IDirectMusicLoader) || | 
|  | IsEqualIID (lpcGUID, &IID_IDirectMusicLoader8)) { | 
|  | dmloader = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusicLoader8Impl)); | 
|  | if (NULL == dmloader) { | 
|  | *ppDMLoad = (LPDIRECTMUSICLOADER8)NULL; | 
|  | return E_OUTOFMEMORY; | 
|  | } | 
|  | dmloader->lpVtbl = &DirectMusicLoader8_Vtbl; | 
|  | dmloader->ref = 1; | 
|  | MultiByteToWideChar (CP_ACP, 0, ".\\", -1, dmloader->wzSearchPath, MAX_PATH); | 
|  | list_init (&dmloader->CacheList); | 
|  | *ppDMLoad = (LPDIRECTMUSICLOADER8)dmloader; | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | WARN("No interface found\n"); | 
|  | return E_NOINTERFACE; | 
|  | } | 
|  |  | 
|  | /* help function for IDirectMusicLoader8Impl_GetObject */ | 
|  | HRESULT WINAPI DMUSIC_GetDefaultGMPath (WCHAR wszPath[MAX_PATH]) | 
|  | { | 
|  | HKEY hkDM; | 
|  | DWORD returnType, sizeOfReturnBuffer = MAX_PATH; | 
|  | char szPath[MAX_PATH]; | 
|  |  | 
|  | if ((RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic" , 0, KEY_READ, &hkDM) != ERROR_SUCCESS) || | 
|  | (RegQueryValueExA (hkDM, "GMFilePath", NULL, &returnType, szPath, &sizeOfReturnBuffer) != ERROR_SUCCESS)) { | 
|  | WARN(": registry entry missing\n" ); | 
|  | return E_FAIL; | 
|  | } | 
|  | /* FIXME: Check return types to ensure we're interpreting data right */ | 
|  | MultiByteToWideChar (CP_ACP, 0, szPath, -1, wszPath, MAX_PATH); | 
|  |  | 
|  | return S_OK; | 
|  | } |