shlwapi: Allow SHOpenRegStream2 A/W to work with not existing values.
[wine] / dlls / shlwapi / regstream.c
1 /*
2  * SHLWAPI Registry Stream functions
3  *
4  * Copyright 1999 Juergen Schmied
5  * Copyright 2002 Jon Griffiths
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #include <stdarg.h>
23 #include <string.h>
24
25 #define COBJMACROS
26
27 #include "winerror.h"
28 #include "windef.h"
29 #include "winbase.h"
30 #include "objbase.h"
31 #include "winreg.h"
32 #include "shlwapi.h"
33
34 #include "wine/debug.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(shell);
37
38 typedef struct
39 {
40         const IStreamVtbl *lpVtbl;
41         LONG   ref;
42         HKEY   hKey;
43         LPBYTE pbBuffer;
44         DWORD  dwLength;
45         DWORD  dwPos;
46 } ISHRegStream;
47
48 /**************************************************************************
49 *  IStream_fnQueryInterface
50 */
51 static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, REFIID riid, LPVOID *ppvObj)
52 {
53         ISHRegStream *This = (ISHRegStream *)iface;
54
55         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
56
57         *ppvObj = NULL;
58
59         if(IsEqualIID(riid, &IID_IUnknown))     /*IUnknown*/
60           *ppvObj = This;
61         else if(IsEqualIID(riid, &IID_IStream)) /*IStream*/
62           *ppvObj = This;
63
64         if(*ppvObj)
65         {
66           IStream_AddRef((IStream*)*ppvObj);
67           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
68           return S_OK;
69         }
70         TRACE("-- Interface: E_NOINTERFACE\n");
71         return E_NOINTERFACE;
72 }
73
74 /**************************************************************************
75 *  IStream_fnAddRef
76 */
77 static ULONG WINAPI IStream_fnAddRef(IStream *iface)
78 {
79         ISHRegStream *This = (ISHRegStream *)iface;
80         ULONG refCount = InterlockedIncrement(&This->ref);
81         
82         TRACE("(%p)->(ref before=%u)\n",This, refCount - 1);
83
84         return refCount;
85 }
86
87 /**************************************************************************
88 *  IStream_fnRelease
89 */
90 static ULONG WINAPI IStream_fnRelease(IStream *iface)
91 {
92         ISHRegStream *This = (ISHRegStream *)iface;
93         ULONG refCount = InterlockedDecrement(&This->ref);
94
95         TRACE("(%p)->(ref before=%u)\n",This, refCount + 1);
96
97         if (!refCount)
98         {
99           TRACE(" destroying SHReg IStream (%p)\n",This);
100
101           HeapFree(GetProcessHeap(),0,This->pbBuffer);
102
103           if (This->hKey)
104             RegCloseKey(This->hKey);
105
106           HeapFree(GetProcessHeap(),0,This);
107           return 0;
108         }
109
110         return refCount;
111 }
112
113 /**************************************************************************
114  * IStream_fnRead
115  */
116 static HRESULT WINAPI IStream_fnRead (IStream * iface, void* pv, ULONG cb, ULONG* pcbRead)
117 {
118         ISHRegStream *This = (ISHRegStream *)iface;
119         DWORD dwBytesToRead;
120
121         TRACE("(%p)->(%p,0x%08x,%p)\n",This, pv, cb, pcbRead);
122
123         if (This->dwPos >= This->dwLength)
124           dwBytesToRead = 0;
125         else
126           dwBytesToRead = This->dwLength - This->dwPos;
127
128         dwBytesToRead = (cb > dwBytesToRead) ? dwBytesToRead : cb;
129         if (dwBytesToRead != 0) /* not at end of buffer and we want to read something */
130         {
131           memmove(pv, This->pbBuffer + This->dwPos, dwBytesToRead);
132           This->dwPos += dwBytesToRead; /* adjust pointer */
133         }
134
135         if (pcbRead)
136           *pcbRead = dwBytesToRead;
137
138         return S_OK;
139 }
140
141 /**************************************************************************
142  * IStream_fnWrite
143  */
144 static HRESULT WINAPI IStream_fnWrite (IStream * iface, const void* pv, ULONG cb, ULONG* pcbWritten)
145 {
146         ISHRegStream *This = (ISHRegStream *)iface;
147         DWORD newLen = This->dwPos + cb;
148
149         TRACE("(%p, %p, %d, %p)\n",This, pv, cb, pcbWritten);
150
151         if (newLen < This->dwPos) /* overflow */
152           return STG_E_INSUFFICIENTMEMORY;
153
154         if (newLen > This->dwLength)
155         {
156           LPBYTE newBuf = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pbBuffer, newLen);
157           if (!newBuf)
158             return STG_E_INSUFFICIENTMEMORY;
159
160           This->dwLength = newLen;
161           This->pbBuffer = newBuf;
162         }
163         memmove(This->pbBuffer + This->dwPos, pv, cb);
164         This->dwPos += cb; /* adjust pointer */
165
166         if (pcbWritten)
167           *pcbWritten = cb;
168
169         return S_OK;
170 }
171
172 /**************************************************************************
173  *  IStream_fnSeek
174  */
175 static HRESULT WINAPI IStream_fnSeek (IStream * iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
176 {
177         ISHRegStream *This = (ISHRegStream *)iface;
178         LARGE_INTEGER tmp;
179         TRACE("(%p, %s, %d %p)\n", This,
180               wine_dbgstr_longlong(dlibMove.QuadPart), dwOrigin, plibNewPosition);
181
182         if (dwOrigin == STREAM_SEEK_SET)
183           tmp = dlibMove;
184         else if (dwOrigin == STREAM_SEEK_CUR)
185           tmp.QuadPart = This->dwPos + dlibMove.QuadPart;
186         else if (dwOrigin == STREAM_SEEK_END)
187           tmp.QuadPart = This->dwLength + dlibMove.QuadPart;
188         else
189           return STG_E_INVALIDPARAMETER;
190
191         if (tmp.QuadPart < 0)
192           return STG_E_INVALIDFUNCTION;
193
194         /* we cut off the high part here */
195         This->dwPos = tmp.LowPart;
196
197         if (plibNewPosition)
198           plibNewPosition->QuadPart = This->dwPos;
199         return S_OK;
200 }
201
202 /**************************************************************************
203  * IStream_fnSetSize
204  */
205 static HRESULT WINAPI IStream_fnSetSize (IStream * iface, ULARGE_INTEGER libNewSize)
206 {
207         ISHRegStream *This = (ISHRegStream *)iface;
208         DWORD newLen;
209         LPBYTE newBuf;
210
211         TRACE("(%p, %s)\n", This, wine_dbgstr_longlong(libNewSize.QuadPart));
212
213         /* we cut off the high part here */
214         newLen = libNewSize.LowPart;
215         newBuf = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->pbBuffer, newLen);
216         if (!newBuf)
217           return STG_E_INSUFFICIENTMEMORY;
218
219         This->pbBuffer = newBuf;
220         This->dwLength = newLen;
221
222         return S_OK;
223 }
224
225 /**************************************************************************
226  * IStream_fnCopyTo
227  */
228 static HRESULT WINAPI IStream_fnCopyTo (IStream * iface, IStream* pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten)
229 {
230         ISHRegStream *This = (ISHRegStream *)iface;
231
232         TRACE("(%p)\n",This);
233         if (pcbRead)
234           pcbRead->QuadPart = 0;
235         if (pcbWritten)
236           pcbWritten->QuadPart = 0;
237
238         /* TODO implement */
239         return E_NOTIMPL;
240 }
241
242 /**************************************************************************
243  * IStream_fnCommit
244  */
245 static HRESULT WINAPI IStream_fnCommit (IStream * iface, DWORD grfCommitFlags)
246 {
247         ISHRegStream *This = (ISHRegStream *)iface;
248
249         TRACE("(%p)\n",This);
250
251         /* commit not supported by this stream */
252         return E_NOTIMPL;
253 }
254
255 /**************************************************************************
256  * IStream_fnRevert
257  */
258 static HRESULT WINAPI IStream_fnRevert (IStream * iface)
259 {
260         ISHRegStream *This = (ISHRegStream *)iface;
261
262         TRACE("(%p)\n",This);
263
264         /* revert not supported by this stream */
265         return E_NOTIMPL;
266 }
267
268 /**************************************************************************
269  * IStream_fnLockUnlockRegion
270  */
271 static HRESULT WINAPI IStream_fnLockUnlockRegion (IStream * iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
272 {
273         ISHRegStream *This = (ISHRegStream *)iface;
274
275         TRACE("(%p)\n",This);
276
277         /* lock/unlock not supported by this stream */
278         return E_NOTIMPL;
279 }
280
281 /*************************************************************************
282  * IStream_fnStat
283  */
284 static HRESULT WINAPI IStream_fnStat (IStream * iface, STATSTG* pstatstg, DWORD grfStatFlag)
285 {
286         ISHRegStream *This = (ISHRegStream *)iface;
287
288         TRACE("(%p, %p, %d)\n",This,pstatstg,grfStatFlag);
289
290         pstatstg->pwcsName = NULL;
291         pstatstg->type = STGTY_STREAM;
292         pstatstg->cbSize.QuadPart = This->dwLength;
293         pstatstg->mtime.dwHighDateTime = 0;
294         pstatstg->mtime.dwLowDateTime = 0;
295         pstatstg->ctime.dwHighDateTime = 0;
296         pstatstg->ctime.dwLowDateTime = 0;
297         pstatstg->atime.dwHighDateTime = 0;
298         pstatstg->atime.dwLowDateTime = 0;
299         pstatstg->grfMode = STGM_READWRITE;
300         pstatstg->grfLocksSupported = 0;
301         pstatstg->clsid = CLSID_NULL;
302         pstatstg->grfStateBits = 0;
303         pstatstg->reserved = 0;
304
305         return S_OK;
306 }
307
308 /*************************************************************************
309  * IStream_fnClone
310  */
311 static HRESULT WINAPI IStream_fnClone (IStream * iface, IStream** ppstm)
312 {
313         ISHRegStream *This = (ISHRegStream *)iface;
314
315         TRACE("(%p)\n",This);
316         *ppstm = NULL;
317
318         /* clone not supported by this stream */
319         return E_NOTIMPL;
320 }
321
322 static const IStreamVtbl rstvt =
323 {
324         IStream_fnQueryInterface,
325         IStream_fnAddRef,
326         IStream_fnRelease,
327         IStream_fnRead,
328         IStream_fnWrite,
329         IStream_fnSeek,
330         IStream_fnSetSize,
331         IStream_fnCopyTo,
332         IStream_fnCommit,
333         IStream_fnRevert,
334         IStream_fnLockUnlockRegion,
335         IStream_fnLockUnlockRegion,
336         IStream_fnStat,
337         IStream_fnClone
338 };
339
340 /* Methods overridden by the dummy stream */
341
342 /**************************************************************************
343  *  IStream_fnAddRefDummy
344  */
345 static ULONG WINAPI IStream_fnAddRefDummy(IStream *iface)
346 {
347         ISHRegStream *This = (ISHRegStream *)iface;
348         TRACE("(%p)\n", This);
349         return 2;
350 }
351
352 /**************************************************************************
353  *  IStream_fnReleaseDummy
354  */
355 static ULONG WINAPI IStream_fnReleaseDummy(IStream *iface)
356 {
357         ISHRegStream *This = (ISHRegStream *)iface;
358         TRACE("(%p)\n", This);
359         return 1;
360 }
361
362 /**************************************************************************
363  * IStream_fnReadDummy
364  */
365 static HRESULT WINAPI IStream_fnReadDummy(IStream *iface, LPVOID pv, ULONG cb, ULONG* pcbRead)
366 {
367   if (pcbRead)
368     *pcbRead = 0;
369   return E_NOTIMPL;
370 }
371
372 static const IStreamVtbl DummyRegStreamVTable =
373 {
374   IStream_fnQueryInterface,
375   IStream_fnAddRefDummy,  /* Overridden */
376   IStream_fnReleaseDummy, /* Overridden */
377   IStream_fnReadDummy,    /* Overridden */
378   IStream_fnWrite,
379   IStream_fnSeek,
380   IStream_fnSetSize,
381   IStream_fnCopyTo,
382   IStream_fnCommit,
383   IStream_fnRevert,
384   IStream_fnLockUnlockRegion,
385   IStream_fnLockUnlockRegion,
386   IStream_fnStat,
387   IStream_fnClone
388 };
389
390 /* Dummy registry stream object */
391 static ISHRegStream rsDummyRegStream =
392 {
393  &DummyRegStreamVTable,
394  1,
395  NULL,
396  NULL,
397  0,
398  0
399 };
400
401 /**************************************************************************
402  * IStream_Create
403  *
404  * Internal helper: Create and initialise a new registry stream object.
405  */
406 static IStream *IStream_Create(HKEY hKey, LPBYTE pbBuffer, DWORD dwLength)
407 {
408  ISHRegStream* regStream;
409
410  regStream = HeapAlloc(GetProcessHeap(), 0, sizeof(ISHRegStream));
411
412  if (regStream)
413  {
414    regStream->lpVtbl = &rstvt;
415    regStream->ref = 1;
416    regStream->hKey = hKey;
417    regStream->pbBuffer = pbBuffer;
418    regStream->dwLength = dwLength;
419    regStream->dwPos = 0;
420  }
421  TRACE ("Returning %p\n", regStream);
422  return (IStream *)regStream;
423 }
424
425 /*************************************************************************
426  * SHOpenRegStream2A    [SHLWAPI.@]
427  *
428  * Create a stream to read binary registry data.
429  *
430  * PARAMS
431  * hKey      [I] Registry handle
432  * pszSubkey [I] The sub key name
433  * pszValue  [I] The value name under the sub key
434  * dwMode    [I] Unused
435  *
436  * RETURNS
437  * Success: An IStream interface referring to the registry data
438  * Failure: NULL, if the registry key could not be opened or is not binary.
439  */
440 IStream * WINAPI SHOpenRegStream2A(HKEY hKey, LPCSTR pszSubkey,
441                                    LPCSTR pszValue,DWORD dwMode)
442 {
443   IStream *tmp;
444   HKEY hStrKey = NULL;
445   LPBYTE lpBuff = NULL;
446   DWORD dwLength = 0;
447   LONG ret;
448
449   TRACE("(%p,%s,%s,0x%08x)\n", hKey, pszSubkey, pszValue, dwMode);
450
451   if (dwMode == STGM_READ)
452     ret = RegOpenKeyExA(hKey, pszSubkey, 0, KEY_READ, &hStrKey);
453   else /* in write mode we make sure the subkey exits */
454     ret = RegCreateKeyExA(hKey, pszSubkey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hStrKey, NULL);
455
456   if (ret == ERROR_SUCCESS)
457   {
458     if (dwMode == STGM_READ || dwMode == STGM_READWRITE)
459     {
460       /* read initial data */
461       ret = RegQueryValueExA(hStrKey, pszValue, 0, 0, 0, &dwLength);
462       if (ret == ERROR_SUCCESS && dwLength)
463       {
464         lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength);
465         RegQueryValueExA(hStrKey, pszValue, 0, 0, lpBuff, &dwLength);
466       }
467     }
468
469     if (!dwLength)
470       lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength);
471
472     tmp = IStream_Create(hStrKey, lpBuff, dwLength);
473
474     return tmp;
475   }
476
477   HeapFree(GetProcessHeap(), 0, lpBuff);
478   if (hStrKey)
479     RegCloseKey(hStrKey);
480   return NULL;
481 }
482
483 /*************************************************************************
484  * SHOpenRegStream2W    [SHLWAPI.@]
485  *
486  * See SHOpenRegStream2A.
487  */
488 IStream * WINAPI SHOpenRegStream2W(HKEY hKey, LPCWSTR pszSubkey,
489                                    LPCWSTR pszValue, DWORD dwMode)
490 {
491   IStream *tmp;
492   HKEY hStrKey = NULL;
493   LPBYTE lpBuff = NULL;
494   DWORD dwLength = 0;
495   LONG ret;
496
497   TRACE("(%p,%s,%s,0x%08x)\n", hKey, debugstr_w(pszSubkey),
498         debugstr_w(pszValue), dwMode);
499
500   if (dwMode == STGM_READ)
501     ret = RegOpenKeyExW(hKey, pszSubkey, 0, KEY_READ, &hStrKey);
502   else /* in write mode we make sure the subkey exits */
503     ret = RegCreateKeyExW(hKey, pszSubkey, 0, NULL, 0, KEY_READ | KEY_WRITE, NULL, &hStrKey, NULL);
504
505   if (ret == ERROR_SUCCESS)
506   {
507     if (dwMode == STGM_READ || dwMode == STGM_READWRITE)
508     {
509       /* read initial data */
510       ret = RegQueryValueExW(hStrKey, pszValue, 0, 0, 0, &dwLength);
511       if (ret == ERROR_SUCCESS && dwLength)
512       {
513         lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength);
514         RegQueryValueExW(hStrKey, pszValue, 0, 0, lpBuff, &dwLength);
515       }
516     }
517
518     if (!dwLength)
519       lpBuff = HeapAlloc(GetProcessHeap(), 0, dwLength);
520
521     tmp = IStream_Create(hStrKey, lpBuff, dwLength);
522
523     return tmp;
524   }
525
526   HeapFree(GetProcessHeap(), 0, lpBuff);
527   if (hStrKey)
528     RegCloseKey(hStrKey);
529   return NULL;
530 }
531
532 /*************************************************************************
533  * SHOpenRegStreamA     [SHLWAPI.@]
534  *
535  * Create a stream to read binary registry data.
536  *
537  * PARAMS
538  * hKey      [I] Registry handle
539  * pszSubkey [I] The sub key name
540  * pszValue  [I] The value name under the sub key
541  * dwMode    [I] STGM mode for opening the file
542  *
543  * RETURNS
544  * Success: An IStream interface referring to the registry data
545  * Failure: If the registry key could not be opened or is not binary,
546  *          A dummy (empty) IStream object is returned.
547  */
548 IStream * WINAPI SHOpenRegStreamA(HKEY hkey, LPCSTR pszSubkey,
549                                   LPCSTR pszValue, DWORD dwMode)
550 {
551   IStream *iStream;
552
553   TRACE("(%p,%s,%s,0x%08x)\n", hkey, pszSubkey, pszValue, dwMode);
554
555   iStream = SHOpenRegStream2A(hkey, pszSubkey, pszValue, dwMode);
556   return iStream ? iStream : (IStream *)&rsDummyRegStream;
557 }
558
559 /*************************************************************************
560  * SHOpenRegStreamW     [SHLWAPI.@]
561  *
562  * See SHOpenRegStreamA.
563  */
564 IStream * WINAPI SHOpenRegStreamW(HKEY hkey, LPCWSTR pszSubkey,
565                                   LPCWSTR pszValue, DWORD dwMode)
566 {
567   IStream *iStream;
568
569   TRACE("(%p,%s,%s,0x%08x)\n", hkey, debugstr_w(pszSubkey),
570         debugstr_w(pszValue), dwMode);
571   iStream = SHOpenRegStream2W(hkey, pszSubkey, pszValue, dwMode);
572   return iStream ? iStream : (IStream *)&rsDummyRegStream;
573 }
574
575 /*************************************************************************
576  * @   [SHLWAPI.12]
577  *
578  * Create an IStream object on a block of memory.
579  *
580  * PARAMS
581  * lpbData   [I] Memory block to create the IStream object on
582  * dwDataLen [I] Length of data block
583  *
584  * RETURNS
585  * Success: A pointer to the IStream object.
586  * Failure: NULL, if any parameters are invalid or an error occurs.
587  *
588  * NOTES
589  *  A copy of the memory pointed to by lpbData is made, and is freed
590  *  when the stream is released.
591  */
592 IStream * WINAPI SHCreateMemStream(const BYTE *lpbData, UINT dwDataLen)
593 {
594   IStream *iStrmRet = NULL;
595   LPBYTE lpbDup;
596
597   TRACE("(%p,%d)\n", lpbData, dwDataLen);
598
599   if (!lpbData)
600     dwDataLen = 0;
601
602   lpbDup = HeapAlloc(GetProcessHeap(), 0, dwDataLen);
603
604   if (lpbDup)
605   {
606     memcpy(lpbDup, lpbData, dwDataLen);
607     iStrmRet = IStream_Create(NULL, lpbDup, dwDataLen);
608
609     if (!iStrmRet)
610       HeapFree(GetProcessHeap(), 0, lpbDup);
611   }
612   return iStrmRet;
613 }
614
615 /*************************************************************************
616  * SHCreateStreamWrapper   [SHLWAPI.@]
617  *
618  * Create an IStream object on a block of memory.
619  *
620  * PARAMS
621  * lpbData    [I] Memory block to create the IStream object on
622  * dwDataLen  [I] Length of data block
623  * dwReserved [I] Reserved, Must be 0.
624  * lppStream  [O] Destination for IStream object
625  *
626  * RETURNS
627  * Success: S_OK. lppStream contains the new IStream object.
628  * Failure: E_INVALIDARG, if any parameters are invalid,
629  *          E_OUTOFMEMORY if memory allocation fails.
630  *
631  * NOTES
632  *  The stream assumes ownership of the memory passed to it.
633  */
634 HRESULT WINAPI SHCreateStreamWrapper(LPBYTE lpbData, DWORD dwDataLen,
635                                      DWORD dwReserved, IStream **lppStream)
636 {
637   IStream* lpStream;
638
639   if (lppStream)
640     *lppStream = NULL;
641
642   if(dwReserved || !lppStream)
643     return E_INVALIDARG;
644
645   lpStream = IStream_Create(NULL, lpbData, dwDataLen);
646
647   if(!lpStream)
648     return E_OUTOFMEMORY;
649
650   IStream_QueryInterface(lpStream, &IID_IStream, (void**)lppStream);
651   IStream_Release(lpStream);
652   return S_OK;
653 }