urlmon: Get rid of URLMonikerImpl_BindToStorage_hack.

This commit is contained in:
Jacek Caban 2009-03-04 19:08:20 +01:00 committed by Alexandre Julliard
parent 0da20926c3
commit 96a34b586f
3 changed files with 1 additions and 761 deletions

View File

@ -33,251 +33,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
/* native urlmon.dll uses this key, too */
static WCHAR BSCBHolder[] = { '_','B','S','C','B','_','H','o','l','d','e','r','_',0 };
/*static BOOL registered_wndclass = FALSE;*/
typedef struct {
const IBindingVtbl *lpVtbl;
LONG ref;
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=%d\n", This, ref);
return ref;
}
static ULONG WINAPI Binding_Release(IBinding* iface)
{
Binding *This = (Binding*)iface;
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) ref=%d\n",This, ref);
if(!ref) {
heap_free(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);
heap_free(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)->(%d): 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, const 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,
This->URLName);
if (hr == S_OK)
{
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, This->URLName);
IBindStatusCallback_OnDataAvailable(This->pbscb, BSCF_LASTDATANOTIFICATION, This->total_read, &fmt, &stg);
if (hr != S_OK)
{
WCHAR *pwchError = 0;
FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL, (DWORD) hr,
0, (LPWSTR) &pwchError,
0, NULL);
if (!pwchError)
{
static const 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 const 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 {
const IMonikerVtbl* lpvtbl; /* VTable relative to the IMoniker interface.*/
@ -493,158 +248,6 @@ static HRESULT WINAPI URLMonikerImpl_BindToObject(IMoniker* iface,
/******************************************************************************
* URLMoniker_BindToStorage
******************************************************************************/
static HRESULT URLMonikerImpl_BindToStorage_hack(LPCWSTR URLName, IBindCtx* pbc, VOID** ppvObject)
{
HRESULT hres;
BINDINFO bi;
DWORD bindf;
WCHAR szFileName[MAX_PATH + 1];
Binding *bind;
int len;
WARN("(%s %p %p)\n", debugstr_w(URLName), pbc, ppvObject);
bind = heap_alloc_zero(sizeof(Binding));
bind->lpVtbl = &BindingVtbl;
bind->ref = 1;
URLMON_LockModule();
len = lstrlenW(URLName)+1;
bind->URLName = heap_alloc(len*sizeof(WCHAR));
memcpy(bind->URLName, URLName, len*sizeof(WCHAR));
hres = UMCreateStreamOnCacheFile(bind->URLName, 0, szFileName, &bind->hCacheFile, &bind->pstrCache);
if(SUCCEEDED(hres)) {
TRACE("Created stream...\n");
*ppvObject = (void *) bind->pstrCache;
IStream_AddRef((IStream *) bind->pstrCache);
hres = IBindCtx_GetObjectParam(pbc, 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(bind->pbscb, &bindf, &bi);
if(SUCCEEDED(hres)) {
URL_COMPONENTSW url;
WCHAR *host, *path, *user, *pass;
TRACE("got bindinfo. bindf = %08x extrainfo = %s bindinfof = %08x bindverb = %08x iid %s\n",
bindf, debugstr_w(bi.szExtraInfo), bi.grfBindInfoF, bi.dwBindVerb, debugstr_guid(&bi.iid));
hres = IBindStatusCallback_OnStartBinding(bind->pbscb, 0, (IBinding*)bind);
TRACE("OnStartBinding rets %08x\n", hres);
bind->expected_size = 0;
bind->total_read = 0;
memset(&url, 0, sizeof(url));
url.dwStructSize = sizeof(url);
url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength = url.dwPasswordLength = 1;
InternetCrackUrlW(URLName, 0, ICU_ESCAPE, &url);
host = heap_alloc((url.dwHostNameLength + 1) * sizeof(WCHAR));
memcpy(host, url.lpszHostName, url.dwHostNameLength * sizeof(WCHAR));
host[url.dwHostNameLength] = '\0';
path = heap_alloc((url.dwUrlPathLength + 1) * sizeof(WCHAR));
memcpy(path, url.lpszUrlPath, url.dwUrlPathLength * sizeof(WCHAR));
path[url.dwUrlPathLength] = '\0';
if (url.dwUserNameLength)
{
user = heap_alloc(((url.dwUserNameLength + 1) * sizeof(WCHAR)));
memcpy(user, url.lpszUserName, url.dwUserNameLength * sizeof(WCHAR));
user[url.dwUserNameLength] = 0;
}
else
{
user = 0;
}
if (url.dwPasswordLength)
{
pass = heap_alloc(((url.dwPasswordLength + 1) * sizeof(WCHAR)));
memcpy(pass, url.lpszPassword, url.dwPasswordLength * sizeof(WCHAR));
pass[url.dwPasswordLength] = 0;
}
else
{
pass = 0;
}
do {
bind->hinternet = InternetOpenA("User Agent", 0, NULL, NULL, 0);
if (!bind->hinternet)
{
hres = HRESULT_FROM_WIN32(GetLastError());
break;
}
if (!url.nPort)
url.nPort = INTERNET_DEFAULT_GOPHER_PORT;
bind->hconnect = InternetConnectW(bind->hinternet, host, url.nPort, user, pass,
INTERNET_SERVICE_GOPHER, 0, (DWORD_PTR)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);
bind->hrequest = GopherOpenFileW(bind->hconnect,
path,
0,
INTERNET_FLAG_RELOAD,
0);
if (bind->hrequest)
{
TRACE("res = %d gle = %u url len = %d\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 %d 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;
}else {
hres = HRESULT_FROM_WIN32(GetLastError());
}
InternetCloseHandle(bind->hconnect);
InternetCloseHandle(bind->hinternet);
} while(0);
Binding_FinishedDownload(bind, hres);
Binding_CloseCacheDownload(bind);
heap_free(user);
heap_free(pass);
heap_free(path);
heap_free(host);
}
}
}
IBinding_Release((IBinding*)bind);
return hres;
}
static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface,
IBindCtx* pbc,
IMoniker* pmkToLeft,
@ -652,26 +255,12 @@ static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface,
VOID** ppvObject)
{
URLMonikerImpl *This = (URLMonikerImpl*)iface;
WCHAR schema[64];
BOOL bret;
URL_COMPONENTSW url = {sizeof(URL_COMPONENTSW), schema,
sizeof(schema)/sizeof(WCHAR), 0, NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0, NULL, 0};
TRACE("(%p)->(%p %p %s %p)\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppvObject);
if(pmkToLeft)
FIXME("Unsupported pmkToLeft\n");
bret = InternetCrackUrlW(This->URLName, 0, ICU_ESCAPE, &url);
if(!bret) {
ERR("InternetCrackUrl failed: %u\n", GetLastError());
return E_FAIL;
}
if(IsEqualGUID(&IID_IStream, riid) && url.nScheme == INTERNET_SCHEME_GOPHER)
return URLMonikerImpl_BindToStorage_hack(This->URLName, pbc, ppvObject);
TRACE("(%p)->(%p %p %s %p)\n", This, pbc, pmkToLeft, debugstr_guid(riid), ppvObject);
return bind_to_storage(This->URLName, pbc, riid, ppvObject);
}

View File

@ -30,342 +30,6 @@
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;
DWORD size;
LPWSTR url, c, ext = NULL;
HRESULT hr;
size = (strlenW(pszURL)+1)*sizeof(WCHAR);
url = heap_alloc(size);
memcpy(url, pszURL, size);
for (c = url; *c && *c != '#' && *c != '?'; ++c)
{
if (*c == '.')
ext = c+1;
else if(*c == '/')
ext = NULL;
}
*c = 0;
if(!CreateUrlCacheEntryW(url, dwSize, ext, pszFileName, 0))
hr = HRESULT_FROM_WIN32(GetLastError());
else
hr = S_OK;
heap_free(url);
if (hr != S_OK)
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 = heap_alloc_zero(sizeof(IUMCacheStream));
if(ucstr)
{
ucstr->pszURL = heap_alloc_zero(sizeof(WCHAR) * (lstrlenW(pszURL) + 1));
if (ucstr->pszURL)
{
ucstr->pszFileName = heap_alloc_zero(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;
}
heap_free(ucstr->pszURL);
}
heap_free(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=%u)\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=%u)\n", This, refCount + 1);
if (!refCount)
{
TRACE(" destroying UMCacheStream (%p)\n",This);
UMCloseCacheFileStream(This);
CloseHandle(This->handle);
heap_free(This->pszFileName);
heap_free(This->pszURL);
heap_free(This);
}
return refCount;
}
static HRESULT WINAPI IStream_fnRead (IStream * iface,
void* pv,
ULONG cb,
ULONG* pcbRead)
{
ULONG dwBytesRead;
IUMCacheStream *This = (IUMCacheStream *)iface;
TRACE("(%p)->(%p,0x%08x,%p)\n",This, pv, cb, pcbRead);
if ( !pv )
return STG_E_INVALIDPOINTER;
if ( !pcbRead)
pcbRead = &dwBytesRead;
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)
{
LARGE_INTEGER newpos;
IUMCacheStream *This = (IUMCacheStream *)iface;
TRACE("(%p)\n",This);
if (!SetFilePointerEx( This->handle, dlibMove, &newpos, dwOrigin ))
return E_FAIL;
if (plibNewPosition)
plibNewPosition->QuadPart = newpos.QuadPart;
return S_OK;
}
static HRESULT WINAPI IStream_fnSetSize (IStream * iface,
ULARGE_INTEGER libNewSize)
{
LARGE_INTEGER newpos;
IUMCacheStream *This = (IUMCacheStream *)iface;
TRACE("(%p)\n",This);
newpos.QuadPart = libNewSize.QuadPart;
if( ! SetFilePointerEx( This->handle, newpos, 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
};
typedef struct ProxyBindStatusCallback
{
const IBindStatusCallbackVtbl *lpVtbl;

View File

@ -56,19 +56,6 @@ static inline void URLMON_UnlockModule(void) { InterlockedDecrement( &URLMON_ref
#define DEFINE_THIS2(cls,ifc,iface) ((cls*)((BYTE*)(iface)-offsetof(cls,ifc)))
#define DEFINE_THIS(cls,ifc,iface) DEFINE_THIS2(cls,lp ## ifc ## Vtbl,iface)
typedef struct
{
const IStreamVtbl *lpVtbl;
LONG 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);
IInternetProtocolInfo *get_protocol_info(LPCWSTR url);
HRESULT get_protocol_handler(LPCWSTR url, CLSID *clsid, IClassFactory **ret);
BOOL is_registered_protocol(LPCWSTR);