/* * IAssemblyName implementation * * Copyright 2008 James Hawkins * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #define COBJMACROS #define INITGUID #include "windef.h" #include "winbase.h" #include "winuser.h" #include "ole2.h" #include "guiddef.h" #include "fusion.h" #include "corerror.h" #include "strsafe.h" #include "wine/debug.h" #include "fusionpriv.h" WINE_DEFAULT_DEBUG_CHANNEL(fusion); typedef struct { IAssemblyName IAssemblyName_iface; LPWSTR path; LPWSTR displayname; LPWSTR name; LPWSTR culture; LPWSTR procarch; WORD version[4]; DWORD versize; BYTE pubkey[8]; BOOL haspubkey; PEKIND pekind; LONG ref; } IAssemblyNameImpl; static const WCHAR separator[] = {',',' ',0}; static const WCHAR version[] = {'V','e','r','s','i','o','n',0}; static const WCHAR culture[] = {'C','u','l','t','u','r','e',0}; static const WCHAR pubkey[] = {'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0}; static const WCHAR procarch[] = {'p','r','o','c','e','s','s','o','r', 'A','r','c','h','i','t','e','c','t','u','r','e',0}; #define CHARS_PER_PUBKEY 16 static inline IAssemblyNameImpl *impl_from_IAssemblyName(IAssemblyName *iface) { return CONTAINING_RECORD(iface, IAssemblyNameImpl, IAssemblyName_iface); } static HRESULT WINAPI IAssemblyNameImpl_QueryInterface(IAssemblyName *iface, REFIID riid, LPVOID *ppobj) { IAssemblyNameImpl *This = impl_from_IAssemblyName(iface); TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj); *ppobj = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAssemblyName)) { IAssemblyName_AddRef(iface); *ppobj = &This->IAssemblyName_iface; return S_OK; } WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj); return E_NOINTERFACE; } static ULONG WINAPI IAssemblyNameImpl_AddRef(IAssemblyName *iface) { IAssemblyNameImpl *This = impl_from_IAssemblyName(iface); ULONG refCount = InterlockedIncrement(&This->ref); TRACE("(%p)->(ref before = %u)\n", This, refCount - 1); return refCount; } static ULONG WINAPI IAssemblyNameImpl_Release(IAssemblyName *iface) { IAssemblyNameImpl *This = impl_from_IAssemblyName(iface); ULONG refCount = InterlockedDecrement(&This->ref); TRACE("(%p)->(ref before = %u)\n", This, refCount + 1); if (!refCount) { heap_free(This->path); heap_free(This->displayname); heap_free(This->name); heap_free(This->culture); heap_free(This->procarch); heap_free(This); } return refCount; } static HRESULT WINAPI IAssemblyNameImpl_SetProperty(IAssemblyName *iface, DWORD PropertyId, LPVOID pvProperty, DWORD cbProperty) { FIXME("(%p, %d, %p, %d) stub!\n", iface, PropertyId, pvProperty, cbProperty); return E_NOTIMPL; } static HRESULT WINAPI IAssemblyNameImpl_GetProperty(IAssemblyName *iface, DWORD PropertyId, LPVOID pvProperty, LPDWORD pcbProperty) { IAssemblyNameImpl *name = impl_from_IAssemblyName(iface); DWORD size; TRACE("(%p, %d, %p, %p)\n", iface, PropertyId, pvProperty, pcbProperty); size = *pcbProperty; switch (PropertyId) { case ASM_NAME_NULL_PUBLIC_KEY: case ASM_NAME_NULL_PUBLIC_KEY_TOKEN: if (name->haspubkey) return S_OK; return S_FALSE; case ASM_NAME_NULL_CUSTOM: return S_OK; case ASM_NAME_NAME: *pcbProperty = 0; if (name->name) { *pcbProperty = (lstrlenW(name->name) + 1) * 2; if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; lstrcpyW(pvProperty, name->name); } break; case ASM_NAME_MAJOR_VERSION: *pcbProperty = 0; if (name->versize >= 1) { *pcbProperty = sizeof(WORD); if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; *((WORD *)pvProperty) = name->version[0]; } break; case ASM_NAME_MINOR_VERSION: *pcbProperty = 0; if (name->versize >= 2) { *pcbProperty = sizeof(WORD); if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; *((WORD *)pvProperty) = name->version[1]; } break; case ASM_NAME_BUILD_NUMBER: *pcbProperty = 0; if (name->versize >= 3) { *pcbProperty = sizeof(WORD); if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; *((WORD *)pvProperty) = name->version[2]; } break; case ASM_NAME_REVISION_NUMBER: *pcbProperty = 0; if (name->versize >= 4) { *pcbProperty = sizeof(WORD); if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; *((WORD *)pvProperty) = name->version[3]; } break; case ASM_NAME_CULTURE: *pcbProperty = 0; if (name->culture) { *pcbProperty = (lstrlenW(name->culture) + 1) * 2; if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; lstrcpyW(pvProperty, name->culture); } break; case ASM_NAME_PUBLIC_KEY_TOKEN: *pcbProperty = 0; if (name->haspubkey) { *pcbProperty = sizeof(DWORD) * 2; if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; memcpy(pvProperty, name->pubkey, sizeof(DWORD) * 2); } break; case ASM_NAME_ARCHITECTURE: *pcbProperty = 0; if (name->pekind != peNone) { *pcbProperty = sizeof(PEKIND); if (size < *pcbProperty) return STRSAFE_E_INSUFFICIENT_BUFFER; *((PEKIND *)pvProperty) = name->pekind; } break; default: *pcbProperty = 0; break; } return S_OK; } static HRESULT WINAPI IAssemblyNameImpl_Finalize(IAssemblyName *iface) { FIXME("(%p) stub!\n", iface); return E_NOTIMPL; } static HRESULT WINAPI IAssemblyNameImpl_GetDisplayName(IAssemblyName *iface, LPOLESTR szDisplayName, LPDWORD pccDisplayName, DWORD dwDisplayFlags) { static const WCHAR equals[] = {'=',0}; IAssemblyNameImpl *name = impl_from_IAssemblyName(iface); WCHAR verstr[30], *cultureval = NULL; DWORD size; TRACE("(%p, %p, %p, %d)\n", iface, szDisplayName, pccDisplayName, dwDisplayFlags); if (dwDisplayFlags == 0) { if (!name->displayname || !*name->displayname) return FUSION_E_INVALID_NAME; size = lstrlenW(name->displayname) + 1; if (*pccDisplayName < size) { *pccDisplayName = size; return E_NOT_SUFFICIENT_BUFFER; } if (szDisplayName) lstrcpyW(szDisplayName, name->displayname); *pccDisplayName = size; return S_OK; } if (!name->name || !*name->name) return FUSION_E_INVALID_NAME; /* Verify buffer size is sufficient */ size = lstrlenW(name->name) + 1; if ((dwDisplayFlags & ASM_DISPLAYF_VERSION) && (name->versize > 0)) { static const WCHAR spec[] = {'%','d',0}; static const WCHAR period[] = {'.',0}; DWORD i; wsprintfW(verstr, spec, name->version[0]); for (i = 1; i < name->versize; i++) { WCHAR value[6]; wsprintfW(value, spec, name->version[i]); lstrcatW(verstr, period); lstrcatW(verstr, value); } size += lstrlenW(separator) + lstrlenW(version) + lstrlenW(equals) + lstrlenW(verstr); } if ((dwDisplayFlags & ASM_DISPLAYF_CULTURE) && (name->culture)) { static const WCHAR neutral[] = {'n','e','u','t','r','a','l', 0}; cultureval = (lstrlenW(name->culture) == 2) ? name->culture : (LPWSTR) neutral; size += lstrlenW(separator) + lstrlenW(culture) + lstrlenW(equals) + lstrlenW(cultureval); } if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && (name->haspubkey)) size += lstrlenW(separator) + lstrlenW(pubkey) + lstrlenW(equals) + CHARS_PER_PUBKEY; if ((dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) && (name->procarch)) size += lstrlenW(separator) + lstrlenW(procarch) + lstrlenW(equals) + lstrlenW(name->procarch); if (size > *pccDisplayName) { *pccDisplayName = size; return E_NOT_SUFFICIENT_BUFFER; } /* Construct the string */ lstrcpyW(szDisplayName, name->name); if ((dwDisplayFlags & ASM_DISPLAYF_VERSION) && (name->versize > 0)) { lstrcatW(szDisplayName, separator); lstrcatW(szDisplayName, version); lstrcatW(szDisplayName, equals); lstrcatW(szDisplayName, verstr); } if ((dwDisplayFlags & ASM_DISPLAYF_CULTURE) && (name->culture)) { lstrcatW(szDisplayName, separator); lstrcatW(szDisplayName, culture); lstrcatW(szDisplayName, equals); lstrcatW(szDisplayName, cultureval); } if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && (name->haspubkey)) { WCHAR pkt[CHARS_PER_PUBKEY + 1]; static const WCHAR spec[] = {'%','0','2','x','%','0','2','x','%','0','2','x', '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x',0}; lstrcatW(szDisplayName, separator); lstrcatW(szDisplayName, pubkey); lstrcatW(szDisplayName, equals); wsprintfW(pkt, spec, name->pubkey[0], name->pubkey[1], name->pubkey[2], name->pubkey[3], name->pubkey[4], name->pubkey[5], name->pubkey[6], name->pubkey[7]); lstrcatW(szDisplayName, pkt); } if ((dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) && (name->procarch)) { lstrcatW(szDisplayName, separator); lstrcatW(szDisplayName, procarch); lstrcatW(szDisplayName, equals); lstrcatW(szDisplayName, name->procarch); } *pccDisplayName = size; return S_OK; } static HRESULT WINAPI IAssemblyNameImpl_Reserved(IAssemblyName *iface, REFIID refIID, IUnknown *pUnkReserved1, IUnknown *pUnkReserved2, LPCOLESTR szReserved, LONGLONG llReserved, LPVOID pvReserved, DWORD cbReserved, LPVOID *ppReserved) { TRACE("(%p, %s, %p, %p, %s, %s, %p, %d, %p)\n", iface, debugstr_guid(refIID), pUnkReserved1, pUnkReserved2, debugstr_w(szReserved), wine_dbgstr_longlong(llReserved), pvReserved, cbReserved, ppReserved); return E_NOTIMPL; } static HRESULT WINAPI IAssemblyNameImpl_GetName(IAssemblyName *iface, LPDWORD lpcwBuffer, WCHAR *pwzName) { IAssemblyNameImpl *name = impl_from_IAssemblyName(iface); DWORD len; TRACE("(%p, %p, %p)\n", iface, lpcwBuffer, pwzName); if (name->name) len = lstrlenW(name->name) + 1; else len = 0; if (*lpcwBuffer < len) { *lpcwBuffer = len; return E_NOT_SUFFICIENT_BUFFER; } if (!name->name) lpcwBuffer[0] = 0; else lstrcpyW(pwzName, name->name); *lpcwBuffer = len; return S_OK; } static HRESULT WINAPI IAssemblyNameImpl_GetVersion(IAssemblyName *iface, LPDWORD pdwVersionHi, LPDWORD pdwVersionLow) { IAssemblyNameImpl *name = impl_from_IAssemblyName(iface); TRACE("(%p, %p, %p)\n", iface, pdwVersionHi, pdwVersionLow); *pdwVersionHi = 0; *pdwVersionLow = 0; if (name->versize != 4) return FUSION_E_INVALID_NAME; *pdwVersionHi = (name->version[0] << 16) + name->version[1]; *pdwVersionLow = (name->version[2] << 16) + name->version[3]; return S_OK; } static HRESULT WINAPI IAssemblyNameImpl_IsEqual(IAssemblyName *iface, IAssemblyName *pName, DWORD flags) { IAssemblyNameImpl *name1 = impl_from_IAssemblyName(iface); IAssemblyNameImpl *name2 = impl_from_IAssemblyName(pName); TRACE("(%p, %p, 0x%08x)\n", iface, pName, flags); if (!pName) return S_FALSE; if (flags & ~ASM_CMPF_IL_ALL) FIXME("unsupported flags\n"); if ((flags & ASM_CMPF_NAME) && lstrcmpW(name1->name, name2->name)) return S_FALSE; if (name1->versize && name2->versize) { if ((flags & ASM_CMPF_MAJOR_VERSION) && name1->version[0] != name2->version[0]) return S_FALSE; if ((flags & ASM_CMPF_MINOR_VERSION) && name1->version[1] != name2->version[1]) return S_FALSE; if ((flags & ASM_CMPF_BUILD_NUMBER) && name1->version[2] != name2->version[2]) return S_FALSE; if ((flags & ASM_CMPF_REVISION_NUMBER) && name1->version[3] != name2->version[3]) return S_FALSE; } if ((flags & ASM_CMPF_PUBLIC_KEY_TOKEN) && name1->haspubkey && name2->haspubkey && memcmp(name1->pubkey, name2->pubkey, sizeof(name1->pubkey))) return S_FALSE; if ((flags & ASM_CMPF_CULTURE) && name1->culture && name2->culture && lstrcmpW(name1->culture, name2->culture)) return S_FALSE; return S_OK; } static HRESULT WINAPI IAssemblyNameImpl_Clone(IAssemblyName *iface, IAssemblyName **pName) { FIXME("(%p, %p) stub!\n", iface, pName); return E_NOTIMPL; } static const IAssemblyNameVtbl AssemblyNameVtbl = { IAssemblyNameImpl_QueryInterface, IAssemblyNameImpl_AddRef, IAssemblyNameImpl_Release, IAssemblyNameImpl_SetProperty, IAssemblyNameImpl_GetProperty, IAssemblyNameImpl_Finalize, IAssemblyNameImpl_GetDisplayName, IAssemblyNameImpl_Reserved, IAssemblyNameImpl_GetName, IAssemblyNameImpl_GetVersion, IAssemblyNameImpl_IsEqual, IAssemblyNameImpl_Clone }; /* Internal methods */ static inline IAssemblyNameImpl *unsafe_impl_from_IAssemblyName(IAssemblyName *iface) { assert(iface->lpVtbl == &AssemblyNameVtbl); return impl_from_IAssemblyName(iface); } HRESULT IAssemblyName_SetPath(IAssemblyName *iface, LPCWSTR path) { IAssemblyNameImpl *name = unsafe_impl_from_IAssemblyName(iface); name->path = strdupW(path); if (!name->path) return E_OUTOFMEMORY; return S_OK; } HRESULT IAssemblyName_GetPath(IAssemblyName *iface, LPWSTR buf, ULONG *len) { ULONG buffer_size = *len; IAssemblyNameImpl *name = unsafe_impl_from_IAssemblyName(iface); if (!name->path) return S_OK; if (!buf) buffer_size = 0; *len = lstrlenW(name->path) + 1; if (*len <= buffer_size) lstrcpyW(buf, name->path); else return E_NOT_SUFFICIENT_BUFFER; return S_OK; } static HRESULT parse_version(IAssemblyNameImpl *name, LPWSTR version) { LPWSTR beg, end; int i; for (i = 0, beg = version; i < 4; i++) { if (!*beg) return S_OK; end = wcschr(beg, '.'); if (end) *end = '\0'; name->version[i] = wcstol(beg, NULL, 10); name->versize++; if (!end && i < 3) return S_OK; beg = end + 1; } return S_OK; } static HRESULT parse_culture(IAssemblyNameImpl *name, LPCWSTR culture) { static const WCHAR empty[] = {0}; if (lstrlenW(culture) == 2) name->culture = strdupW(culture); else name->culture = strdupW(empty); return S_OK; } static BOOL is_hex(WCHAR c) { return ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || (c >= '0' && c <= '9')); } static BYTE hextobyte(WCHAR c) { if(c >= '0' && c <= '9') return c - '0'; if(c >= 'A' && c <= 'F') return c - 'A' + 10; if(c >= 'a' && c <= 'f') return c - 'a' + 10; return 0; } static HRESULT parse_pubkey(IAssemblyNameImpl *name, LPCWSTR pubkey) { int i; BYTE val; static const WCHAR nullstr[] = {'n','u','l','l',0}; if(lstrcmpiW(pubkey, nullstr) == 0) return FUSION_E_PRIVATE_ASM_DISALLOWED; if (lstrlenW(pubkey) < CHARS_PER_PUBKEY) return FUSION_E_INVALID_NAME; for (i = 0; i < CHARS_PER_PUBKEY; i++) if (!is_hex(pubkey[i])) return FUSION_E_INVALID_NAME; name->haspubkey = TRUE; for (i = 0; i < CHARS_PER_PUBKEY; i += 2) { val = (hextobyte(pubkey[i]) << 4) + hextobyte(pubkey[i + 1]); name->pubkey[i / 2] = val; } return S_OK; } static HRESULT parse_procarch(IAssemblyNameImpl *name, LPCWSTR procarch) { static const WCHAR msilW[] = {'m','s','i','l',0}; static const WCHAR x86W[] = {'x','8','6',0}; static const WCHAR ia64W[] = {'i','a','6','4',0}; static const WCHAR amd64W[] = {'a','m','d','6','4',0}; if (!lstrcmpiW(procarch, msilW)) name->pekind = peMSIL; else if (!lstrcmpiW(procarch, x86W)) name->pekind = peI386; else if (!lstrcmpiW(procarch, ia64W)) name->pekind = peIA64; else if (!lstrcmpiW(procarch, amd64W)) name->pekind = peAMD64; else { ERR("unrecognized architecture: %s\n", wine_dbgstr_w(procarch)); return FUSION_E_INVALID_NAME; } return S_OK; } static WCHAR *parse_value( const WCHAR *str, unsigned int len ) { WCHAR *ret; const WCHAR *p = str; BOOL quoted = FALSE; unsigned int i = 0; if (!(ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL; if (*p == '\"') { quoted = TRUE; p++; } while (*p && *p != '\"') ret[i++] = *p++; if ((quoted && *p != '\"') || (!quoted && *p == '\"')) { heap_free( ret ); return NULL; } ret[i] = 0; return ret; } static HRESULT parse_display_name(IAssemblyNameImpl *name, LPCWSTR szAssemblyName) { LPWSTR str, save, ptr, ptr2, value; HRESULT hr = S_OK; BOOL done = FALSE; if (!szAssemblyName) return S_OK; name->displayname = strdupW(szAssemblyName); if (!name->displayname) return E_OUTOFMEMORY; str = strdupW(szAssemblyName); save = str; if (!str) { hr = E_OUTOFMEMORY; goto done; } ptr = wcschr(str, ','); if (ptr) *ptr = '\0'; /* no ',' but ' ' only */ if( !ptr && wcschr(str, ' ') ) { hr = FUSION_E_INVALID_NAME; goto done; } name->name = strdupW(str); if (!name->name) { hr = E_OUTOFMEMORY; goto done; } if (!ptr) goto done; str = ptr + 1; while (!done) { ptr = wcschr(str, '='); if (!ptr) { hr = FUSION_E_INVALID_NAME; goto done; } *(ptr++) = '\0'; if (!*ptr) { hr = FUSION_E_INVALID_NAME; goto done; } if (!(ptr2 = wcschr(ptr, ','))) { if (!(ptr2 = wcschr(ptr, '\0'))) { hr = FUSION_E_INVALID_NAME; goto done; } done = TRUE; } *ptr2 = '\0'; if (!(value = parse_value( ptr, ptr2 - ptr ))) { hr = FUSION_E_INVALID_NAME; goto done; } while (*str == ' ') str++; if (!lstrcmpiW(str, version)) hr = parse_version( name, value ); else if (!lstrcmpiW(str, culture)) hr = parse_culture( name, value ); else if (!lstrcmpiW(str, pubkey)) hr = parse_pubkey( name, value ); else if (!lstrcmpiW(str, procarch)) { name->procarch = value; value = NULL; hr = parse_procarch( name, name->procarch ); } heap_free( value ); if (FAILED(hr)) goto done; str = ptr2 + 1; } done: heap_free(save); if (FAILED(hr)) { heap_free(name->displayname); heap_free(name->name); heap_free(name->culture); heap_free(name->procarch); } return hr; } /****************************************************************** * CreateAssemblyNameObject (FUSION.@) */ HRESULT WINAPI CreateAssemblyNameObject(IAssemblyName **ppAssemblyNameObj, LPCWSTR szAssemblyName, DWORD dwFlags, LPVOID pvReserved) { IAssemblyNameImpl *name; HRESULT hr; TRACE("(%p, %s, %08x, %p)\n", ppAssemblyNameObj, debugstr_w(szAssemblyName), dwFlags, pvReserved); if (!ppAssemblyNameObj) return E_INVALIDARG; if ((dwFlags & CANOF_PARSE_DISPLAY_NAME) && (!szAssemblyName || !*szAssemblyName)) return E_INVALIDARG; if (!(name = heap_alloc_zero(sizeof(*name)))) return E_OUTOFMEMORY; name->IAssemblyName_iface.lpVtbl = &AssemblyNameVtbl; name->ref = 1; hr = parse_display_name(name, szAssemblyName); if (FAILED(hr)) { heap_free(name); return hr; } *ppAssemblyNameObj = &name->IAssemblyName_iface; return S_OK; }