From 9259eba707a529c1a8873bc1eff1a6a9f4a75683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9=20van=20Geldorp?= Date: Sat, 28 May 2005 21:44:21 +0000 Subject: [PATCH] Alexandre Julliard - Added rules for building import libraries in the individual dll makefiles, and added support for building a .def.a static import library too. Troy Rollo - Implement URLMonikerImpl_BindToStorage. - Correct bug truncating downloaded files to 4096 bytes. Jacek Caban - Separated IBinding and IMoniker interfaces. Mike McCormack - Add the missing interface method PromptAction to IInternetZoneManager. - Stub implementation for GetSoftwareUpdateInfo. Francois Gouget - Update win32.api to match the current sources. - Declare CoGetClassObjectFromURL() in urlmon.h. Christian Costa - Added stub implementation for CoGetClassObjectFromURL. svn path=/trunk/; revision=15625 --- reactos/lib/urlmon/Makefile.in | 2 + reactos/lib/urlmon/sec_mgr.c | 17 + reactos/lib/urlmon/umon.c | 700 ++++++++++++++++++++++--------- reactos/lib/urlmon/umstream.c | 380 +++++++++++++++++ reactos/lib/urlmon/urlmon.spec | 2 +- reactos/lib/urlmon/urlmon.xml | 1 + reactos/lib/urlmon/urlmon_main.c | 14 + reactos/lib/urlmon/urlmon_main.h | 13 + 8 files changed, 926 insertions(+), 203 deletions(-) create mode 100644 reactos/lib/urlmon/umstream.c diff --git a/reactos/lib/urlmon/Makefile.in b/reactos/lib/urlmon/Makefile.in index 95aaaf5ba9a..d5aa7c7031c 100644 --- a/reactos/lib/urlmon/Makefile.in +++ b/reactos/lib/urlmon/Makefile.in @@ -3,6 +3,7 @@ TOPOBJDIR = ../.. SRCDIR = @srcdir@ VPATH = @srcdir@ MODULE = urlmon.dll +IMPORTLIB = liburlmon.$(IMPLIBEXT) IMPORTS = cabinet ole32 shlwapi wininet user32 advapi32 kernel32 ntdll EXTRALIBS = -luuid @@ -10,6 +11,7 @@ C_SRCS = \ regsvr.c \ sec_mgr.c \ umon.c \ + umstream.c \ urlmon_main.c SUBDIRS = tests diff --git a/reactos/lib/urlmon/sec_mgr.c b/reactos/lib/urlmon/sec_mgr.c index 99162c40002..ed5577300b4 100644 --- a/reactos/lib/urlmon/sec_mgr.c +++ b/reactos/lib/urlmon/sec_mgr.c @@ -352,6 +352,21 @@ static HRESULT WINAPI ZoneMgrImpl_SetZoneActionPolicy(IInternetZoneManager* ifac return E_NOTIMPL; } +/******************************************************************** + * IInternetZoneManager_PromptAction + */ +static HRESULT WINAPI ZoneMgrImpl_PromptAction(IInternetZoneManager* iface, + DWORD dwAction, + HWND hwndParent, + LPCWSTR pwszUrl, + LPCWSTR pwszText, + DWORD dwPromptFlags) +{ + FIXME("%p %08lx %p %s %s %08lx\n", iface, dwAction, hwndParent, + debugstr_w(pwszUrl), debugstr_w(pwszText), dwPromptFlags ); + return E_NOTIMPL; +} + /******************************************************************** * IInternetZoneManager_LogAction */ @@ -425,12 +440,14 @@ static IInternetZoneManagerVtbl ZoneMgrImplVtbl = { ZoneMgrImpl_SetZoneCustomPolicy, ZoneMgrImpl_GetZoneActionPolicy, ZoneMgrImpl_SetZoneActionPolicy, + ZoneMgrImpl_PromptAction, ZoneMgrImpl_LogAction, ZoneMgrImpl_CreateZoneEnumerator, ZoneMgrImpl_GetZoneAt, ZoneMgrImpl_DestroyZoneEnumerator, ZoneMgrImpl_CopyTemplatePoliciesToZone, }; + HRESULT ZoneMgrImpl_Construct(IUnknown *pUnkOuter, LPVOID *ppobj) { ZoneMgrImpl* ret = HeapAlloc(GetProcessHeap(), 0, sizeof(ZoneMgrImpl)); diff --git a/reactos/lib/urlmon/umon.c b/reactos/lib/urlmon/umon.c index 7436ed21c25..d8d85e39c03 100644 --- a/reactos/lib/urlmon/umon.c +++ b/reactos/lib/urlmon/umon.c @@ -48,19 +48,252 @@ static const WCHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e', /*static BOOL registered_wndclass = FALSE;*/ -/* filemoniker data structure */ -typedef struct URLMonikerImpl{ +typedef struct { + IBindingVtbl *lpVtbl; - IMonikerVtbl* lpvtbl1; /* VTable relative to the IMoniker interface.*/ - IBindingVtbl* lpvtbl2; /* VTable to IBinding interface */ + ULONG ref; - ULONG ref; /* reference counter for this object */ - - LPOLESTR URLName; /* URL string identified by this URLmoniker */ + LPWSTR URLName; HWND hwndCallback; IBindCtx *pBC; HINTERNET hinternet, hconnect, hrequest; + HANDLE hCacheFile; + IUMCacheStream *pstrCache; + IBindStatusCallback *pbscb; + DWORD total_read, expected_size; +} Binding; + +static HRESULT WINAPI Binding_QueryInterface(IBinding* iface, REFIID riid, void **ppvObject) +{ + Binding *This = (Binding*)iface; + + TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppvObject); + + if((This == NULL) || (ppvObject == NULL)) + return E_INVALIDARG; + + if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IBinding, riid)) { + *ppvObject = iface; + IBinding_AddRef(iface); + return S_OK; + } + + *ppvObject = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI Binding_AddRef(IBinding* iface) +{ + Binding *This = (Binding*)iface; + ULONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%ld\n", This, ref); + + return ref; +} + +static ULONG WINAPI Binding_Release(IBinding* iface) +{ + Binding *This = (Binding*)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%ld\n",This, ref); + + if(!ref) { + HeapFree(GetProcessHeap(), 0, This->URLName); + if (This->hCacheFile) + CloseHandle(This->hCacheFile); + if (This->pstrCache) + { + UMCloseCacheFileStream(This->pstrCache); + IStream_Release((IStream *)This->pstrCache); + } + if (This->pbscb) + IBindStatusCallback_Release(This->pbscb); + + HeapFree(GetProcessHeap(), 0, This); + + URLMON_UnlockModule(); + } + + return ref; +} + +static HRESULT WINAPI Binding_Abort(IBinding* iface) +{ + Binding *This = (Binding*)iface; + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI Binding_GetBindResult(IBinding* iface, CLSID* pclsidProtocol, DWORD* pdwResult, LPOLESTR* pszResult, DWORD* pdwReserved) +{ + Binding *This = (Binding*)iface; + + FIXME("(%p)->(%p, %p, %p, %p): stub\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved); + + return E_NOTIMPL; +} + +static HRESULT WINAPI Binding_GetPriority(IBinding* iface, LONG* pnPriority) +{ + Binding *This = (Binding*)iface; + + FIXME("(%p)->(%p): stub\n", This, pnPriority); + + return E_NOTIMPL; +} + +static HRESULT WINAPI Binding_Resume(IBinding* iface) +{ + Binding *This = (Binding*)iface; + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI Binding_SetPriority(IBinding* iface, LONG nPriority) +{ + Binding *This = (Binding*)iface; + + FIXME("(%p)->(%ld): stub\n", This, nPriority); + + return E_NOTIMPL; +} + +static HRESULT WINAPI Binding_Suspend(IBinding* iface) +{ + Binding *This = (Binding*)iface; + + FIXME("(%p): stub\n", This); + + return E_NOTIMPL; +} + +static void Binding_CloseCacheDownload(Binding *This) +{ + CloseHandle(This->hCacheFile); + This->hCacheFile = 0; + UMCloseCacheFileStream(This->pstrCache); + IStream_Release((IStream *)This->pstrCache); + This->pstrCache = 0; +} + +static HRESULT Binding_MoreCacheData(Binding *This, char *buf, DWORD dwBytes) +{ + DWORD written; + + if (WriteFile(This->hCacheFile, buf, dwBytes, &written, NULL) && written == dwBytes) + { + HRESULT hr; + + This->total_read += written; + hr = IBindStatusCallback_OnProgress(This->pbscb, + This->total_read + written, + This->expected_size, + (This->total_read == written) ? + BINDSTATUS_BEGINDOWNLOADDATA : + BINDSTATUS_DOWNLOADINGDATA, + NULL); + if (!hr) + { + STGMEDIUM stg; + FORMATETC fmt; + + fmt.cfFormat = 0; + fmt.ptd = NULL; + fmt.dwAspect = 0; + fmt.lindex = -1; + fmt.tymed = TYMED_ISTREAM; + + stg.tymed = TYMED_ISTREAM; + stg.u.pstm = (IStream *)This->pstrCache; + stg.pUnkForRelease = NULL; + + hr = IBindStatusCallback_OnDataAvailable(This->pbscb, + (This->total_read == written) ? + BSCF_FIRSTDATANOTIFICATION : + BSCF_INTERMEDIATEDATANOTIFICATION, + This->total_read + written, + &fmt, + &stg); + } + if (written < dwBytes) + return STG_E_MEDIUMFULL; + else + return hr; + } + return HRESULT_FROM_WIN32(GetLastError()); +} + +static void Binding_FinishedDownload(Binding *This, HRESULT hr) +{ + STGMEDIUM stg; + FORMATETC fmt; + + fmt.ptd = NULL; + fmt.dwAspect = 0; + fmt.lindex = -1; + fmt.tymed = TYMED_ISTREAM; + + stg.tymed = TYMED_ISTREAM; + stg.u.pstm = (IStream *)This->pstrCache; + stg.pUnkForRelease = NULL; + + IBindStatusCallback_OnProgress(This->pbscb, This->total_read, This->expected_size, BINDSTATUS_ENDDOWNLOADDATA, NULL); + IBindStatusCallback_OnDataAvailable(This->pbscb, BSCF_LASTDATANOTIFICATION, This->total_read, &fmt, &stg); + if (hr) + { + WCHAR *pwchError = 0; + + FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, (DWORD) hr, + 0, (LPWSTR) &pwchError, + 0, NULL); + if (!pwchError) + { + static WCHAR achFormat[] = { '%', '0', '8', 'x', 0 }; + + pwchError =(WCHAR *) LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * 9); + wsprintfW(pwchError, achFormat, hr); + } + IBindStatusCallback_OnStopBinding(This->pbscb, hr, pwchError); + LocalFree(pwchError); + } + else + { + IBindStatusCallback_OnStopBinding(This->pbscb, hr, NULL); + } + IBindStatusCallback_Release(This->pbscb); + This->pbscb = 0; +} + +static IBindingVtbl BindingVtbl = +{ + Binding_QueryInterface, + Binding_AddRef, + Binding_Release, + Binding_Abort, + Binding_Suspend, + Binding_Resume, + Binding_SetPriority, + Binding_GetPriority, + Binding_GetBindResult +}; + +/* filemoniker data structure */ +typedef struct { + + IMonikerVtbl* lpvtbl; /* VTable relative to the IMoniker interface.*/ + + ULONG ref; /* reference counter for this object */ + + LPOLESTR URLName; /* URL string identified by this URLmoniker */ } URLMonikerImpl; /******************************************************************************* @@ -133,6 +366,7 @@ static ULONG WINAPI URLMonikerImpl_Release(IMoniker* iface) return refCount; } + /****************************************************************************** * URLMoniker_GetClassID ******************************************************************************/ @@ -310,6 +544,8 @@ static void CALLBACK URLMON_InternetCallback(HINTERNET hinet, /*DWORD_PTR*/ DWOR return; } #endif + + /****************************************************************************** * URLMoniker_BindToStorage ******************************************************************************/ @@ -321,12 +557,12 @@ static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface, { URLMonikerImpl *This = (URLMonikerImpl *)iface; HRESULT hres; - IBindStatusCallback *pbscb; BINDINFO bi; DWORD bindf; - IStream *pstr; + WCHAR szFileName[MAX_PATH + 1]; + Binding *bind; + int len; - FIXME("(%p)->(%p,%p,%s,%p): stub\n",This,pbc,pmkToLeft,debugstr_guid(riid),ppvObject); if(pmkToLeft) { FIXME("pmkToLeft != NULL\n"); return E_NOTIMPL; @@ -336,122 +572,288 @@ static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface, return E_NOTIMPL; } - /* FIXME This is a bad hack (tm). We should clearly download to a temporary file. - We also need to implement IStream ourselves so that IStream_Read can return - E_PENDING */ + bind = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(Binding)); + bind->lpVtbl = &BindingVtbl; + bind->ref = 1; + URLMON_LockModule(); - hres = CreateStreamOnHGlobal(0, TRUE, &pstr); + len = lstrlenW(This->URLName)+1; + bind->URLName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)); + memcpy(bind->URLName, This->URLName, len*sizeof(WCHAR)); + + hres = UMCreateStreamOnCacheFile(bind->URLName, 0, szFileName, &bind->hCacheFile, &bind->pstrCache); if(SUCCEEDED(hres)) { - TRACE("Created dummy stream...\n"); + TRACE("Created stream...\n"); - hres = IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown**)&pbscb); + *ppvObject = (void *) bind->pstrCache; + IStream_AddRef((IStream *) bind->pstrCache); + + hres = IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown**)&bind->pbscb); if(SUCCEEDED(hres)) { TRACE("Got IBindStatusCallback...\n"); memset(&bi, 0, sizeof(bi)); bi.cbSize = sizeof(bi); bindf = 0; - hres = IBindStatusCallback_GetBindInfo(pbscb, &bindf, &bi); + hres = IBindStatusCallback_GetBindInfo(bind->pbscb, &bindf, &bi); if(SUCCEEDED(hres)) { + WCHAR *urlcopy, *tmpwc; URL_COMPONENTSW url; - WCHAR *host, *path; - DWORD len, lensz = sizeof(len), total_read = 0; - LARGE_INTEGER last_read_pos; - FORMATETC fmt; - STGMEDIUM stg; + WCHAR *host, *path, *user, *pass; + DWORD lensz = sizeof(bind->expected_size); + DWORD dwService = 0; + BOOL bSuccess; TRACE("got bindinfo. bindf = %08lx extrainfo = %s bindinfof = %08lx bindverb = %08lx iid %s\n", bindf, debugstr_w(bi.szExtraInfo), bi.grfBindInfoF, bi.dwBindVerb, debugstr_guid(&bi.iid)); - hres = IBindStatusCallback_OnStartBinding(pbscb, 0, (IBinding*)&This->lpvtbl2); + hres = IBindStatusCallback_OnStartBinding(bind->pbscb, 0, (IBinding*)bind); TRACE("OnStartBinding rets %08lx\n", hres); -#if 0 - if(!registered_wndclass) { - WNDCLASSA urlmon_wndclass = {0, URLMON_WndProc,0, 0, URLMON_hInstance, 0, 0, 0, NULL, "URLMON_Callback_Window_Class"}; - RegisterClassA(&urlmon_wndclass); - registered_wndclass = TRUE; - } + /* This class will accept URLs with the backslash in them. But InternetCrackURL will not - it + * requires forward slashes (this is the behaviour of Microsoft's INETAPI). So we need to make + * a copy of the URL here and change the backslash to a forward slash everywhere it appears - + * but only before any '#' or '?', after which backslash should be left alone. + */ + urlcopy = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(bind->URLName) + 1)); + lstrcpyW(urlcopy, bind->URLName); + for (tmpwc = urlcopy; *tmpwc && *tmpwc != '#' && *tmpwc != '?'; ++tmpwc) + if (*tmpwc == '\\') + *tmpwc = '/'; - This->hwndCallback = CreateWindowA("URLMON_Callback_Window_Class", NULL, 0, 0, 0, 0, 0, 0, 0, - URLMON_hInstance, NULL); +#if 0 + if(!registered_wndclass) { + WNDCLASSA urlmon_wndclass = {0, URLMON_WndProc,0, 0, URLMON_hInstance, 0, 0, 0, NULL, "URLMON_Callback_Window_Class"}; + RegisterClassA(&urlmon_wndclass); + registered_wndclass = TRUE; + } + + This->hwndCallback = CreateWindowA("URLMON_Callback_Window_Class", NULL, 0, 0, 0, 0, 0, 0, 0, + URLMON_hInstance, NULL); #endif + bind->expected_size = 0; + bind->total_read = 0; + memset(&url, 0, sizeof(url)); url.dwStructSize = sizeof(url); - url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = 1; - InternetCrackUrlW(This->URLName, 0, 0, &url); + url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength = url.dwPasswordLength = 1; + InternetCrackUrlW(urlcopy, 0, 0, &url); host = HeapAlloc(GetProcessHeap(), 0, (url.dwHostNameLength + 1) * sizeof(WCHAR)); memcpy(host, url.lpszHostName, url.dwHostNameLength * sizeof(WCHAR)); host[url.dwHostNameLength] = '\0'; path = HeapAlloc(GetProcessHeap(), 0, (url.dwUrlPathLength + 1) * sizeof(WCHAR)); memcpy(path, url.lpszUrlPath, url.dwUrlPathLength * sizeof(WCHAR)); path[url.dwUrlPathLength] = '\0'; - - This->hinternet = InternetOpenA("User Agent", 0, NULL, NULL, 0 /*INTERNET_FLAG_ASYNC*/); -/* InternetSetStatusCallback(This->hinternet, URLMON_InternetCallback);*/ - - This->hconnect = InternetConnectW(This->hinternet, host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, - INTERNET_SERVICE_HTTP, 0, (DWORD)This); - This->hrequest = HttpOpenRequestW(This->hconnect, NULL, path, NULL, NULL, NULL, 0, (DWORD)This); - - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, 0x22, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, BINDSTATUS_FINDINGRESOURCE, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, BINDSTATUS_CONNECTING, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, BINDSTATUS_SENDINGREQUEST, NULL); - hres = E_OUTOFMEMORY; /* FIXME */ - if(HttpSendRequestW(This->hrequest, NULL, 0, NULL, 0)) { - len = 0; - HttpQueryInfoW(This->hrequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &len, &lensz, NULL); - - TRACE("res = %ld gle = %08lx url len = %ld\n", hres, GetLastError(), len); - - last_read_pos.u.LowPart = last_read_pos.u.HighPart = 0; - fmt.cfFormat = 0; - fmt.ptd = NULL; - fmt.dwAspect = 0; - fmt.lindex = -1; - fmt.tymed = TYMED_ISTREAM; - stg.tymed = TYMED_ISTREAM; - stg.u.pstm = pstr; - stg.pUnkForRelease = NULL; - - while(1) { - char buf[4096]; - DWORD bufread; - DWORD written; - if(InternetReadFile(This->hrequest, buf, sizeof(buf), &bufread)) { - TRACE("read %ld bytes %s...\n", bufread, debugstr_an(buf, 10)); - if(bufread == 0) break; - IStream_Write(pstr, buf, bufread, &written); - total_read += bufread; - IStream_Seek(pstr, last_read_pos, STREAM_SEEK_SET, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, total_read, len, (total_read == bufread) ? - BINDSTATUS_BEGINDOWNLOADDATA : - BINDSTATUS_DOWNLOADINGDATA, NULL); - hres = IBindStatusCallback_OnDataAvailable(pbscb, - (total_read == bufread) ? BSCF_FIRSTDATANOTIFICATION : - BSCF_INTERMEDIATEDATANOTIFICATION, - total_read, &fmt, &stg); - last_read_pos.u.LowPart += bufread; /* FIXME */ - } else - break; - } - hres = IBindStatusCallback_OnProgress(pbscb, total_read, len, BINDSTATUS_ENDDOWNLOADDATA, NULL); - hres = IBindStatusCallback_OnDataAvailable(pbscb, BSCF_LASTDATANOTIFICATION, total_read, &fmt, &stg); - TRACE("OnDataAvail rets %08lx\n", hres); - hres = IBindStatusCallback_OnStopBinding(pbscb, S_OK, NULL); - TRACE("OnStop rets %08lx\n", hres); - hres = S_OK; + if (url.dwUserNameLength) + { + user = HeapAlloc(GetProcessHeap(), 0, ((url.dwUserNameLength + 1) * sizeof(WCHAR))); + memcpy(user, url.lpszUserName, url.dwUserNameLength * sizeof(WCHAR)); + user[url.dwUserNameLength] = 0; } - InternetCloseHandle(This->hrequest); - InternetCloseHandle(This->hconnect); - InternetCloseHandle(This->hinternet); - IBindStatusCallback_Release(pbscb); + else + { + user = 0; + } + if (url.dwPasswordLength) + { + pass = HeapAlloc(GetProcessHeap(), 0, ((url.dwPasswordLength + 1) * sizeof(WCHAR))); + memcpy(pass, url.lpszPassword, url.dwPasswordLength * sizeof(WCHAR)); + pass[url.dwPasswordLength] = 0; + } + else + { + pass = 0; + } + + switch ((DWORD) url.nScheme) + { + case INTERNET_SCHEME_FTP: + case INTERNET_SCHEME_GOPHER: + case INTERNET_SCHEME_HTTP: + case INTERNET_SCHEME_HTTPS: + + bind->hinternet = InternetOpenA("User Agent", 0, NULL, NULL, 0 /*INTERNET_FLAG_ASYNC*/); +/* InternetSetStatusCallback(bind->hinternet, URLMON_InternetCallback);*/ + if (!bind->hinternet) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + break; + } + + switch ((DWORD) url.nScheme) + { + case INTERNET_SCHEME_FTP: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_FTP_PORT; + dwService = INTERNET_SERVICE_FTP; + break; + + case INTERNET_SCHEME_GOPHER: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_GOPHER_PORT; + dwService = INTERNET_SERVICE_GOPHER; + break; + + case INTERNET_SCHEME_HTTP: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_HTTP_PORT; + dwService = INTERNET_SERVICE_HTTP; + break; + + case INTERNET_SCHEME_HTTPS: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_HTTPS_PORT; + dwService = INTERNET_SERVICE_HTTP; + break; + } + + bind->hconnect = InternetConnectW(bind->hinternet, host, url.nPort, user, pass, + dwService, 0, (DWORD)bind); + if (!bind->hconnect) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(bind->hinternet); + break; + } + + hres = IBindStatusCallback_OnProgress(bind->pbscb, 0, 0, 0x22, NULL); + hres = IBindStatusCallback_OnProgress(bind->pbscb, 0, 0, BINDSTATUS_FINDINGRESOURCE, NULL); + hres = IBindStatusCallback_OnProgress(bind->pbscb, 0, 0, BINDSTATUS_CONNECTING, NULL); + hres = IBindStatusCallback_OnProgress(bind->pbscb, 0, 0, BINDSTATUS_SENDINGREQUEST, NULL); + + bSuccess = FALSE; + + switch (dwService) + { + case INTERNET_SERVICE_GOPHER: + bind->hrequest = GopherOpenFileW(bind->hconnect, + path, + 0, + INTERNET_FLAG_RELOAD, + 0); + if (bind->hrequest) + bSuccess = TRUE; + else + hres = HRESULT_FROM_WIN32(GetLastError()); + break; + + case INTERNET_SERVICE_FTP: + bind->hrequest = FtpOpenFileW(bind->hconnect, + path, + GENERIC_READ, + FTP_TRANSFER_TYPE_BINARY | + INTERNET_FLAG_TRANSFER_BINARY | + INTERNET_FLAG_RELOAD, + 0); + if (bind->hrequest) + bSuccess = TRUE; + else + hres = HRESULT_FROM_WIN32(GetLastError()); + break; + + case INTERNET_SERVICE_HTTP: + bind->hrequest = HttpOpenRequestW(bind->hconnect, NULL, path, NULL, NULL, NULL, 0, (DWORD)bind); + if (!bind->hrequest) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + } + else if (!HttpSendRequestW(bind->hrequest, NULL, 0, NULL, 0)) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + InternetCloseHandle(bind->hrequest); + } + else + { + HttpQueryInfoW(bind->hrequest, + HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, + &bind->expected_size, + &lensz, + NULL); + bSuccess = TRUE; + } + break; + } + if(bSuccess) + { + TRACE("res = %ld gle = %08lx url len = %ld\n", hres, GetLastError(), bind->expected_size); + + IBindStatusCallback_OnProgress(bind->pbscb, 0, 0, BINDSTATUS_CACHEFILENAMEAVAILABLE, szFileName); + + while(1) { + char buf[4096]; + DWORD bufread; + if(InternetReadFile(bind->hrequest, buf, sizeof(buf), &bufread)) { + TRACE("read %ld bytes %s...\n", bufread, debugstr_an(buf, 10)); + if(bufread == 0) break; + hres = Binding_MoreCacheData(bind, buf, bufread); + } else + break; + } + InternetCloseHandle(bind->hrequest); + hres = S_OK; + } + + InternetCloseHandle(bind->hconnect); + InternetCloseHandle(bind->hinternet); + break; + + case INTERNET_SCHEME_FILE: + path = bind->URLName + 5; /* Skip the "file:" part */ + if ((path[0] != '/' && path[0] != '\\') || + (path[1] != '/' && path[1] != '\\')) + { + hres = E_FAIL; + } + else + { + HANDLE h; + + path += 2; + if (path[0] == '/' || path[0] == '\\') + ++path; + h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + if (h == (HANDLE) HFILE_ERROR) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + } + else + { + char buf[4096]; + DWORD bufread; + + IBindStatusCallback_OnProgress(bind->pbscb, 0, 0, BINDSTATUS_CACHEFILENAMEAVAILABLE, szFileName); + + while (ReadFile(h, buf, sizeof(buf), &bufread, NULL) && bufread > 0) + hres = Binding_MoreCacheData(bind, buf, bufread); + + CloseHandle(h); + hres = S_OK; + } + } + + break; + + default: + FIXME("Unsupported URI scheme"); + break; + } + Binding_CloseCacheDownload(bind); + Binding_FinishedDownload(bind, hres); + + if (user) + HeapFree(GetProcessHeap(), 0, user); + if (pass) + HeapFree(GetProcessHeap(), 0, pass); + HeapFree(GetProcessHeap(), 0, path); + HeapFree(GetProcessHeap(), 0, host); + HeapFree(GetProcessHeap(), 0, urlcopy); } } } - *ppvObject = (VOID*)pstr; + + IBinding_Release((IBinding*)bind); + return hres; } @@ -698,98 +1100,6 @@ static HRESULT WINAPI URLMonikerImpl_IsSystemMoniker(IMoniker* iface,DWORD* pwdM return S_OK; } -static HRESULT WINAPI URLMonikerImpl_IBinding_QueryInterface(IBinding* iface,REFIID riid,void** ppvObject) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - - TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppvObject); - - /* Perform a sanity check on the parameters.*/ - if ( (This==0) || (ppvObject==0) ) - return E_INVALIDARG; - - /* Initialize the return parameter */ - *ppvObject = 0; - - /* Compare the riid with the interface IDs implemented by this object.*/ - if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IBinding, riid)) - *ppvObject = iface; - - /* Check that we obtained an interface.*/ - if ((*ppvObject)==0) - return E_NOINTERFACE; - - /* Query Interface always increases the reference count by one when it is successful */ - IBinding_AddRef(iface); - - return S_OK; - -} - -static ULONG WINAPI URLMonikerImpl_IBinding_AddRef(IBinding* iface) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - TRACE("(%p)\n",This); - - return URLMonikerImpl_AddRef((IMoniker*)This); -} - -static ULONG WINAPI URLMonikerImpl_IBinding_Release(IBinding* iface) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - TRACE("(%p)\n",This); - - return URLMonikerImpl_Release((IMoniker*)This); -} - -static HRESULT WINAPI URLMonikerImpl_IBinding_Abort(IBinding* iface) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - FIXME("(%p): stub\n", This); - - return E_NOTIMPL; -} - -static HRESULT WINAPI URLMonikerImpl_IBinding_GetBindResult(IBinding* iface, CLSID* pclsidProtocol, DWORD* pdwResult, LPOLESTR* pszResult, DWORD* pdwReserved) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - FIXME("(%p)->(%p, %p, %p, %p): stub\n", This, pclsidProtocol, pdwResult, pszResult, pdwReserved); - - return E_NOTIMPL; -} - -static HRESULT WINAPI URLMonikerImpl_IBinding_GetPriority(IBinding* iface, LONG* pnPriority) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - FIXME("(%p)->(%p): stub\n", This, pnPriority); - - return E_NOTIMPL; -} - -static HRESULT WINAPI URLMonikerImpl_IBinding_Resume(IBinding* iface) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - FIXME("(%p): stub\n", This); - - return E_NOTIMPL; -} - -static HRESULT WINAPI URLMonikerImpl_IBinding_SetPriority(IBinding* iface, LONG nPriority) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - FIXME("(%p)->(%ld): stub\n", This, nPriority); - - return E_NOTIMPL; -} - -static HRESULT WINAPI URLMonikerImpl_IBinding_Suspend(IBinding* iface) -{ - ICOM_THIS_MULTI(URLMonikerImpl, lpvtbl2, iface); - FIXME("(%p): stub\n", This); - - return E_NOTIMPL; -} - /********************************************************************************/ /* Virtual function table for the URLMonikerImpl class which include IPersist,*/ /* IPersistStream and IMoniker functions. */ @@ -820,19 +1130,6 @@ static IMonikerVtbl VT_URLMonikerImpl = URLMonikerImpl_IsSystemMoniker }; -static IBindingVtbl VTBinding_URLMonikerImpl = -{ - URLMonikerImpl_IBinding_QueryInterface, - URLMonikerImpl_IBinding_AddRef, - URLMonikerImpl_IBinding_Release, - URLMonikerImpl_IBinding_Abort, - URLMonikerImpl_IBinding_Suspend, - URLMonikerImpl_IBinding_Resume, - URLMonikerImpl_IBinding_SetPriority, - URLMonikerImpl_IBinding_GetPriority, - URLMonikerImpl_IBinding_GetBindResult -}; - /****************************************************************************** * URLMoniker_Construct (local function) *******************************************************************************/ @@ -845,9 +1142,8 @@ static HRESULT URLMonikerImpl_Construct(URLMonikerImpl* This, LPCOLESTR lpszLeft memset(This, 0, sizeof(*This)); /* Initialize the virtual function table. */ - This->lpvtbl1 = &VT_URLMonikerImpl; - This->lpvtbl2 = &VTBinding_URLMonikerImpl; - This->ref = 0; + This->lpvtbl = &VT_URLMonikerImpl; + This->ref = 0; if(lpszLeftURLName) { hres = UrlCombineW(lpszLeftURLName, lpszURLName, NULL, &sizeStr, 0); diff --git a/reactos/lib/urlmon/umstream.c b/reactos/lib/urlmon/umstream.c new file mode 100644 index 00000000000..da9e8f2cc65 --- /dev/null +++ b/reactos/lib/urlmon/umstream.c @@ -0,0 +1,380 @@ +/* + * Based on ../shell32/memorystream.c + * + * Copyright 1999 Juergen Schmied + * Copyright 2003 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winuser.h" +#include "objbase.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "ole2.h" +#include "urlmon.h" +#include "wininet.h" +#include "shlwapi.h" +#include "urlmon_main.h" + +WINE_DEFAULT_DEBUG_CHANNEL(urlmon); + +static const IStreamVtbl stvt; + +HRESULT UMCreateStreamOnCacheFile(LPCWSTR pszURL, + DWORD dwSize, + LPWSTR pszFileName, + HANDLE *phfile, + IUMCacheStream **ppstr) +{ + IUMCacheStream* ucstr; + HANDLE handle; + LPWSTR ext; + LPCWSTR c; + LPCWSTR eloc = 0; + HRESULT hr; + + for (c = pszURL; *c && *c != '#' && *c != '?'; ++c) + { + if (*c == '.') + eloc = c + 1; + else if (*c == '/' || *c == '\\') + eloc = 0; + } + + if (!eloc) + eloc = c; + + ext = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (c - eloc + 1)); + memcpy(ext, eloc, sizeof(WCHAR) * (c - eloc)); + ext[c - eloc] = 0; + + if(!CreateUrlCacheEntryW(pszURL, dwSize, ext, pszFileName, 0)) + hr = HRESULT_FROM_WIN32(GetLastError()); + else + hr = 0; + + HeapFree(GetProcessHeap(), 0, ext); + + if (hr) + return hr; + + TRACE("Opening %s\n", debugstr_w(pszFileName) ); + + handle = CreateFileW( pszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL ); + if( handle == INVALID_HANDLE_VALUE ) + return HRESULT_FROM_WIN32(GetLastError()); + + if (phfile) + { + /* Call CreateFileW again because we need a handle with its own file pointer, and DuplicateHandle will return + * a handle that shares its file pointer with the original. + */ + *phfile = CreateFileW( pszFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + + if (*phfile == (HANDLE) HFILE_ERROR) + { + DWORD dwError = GetLastError(); + + CloseHandle(handle); + return HRESULT_FROM_WIN32(dwError); + } + } + + ucstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,sizeof(IUMCacheStream)); + if(ucstr ) + { + ucstr->pszURL = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(WCHAR) * (lstrlenW(pszURL) + 1)); + if (ucstr->pszURL) + { + ucstr->pszFileName = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(WCHAR) * (lstrlenW(pszFileName) + 1)); + if (ucstr->pszFileName) + { + ucstr->lpVtbl=&stvt; + ucstr->ref = 1; + ucstr->handle = handle; + ucstr->closed = 0; + lstrcpyW(ucstr->pszURL, pszURL); + lstrcpyW(ucstr->pszFileName, pszFileName); + + *ppstr = ucstr; + + return S_OK; + } + HeapFree(GetProcessHeap(), 0, ucstr->pszURL); + } + HeapFree(GetProcessHeap(), 0, ucstr); + } + CloseHandle(handle); + if (phfile) + CloseHandle(*phfile); + return E_OUTOFMEMORY; +} + +void UMCloseCacheFileStream(IUMCacheStream *This) +{ + if (!This->closed) + { + FILETIME ftZero; + + ftZero.dwLowDateTime = ftZero.dwHighDateTime = 0; + + This->closed = 1; + CommitUrlCacheEntryW(This->pszURL, + This->pszFileName, + ftZero, + ftZero, + NORMAL_CACHE_ENTRY, + 0, + 0, + 0, + 0); + } +} + +/************************************************************************** +* IStream_fnQueryInterface +*/ +static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, + REFIID riid, + LPVOID *ppvObj) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj); + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_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) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + ULONG refCount = InterlockedIncrement(&This->ref); + + TRACE("(%p)->(count=%lu)\n", This, refCount - 1); + + return refCount; +} + +/************************************************************************** +* IStream_fnRelease +*/ +static ULONG WINAPI IStream_fnRelease(IStream *iface) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + ULONG refCount = InterlockedDecrement(&This->ref); + + TRACE("(%p)->(count=%lu)\n", This, refCount + 1); + + if (!refCount) + { + TRACE(" destroying UMCacheStream (%p)\n",This); + UMCloseCacheFileStream(This); + CloseHandle(This->handle); + HeapFree(GetProcessHeap(), 0, This->pszFileName); + HeapFree(GetProcessHeap(), 0, This->pszURL); + HeapFree(GetProcessHeap(),0,This); + } + return refCount; +} + +static HRESULT WINAPI IStream_fnRead (IStream * iface, + void* pv, + ULONG cb, + ULONG* pcbRead) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)->(%p,0x%08lx,%p)\n",This, pv, cb, pcbRead); + + if ( !pv ) + return STG_E_INVALIDPOINTER; + + if ( ! ReadFile( This->handle, pv, cb, pcbRead, NULL ) ) + return S_FALSE; + + if (!*pcbRead) + return This->closed ? S_FALSE : E_PENDING; + return S_OK; +} + +static HRESULT WINAPI IStream_fnWrite (IStream * iface, + const void* pv, + ULONG cb, + ULONG* pcbWritten) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI IStream_fnSeek ( IStream * iface, + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER* plibNewPosition) +{ + DWORD pos, newposlo, newposhi; + + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + pos = dlibMove.QuadPart; /* FIXME: truncates */ + newposhi = 0; + newposlo = SetFilePointer( This->handle, pos, &newposhi, dwOrigin ); + if( newposlo == INVALID_SET_FILE_POINTER ) + return E_FAIL; + + if (plibNewPosition) + plibNewPosition->QuadPart = newposlo | ( (LONGLONG)newposhi<<32); + + return S_OK; +} + +static HRESULT WINAPI IStream_fnSetSize (IStream * iface, + ULARGE_INTEGER libNewSize) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + if( ! SetFilePointer( This->handle, libNewSize.QuadPart, NULL, FILE_BEGIN ) ) + return E_FAIL; + + if( ! SetEndOfFile( This->handle ) ) + return E_FAIL; + + return S_OK; +} + +static HRESULT WINAPI IStream_fnCopyTo (IStream * iface, + IStream* pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER* pcbRead, + ULARGE_INTEGER* pcbWritten) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IStream_fnCommit (IStream * iface, + DWORD grfCommitFlags) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IStream_fnRevert (IStream * iface) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnLockRegion (IStream * iface, + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnUnlockRegion (IStream * iface, + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnStat (IStream * iface, + STATSTG* pstatstg, + DWORD grfStatFlag) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnClone (IStream * iface, + IStream** ppstm) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +static const IStreamVtbl stvt = +{ + IStream_fnQueryInterface, + IStream_fnAddRef, + IStream_fnRelease, + IStream_fnRead, + IStream_fnWrite, + IStream_fnSeek, + IStream_fnSetSize, + IStream_fnCopyTo, + IStream_fnCommit, + IStream_fnRevert, + IStream_fnLockRegion, + IStream_fnUnlockRegion, + IStream_fnStat, + IStream_fnClone + +}; diff --git a/reactos/lib/urlmon/urlmon.spec b/reactos/lib/urlmon/urlmon.spec index 0a7b1b2b70b..aaa7a37947d 100644 --- a/reactos/lib/urlmon/urlmon.spec +++ b/reactos/lib/urlmon/urlmon.spec @@ -8,7 +8,7 @@ @ stub AsyncGetClassBits @ stub AsyncInstallDistributionUnit @ stub BindAsyncMoniker -@ stub CoGetClassObjectFromURL +@ stdcall CoGetClassObjectFromURL(ptr wstr long long wstr ptr long ptr ptr ptr) @ stub CoInstall @ stdcall CoInternetCombineUrl(wstr wstr long wstr long ptr long) @ stdcall CoInternetCompareUrl(wstr wstr long) diff --git a/reactos/lib/urlmon/urlmon.xml b/reactos/lib/urlmon/urlmon.xml index 5253d0c7ed7..4a75006fc3e 100644 --- a/reactos/lib/urlmon/urlmon.xml +++ b/reactos/lib/urlmon/urlmon.xml @@ -23,6 +23,7 @@ regsvr.c sec_mgr.c umon.c + umstream.c urlmon_main.c urlmon.spec diff --git a/reactos/lib/urlmon/urlmon_main.c b/reactos/lib/urlmon/urlmon_main.c index a02ab4881a0..641df1c837b 100644 --- a/reactos/lib/urlmon/urlmon_main.c +++ b/reactos/lib/urlmon/urlmon_main.c @@ -341,3 +341,17 @@ HRESULT WINAPI FaultInIEFeature( HWND hwnd, uCLSSPEC * pClassSpec, FIXME("%p %p %p %08lx\n", hwnd, pClassSpec, pQuery, flags); return E_NOTIMPL; } + +/************************************************************************** + * CoGetClassObjectFromURL (URLMON.@) + */ +HRESULT WINAPI CoGetClassObjectFromURL( REFCLSID rclsid, LPCWSTR szCodeURL, DWORD dwFileVersionMS, + DWORD dwFileVersionLS, LPCWSTR szContentType, + LPBINDCTX pBindCtx, DWORD dwClsContext, LPVOID pvReserved, + REFIID riid, LPVOID *ppv ) +{ + FIXME("(%s %s %ld %ld %s %p %ld %p %s %p) Stub!\n", debugstr_guid(rclsid), debugstr_w(szCodeURL), + dwFileVersionMS, dwFileVersionLS, debugstr_w(szContentType), pBindCtx, dwClsContext, pvReserved, + debugstr_guid(riid), ppv); + return E_NOINTERFACE; +} diff --git a/reactos/lib/urlmon/urlmon_main.h b/reactos/lib/urlmon/urlmon_main.h index 4f9dc6e3a81..5e01edb6985 100644 --- a/reactos/lib/urlmon/urlmon_main.h +++ b/reactos/lib/urlmon/urlmon_main.h @@ -37,4 +37,17 @@ static inline void URLMON_UnlockModule() { InterlockedDecrement( &URLMON_refCoun #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) +typedef struct +{ + const IStreamVtbl *lpVtbl; + DWORD ref; + HANDLE handle; + BOOL closed; + WCHAR *pszFileName; + WCHAR *pszURL; +} IUMCacheStream; + +HRESULT UMCreateStreamOnCacheFile(LPCWSTR pszURL, DWORD dwSize, LPWSTR pszFileName, HANDLE *phfile, IUMCacheStream **ppstr); +void UMCloseCacheFileStream(IUMCacheStream *pstr); + #endif /* __WINE_URLMON_MAIN_H */