From 54f041ad32fb0c83fa1f3da5f36e74f0b4f015a9 Mon Sep 17 00:00:00 2001 From: Richard Cohen Date: Thu, 2 Jun 2005 19:42:48 +0000 Subject: [PATCH] - Base FileMonikerImpl_Save() on XP. - Correct handling of Unicode strings & multibyte locales. - More error checking. - Change ERR to WARN. - Match Windows quick & dirty estimate for GetSizeMax(). --- dlls/ole32/filemoniker.c | 281 +++++++++++++++++++++++---------------- 1 file changed, 164 insertions(+), 117 deletions(-) diff --git a/dlls/ole32/filemoniker.c b/dlls/ole32/filemoniker.c index 9b8cdb1c7db..27ca79c22e1 100644 --- a/dlls/ole32/filemoniker.c +++ b/dlls/ole32/filemoniker.c @@ -182,121 +182,155 @@ FileMonikerImpl_IsDirty(IMoniker* iface) * written by FileMonikerImpl_Save */ static HRESULT WINAPI -FileMonikerImpl_Load(IMoniker* iface,IStream* pStm) +FileMonikerImpl_Load(IMoniker* iface, IStream* pStm) { HRESULT res; - CHAR* filePathA; - WCHAR* filePathW; + CHAR* filePathA = NULL; + WCHAR* filePathW = NULL; ULONG bread; WORD wbuffer; - DWORD dwbuffer,length,i,doubleLenHex,doubleLenDec; + DWORD dwbuffer, bytesA, bytesW, len; + int i; FileMonikerImpl *This = (FileMonikerImpl *)iface; TRACE("(%p,%p)\n",iface,pStm); - /* first WORD is non significative */ + /* first WORD must be 0 */ res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); if (bread!=sizeof(WORD) || wbuffer!=0) { - ERR("Couldn't read 0 word\n"); - return E_FAIL; + WARN("Couldn't read 0 word\n"); + goto fail; } /* read filePath string length (plus one) */ - res=IStream_Read(pStm,&length,sizeof(DWORD),&bread); + res=IStream_Read(pStm,&bytesA,sizeof(DWORD),&bread); if (bread != sizeof(DWORD)) { - ERR("Couldn't read file string length\n"); - return E_FAIL; + WARN("Couldn't read file string length\n"); + goto fail; } /* read filePath string */ - filePathA=HeapAlloc(GetProcessHeap(),0,length); - res=IStream_Read(pStm,filePathA,length,&bread); - HeapFree(GetProcessHeap(),0,filePathA); - if (bread != length) + filePathA=HeapAlloc(GetProcessHeap(),0,bytesA); + if (!filePathA) { - ERR("Couldn't read file path string\n"); - return E_FAIL; + res = E_OUTOFMEMORY; + goto fail; + } + + res=IStream_Read(pStm,filePathA,bytesA,&bread); + if (bread != bytesA) + { + WARN("Couldn't read file path string\n"); + goto fail; } /* read the first constant */ IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); if (bread != sizeof(DWORD) || dwbuffer != 0xDEADFFFF) { - ERR("Couldn't read 0xDEADFFFF constant\n"); - return E_FAIL; + WARN("Couldn't read 0xDEADFFFF constant\n"); + goto fail; } - length--; - - for(i=0;i<10;i++){ - res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); - if (bread!=sizeof(WORD) || wbuffer!=0) + for(i=0;i<5;i++) + { + res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); + if (bread!=sizeof(DWORD) || dwbuffer!=0) { - ERR("Couldn't read 0 padding\n"); - return E_FAIL; + WARN("Couldn't read 0 padding\n"); + goto fail; } } - if (length>8) - length=0; + res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); + if (bread!=sizeof(DWORD)) + goto fail; - doubleLenHex=doubleLenDec=2*length; - if (length > 5) - doubleLenDec+=6; + if (!dwbuffer) /* No W-string */ + { + bytesA--; + len=MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, filePathA, bytesA, NULL, 0); + if (!len) + goto fail; + + filePathW=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR)); + if (!filePathW) + { + res = E_OUTOFMEMORY; + goto fail; + } + MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, filePathA, -1, filePathW, len+1); + goto succeed; + } + + if (dwbuffer < 6) + goto fail; + + bytesW=dwbuffer - 6; res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); - if (bread!=sizeof(DWORD) || dwbuffer!=doubleLenDec) - return E_FAIL; - - if (length==0) - return res; - - res=IStream_Read(pStm,&dwbuffer,sizeof(DWORD),&bread); - if (bread!=sizeof(DWORD) || dwbuffer!=doubleLenHex) - return E_FAIL; + if (bread!=sizeof(DWORD) || dwbuffer!=bytesW) + goto fail; res=IStream_Read(pStm,&wbuffer,sizeof(WORD),&bread); if (bread!=sizeof(WORD) || wbuffer!=0x3) - return E_FAIL; + goto fail; - filePathW=HeapAlloc(GetProcessHeap(),0,(length+1)*sizeof(WCHAR)); - filePathW[length]=0; - res=IStream_Read(pStm,filePathW,doubleLenHex,&bread); - if (bread!=doubleLenHex) { - HeapFree(GetProcessHeap(), 0, filePathW); - return E_FAIL; + len=bytesW/sizeof(WCHAR); + filePathW=HeapAlloc(GetProcessHeap(),0,(len+1)*sizeof(WCHAR)); + if(!filePathW) + { + res = E_OUTOFMEMORY; + goto fail; } + res=IStream_Read(pStm,filePathW,bytesW,&bread); + if (bread!=bytesW) + goto fail; + filePathW[len]=0; + + succeed: + HeapFree(GetProcessHeap(),0,filePathA); HeapFree(GetProcessHeap(),0,This->filePathName); - This->filePathName=filePathW; + return S_OK; + + fail: + HeapFree(GetProcessHeap(), 0, filePathA); + HeapFree(GetProcessHeap(), 0, filePathW); + + if (SUCCEEDED(res)) + res = E_FAIL; return res; } /****************************************************************************** * FileMoniker_Save * - * This function saves data of this object. In the beginning I thougth + * This function saves data of this object. In the beginning I thought * that I have just to write the filePath string on Stream. But, when I - * tested this function whith windows programs samples, I noticed that it - * was not the case. So I analysed data written by this function on - * Windows and what this did function exactly ! But I have no idea about - * its logic ! - * I guessed data which must be written on stream is: + * tested this function with windows programs samples, I noticed that it + * was not the case. This implementation is based on XP SP2. Other versions + * of Windows have minor variations. + * + * Data which must be written on stream is: * 1) WORD constant:zero * 2) length of the path string ("\0" included) * 3) path string type A * 4) DWORD constant : 0xDEADFFFF - * 5) ten WORD constant: zero - * 6) DWORD: double-length of the the path string type W ("\0" not + * 5) five DWORD constant: zero + * 6) If we're only writing the multibyte version, + * write a zero DWORD and finish. + * + * 7) DWORD: double-length of the the path string type W ("\0" not * included) - * 7) WORD constant: 0x3 - * 8) filePath unicode string. - * if the length(filePath) > 8 or length(filePath) == 8 stop at step 5) + * 8) WORD constant: 0x3 + * 9) filePath unicode string. + * */ static HRESULT WINAPI FileMonikerImpl_Save(IMoniker* iface, IStream* pStm, BOOL fClearDirty) @@ -305,64 +339,92 @@ FileMonikerImpl_Save(IMoniker* iface, IStream* pStm, BOOL fClearDirty) HRESULT res; LPOLESTR filePathW=This->filePathName; - CHAR* filePathA; - DWORD len; + CHAR* filePathA; + DWORD bytesA, bytesW, len; - DWORD constant1 = 0xDEADFFFF; /* these constants are detected after analysing the data structure written by */ - WORD constant2 = 0x3; /* FileMoniker_Save function in a windows program system */ + static const DWORD DEADFFFF = 0xDEADFFFF; /* Constants */ + static const DWORD ZERO = 0; + static const WORD THREE = 0x3; - WORD zero=0; - DWORD doubleLenHex; - DWORD doubleLenDec; - int i=0; + int i; + BOOL bUsedDefault, bWriteWide; TRACE("(%p,%p,%d)\n",iface,pStm,fClearDirty); if (pStm==NULL) return E_POINTER; - /* write a DWORD set to 0 : constant */ - res=IStream_Write(pStm,&zero,sizeof(WORD),NULL); + /* write a 0 WORD */ + res=IStream_Write(pStm,&ZERO,sizeof(WORD),NULL); + if (!SUCCEEDED(res)) return res; - /* write length of filePath string ( "\0" included )*/ - len = WideCharToMultiByte( CP_ACP, 0, filePathW, -1, NULL, 0, NULL, NULL ); - res=IStream_Write(pStm,&len,sizeof(DWORD),NULL); + /* write length of filePath string ( 0 included )*/ + bytesA = WideCharToMultiByte( CP_ACP, 0, filePathW, -1, NULL, 0, NULL, NULL ); + res=IStream_Write(pStm,&bytesA,sizeof(DWORD),NULL); + if (!SUCCEEDED(res)) return res; - /* write filePath string type A */ - filePathA=HeapAlloc(GetProcessHeap(),0,len); - WideCharToMultiByte( CP_ACP, 0, filePathW, -1, filePathA, len, NULL, NULL ); - res=IStream_Write(pStm,filePathA,len,NULL); + /* write A string (with '\0') */ + filePathA=HeapAlloc(GetProcessHeap(),0,bytesA); + if (!filePathA) + return E_OUTOFMEMORY; + WideCharToMultiByte( CP_ACP, 0, filePathW, -1, filePathA, bytesA, NULL, &bUsedDefault); + res=IStream_Write(pStm,filePathA,bytesA,NULL); HeapFree(GetProcessHeap(),0,filePathA); + if (!SUCCEEDED(res)) return res; - /* write a DWORD set to 0xDEADFFFF: constant */ - res=IStream_Write(pStm,&constant1,sizeof(DWORD),NULL); + /* write a DWORD 0xDEADFFFF */ + res=IStream_Write(pStm,&DEADFFFF,sizeof(DWORD),NULL); + if (!SUCCEEDED(res)) return res; - len--; - /* write 10 times a DWORD set to 0 : constants */ - for(i=0;i<10;i++) - res=IStream_Write(pStm,&zero,sizeof(WORD),NULL); + /* write 5 zero DWORDs */ + for(i=0;i<5;i++) + { + res=IStream_Write(pStm,&ZERO,sizeof(DWORD),NULL); + if (!SUCCEEDED(res)) return res; + } - if (len>8) - len=0; + /* Write the wide version if: + * + couldn't convert to CP_ACP, + * or + it's a directory, + * or + there's a character > 0xFF + */ + len = lstrlenW(filePathW); + bWriteWide = (bUsedDefault || (len > 0 && filePathW[len-1]=='\\' )); + if (!bWriteWide) + { + WCHAR* pch; + for(pch=filePathW;*pch;++pch) + { + if (*pch > 0xFF) + { + bWriteWide = TRUE; + break; + } + } + } - doubleLenHex=doubleLenDec=2*len; - if (len > 5) - doubleLenDec+=6; - - /* write double-length of the path string ( "\0" included )*/ - res=IStream_Write(pStm,&doubleLenDec,sizeof(DWORD),NULL); - - if (len==0) + if (!bWriteWide) + { + res=IStream_Write(pStm,&ZERO,sizeof(DWORD),NULL); return res; + } - /* write double-length (hexa representation) of the path string ( "\0" included ) */ - res=IStream_Write(pStm,&doubleLenHex,sizeof(DWORD),NULL); + /* write bytes needed for the filepathW (without 0) + 6 */ + bytesW = len*sizeof(WCHAR) + 6; + res=IStream_Write(pStm,&bytesW,sizeof(DWORD),NULL); + if (!SUCCEEDED(res)) return res; - /* write a WORD set to 0x3: constant */ - res=IStream_Write(pStm,&constant2,sizeof(WORD),NULL); + /* try again, without the extra 6 */ + bytesW -= 6; + res=IStream_Write(pStm,&bytesW,sizeof(DWORD),NULL); + if (!SUCCEEDED(res)) return res; - /* write path unicode string */ - res=IStream_Write(pStm,filePathW,doubleLenHex,NULL); + /* write a WORD 3 */ + res=IStream_Write(pStm,&THREE,sizeof(WORD),NULL); + if (!SUCCEEDED(res)) return res; + + /* write W string (no 0) */ + res=IStream_Write(pStm,filePathW,bytesW,NULL); return res; } @@ -374,32 +436,17 @@ static HRESULT WINAPI FileMonikerImpl_GetSizeMax(IMoniker* iface, ULARGE_INTEGER* pcbSize) { FileMonikerImpl *This = (FileMonikerImpl *)iface; - DWORD len=lstrlenW(This->filePathName); - DWORD sizeMAx; TRACE("(%p,%p)\n",iface,pcbSize); if (!pcbSize) return E_POINTER; - /* for more details see FileMonikerImpl_Save coments */ - - sizeMAx = sizeof(WORD) + /* first WORD is 0 */ - sizeof(DWORD)+ /* length of filePath including "\0" in the end of the string */ - (len+1)+ /* filePath string */ - sizeof(DWORD)+ /* constant : 0xDEADFFFF */ - 10*sizeof(WORD)+ /* 10 zero WORD */ - sizeof(DWORD); /* size of the unicode filePath: "\0" not included */ - - if (len==0 || len > 8) - return S_OK; - - sizeMAx += sizeof(DWORD)+ /* size of the unicode filePath: "\0" not included */ - sizeof(WORD)+ /* constant : 0x3 */ - len*sizeof(WCHAR); /* unicde filePath string */ - - pcbSize->u.LowPart=sizeMAx; - pcbSize->u.HighPart=0; + /* We could calculate exactly (see ...::Save()) but instead + * we'll make a quick over-estimate, like Windows (NT4, XP) does. + */ + pcbSize->u.LowPart = 0x38 + 4 * lstrlenW(This->filePathName); + pcbSize->u.HighPart = 0; return S_OK; }