/* * 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 <stdarg.h> #include <assert.h> #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 "wine/debug.h" #include "wine/unicode.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; 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) { HeapFree(GetProcessHeap(), 0, This->path); HeapFree(GetProcessHeap(), 0, This->displayname); HeapFree(GetProcessHeap(), 0, This->name); HeapFree(GetProcessHeap(), 0, This->culture); HeapFree(GetProcessHeap(), 0, This->procarch); HeapFree(GetProcessHeap(), 0, 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); TRACE("(%p, %d, %p, %p)\n", iface, PropertyId, pvProperty, pcbProperty); *((LPWSTR)pvProperty) = '\0'; 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) { lstrcpyW(pvProperty, name->name); *pcbProperty = (lstrlenW(name->name) + 1) * 2; } break; case ASM_NAME_MAJOR_VERSION: *pcbProperty = 0; *((WORD *)pvProperty) = name->version[0]; if (name->versize >= 1) *pcbProperty = sizeof(WORD); break; case ASM_NAME_MINOR_VERSION: *pcbProperty = 0; *((WORD *)pvProperty) = name->version[1]; if (name->versize >= 2) *pcbProperty = sizeof(WORD); break; case ASM_NAME_BUILD_NUMBER: *pcbProperty = 0; *((WORD *)pvProperty) = name->version[2]; if (name->versize >= 3) *pcbProperty = sizeof(WORD); break; case ASM_NAME_REVISION_NUMBER: *pcbProperty = 0; *((WORD *)pvProperty) = name->version[3]; if (name->versize >= 4) *pcbProperty = sizeof(WORD); break; case ASM_NAME_CULTURE: *pcbProperty = 0; if (name->culture) { lstrcpyW(pvProperty, name->culture); *pcbProperty = (lstrlenW(name->culture) + 1) * 2; } break; case ASM_NAME_PUBLIC_KEY_TOKEN: *pcbProperty = 0; if (name->haspubkey) { memcpy(pvProperty, name->pubkey, sizeof(DWORD) * 2); *pcbProperty = sizeof(DWORD) * 2; } 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 = strlenW(name->displayname) + 1; if (*pccDisplayName < size) { *pccDisplayName = size; return E_NOT_SUFFICIENT_BUFFER; } if (szDisplayName) strcpyW(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) return S_FALSE; /* 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','x','%','0','x','%','0','x', '%','0','x','%','0','x','%','0','x','%','0','x','%','0','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, %x%08x, %p, %d, %p)\n", iface, debugstr_guid(refIID), pUnkReserved1, pUnkReserved2, debugstr_w(szReserved), (DWORD)(llReserved >> 32), (DWORD)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 = strlenW(name->name) + 1; else len = 0; if (*lpcwBuffer < len) { *lpcwBuffer = len; return E_NOT_SUFFICIENT_BUFFER; } if (!name->name) lpcwBuffer[0] = 0; else strcpyW(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) && strcmpW(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 && strcmpW(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 = strchrW(beg, '.'); if (end) *end = '\0'; name->version[i] = atolW(beg); 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 WCHAR *parse_value( const WCHAR *str, unsigned int len ) { WCHAR *ret; const WCHAR *p = str; BOOL quoted = FALSE; unsigned int i = 0; if (!(ret = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) ))) return NULL; if (*p == '\"') { quoted = TRUE; p++; } while (*p && *p != '\"') ret[i++] = *p++; if ((quoted && *p != '\"') || (!quoted && *p == '\"')) { HeapFree( GetProcessHeap(), 0, 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 = strchrW(str, ','); if (ptr) *ptr = '\0'; /* no ',' but ' ' only */ if( !ptr && strchrW(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 = strchrW(str, '='); if (!ptr) { hr = FUSION_E_INVALID_NAME; goto done; } *(ptr++) = '\0'; if (!*ptr) { hr = FUSION_E_INVALID_NAME; goto done; } if (!(ptr2 = strchrW(ptr, ','))) { if (!(ptr2 = strchrW(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; } HeapFree( GetProcessHeap(), 0, value ); if (FAILED(hr)) goto done; str = ptr2 + 1; } done: HeapFree(GetProcessHeap(), 0, save); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, name->displayname); HeapFree(GetProcessHeap(), 0, name->name); HeapFree(GetProcessHeap(), 0, name->culture); HeapFree(GetProcessHeap(), 0, 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; name = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAssemblyNameImpl)); if (!name) return E_OUTOFMEMORY; name->IAssemblyName_iface.lpVtbl = &AssemblyNameVtbl; name->ref = 1; hr = parse_display_name(name, szAssemblyName); if (FAILED(hr)) { HeapFree(GetProcessHeap(), 0, name); return hr; } *ppAssemblyNameObj = &name->IAssemblyName_iface; return S_OK; }