msvcrt: Implement _wsearchenv.
[wine] / dlls / urlmon / umstream.c
1 /*
2  * Based on ../shell32/memorystream.c
3  *
4  * Copyright 1999 Juergen Schmied
5  * Copyright 2003 Mike McCormack for CodeWeavers
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
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winternl.h"
30 #include "winuser.h"
31 #include "objbase.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34 #include "ole2.h"
35 #include "urlmon.h"
36 #include "wininet.h"
37 #include "shlwapi.h"
38 #include "urlmon_main.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
41
42 static const IStreamVtbl stvt;
43
44 HRESULT UMCreateStreamOnCacheFile(LPCWSTR pszURL,
45                                   DWORD dwSize,
46                                   LPWSTR pszFileName,
47                                   HANDLE *phfile,
48                                   IUMCacheStream **ppstr)
49 {
50     IUMCacheStream* ucstr;
51     HANDLE handle;
52     DWORD size;
53     LPWSTR url, c, ext = NULL;
54     HRESULT hr;
55
56     size = (strlenW(pszURL)+1)*sizeof(WCHAR);
57     url = HeapAlloc(GetProcessHeap(), 0, size);
58     memcpy(url, pszURL, size);
59
60     for (c = url; *c && *c != '#' && *c != '?'; ++c)
61     {
62         if (*c == '.')
63             ext = c+1;
64         else if(*c == '/')
65             ext = NULL;
66     }
67
68     *c = 0;
69
70     if(!CreateUrlCacheEntryW(url, dwSize, ext, pszFileName, 0))
71        hr = HRESULT_FROM_WIN32(GetLastError());
72     else
73        hr = 0;
74
75     HeapFree(GetProcessHeap(), 0, url);
76
77     if (hr)
78        return hr;
79
80     TRACE("Opening %s\n", debugstr_w(pszFileName) );
81
82     handle = CreateFileW( pszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL );
83     if( handle == INVALID_HANDLE_VALUE )
84        return HRESULT_FROM_WIN32(GetLastError());
85
86     if (phfile)
87     {
88        /* Call CreateFileW again because we need a handle with its own file pointer, and DuplicateHandle will return
89         * a handle that shares its file pointer with the original.
90         */
91            *phfile = CreateFileW( pszFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL );
92
93        if (*phfile == (HANDLE) HFILE_ERROR)
94        {
95            DWORD dwError = GetLastError();
96
97            CloseHandle(handle);
98            return HRESULT_FROM_WIN32(dwError);
99        }
100     }
101
102     ucstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,sizeof(IUMCacheStream));
103     if(ucstr )
104     {
105        ucstr->pszURL = HeapAlloc(GetProcessHeap(),
106                                  HEAP_ZERO_MEMORY,
107                                  sizeof(WCHAR) * (lstrlenW(pszURL) + 1));
108        if (ucstr->pszURL)
109        {
110             ucstr->pszFileName = HeapAlloc(GetProcessHeap(),
111                                            HEAP_ZERO_MEMORY,
112                                            sizeof(WCHAR) * (lstrlenW(pszFileName) + 1));
113            if (ucstr->pszFileName)
114            {
115               ucstr->lpVtbl=&stvt;
116               ucstr->ref = 1;
117               ucstr->handle = handle;
118               ucstr->closed = 0;
119               lstrcpyW(ucstr->pszURL, pszURL);
120               lstrcpyW(ucstr->pszFileName, pszFileName);
121
122               *ppstr = ucstr;
123
124               return S_OK;
125            }
126            HeapFree(GetProcessHeap(), 0, ucstr->pszURL);
127        }
128        HeapFree(GetProcessHeap(), 0, ucstr);
129     }
130     CloseHandle(handle);
131     if (phfile)
132        CloseHandle(*phfile);
133     return E_OUTOFMEMORY;
134 }
135
136 void UMCloseCacheFileStream(IUMCacheStream *This)
137 {
138     if (!This->closed)
139     {
140        FILETIME ftZero;
141
142        ftZero.dwLowDateTime = ftZero.dwHighDateTime = 0;
143
144        This->closed = 1;
145        CommitUrlCacheEntryW(This->pszURL,
146                             This->pszFileName,
147                             ftZero,
148                             ftZero,
149                             NORMAL_CACHE_ENTRY,
150                             0,
151                             0,
152                             0,
153                             0);
154     }
155 }
156
157 /**************************************************************************
158 *  IStream_fnQueryInterface
159 */
160 static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface,
161                                                REFIID riid,
162                                                LPVOID *ppvObj)
163 {
164     IUMCacheStream *This = (IUMCacheStream *)iface;
165
166     TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
167
168     *ppvObj = NULL;
169
170     if(IsEqualIID(riid, &IID_IUnknown) ||
171        IsEqualIID(riid, &IID_IStream))
172     {
173       *ppvObj = This;
174     }
175
176     if(*ppvObj)
177     {
178       IStream_AddRef((IStream*)*ppvObj);
179       TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
180       return S_OK;
181     }
182     TRACE("-- Interface: E_NOINTERFACE\n");
183     return E_NOINTERFACE;
184 }
185
186 /**************************************************************************
187 *  IStream_fnAddRef
188 */
189 static ULONG WINAPI IStream_fnAddRef(IStream *iface)
190 {
191     IUMCacheStream *This = (IUMCacheStream *)iface;
192     ULONG refCount = InterlockedIncrement(&This->ref);
193
194     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
195
196     return refCount;
197 }
198
199 /**************************************************************************
200 *  IStream_fnRelease
201 */
202 static ULONG WINAPI IStream_fnRelease(IStream *iface)
203 {
204     IUMCacheStream *This = (IUMCacheStream *)iface;
205     ULONG refCount = InterlockedDecrement(&This->ref);
206
207     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
208
209     if (!refCount)
210     {
211        TRACE(" destroying UMCacheStream (%p)\n",This);
212        UMCloseCacheFileStream(This);
213        CloseHandle(This->handle);
214        HeapFree(GetProcessHeap(), 0, This->pszFileName);
215        HeapFree(GetProcessHeap(), 0, This->pszURL);
216        HeapFree(GetProcessHeap(),0,This);
217     }
218     return refCount;
219 }
220
221 static HRESULT WINAPI IStream_fnRead (IStream * iface, 
222                                       void* pv,
223                                       ULONG cb,
224                                       ULONG* pcbRead)
225 {
226     ULONG dwBytesRead;
227     IUMCacheStream *This = (IUMCacheStream *)iface;
228
229     TRACE("(%p)->(%p,0x%08x,%p)\n",This, pv, cb, pcbRead);
230
231     if ( !pv )
232        return STG_E_INVALIDPOINTER;
233
234     if ( !pcbRead)
235         pcbRead = &dwBytesRead;
236
237     if ( ! ReadFile( This->handle, pv, cb, (LPDWORD)pcbRead, NULL ) )
238        return S_FALSE;
239
240     if (!*pcbRead)
241         return This->closed ? S_FALSE : E_PENDING;
242     return S_OK;
243 }
244
245 static HRESULT WINAPI IStream_fnWrite (IStream * iface,
246                                        const void* pv,
247                                        ULONG cb,
248                                        ULONG* pcbWritten)
249 {
250     return E_NOTIMPL;
251 }
252
253 static HRESULT WINAPI IStream_fnSeek (IStream * iface,
254                                    LARGE_INTEGER dlibMove,
255                                    DWORD dwOrigin,
256                                    ULARGE_INTEGER* plibNewPosition)
257 {
258     LARGE_INTEGER newpos;
259     IUMCacheStream *This = (IUMCacheStream *)iface;
260
261     TRACE("(%p)\n",This);
262
263     if (!SetFilePointerEx( This->handle, dlibMove, &newpos, dwOrigin ))
264        return E_FAIL;
265
266     if (plibNewPosition)
267         plibNewPosition->QuadPart = newpos.QuadPart;
268
269     return S_OK;
270 }
271
272 static HRESULT WINAPI IStream_fnSetSize (IStream * iface,
273                                          ULARGE_INTEGER libNewSize)
274 {
275     LARGE_INTEGER newpos;
276     IUMCacheStream *This = (IUMCacheStream *)iface;
277
278     TRACE("(%p)\n",This);
279
280     newpos.QuadPart = libNewSize.QuadPart;
281     if( ! SetFilePointerEx( This->handle, newpos, NULL, FILE_BEGIN ) )
282        return E_FAIL;
283
284     if( ! SetEndOfFile( This->handle ) )
285        return E_FAIL;
286
287     return S_OK;
288 }
289
290 static HRESULT WINAPI IStream_fnCopyTo (IStream * iface,
291                                    IStream* pstm,
292                                    ULARGE_INTEGER cb,
293                                    ULARGE_INTEGER* pcbRead,
294                                    ULARGE_INTEGER* pcbWritten)
295 {
296     IUMCacheStream *This = (IUMCacheStream *)iface;
297
298     TRACE("(%p)\n",This);
299
300     return E_NOTIMPL;
301 }
302
303 static HRESULT WINAPI IStream_fnCommit (IStream * iface,
304                                    DWORD grfCommitFlags)
305 {
306     IUMCacheStream *This = (IUMCacheStream *)iface;
307
308     TRACE("(%p)\n",This);
309
310     return E_NOTIMPL;
311 }
312
313 static HRESULT WINAPI IStream_fnRevert (IStream * iface)
314 {
315     IUMCacheStream *This = (IUMCacheStream *)iface;
316
317     TRACE("(%p)\n",This);
318
319     return E_NOTIMPL;
320 }
321 static HRESULT WINAPI IStream_fnLockRegion (IStream * iface,
322                                             ULARGE_INTEGER libOffset,
323                                             ULARGE_INTEGER cb,
324                                             DWORD dwLockType)
325 {
326     IUMCacheStream *This = (IUMCacheStream *)iface;
327
328     TRACE("(%p)\n",This);
329
330     return E_NOTIMPL;
331 }
332 static HRESULT WINAPI IStream_fnUnlockRegion (IStream * iface,
333                                               ULARGE_INTEGER libOffset,
334                                               ULARGE_INTEGER cb,
335                                               DWORD dwLockType)
336 {
337     IUMCacheStream *This = (IUMCacheStream *)iface;
338
339     TRACE("(%p)\n",This);
340
341     return E_NOTIMPL;
342 }
343 static HRESULT WINAPI IStream_fnStat (IStream * iface,
344                                       STATSTG*   pstatstg,
345                                       DWORD grfStatFlag)
346 {
347     IUMCacheStream *This = (IUMCacheStream *)iface;
348
349     TRACE("(%p)\n",This);
350
351     return E_NOTIMPL;
352 }
353 static HRESULT WINAPI IStream_fnClone (IStream * iface,
354                                        IStream** ppstm)
355 {
356     IUMCacheStream *This = (IUMCacheStream *)iface;
357
358     TRACE("(%p)\n",This);
359
360     return E_NOTIMPL;
361 }
362
363 static const IStreamVtbl stvt =
364 {
365     IStream_fnQueryInterface,
366     IStream_fnAddRef,
367     IStream_fnRelease,
368     IStream_fnRead,
369     IStream_fnWrite,
370     IStream_fnSeek,
371     IStream_fnSetSize,
372     IStream_fnCopyTo,
373     IStream_fnCommit,
374     IStream_fnRevert,
375     IStream_fnLockRegion,
376     IStream_fnUnlockRegion,
377     IStream_fnStat,
378     IStream_fnClone
379
380 };
381
382 typedef struct ProxyBindStatusCallback
383 {
384     const IBindStatusCallbackVtbl *lpVtbl;
385
386     IBindStatusCallback *pBSC;
387 } ProxyBindStatusCallback;
388
389 static HRESULT WINAPI ProxyBindStatusCallback_QueryInterface(IBindStatusCallback *iface, REFIID riid, void **ppv)
390 {
391     if (IsEqualGUID(&IID_IBindStatusCallback, riid) ||
392         IsEqualGUID(&IID_IUnknown, riid))
393     {
394         *ppv = iface;
395         IUnknown_AddRef(iface);
396         return S_OK;
397     }
398
399     *ppv = NULL;
400     return E_NOINTERFACE;
401 }
402
403 static ULONG WINAPI ProxyBindStatusCallback_AddRef(IBindStatusCallback *iface)
404 {
405     return 2;
406 }
407
408 static ULONG WINAPI ProxyBindStatusCallback_Release(IBindStatusCallback *iface)
409 {
410     return 1;
411 }
412
413 static HRESULT WINAPI ProxyBindStatusCallback_OnStartBinding(IBindStatusCallback *iface, DWORD dwReserved,
414                                                IBinding *pib)
415 {
416     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
417     return IBindStatusCallback_OnStartBinding(This->pBSC, dwReserved, pib);
418 }
419
420 static HRESULT WINAPI ProxyBindStatusCallback_GetPriority(IBindStatusCallback *iface, LONG *pnPriority)
421 {
422     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
423     return IBindStatusCallback_GetPriority(This->pBSC, pnPriority);
424 }
425
426 static HRESULT WINAPI ProxyBindStatusCallback_OnLowResource(IBindStatusCallback *iface, DWORD reserved)
427 {
428     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
429     return IBindStatusCallback_OnLowResource(This->pBSC, reserved);
430 }
431
432 static HRESULT WINAPI ProxyBindStatusCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress,
433                                            ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
434 {
435     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
436     return IBindStatusCallback_OnProgress(This->pBSC, ulProgress,
437                                           ulProgressMax, ulStatusCode,
438                                           szStatusText);
439 }
440
441 static HRESULT WINAPI ProxyBindStatusCallback_OnStopBinding(IBindStatusCallback *iface, HRESULT hresult, LPCWSTR szError)
442 {
443     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
444     return IBindStatusCallback_OnStopBinding(This->pBSC, hresult, szError);
445 }
446
447 static HRESULT WINAPI ProxyBindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
448 {
449     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
450     return IBindStatusCallback_GetBindInfo(This->pBSC, grfBINDF, pbindinfo);
451 }
452
453 static HRESULT WINAPI ProxyBindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
454                                                               DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
455 {
456     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
457     return IBindStatusCallback_OnDataAvailable(This->pBSC, grfBSCF, dwSize,
458                                                pformatetc, pstgmed);
459 }
460
461 static HRESULT WINAPI ProxyBindStatusCallback_OnObjectAvailable(IBindStatusCallback *iface, REFIID riid, IUnknown *punk)
462 {
463     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
464     return IBindStatusCallback_OnObjectAvailable(This->pBSC, riid, punk);
465 }
466
467 static HRESULT WINAPI BlockingBindStatusCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF,
468                                                                  DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed)
469 {
470     return S_OK;
471 }
472
473 static const IBindStatusCallbackVtbl BlockingBindStatusCallbackVtbl =
474 {
475     ProxyBindStatusCallback_QueryInterface,
476     ProxyBindStatusCallback_AddRef,
477     ProxyBindStatusCallback_Release,
478     ProxyBindStatusCallback_OnStartBinding,
479     ProxyBindStatusCallback_GetPriority,
480     ProxyBindStatusCallback_OnLowResource,
481     ProxyBindStatusCallback_OnProgress,
482     ProxyBindStatusCallback_OnStopBinding,
483     ProxyBindStatusCallback_GetBindInfo,
484     BlockingBindStatusCallback_OnDataAvailable,
485     ProxyBindStatusCallback_OnObjectAvailable
486 };
487
488 static HRESULT WINAPI AsyncBindStatusCallback_GetBindInfo(IBindStatusCallback *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
489 {
490     ProxyBindStatusCallback *This = (ProxyBindStatusCallback *)iface;
491     HRESULT hr = IBindStatusCallback_GetBindInfo(This->pBSC, grfBINDF, pbindinfo);
492     *grfBINDF |= BINDF_PULLDATA | BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE;
493     return hr;
494 }
495
496 static const IBindStatusCallbackVtbl AsyncBindStatusCallbackVtbl =
497 {
498     ProxyBindStatusCallback_QueryInterface,
499     ProxyBindStatusCallback_AddRef,
500     ProxyBindStatusCallback_Release,
501     ProxyBindStatusCallback_OnStartBinding,
502     ProxyBindStatusCallback_GetPriority,
503     ProxyBindStatusCallback_OnLowResource,
504     ProxyBindStatusCallback_OnProgress,
505     ProxyBindStatusCallback_OnStopBinding,
506     AsyncBindStatusCallback_GetBindInfo,
507     ProxyBindStatusCallback_OnDataAvailable,
508     ProxyBindStatusCallback_OnObjectAvailable
509 };
510
511 static HRESULT URLStartDownload(LPCWSTR szURL, LPSTREAM *ppStream, IBindStatusCallback *pBSC)
512 {
513     HRESULT hr;
514     IMoniker *pMoniker;
515     IBindCtx *pbc;
516
517     *ppStream = NULL;
518
519     hr = CreateURLMoniker(NULL, szURL, &pMoniker);
520     if (FAILED(hr))
521         return hr;
522
523     hr = CreateBindCtx(0, &pbc);
524     if (FAILED(hr))
525     {
526         IMoniker_Release(pMoniker);
527         return hr;
528     }
529
530     hr = RegisterBindStatusCallback(pbc, pBSC, NULL, 0);
531     if (FAILED(hr))
532     {
533         IBindCtx_Release(pbc);
534         IMoniker_Release(pMoniker);
535         return hr;
536     }
537
538     hr = IMoniker_BindToStorage(pMoniker, pbc, NULL, &IID_IStream, (void **)ppStream);
539
540     /* BindToStorage returning E_PENDING because it's asynchronous is not an error */
541     if (hr == E_PENDING) hr = S_OK;
542
543     IBindCtx_Release(pbc);
544     IMoniker_Release(pMoniker);
545
546     return hr;
547 }
548
549 /***********************************************************************
550  *              URLOpenBlockingStreamA (URLMON.@)
551  */
552 HRESULT WINAPI URLOpenBlockingStreamA(LPUNKNOWN pCaller, LPCSTR szURL,
553                                       LPSTREAM *ppStream, DWORD dwReserved,
554                                       LPBINDSTATUSCALLBACK lpfnCB)
555 {
556     LPWSTR szURLW;
557     int len;
558     HRESULT hr;
559
560     TRACE("(%p, %s, %p, 0x%x, %p)\n", pCaller, szURL, ppStream, dwReserved, lpfnCB);
561
562     if (!szURL || !ppStream)
563         return E_INVALIDARG;
564
565     len = MultiByteToWideChar(CP_ACP, 0, szURL, -1, NULL, 0);
566     szURLW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
567     if (!szURLW)
568     {
569         *ppStream = NULL;
570         return E_OUTOFMEMORY;
571     }
572     MultiByteToWideChar(CP_ACP, 0, szURL, -1, szURLW, len);
573
574     hr = URLOpenBlockingStreamW(pCaller, szURLW, ppStream, dwReserved, lpfnCB);
575
576     HeapFree(GetProcessHeap(), 0, szURLW);
577
578     return hr;
579 }
580
581 /***********************************************************************
582  *              URLOpenBlockingStreamW (URLMON.@)
583  */
584 HRESULT WINAPI URLOpenBlockingStreamW(LPUNKNOWN pCaller, LPCWSTR szURL,
585                                       LPSTREAM *ppStream, DWORD dwReserved,
586                                       LPBINDSTATUSCALLBACK lpfnCB)
587 {
588     ProxyBindStatusCallback blocking_bsc;
589
590     TRACE("(%p, %s, %p, 0x%x, %p)\n", pCaller, debugstr_w(szURL), ppStream,
591           dwReserved, lpfnCB);
592
593     if (!szURL || !ppStream)
594         return E_INVALIDARG;
595
596     blocking_bsc.lpVtbl = &BlockingBindStatusCallbackVtbl;
597     blocking_bsc.pBSC = lpfnCB;
598
599     return URLStartDownload(szURL, ppStream, (IBindStatusCallback *)&blocking_bsc);
600 }
601
602 /***********************************************************************
603  *              URLOpenStreamA (URLMON.@)
604  */
605 HRESULT WINAPI URLOpenStreamA(LPUNKNOWN pCaller, LPCSTR szURL, DWORD dwReserved,
606                               LPBINDSTATUSCALLBACK lpfnCB)
607 {
608     LPWSTR szURLW;
609     int len;
610     HRESULT hr;
611
612     TRACE("(%p, %s, 0x%x, %p)\n", pCaller, szURL, dwReserved, lpfnCB);
613
614     if (!szURL)
615         return E_INVALIDARG;
616
617     len = MultiByteToWideChar(CP_ACP, 0, szURL, -1, NULL, 0);
618     szURLW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
619     if (!szURLW)
620         return E_OUTOFMEMORY;
621     MultiByteToWideChar(CP_ACP, 0, szURL, -1, szURLW, len);
622
623     hr = URLOpenStreamW(pCaller, szURLW, dwReserved, lpfnCB);
624
625     HeapFree(GetProcessHeap(), 0, szURLW);
626
627     return hr;
628 }
629
630 /***********************************************************************
631  *              URLOpenStreamW (URLMON.@)
632  */
633 HRESULT WINAPI URLOpenStreamW(LPUNKNOWN pCaller, LPCWSTR szURL, DWORD dwReserved,
634                               LPBINDSTATUSCALLBACK lpfnCB)
635 {
636     HRESULT hr;
637     ProxyBindStatusCallback async_bsc;
638     IStream *pStream;
639
640     TRACE("(%p, %s, 0x%x, %p)\n", pCaller, debugstr_w(szURL), dwReserved,
641           lpfnCB);
642
643     if (!szURL)
644         return E_INVALIDARG;
645
646     async_bsc.lpVtbl = &AsyncBindStatusCallbackVtbl;
647     async_bsc.pBSC = lpfnCB;
648
649     hr = URLStartDownload(szURL, &pStream, (IBindStatusCallback *)&async_bsc);
650     if (SUCCEEDED(hr) && pStream)
651         IStream_Release(pStream);
652
653     return hr;
654 }