[PATCH 1/5] oleaut32: Load and store all of the strings in a typelib
Andrew Eikum
aeikum at codeweavers.com
Thu May 30 16:42:38 CDT 2013
This lets us easily avoid duplicate strings in the typelib.
---
dlls/oleaut32/typelib.c | 248 ++++++++++++++++++++++++++++++------------------
1 file changed, 158 insertions(+), 90 deletions(-)
diff --git a/dlls/oleaut32/typelib.c b/dlls/oleaut32/typelib.c
index 2711aac..24e06b9 100644
--- a/dlls/oleaut32/typelib.c
+++ b/dlls/oleaut32/typelib.c
@@ -982,6 +982,12 @@ typedef struct tagTLBImpLib
struct list entry;
} TLBImpLib;
+typedef struct tagTLBString {
+ BSTR str;
+ UINT offset;
+ struct list entry;
+} TLBString;
+
/* internal ITypeLib data */
typedef struct tagITypeLibImpl
{
@@ -995,10 +1001,12 @@ typedef struct tagITypeLibImpl
/* strings can be stored in tlb as multibyte strings BUT they are *always*
* exported to the application as a UNICODE string.
*/
+ struct list string_list;
+
BSTR Name;
- BSTR DocString;
- BSTR HelpFile;
- BSTR HelpStringDll;
+ const TLBString *DocString;
+ const TLBString *HelpFile;
+ const TLBString *HelpStringDll;
DWORD dwHelpContext;
int TypeInfoCount; /* nr of typeinfo's in librarry */
struct tagITypeInfoImpl **typeinfos;
@@ -1087,8 +1095,8 @@ typedef struct tagTLBFuncDesc
TLBParDesc *pParamDesc; /* array with param names and custom data */
int helpcontext;
int HelpStringContext;
- BSTR HelpString;
- BSTR Entry; /* if IS_INTRESOURCE true, it's numeric; if -1 it isn't present */
+ const TLBString *HelpString;
+ const TLBString *Entry; /* if IS_INTRESOURCE true, it's numeric; if -1 it isn't present */
struct list custdata_list;
} TLBFuncDesc;
@@ -1099,7 +1107,7 @@ typedef struct tagTLBVarDesc
BSTR Name; /* the name of this variable */
int HelpContext;
int HelpStringContext;
- BSTR HelpString;
+ const TLBString *HelpString;
struct list custdata_list;
} TLBVarDesc;
@@ -1128,8 +1136,9 @@ typedef struct tagITypeInfoImpl
* so why should we do it in unicode?
*/
BSTR Name;
- BSTR DocString;
- BSTR DllName;
+ const TLBString *DocString;
+ const TLBString *DllName;
+ const TLBString *Schema;
DWORD dwHelpContext;
DWORD dwHelpStringContext;
@@ -1185,6 +1194,11 @@ typedef struct tagTLBContext
static void MSFT_DoRefType(TLBContext *pcx, ITypeLibImpl *pTL, int offset);
+static inline BSTR TLB_get_bstr(const TLBString *str)
+{
+ return str != NULL ? str->str : NULL;
+}
+
/*
debug
*/
@@ -1315,8 +1329,15 @@ static void dump_TLBFuncDescOne(const TLBFuncDesc * pfd)
dump_FUNCDESC(&(pfd->funcdesc));
- MESSAGE("\thelpstring: %s\n", debugstr_w(pfd->HelpString));
- MESSAGE("\tentry: %s\n", (pfd->Entry == (void *)-1) ? "invalid" : debugstr_w(pfd->Entry));
+ MESSAGE("\thelpstring: %s\n", debugstr_w(TLB_get_bstr(pfd->HelpString)));
+ if(pfd->Entry == NULL)
+ MESSAGE("\tentry: (null)\n");
+ else if(pfd->Entry == (void*)-1)
+ MESSAGE("\tentry: invalid\n");
+ else if(IS_INTRESOURCE(pfd->Entry))
+ MESSAGE("\tentry: %p\n", pfd->Entry);
+ else
+ MESSAGE("\tentry: %s\n", debugstr_w(TLB_get_bstr(pfd->Entry)));
}
static void dump_TLBFuncDesc(const TLBFuncDesc * pfd, UINT n)
{
@@ -1456,14 +1477,14 @@ static void dump_DispParms(const DISPPARAMS * pdp)
static void dump_TypeInfo(const ITypeInfoImpl * pty)
{
TRACE("%p ref=%u\n", pty, pty->ref);
- TRACE("%s %s\n", debugstr_w(pty->Name), debugstr_w(pty->DocString));
+ TRACE("%s %s\n", debugstr_w(pty->Name), debugstr_w(TLB_get_bstr(pty->DocString)));
TRACE("attr:%s\n", debugstr_guid(&(pty->TypeAttr.guid)));
TRACE("kind:%s\n", typekind_desc[pty->TypeAttr.typekind]);
TRACE("fct:%u var:%u impl:%u\n",
pty->TypeAttr.cFuncs, pty->TypeAttr.cVars, pty->TypeAttr.cImplTypes);
TRACE("wTypeFlags: 0x%04x\n", pty->TypeAttr.wTypeFlags);
TRACE("parent tlb:%p index in TLB:%u\n",pty->pTypeLib, pty->index);
- if (pty->TypeAttr.typekind == TKIND_MODULE) TRACE("dllname:%s\n", debugstr_w(pty->DllName));
+ if (pty->TypeAttr.typekind == TKIND_MODULE) TRACE("dllname:%s\n", debugstr_w(TLB_get_bstr(pty->DllName)));
if (TRACE_ON(ole))
dump_TLBFuncDesc(pty->funcdescs, pty->TypeAttr.cFuncs);
dump_TLBVarDesc(pty->vardescs, pty->TypeAttr.cVars);
@@ -1774,6 +1795,30 @@ static HRESULT TLB_set_custdata(struct list *custdata_list, REFGUID guid, VARIAN
return VariantCopy(&cust_data->data, var);
}
+static TLBString *TLB_append_str(struct list *string_list, BSTR new_str)
+{
+ TLBString *str;
+
+ LIST_FOR_EACH_ENTRY(str, string_list, TLBString, entry) {
+ if (strcmpW(str->str, new_str) == 0)
+ return str;
+ }
+
+ str = heap_alloc(sizeof(TLBString));
+ if (!str)
+ return NULL;
+
+ str->str = SysAllocString(new_str);
+ if (!str->str) {
+ heap_free(str);
+ return NULL;
+ }
+
+ list_add_tail(string_list, &str->entry);
+
+ return str;
+}
+
/**********************************************************************
*
* Functions for reading MSFT typelibs (those created by CreateTypeLib2)
@@ -1898,36 +1943,20 @@ static BSTR MSFT_ReadName( TLBContext *pcx, int offset)
return bstrName;
}
-static BSTR MSFT_ReadString( TLBContext *pcx, int offset)
+static TLBString *MSFT_ReadString( TLBContext *pcx, int offset)
{
- char * string;
- INT16 length;
- int lengthInChars;
- BSTR bstr = NULL;
+ TLBString *tlbstr;
- if(offset<0) return NULL;
- MSFT_ReadLEWords(&length, sizeof(INT16), pcx, pcx->pTblDir->pStringtab.offset+offset);
- if(length <= 0) return 0;
- string = heap_alloc_zero(length +1);
- MSFT_Read(string, length, pcx, DO_NOT_SEEK);
- string[length]='\0';
-
- lengthInChars = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
- string, -1, NULL, 0);
-
- /* no invalid characters in string */
- if (lengthInChars)
- {
- bstr = SysAllocStringByteLen(NULL, lengthInChars * sizeof(WCHAR));
-
- /* don't check for invalid character since this has been done previously */
- MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, string, -1, bstr, lengthInChars);
+ LIST_FOR_EACH_ENTRY(tlbstr, &pcx->pLibInfo->string_list, TLBString, entry) {
+ if (tlbstr->offset == offset) {
+ TRACE_(typelib)("%s\n", debugstr_w(tlbstr->str));
+ return tlbstr;
+ }
}
- heap_free(string);
- TRACE_(typelib)("%s %d\n", debugstr_w(bstr), lengthInChars);
- return bstr;
+ return NULL;
}
+
/*
* read a value and fill a VARIANT structure
*/
@@ -2172,13 +2201,13 @@ MSFT_DoFuncs(TLBContext* pcx,
{
if (!IS_INTRESOURCE(pFuncRec->oEntry))
ERR("ordinal 0x%08x invalid, IS_INTRESOURCE is false\n", pFuncRec->oEntry);
- ptfd->Entry = (BSTR)(DWORD_PTR)LOWORD(pFuncRec->oEntry);
+ ptfd->Entry = (TLBString*)(DWORD_PTR)LOWORD(pFuncRec->oEntry);
}
else
ptfd->Entry = MSFT_ReadString(pcx, pFuncRec->oEntry);
}
else
- ptfd->Entry = (BSTR)-1;
+ ptfd->Entry = (TLBString*)-1;
if (optional > FIELD_OFFSET(MSFT_FuncRecord, HelpStringContext))
ptfd->HelpStringContext = pFuncRec->HelpStringContext;
@@ -2534,6 +2563,51 @@ static ITypeInfoImpl * MSFT_DoTypeInfo(
return ptiRet;
}
+static HRESULT MSFT_ReadAllStrings(TLBContext *pcx)
+{
+ char *string;
+ INT16 len_str, len_piece;
+ int offs = 0, lengthInChars;
+
+ MSFT_Seek(pcx, pcx->pTblDir->pStringtab.offset);
+ while (1) {
+ TLBString *tlbstr;
+
+ if (offs >= pcx->pTblDir->pStringtab.length)
+ return S_OK;
+
+ MSFT_ReadLEWords(&len_str, sizeof(INT16), pcx, DO_NOT_SEEK);
+ len_piece = len_str + sizeof(INT16);
+ if(len_piece % 4)
+ len_piece = (len_piece + 4) & ~0x3;
+ if(len_piece < 8)
+ len_piece = 8;
+
+ string = heap_alloc(len_piece + 1);
+ MSFT_Read(string, len_piece - sizeof(INT16), pcx, DO_NOT_SEEK);
+ string[len_str] = '\0';
+
+ lengthInChars = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED | MB_ERR_INVALID_CHARS,
+ string, -1, NULL, 0);
+ if (!lengthInChars) {
+ heap_free(string);
+ return E_UNEXPECTED;
+ }
+
+ tlbstr = heap_alloc(sizeof(TLBString));
+
+ tlbstr->offset = offs;
+ tlbstr->str = SysAllocStringByteLen(NULL, lengthInChars * sizeof(WCHAR));
+ MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, string, -1, tlbstr->str, lengthInChars);
+
+ heap_free(string);
+
+ list_add_tail(&pcx->pLibInfo->string_list, &tlbstr->entry);
+
+ offs += len_piece;
+ }
+}
+
/* Because type library parsing has some degree of overhead, and some apps repeatedly load the same
* typelibs over and over, we cache them here. According to MSDN Microsoft have a similar scheme in
* place. This will cause a deliberate memory leak, but generally losing RAM for cycles is an acceptable
@@ -3103,6 +3177,7 @@ static ITypeLibImpl* TypeLibImpl_Constructor(void)
list_init(&pTypeLibImpl->implib_list);
list_init(&pTypeLibImpl->custdata_list);
+ list_init(&pTypeLibImpl->string_list);
list_init(&pTypeLibImpl->ref_list);
pTypeLibImpl->dispatch_href = -1;
@@ -3162,6 +3237,8 @@ static ITypeLib2* ITypeLib2_Constructor_MSFT(LPVOID pLib, DWORD dwTLBLength)
return NULL;
}
+ MSFT_ReadAllStrings(&cx);
+
/* now fill our internal data */
/* TLIBATTR fields */
MSFT_ReadGuid(&pTypeLibImpl->LibAttr.guid, tlbHeader.posguid, &cx);
@@ -3341,18 +3418,23 @@ static BOOL TLB_GUIDFromString(const char *str, GUID *guid)
return TRUE;
}
-static WORD SLTG_ReadString(const char *ptr, BSTR *pBstr)
+static WORD SLTG_ReadString(const char *ptr, const TLBString **pStr, ITypeLibImpl *lib)
{
WORD bytelen;
DWORD len;
+ BSTR tmp_str;
- *pBstr = NULL;
+ *pStr = NULL;
bytelen = *(const WORD*)ptr;
if(bytelen == 0xffff) return 2;
+
len = MultiByteToWideChar(CP_ACP, 0, ptr + 2, bytelen, NULL, 0);
- *pBstr = SysAllocStringLen(NULL, len);
- if (*pBstr)
- len = MultiByteToWideChar(CP_ACP, 0, ptr + 2, bytelen, *pBstr, len);
+ tmp_str = SysAllocStringLen(NULL, len);
+ if (*tmp_str) {
+ MultiByteToWideChar(CP_ACP, 0, ptr + 2, bytelen, tmp_str, len);
+ *pStr = TLB_append_str(&lib->string_list, tmp_str);
+ SysFreeString(tmp_str);
+ }
return bytelen + 2;
}
@@ -3386,9 +3468,9 @@ static DWORD SLTG_ReadLibBlk(LPVOID pLibBlk, ITypeLibImpl *pTypeLibImpl)
}
ptr += 2;
- ptr += SLTG_ReadString(ptr, &pTypeLibImpl->DocString);
+ ptr += SLTG_ReadString(ptr, &pTypeLibImpl->DocString, pTypeLibImpl);
- ptr += SLTG_ReadString(ptr, &pTypeLibImpl->HelpFile);
+ ptr += SLTG_ReadString(ptr, &pTypeLibImpl->HelpFile, pTypeLibImpl);
pTypeLibImpl->dwHelpContext = *(DWORD*)ptr;
ptr += 4;
@@ -4365,6 +4447,7 @@ static ULONG WINAPI ITypeLib2_fnRelease( ITypeLib2 *iface)
{
TLBImpLib *pImpLib, *pImpLibNext;
TLBRefType *ref_type;
+ TLBString *tlbstr, *tlbstr_next;
void *cursor2;
int i;
@@ -4383,14 +4466,10 @@ static ULONG WINAPI ITypeLib2_fnRelease( ITypeLib2 *iface)
SysFreeString(This->Name);
This->Name = NULL;
- SysFreeString(This->DocString);
- This->DocString = NULL;
-
- SysFreeString(This->HelpFile);
- This->HelpFile = NULL;
-
- SysFreeString(This->HelpStringDll);
- This->HelpStringDll = NULL;
+ LIST_FOR_EACH_ENTRY_SAFE(tlbstr, tlbstr_next, &This->string_list, TLBString, entry) {
+ list_remove(&tlbstr->entry);
+ heap_free(tlbstr);
+ }
TLB_FreeCustData(&This->custdata_list);
@@ -4597,7 +4676,7 @@ static HRESULT WINAPI ITypeLib2_fnGetDocumentation(
{
if (This->DocString)
{
- if(!(*pBstrDocString = SysAllocString(This->DocString)))
+ if(!(*pBstrDocString = SysAllocString(TLB_get_bstr(This->DocString))))
goto memerr2;
}
else if (This->Name)
@@ -4616,7 +4695,7 @@ static HRESULT WINAPI ITypeLib2_fnGetDocumentation(
{
if (This->HelpFile)
{
- if(!(*pBstrHelpFile = SysAllocString(This->HelpFile)))
+ if(!(*pBstrHelpFile = SysAllocString(TLB_get_bstr(This->HelpFile))))
goto memerr3;
}
else
@@ -4843,11 +4922,11 @@ static HRESULT WINAPI ITypeLib2_fnGetDocumentation2(
{
/* documentation for the typelib */
if(pbstrHelpString)
- *pbstrHelpString=SysAllocString(This->DocString);
+ *pbstrHelpString=SysAllocString(TLB_get_bstr(This->DocString));
if(pdwHelpStringContext)
*pdwHelpStringContext=This->dwHelpContext;
if(pbstrHelpStringDll)
- *pbstrHelpStringDll=SysAllocString(This->HelpStringDll);
+ *pbstrHelpStringDll=SysAllocString(TLB_get_bstr(This->HelpStringDll));
result = S_OK;
}
@@ -5216,12 +5295,6 @@ static void ITypeInfoImpl_Destroy(ITypeInfoImpl *This)
SysFreeString(This->Name);
This->Name = NULL;
- SysFreeString(This->DocString);
- This->DocString = NULL;
-
- SysFreeString(This->DllName);
- This->DllName = NULL;
-
for (i = 0; i < This->TypeAttr.cFuncs; ++i)
{
int j;
@@ -5240,9 +5313,6 @@ static void ITypeInfoImpl_Destroy(ITypeInfoImpl *This)
heap_free(pFInfo->funcdesc.lprgelemdescParam);
heap_free(pFInfo->pParamDesc);
TLB_FreeCustData(&pFInfo->custdata_list);
- if (!IS_INTRESOURCE(pFInfo->Entry) && pFInfo->Entry != (BSTR)-1)
- SysFreeString(pFInfo->Entry);
- SysFreeString(pFInfo->HelpString);
SysFreeString(pFInfo->Name);
}
heap_free(This->funcdescs);
@@ -5257,7 +5327,6 @@ static void ITypeInfoImpl_Destroy(ITypeInfoImpl *This)
}
TLB_FreeCustData(&pVInfo->custdata_list);
SysFreeString(pVInfo->Name);
- SysFreeString(pVInfo->HelpString);
}
heap_free(This->vardescs);
@@ -7007,11 +7076,11 @@ static HRESULT WINAPI ITypeInfo_fnGetDocumentation( ITypeInfo2 *iface,
if(pBstrName)
*pBstrName=SysAllocString(This->Name);
if(pBstrDocString)
- *pBstrDocString=SysAllocString(This->DocString);
+ *pBstrDocString=SysAllocString(TLB_get_bstr(This->DocString));
if(pdwHelpContext)
*pdwHelpContext=This->dwHelpContext;
if(pBstrHelpFile)
- *pBstrHelpFile=SysAllocString(This->pTypeLib->HelpFile);
+ *pBstrHelpFile=SysAllocString(TLB_get_bstr(This->pTypeLib->HelpFile));
return S_OK;
}else {/* for a member */
pFDesc = TLB_get_funcdesc_by_memberid(This->funcdescs, This->TypeAttr.cFuncs, memid);
@@ -7019,9 +7088,11 @@ static HRESULT WINAPI ITypeInfo_fnGetDocumentation( ITypeInfo2 *iface,
if(pBstrName)
*pBstrName = SysAllocString(pFDesc->Name);
if(pBstrDocString)
- *pBstrDocString=SysAllocString(pFDesc->HelpString);
+ *pBstrDocString=SysAllocString(TLB_get_bstr(pFDesc->HelpString));
if(pdwHelpContext)
*pdwHelpContext=pFDesc->helpcontext;
+ if(pBstrHelpFile)
+ *pBstrHelpFile = SysAllocString(TLB_get_bstr(This->pTypeLib->HelpFile));
return S_OK;
}
pVDesc = TLB_get_vardesc_by_memberid(This->vardescs, This->TypeAttr.cVars, memid);
@@ -7029,11 +7100,11 @@ static HRESULT WINAPI ITypeInfo_fnGetDocumentation( ITypeInfo2 *iface,
if(pBstrName)
*pBstrName = SysAllocString(pVDesc->Name);
if(pBstrDocString)
- *pBstrDocString=SysAllocString(pVDesc->HelpString);
+ *pBstrDocString=SysAllocString(TLB_get_bstr(pVDesc->HelpString));
if(pdwHelpContext)
*pdwHelpContext=pVDesc->HelpContext;
if(pBstrHelpFile)
- *pBstrHelpFile = SysAllocString(This->pTypeLib->HelpFile);
+ *pBstrHelpFile = SysAllocString(TLB_get_bstr(This->pTypeLib->HelpFile));
return S_OK;
}
}
@@ -7085,11 +7156,11 @@ static HRESULT WINAPI ITypeInfo_fnGetDllEntry( ITypeInfo2 *iface, MEMBERID memid
dump_TLBFuncDescOne(pFDesc);
if (pBstrDllName)
- *pBstrDllName = SysAllocString(This->DllName);
+ *pBstrDllName = SysAllocString(TLB_get_bstr(This->DllName));
if (!IS_INTRESOURCE(pFDesc->Entry) && (pFDesc->Entry != (void*)-1)) {
if (pBstrName)
- *pBstrName = SysAllocString(pFDesc->Entry);
+ *pBstrName = SysAllocString(TLB_get_bstr(pFDesc->Entry));
if (pwOrdinal)
*pwOrdinal = -1;
return S_OK;
@@ -7725,29 +7796,29 @@ static HRESULT WINAPI ITypeInfo2_fnGetDocumentation2(
*pdwHelpStringContext=This->dwHelpStringContext;
if(pbstrHelpStringDll)
*pbstrHelpStringDll=
- SysAllocString(This->pTypeLib->HelpStringDll);/* FIXME */
+ SysAllocString(TLB_get_bstr(This->pTypeLib->HelpStringDll));/* FIXME */
return S_OK;
}else {/* for a member */
pFDesc = TLB_get_funcdesc_by_memberid(This->funcdescs, This->TypeAttr.cFuncs, memid);
if(pFDesc){
if(pbstrHelpString)
- *pbstrHelpString=SysAllocString(pFDesc->HelpString);
+ *pbstrHelpString=SysAllocString(TLB_get_bstr(pFDesc->HelpString));
if(pdwHelpStringContext)
*pdwHelpStringContext=pFDesc->HelpStringContext;
if(pbstrHelpStringDll)
*pbstrHelpStringDll=
- SysAllocString(This->pTypeLib->HelpStringDll);/* FIXME */
+ SysAllocString(TLB_get_bstr(This->pTypeLib->HelpStringDll));/* FIXME */
return S_OK;
}
pVDesc = TLB_get_vardesc_by_memberid(This->vardescs, This->TypeAttr.cVars, memid);
if(pVDesc){
if(pbstrHelpString)
- *pbstrHelpString=SysAllocString(pVDesc->HelpString);
+ *pbstrHelpString=SysAllocString(TLB_get_bstr(pVDesc->HelpString));
if(pdwHelpStringContext)
*pdwHelpStringContext=pVDesc->HelpStringContext;
if(pbstrHelpStringDll)
*pbstrHelpStringDll=
- SysAllocString(This->pTypeLib->HelpStringDll);/* FIXME */
+ SysAllocString(TLB_get_bstr(This->pTypeLib->HelpStringDll));/* FIXME */
return S_OK;
}
}
@@ -8296,8 +8367,7 @@ static HRESULT WINAPI ICreateTypeLib2_fnSetDocString(ICreateTypeLib2 *iface,
if (!doc)
return E_INVALIDARG;
- SysFreeString(This->DocString);
- This->DocString = SysAllocString(doc);
+ This->DocString = TLB_append_str(&This->string_list, doc);
return S_OK;
}
@@ -8312,8 +8382,7 @@ static HRESULT WINAPI ICreateTypeLib2_fnSetHelpFileName(ICreateTypeLib2 *iface,
if (!helpFileName)
return E_INVALIDARG;
- SysFreeString(This->HelpFile);
- This->HelpFile = SysAllocString(helpFileName);
+ This->HelpFile = TLB_append_str(&This->string_list, helpFileName);
return S_OK;
}
@@ -8394,8 +8463,7 @@ static HRESULT WINAPI ICreateTypeLib2_fnSetHelpStringDll(ICreateTypeLib2 *iface,
if (!filename)
return E_INVALIDARG;
- SysFreeString(This->HelpStringDll);
- This->HelpStringDll = SysAllocString(filename);
+ This->HelpStringDll = TLB_append_str(&This->string_list, filename);
return S_OK;
}
@@ -8507,8 +8575,7 @@ static HRESULT WINAPI ICreateTypeInfo2_fnSetDocString(ICreateTypeInfo2 *iface,
if (!doc)
return E_INVALIDARG;
- SysFreeString(This->DocString);
- This->DocString = SysAllocString(doc);
+ This->DocString = TLB_append_str(&This->pTypeLib->string_list, doc);
return S_OK;
}
@@ -8884,8 +8951,9 @@ static HRESULT WINAPI ICreateTypeInfo2_fnSetSchema(ICreateTypeInfo2 *iface,
if (!schema)
return E_INVALIDARG;
- SysFreeString(This->TypeAttr.lpstrSchema);
- This->TypeAttr.lpstrSchema = SysAllocString(schema);
+ This->Schema = TLB_append_str(&This->pTypeLib->string_list, schema);
+
+ This->TypeAttr.lpstrSchema = This->Schema->str;
return S_OK;
}
--
1.8.3
More information about the wine-patches
mailing list