/* * SHLWAPI Registry Stream functions * * Copyright 1999 Juergen Schmied * Copyright 2002 Jon Griffiths * * 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 <stdarg.h> #include <string.h> #define COBJMACROS #include "winerror.h" #include "windef.h" #include "winbase.h" #include "objbase.h" #include "winreg.h" #include "shlwapi.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(shell); typedef struct { const IStreamVtbl *lpVtbl; LONG ref; HKEY hKey; LPBYTE pbBuffer; DWORD dwLength; DWORD dwPos; DWORD dwMode; union { LPSTR keyNameA; LPWSTR keyNameW; }u; BOOL bUnicode; } ISHRegStream; /************************************************************************** * IStream_fnQueryInterface */ static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj); *ppvObj = NULL; if(IsEqualIID(riid, &IID_IUnknown)) /*IUnknown*/ *ppvObj = This; else if(IsEqualIID(riid, &IID_IStream)) /*IStream*/ *ppvObj = This; if(*ppvObj) { IStream_AddRef((IStream*)*ppvObj); TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj); return S_OK; } TRACE("-- Interface: E_NOINTERFACE\n"); return E_NOINTERFACE; } /************************************************************************** * IStream_fnAddRef */ static ULONG WINAPI IStream_fnAddRef(IStream *iface) { ISHRegStream *This = (ISHRegStream *)iface; ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p)->(ref before=%u)\n",This, refCount - 1); return refCount; } /************************************************************************** * IStream_fnRelease */ static ULONG WINAPI IStream_fnRelease(IStream *iface) { ISHRegStream *This = (ISHRegStream *)iface; ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p)->(ref before=%u)\n",This, refCount + 1); if (!refCount) { TRACE(" destroying SHReg IStream (%p)\n",This); if (This->hKey) { /* write back data in REG_BINARY */ if (This->dwMode == STGM_READWRITE || This->dwMode == STGM_WRITE) { if (This->dwLength) { if (This->bUnicode) RegSetValueExW(This->hKey, This->u.keyNameW, 0, REG_BINARY, (const BYTE *) This->pbBuffer, This->dwLength); else RegSetValueExA(This->hKey, This->u.keyNameA, 0, REG_BINARY, (const BYTE *) This->pbBuffer, This->dwLength); } else { if (This->bUnicode) RegDeleteValueW(This->hKey, This->u.keyNameW); else RegDeleteValueA(This->hKey, This->u.keyNameA); } } RegCloseKey(This->hKey); } HeapFree(GetProcessHeap(),0,This->u.keyNameA); HeapFree(GetProcessHeap(),0,This->pbBuffer); HeapFree(GetProcessHeap(),0,This); return 0; } return refCount; } /************************************************************************** * IStream_fnRead */ static HRESULT WINAPI IStream_fnRead (IStream * iface, void* pv, ULONG cb, ULONG* pcbRead) { ISHRegStream *This = (ISHRegStream *)iface; DWORD dwBytesToRead; TRACE("(%p)->(%p,0x%08x,%p)\n",This, pv, cb, pcbRead); if (This->dwPos >= This->dwLength) dwBytesToRead = 0; else dwBytesToRead = This->dwLength - This->dwPos; dwBytesToRead = (cb > dwBytesToRead) ? dwBytesToRead : cb; if (dwBytesToRead != 0) /* not at end of buffer and we want to read something */ { memmove(pv, This->pbBuffer + This->dwPos, dwBytesToRead); This->dwPos += dwBytesToRead; /* adjust pointer */ } if (pcbRead) *pcbRead = dwBytesToRead; return S_OK; } /************************************************************************** * IStream_fnWrite */ static HRESULT WINAPI IStream_fnWrite (IStream * iface, const void* pv, ULONG cb, ULONG* pcbWritten) { ISHRegStream *This = (ISHRegStream *)iface; DWORD newLen = This->dwPos + cb; TRACE("(%p, %p, %d, %p)\n",This, pv, cb, pcbWritten); if (newLen < This->dwPos) /* overflow */ return STG_E_INSUFFICIENTMEMORY; if (newLen > This->dwLength) { LPBYTE newBuf = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pbBuffer, newLen); if (!newBuf) return STG_E_INSUFFICIENTMEMORY; This->dwLength = newLen; This->pbBuffer = newBuf; } memmove(This->pbBuffer + This->dwPos, pv, cb); This->dwPos += cb; /* adjust pointer */ if (pcbWritten) *pcbWritten = cb; return S_OK; } /************************************************************************** * IStream_fnSeek */ static HRESULT WINAPI IStream_fnSeek (IStream * iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition) { ISHRegStream *This = (ISHRegStream *)iface; LARGE_INTEGER tmp; TRACE("(%p, %s, %d %p)\n", This, wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition); if (dwOrigin == STREAM_SEEK_SET) tmp = dlibMove; else if (dwOrigin == STREAM_SEEK_CUR) tmp.QuadPart = This->dwPos + dlibMove.QuadPart; else if (dwOrigin == STREAM_SEEK_END) tmp.QuadPart = This->dwLength + dlibMove.QuadPart; else return STG_E_INVALIDPARAMETER; if (tmp.QuadPart < 0) return STG_E_INVALIDFUNCTION; /* we cut off the high part here */ This->dwPos = tmp.LowPart; if (plibNewPosition) plibNewPosition->QuadPart = This->dwPos; return S_OK; } /************************************************************************** * IStream_fnSetSize */ static HRESULT WINAPI IStream_fnSetSize (IStream * iface, ULARGE_INTEGER libNewSize) { ISHRegStream *This = (ISHRegStream *)iface; DWORD newLen; LPBYTE newBuf; TRACE("(%p, %s)\n", This, wine_dbgstr_longlong(libNewSize.QuadPart)); /* we cut off the high part here */ newLen = libNewSize.LowPart; newBuf = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pbBuffer, newLen); if (!newBuf) return STG_E_INSUFFICIENTMEMORY; This->pbBuffer = newBuf; This->dwLength = newLen; return S_OK; } /************************************************************************** * IStream_fnCopyTo */ static HRESULT WINAPI IStream_fnCopyTo (IStream * iface, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n",This); if (pcbRead) pcbRead->QuadPart = 0; if (pcbWritten) pcbWritten->QuadPart = 0; /* TODO implement */ return E_NOTIMPL; } /************************************************************************** * IStream_fnCommit */ static HRESULT WINAPI IStream_fnCommit (IStream * iface, DWORD grfCommitFlags) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n",This); /* commit not supported by this stream */ return E_NOTIMPL; } /************************************************************************** * IStream_fnRevert */ static HRESULT WINAPI IStream_fnRevert (IStream * iface) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n",This); /* revert not supported by this stream */ return E_NOTIMPL; } /************************************************************************** * IStream_fnLockUnlockRegion */ static HRESULT WINAPI IStream_fnLockUnlockRegion (IStream * iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n",This); /* lock/unlock not supported by this stream */ return E_NOTIMPL; } /************************************************************************* * IStream_fnStat */ static HRESULT WINAPI IStream_fnStat (IStream * iface, STATSTG* pstatstg, DWORD grfStatFlag) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p, %p, %d)\n",This,pstatstg,grfStatFlag); pstatstg->pwcsName = NULL; pstatstg->type = STGTY_STREAM; pstatstg->cbSize.QuadPart = This->dwLength; pstatstg->mtime.dwHighDateTime = 0; pstatstg->mtime.dwLowDateTime = 0; pstatstg->ctime.dwHighDateTime = 0; pstatstg->ctime.dwLowDateTime = 0; pstatstg->atime.dwHighDateTime = 0; pstatstg->atime.dwLowDateTime = 0; pstatstg->grfMode = This->dwMode; pstatstg->grfLocksSupported = 0; pstatstg->clsid = CLSID_NULL; pstatstg->grfStateBits = 0; pstatstg->reserved = 0; return S_OK; } /************************************************************************* * IStream_fnClone */ static HRESULT WINAPI IStream_fnClone (IStream * iface, IStream** ppstm) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n",This); *ppstm = NULL; /* clone not supported by this stream */ return E_NOTIMPL; } static const IStreamVtbl rstvt = { IStream_fnQueryInterface, IStream_fnAddRef, IStream_fnRelease, IStream_fnRead, IStream_fnWrite, IStream_fnSeek, IStream_fnSetSize, IStream_fnCopyTo, IStream_fnCommit, IStream_fnRevert, IStream_fnLockUnlockRegion, IStream_fnLockUnlockRegion, IStream_fnStat, IStream_fnClone }; /* Methods overridden by the dummy stream */ /************************************************************************** * IStream_fnAddRefDummy */ static ULONG WINAPI IStream_fnAddRefDummy(IStream *iface) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n", This); return 2; } /************************************************************************** * IStream_fnReleaseDummy */ static ULONG WINAPI IStream_fnReleaseDummy(IStream *iface) { ISHRegStream *This = (ISHRegStream *)iface; TRACE("(%p)\n", This); return 1; } /************************************************************************** * IStream_fnReadDummy */ static HRESULT WINAPI IStream_fnReadDummy(IStream *iface, LPVOID pv, ULONG cb, ULONG* pcbRead) { if (pcbRead) *pcbRead = 0; return E_NOTIMPL; } static const IStreamVtbl DummyRegStreamVTable = { IStream_fnQueryInterface, IStream_fnAddRefDummy, /* Overridden */ IStream_fnReleaseDummy, /* Overridden */ IStream_fnReadDummy, /* Overridden */ IStream_fnWrite, IStream_fnSeek, IStream_fnSetSize, IStream_fnCopyTo, IStream_fnCommit, IStream_fnRevert, IStream_fnLockUnlockRegion, IStream_fnLockUnlockRegion, IStream_fnStat, IStream_fnClone }; /* Dummy registry stream object */ static ISHRegStream rsDummyRegStream = { &DummyRegStreamVTable, 1, NULL, NULL, 0, 0, STGM_READWRITE, {NULL}, FALSE }; /************************************************************************** * IStream_Create * * Internal helper: Create and initialise a new registry stream object. */ static ISHRegStream *IStream_Create(HKEY hKey, LPBYTE pbBuffer, DWORD dwLength) { ISHRegStream* regStream; regStream = HeapAlloc(GetProcessHeap(), 0, sizeof(ISHRegStream)); if (regStream) { regStream->lpVtbl = &rstvt; regStream->ref = 1; regStream->hKey = hKey; regStream->pbBuffer = pbBuffer; regStream->dwLength = dwLength; regStream->dwPos = 0; regStream->dwMode = STGM_READWRITE; regStream->u.keyNameA = NULL; regStream->bUnicode = FALSE; } TRACE ("Returning %p\n", regStream); return regStream; } /************************************************************************* * SHOpenRegStream2A [SHLWAPI.@] * * Create a stream to read binary registry data. * * PARAMS * hKey [I] Registry handle * pszSubkey [I] The sub key name * pszValue [I] The value name under the sub key * dwMode [I] Unused * * RETURNS * Success: An IStream interface referring to the registry data * Failure: NULL, if the registry key could not be opened or is not binary. */ IStream * WINAPI SHOpenRegStream2A(HKEY hKey, LPCSTR pszSubkey, LPCSTR pszValue,DWORD dwMode) { ISHRegStream *tmp; HKEY hStrKey = NULL; LPBYTE lpBuff = NULL; DWORD dwLength = 0; LONG ret; TRACE("(%p,%s,%s,0x%08x)\n", hKey, pszSubkey, pszValue, dwMode); if (dwMode == STGM_READ) ret = RegOpenKeyExA(hKey, pszSubkey, 0, KEY_READ, &hStrKey); else /* in write mode we make sure the subkey exits */ ret = RegCreateKeyExA(hKey, pszSubkey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hStrKey, NULL); if (ret == ERROR_SUCCESS) { if (dwMode == STGM_READ || dwMode == STGM_READWRITE) { /* read initial data */ ret = RegQueryValueExA(hStrKey, pszValue, 0, 0, 0, &dwLength); if (ret == ERROR_SUCCESS && dwLength) { lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength); RegQueryValueExA(hStrKey, pszValue, 0, 0, lpBuff, &dwLength); } } if (!dwLength) lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength); tmp = IStream_Create(hStrKey, lpBuff, dwLength); if(tmp) { if(pszValue) { int len = lstrlenA(pszValue) + 1; tmp->u.keyNameA = HeapAlloc(GetProcessHeap(), 0, len); memcpy(tmp->u.keyNameA, pszValue, len); } tmp->dwMode = dwMode; tmp->bUnicode = FALSE; return (IStream *)tmp; } } HeapFree(GetProcessHeap(), 0, lpBuff); if (hStrKey) RegCloseKey(hStrKey); return NULL; } /************************************************************************* * SHOpenRegStream2W [SHLWAPI.@] * * See SHOpenRegStream2A. */ IStream * WINAPI SHOpenRegStream2W(HKEY hKey, LPCWSTR pszSubkey, LPCWSTR pszValue, DWORD dwMode) { ISHRegStream *tmp; HKEY hStrKey = NULL; LPBYTE lpBuff = NULL; DWORD dwLength = 0; LONG ret; TRACE("(%p,%s,%s,0x%08x)\n", hKey, debugstr_w(pszSubkey), debugstr_w(pszValue), dwMode); if (dwMode == STGM_READ) ret = RegOpenKeyExW(hKey, pszSubkey, 0, KEY_READ, &hStrKey); else /* in write mode we make sure the subkey exits */ ret = RegCreateKeyExW(hKey, pszSubkey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hStrKey, NULL); if (ret == ERROR_SUCCESS) { if (dwMode == STGM_READ || dwMode == STGM_READWRITE) { /* read initial data */ ret = RegQueryValueExW(hStrKey, pszValue, 0, 0, 0, &dwLength); if (ret == ERROR_SUCCESS && dwLength) { lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength); RegQueryValueExW(hStrKey, pszValue, 0, 0, lpBuff, &dwLength); } } if (!dwLength) lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength); tmp = IStream_Create(hStrKey, lpBuff, dwLength); if(tmp) { if(pszValue) { int len = lstrlenW(pszValue) + 1; tmp->u.keyNameW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); memcpy(tmp->u.keyNameW, pszValue, len * sizeof(WCHAR)); } tmp->dwMode = dwMode; tmp->bUnicode = TRUE; return (IStream *)tmp; } } HeapFree(GetProcessHeap(), 0, lpBuff); if (hStrKey) RegCloseKey(hStrKey); return NULL; } /************************************************************************* * SHOpenRegStreamA [SHLWAPI.@] * * Create a stream to read binary registry data. * * PARAMS * hKey [I] Registry handle * pszSubkey [I] The sub key name * pszValue [I] The value name under the sub key * dwMode [I] STGM mode for opening the file * * RETURNS * Success: An IStream interface referring to the registry data * Failure: If the registry key could not be opened or is not binary, * A dummy (empty) IStream object is returned. */ IStream * WINAPI SHOpenRegStreamA(HKEY hkey, LPCSTR pszSubkey, LPCSTR pszValue, DWORD dwMode) { IStream *iStream; TRACE("(%p,%s,%s,0x%08x)\n", hkey, pszSubkey, pszValue, dwMode); iStream = SHOpenRegStream2A(hkey, pszSubkey, pszValue, dwMode); return iStream ? iStream : (IStream *)&rsDummyRegStream; } /************************************************************************* * SHOpenRegStreamW [SHLWAPI.@] * * See SHOpenRegStreamA. */ IStream * WINAPI SHOpenRegStreamW(HKEY hkey, LPCWSTR pszSubkey, LPCWSTR pszValue, DWORD dwMode) { IStream *iStream; TRACE("(%p,%s,%s,0x%08x)\n", hkey, debugstr_w(pszSubkey), debugstr_w(pszValue), dwMode); iStream = SHOpenRegStream2W(hkey, pszSubkey, pszValue, dwMode); return iStream ? iStream : (IStream *)&rsDummyRegStream; } /************************************************************************* * @ [SHLWAPI.12] * * Create an IStream object on a block of memory. * * PARAMS * lpbData [I] Memory block to create the IStream object on * dwDataLen [I] Length of data block * * RETURNS * Success: A pointer to the IStream object. * Failure: NULL, if any parameters are invalid or an error occurs. * * NOTES * A copy of the memory pointed to by lpbData is made, and is freed * when the stream is released. */ IStream * WINAPI SHCreateMemStream(const BYTE *lpbData, UINT dwDataLen) { IStream *iStrmRet = NULL; LPBYTE lpbDup; TRACE("(%p,%d)\n", lpbData, dwDataLen); if (!lpbData) dwDataLen = 0; lpbDup = HeapAlloc(GetProcessHeap(), 0, dwDataLen); if (lpbDup) { memcpy(lpbDup, lpbData, dwDataLen); iStrmRet = (IStream *)IStream_Create(NULL, lpbDup, dwDataLen); if (!iStrmRet) HeapFree(GetProcessHeap(), 0, lpbDup); } return iStrmRet; } /************************************************************************* * SHCreateStreamWrapper [SHLWAPI.@] * * Create an IStream object on a block of memory. * * PARAMS * lpbData [I] Memory block to create the IStream object on * dwDataLen [I] Length of data block * dwReserved [I] Reserved, Must be 0. * lppStream [O] Destination for IStream object * * RETURNS * Success: S_OK. lppStream contains the new IStream object. * Failure: E_INVALIDARG, if any parameters are invalid, * E_OUTOFMEMORY if memory allocation fails. * * NOTES * The stream assumes ownership of the memory passed to it. */ HRESULT WINAPI SHCreateStreamWrapper(LPBYTE lpbData, DWORD dwDataLen, DWORD dwReserved, IStream **lppStream) { IStream* lpStream; if (lppStream) *lppStream = NULL; if(dwReserved || !lppStream) return E_INVALIDARG; lpStream = (IStream *)IStream_Create(NULL, lpbData, dwDataLen); if(!lpStream) return E_OUTOFMEMORY; IStream_QueryInterface(lpStream, &IID_IStream, (void**)lppStream); IStream_Release(lpStream); return S_OK; }