xref: /reactos/dll/win32/fusion/asmname.c (revision 12e94103)
1 /*
2  * IAssemblyName implementation
3  *
4  * Copyright 2008 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include <stdarg.h>
22 #include <assert.h>
23 
24 #define COBJMACROS
25 #define INITGUID
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winuser.h"
30 #include "ole2.h"
31 #include "guiddef.h"
32 #include "fusion.h"
33 #include "corerror.h"
34 #include "strsafe.h"
35 
36 #include "wine/debug.h"
37 #include "wine/unicode.h"
38 #include "fusionpriv.h"
39 
40 WINE_DEFAULT_DEBUG_CHANNEL(fusion);
41 
42 typedef struct {
43     IAssemblyName IAssemblyName_iface;
44 
45     LPWSTR path;
46 
47     LPWSTR displayname;
48     LPWSTR name;
49     LPWSTR culture;
50     LPWSTR procarch;
51 
52     WORD version[4];
53     DWORD versize;
54 
55     BYTE pubkey[8];
56     BOOL haspubkey;
57 
58     PEKIND pekind;
59 
60     LONG ref;
61 } IAssemblyNameImpl;
62 
63 static const WCHAR separator[] = {',',' ',0};
64 static const WCHAR version[] = {'V','e','r','s','i','o','n',0};
65 static const WCHAR culture[] = {'C','u','l','t','u','r','e',0};
66 static const WCHAR pubkey[] =
67     {'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
68 static const WCHAR procarch[] = {'p','r','o','c','e','s','s','o','r',
69     'A','r','c','h','i','t','e','c','t','u','r','e',0};
70 
71 #define CHARS_PER_PUBKEY 16
72 
73 static inline IAssemblyNameImpl *impl_from_IAssemblyName(IAssemblyName *iface)
74 {
75     return CONTAINING_RECORD(iface, IAssemblyNameImpl, IAssemblyName_iface);
76 }
77 
78 static HRESULT WINAPI IAssemblyNameImpl_QueryInterface(IAssemblyName *iface,
79                                                        REFIID riid, LPVOID *ppobj)
80 {
81     IAssemblyNameImpl *This = impl_from_IAssemblyName(iface);
82 
83     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
84 
85     *ppobj = NULL;
86 
87     if (IsEqualIID(riid, &IID_IUnknown) ||
88         IsEqualIID(riid, &IID_IAssemblyName))
89     {
90         IAssemblyName_AddRef(iface);
91         *ppobj = &This->IAssemblyName_iface;
92         return S_OK;
93     }
94 
95     WARN("(%p, %s, %p): not found\n", This, debugstr_guid(riid), ppobj);
96     return E_NOINTERFACE;
97 }
98 
99 static ULONG WINAPI IAssemblyNameImpl_AddRef(IAssemblyName *iface)
100 {
101     IAssemblyNameImpl *This = impl_from_IAssemblyName(iface);
102     ULONG refCount = InterlockedIncrement(&This->ref);
103 
104     TRACE("(%p)->(ref before = %u)\n", This, refCount - 1);
105 
106     return refCount;
107 }
108 
109 static ULONG WINAPI IAssemblyNameImpl_Release(IAssemblyName *iface)
110 {
111     IAssemblyNameImpl *This = impl_from_IAssemblyName(iface);
112     ULONG refCount = InterlockedDecrement(&This->ref);
113 
114     TRACE("(%p)->(ref before = %u)\n", This, refCount + 1);
115 
116     if (!refCount)
117     {
118         heap_free(This->path);
119         heap_free(This->displayname);
120         heap_free(This->name);
121         heap_free(This->culture);
122         heap_free(This->procarch);
123         heap_free(This);
124     }
125 
126     return refCount;
127 }
128 
129 static HRESULT WINAPI IAssemblyNameImpl_SetProperty(IAssemblyName *iface,
130                                                     DWORD PropertyId,
131                                                     LPVOID pvProperty,
132                                                     DWORD cbProperty)
133 {
134     FIXME("(%p, %d, %p, %d) stub!\n", iface, PropertyId, pvProperty, cbProperty);
135     return E_NOTIMPL;
136 }
137 
138 static HRESULT WINAPI IAssemblyNameImpl_GetProperty(IAssemblyName *iface,
139                                                     DWORD PropertyId,
140                                                     LPVOID pvProperty,
141                                                     LPDWORD pcbProperty)
142 {
143     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
144     DWORD size;
145 
146     TRACE("(%p, %d, %p, %p)\n", iface, PropertyId, pvProperty, pcbProperty);
147 
148     size = *pcbProperty;
149     switch (PropertyId)
150     {
151         case ASM_NAME_NULL_PUBLIC_KEY:
152         case ASM_NAME_NULL_PUBLIC_KEY_TOKEN:
153             if (name->haspubkey)
154                 return S_OK;
155             return S_FALSE;
156 
157         case ASM_NAME_NULL_CUSTOM:
158             return S_OK;
159 
160         case ASM_NAME_NAME:
161             *pcbProperty = 0;
162             if (name->name)
163             {
164                 *pcbProperty = (lstrlenW(name->name) + 1) * 2;
165                 if (size < *pcbProperty)
166                     return STRSAFE_E_INSUFFICIENT_BUFFER;
167                 lstrcpyW(pvProperty, name->name);
168             }
169             break;
170 
171         case ASM_NAME_MAJOR_VERSION:
172             *pcbProperty = 0;
173             if (name->versize >= 1)
174             {
175                 *pcbProperty = sizeof(WORD);
176                 if (size < *pcbProperty)
177                     return STRSAFE_E_INSUFFICIENT_BUFFER;
178                 *((WORD *)pvProperty) = name->version[0];
179             }
180             break;
181 
182         case ASM_NAME_MINOR_VERSION:
183             *pcbProperty = 0;
184             if (name->versize >= 2)
185             {
186                 *pcbProperty = sizeof(WORD);
187                 if (size < *pcbProperty)
188                     return STRSAFE_E_INSUFFICIENT_BUFFER;
189                 *((WORD *)pvProperty) = name->version[1];
190             }
191             break;
192 
193         case ASM_NAME_BUILD_NUMBER:
194             *pcbProperty = 0;
195             if (name->versize >= 3)
196             {
197                 *pcbProperty = sizeof(WORD);
198                 if (size < *pcbProperty)
199                     return STRSAFE_E_INSUFFICIENT_BUFFER;
200                 *((WORD *)pvProperty) = name->version[2];
201             }
202             break;
203 
204         case ASM_NAME_REVISION_NUMBER:
205             *pcbProperty = 0;
206             if (name->versize >= 4)
207             {
208                 *pcbProperty = sizeof(WORD);
209                 if (size < *pcbProperty)
210                     return STRSAFE_E_INSUFFICIENT_BUFFER;
211                 *((WORD *)pvProperty) = name->version[3];
212             }
213             break;
214 
215         case ASM_NAME_CULTURE:
216             *pcbProperty = 0;
217             if (name->culture)
218             {
219                 *pcbProperty = (lstrlenW(name->culture) + 1) * 2;
220                 if (size < *pcbProperty)
221                     return STRSAFE_E_INSUFFICIENT_BUFFER;
222                 lstrcpyW(pvProperty, name->culture);
223             }
224             break;
225 
226         case ASM_NAME_PUBLIC_KEY_TOKEN:
227             *pcbProperty = 0;
228             if (name->haspubkey)
229             {
230                 *pcbProperty = sizeof(DWORD) * 2;
231                 if (size < *pcbProperty)
232                     return STRSAFE_E_INSUFFICIENT_BUFFER;
233                 memcpy(pvProperty, name->pubkey, sizeof(DWORD) * 2);
234             }
235             break;
236 
237         case ASM_NAME_ARCHITECTURE:
238             *pcbProperty = 0;
239             if (name->pekind != peNone)
240             {
241                 *pcbProperty = sizeof(PEKIND);
242                 if (size < *pcbProperty)
243                     return STRSAFE_E_INSUFFICIENT_BUFFER;
244                 *((PEKIND *)pvProperty) = name->pekind;
245             }
246             break;
247 
248         default:
249             *pcbProperty = 0;
250             break;
251     }
252 
253     return S_OK;
254 }
255 
256 static HRESULT WINAPI IAssemblyNameImpl_Finalize(IAssemblyName *iface)
257 {
258     FIXME("(%p) stub!\n", iface);
259     return E_NOTIMPL;
260 }
261 
262 static HRESULT WINAPI IAssemblyNameImpl_GetDisplayName(IAssemblyName *iface,
263                                                        LPOLESTR szDisplayName,
264                                                        LPDWORD pccDisplayName,
265                                                        DWORD dwDisplayFlags)
266 {
267     static const WCHAR equals[] = {'=',0};
268     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
269     WCHAR verstr[30], *cultureval = NULL;
270     DWORD size;
271 
272     TRACE("(%p, %p, %p, %d)\n", iface, szDisplayName,
273           pccDisplayName, dwDisplayFlags);
274 
275     if (dwDisplayFlags == 0)
276     {
277         if (!name->displayname || !*name->displayname)
278             return FUSION_E_INVALID_NAME;
279 
280         size = strlenW(name->displayname) + 1;
281 
282         if (*pccDisplayName < size)
283         {
284             *pccDisplayName = size;
285             return E_NOT_SUFFICIENT_BUFFER;
286         }
287 
288         if (szDisplayName) strcpyW(szDisplayName, name->displayname);
289         *pccDisplayName = size;
290 
291         return S_OK;
292     }
293 
294     if (!name->name || !*name->name)
295         return FUSION_E_INVALID_NAME;
296 
297     /* Verify buffer size is sufficient */
298     size = lstrlenW(name->name) + 1;
299 
300     if ((dwDisplayFlags & ASM_DISPLAYF_VERSION) && (name->versize > 0))
301     {
302         static const WCHAR spec[] = {'%','d',0};
303         static const WCHAR period[] = {'.',0};
304         DWORD i;
305 
306         wsprintfW(verstr, spec, name->version[0]);
307 
308         for (i = 1; i < name->versize; i++)
309         {
310             WCHAR value[6];
311             wsprintfW(value, spec, name->version[i]);
312 
313             lstrcatW(verstr, period);
314             lstrcatW(verstr, value);
315         }
316 
317         size += lstrlenW(separator) + lstrlenW(version) + lstrlenW(equals) + lstrlenW(verstr);
318     }
319 
320     if ((dwDisplayFlags & ASM_DISPLAYF_CULTURE) && (name->culture))
321     {
322         static const WCHAR neutral[] = {'n','e','u','t','r','a','l', 0};
323 
324         cultureval = (lstrlenW(name->culture) == 2) ? name->culture : (LPWSTR) neutral;
325         size += lstrlenW(separator) + lstrlenW(culture) + lstrlenW(equals) + lstrlenW(cultureval);
326     }
327 
328     if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && (name->haspubkey))
329         size += lstrlenW(separator) + lstrlenW(pubkey) + lstrlenW(equals) + CHARS_PER_PUBKEY;
330 
331     if ((dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) && (name->procarch))
332         size += lstrlenW(separator) + lstrlenW(procarch) + lstrlenW(equals) + lstrlenW(name->procarch);
333 
334     if (size > *pccDisplayName)
335     {
336         *pccDisplayName = size;
337         return E_NOT_SUFFICIENT_BUFFER;
338     }
339 
340     /* Construct the string */
341     lstrcpyW(szDisplayName, name->name);
342 
343     if ((dwDisplayFlags & ASM_DISPLAYF_VERSION) && (name->versize > 0))
344     {
345         lstrcatW(szDisplayName, separator);
346 
347         lstrcatW(szDisplayName, version);
348         lstrcatW(szDisplayName, equals);
349         lstrcatW(szDisplayName, verstr);
350     }
351 
352     if ((dwDisplayFlags & ASM_DISPLAYF_CULTURE) && (name->culture))
353     {
354         lstrcatW(szDisplayName, separator);
355 
356         lstrcatW(szDisplayName, culture);
357         lstrcatW(szDisplayName, equals);
358         lstrcatW(szDisplayName, cultureval);
359     }
360 
361     if ((dwDisplayFlags & ASM_DISPLAYF_PUBLIC_KEY_TOKEN) && (name->haspubkey))
362     {
363         WCHAR pkt[CHARS_PER_PUBKEY + 1];
364         static const WCHAR spec[] = {'%','0','2','x','%','0','2','x','%','0','2','x',
365             '%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x','%','0','2','x',0};
366 
367         lstrcatW(szDisplayName, separator);
368 
369         lstrcatW(szDisplayName, pubkey);
370         lstrcatW(szDisplayName, equals);
371 
372         wsprintfW(pkt, spec, name->pubkey[0], name->pubkey[1], name->pubkey[2],
373             name->pubkey[3], name->pubkey[4], name->pubkey[5], name->pubkey[6],
374             name->pubkey[7]);
375 
376         lstrcatW(szDisplayName, pkt);
377     }
378 
379     if ((dwDisplayFlags & ASM_DISPLAYF_PROCESSORARCHITECTURE) && (name->procarch))
380     {
381         lstrcatW(szDisplayName, separator);
382 
383         lstrcatW(szDisplayName, procarch);
384         lstrcatW(szDisplayName, equals);
385         lstrcatW(szDisplayName, name->procarch);
386     }
387 
388     *pccDisplayName = size;
389     return S_OK;
390 }
391 
392 static HRESULT WINAPI IAssemblyNameImpl_Reserved(IAssemblyName *iface,
393                                                  REFIID refIID,
394                                                  IUnknown *pUnkReserved1,
395                                                  IUnknown *pUnkReserved2,
396                                                  LPCOLESTR szReserved,
397                                                  LONGLONG llReserved,
398                                                  LPVOID pvReserved,
399                                                  DWORD cbReserved,
400                                                  LPVOID *ppReserved)
401 {
402     TRACE("(%p, %s, %p, %p, %s, %s, %p, %d, %p)\n", iface,
403           debugstr_guid(refIID), pUnkReserved1, pUnkReserved2,
404           debugstr_w(szReserved), wine_dbgstr_longlong(llReserved),
405           pvReserved, cbReserved, ppReserved);
406 
407     return E_NOTIMPL;
408 }
409 
410 static HRESULT WINAPI IAssemblyNameImpl_GetName(IAssemblyName *iface,
411                                                 LPDWORD lpcwBuffer,
412                                                 WCHAR *pwzName)
413 {
414     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
415     DWORD len;
416 
417     TRACE("(%p, %p, %p)\n", iface, lpcwBuffer, pwzName);
418 
419     if (name->name)
420         len = strlenW(name->name) + 1;
421     else
422         len = 0;
423 
424     if (*lpcwBuffer < len)
425     {
426         *lpcwBuffer = len;
427         return E_NOT_SUFFICIENT_BUFFER;
428     }
429     if (!name->name) lpcwBuffer[0] = 0;
430     else strcpyW(pwzName, name->name);
431 
432     *lpcwBuffer = len;
433     return S_OK;
434 }
435 
436 static HRESULT WINAPI IAssemblyNameImpl_GetVersion(IAssemblyName *iface,
437                                                    LPDWORD pdwVersionHi,
438                                                    LPDWORD pdwVersionLow)
439 {
440     IAssemblyNameImpl *name = impl_from_IAssemblyName(iface);
441 
442     TRACE("(%p, %p, %p)\n", iface, pdwVersionHi, pdwVersionLow);
443 
444     *pdwVersionHi = 0;
445     *pdwVersionLow = 0;
446 
447     if (name->versize != 4)
448         return FUSION_E_INVALID_NAME;
449 
450     *pdwVersionHi = (name->version[0] << 16) + name->version[1];
451     *pdwVersionLow = (name->version[2] << 16) + name->version[3];
452 
453     return S_OK;
454 }
455 
456 static HRESULT WINAPI IAssemblyNameImpl_IsEqual(IAssemblyName *iface,
457                                                 IAssemblyName *pName,
458                                                 DWORD flags)
459 {
460     IAssemblyNameImpl *name1 = impl_from_IAssemblyName(iface);
461     IAssemblyNameImpl *name2 = impl_from_IAssemblyName(pName);
462 
463     TRACE("(%p, %p, 0x%08x)\n", iface, pName, flags);
464 
465     if (!pName) return S_FALSE;
466     if (flags & ~ASM_CMPF_IL_ALL) FIXME("unsupported flags\n");
467 
468     if ((flags & ASM_CMPF_NAME) && strcmpW(name1->name, name2->name)) return S_FALSE;
469     if (name1->versize && name2->versize)
470     {
471         if ((flags & ASM_CMPF_MAJOR_VERSION) &&
472             name1->version[0] != name2->version[0]) return S_FALSE;
473         if ((flags & ASM_CMPF_MINOR_VERSION) &&
474             name1->version[1] != name2->version[1]) return S_FALSE;
475         if ((flags & ASM_CMPF_BUILD_NUMBER) &&
476             name1->version[2] != name2->version[2]) return S_FALSE;
477         if ((flags & ASM_CMPF_REVISION_NUMBER) &&
478             name1->version[3] != name2->version[3]) return S_FALSE;
479     }
480     if ((flags & ASM_CMPF_PUBLIC_KEY_TOKEN) &&
481         name1->haspubkey && name2->haspubkey &&
482         memcmp(name1->pubkey, name2->pubkey, sizeof(name1->pubkey))) return S_FALSE;
483 
484     if ((flags & ASM_CMPF_CULTURE) &&
485         name1->culture && name2->culture &&
486         strcmpW(name1->culture, name2->culture)) return S_FALSE;
487 
488     return S_OK;
489 }
490 
491 static HRESULT WINAPI IAssemblyNameImpl_Clone(IAssemblyName *iface,
492                                               IAssemblyName **pName)
493 {
494     FIXME("(%p, %p) stub!\n", iface, pName);
495     return E_NOTIMPL;
496 }
497 
498 static const IAssemblyNameVtbl AssemblyNameVtbl = {
499     IAssemblyNameImpl_QueryInterface,
500     IAssemblyNameImpl_AddRef,
501     IAssemblyNameImpl_Release,
502     IAssemblyNameImpl_SetProperty,
503     IAssemblyNameImpl_GetProperty,
504     IAssemblyNameImpl_Finalize,
505     IAssemblyNameImpl_GetDisplayName,
506     IAssemblyNameImpl_Reserved,
507     IAssemblyNameImpl_GetName,
508     IAssemblyNameImpl_GetVersion,
509     IAssemblyNameImpl_IsEqual,
510     IAssemblyNameImpl_Clone
511 };
512 
513 /* Internal methods */
514 static inline IAssemblyNameImpl *unsafe_impl_from_IAssemblyName(IAssemblyName *iface)
515 {
516     assert(iface->lpVtbl == &AssemblyNameVtbl);
517 
518     return impl_from_IAssemblyName(iface);
519 }
520 
521 HRESULT IAssemblyName_SetPath(IAssemblyName *iface, LPCWSTR path)
522 {
523     IAssemblyNameImpl *name = unsafe_impl_from_IAssemblyName(iface);
524 
525     name->path = strdupW(path);
526     if (!name->path)
527         return E_OUTOFMEMORY;
528 
529     return S_OK;
530 }
531 
532 HRESULT IAssemblyName_GetPath(IAssemblyName *iface, LPWSTR buf, ULONG *len)
533 {
534     ULONG buffer_size = *len;
535     IAssemblyNameImpl *name = unsafe_impl_from_IAssemblyName(iface);
536 
537     if (!name->path)
538         return S_OK;
539 
540     if (!buf)
541         buffer_size = 0;
542 
543     *len = lstrlenW(name->path) + 1;
544 
545     if (*len <= buffer_size)
546         lstrcpyW(buf, name->path);
547     else
548         return E_NOT_SUFFICIENT_BUFFER;
549 
550     return S_OK;
551 }
552 
553 static HRESULT parse_version(IAssemblyNameImpl *name, LPWSTR version)
554 {
555     LPWSTR beg, end;
556     int i;
557 
558     for (i = 0, beg = version; i < 4; i++)
559     {
560         if (!*beg)
561             return S_OK;
562 
563         end = strchrW(beg, '.');
564 
565         if (end) *end = '\0';
566         name->version[i] = atolW(beg);
567         name->versize++;
568 
569         if (!end && i < 3)
570             return S_OK;
571 
572         beg = end + 1;
573     }
574 
575     return S_OK;
576 }
577 
578 static HRESULT parse_culture(IAssemblyNameImpl *name, LPCWSTR culture)
579 {
580     static const WCHAR empty[] = {0};
581 
582     if (lstrlenW(culture) == 2)
583         name->culture = strdupW(culture);
584     else
585         name->culture = strdupW(empty);
586 
587     return S_OK;
588 }
589 
590 static BOOL is_hex(WCHAR c)
591 {
592     return ((c >= 'a' && c <= 'f') ||
593             (c >= 'A' && c <= 'F') ||
594             (c >= '0' && c <= '9'));
595 }
596 
597 static BYTE hextobyte(WCHAR c)
598 {
599     if(c >= '0' && c <= '9')
600         return c - '0';
601     if(c >= 'A' && c <= 'F')
602         return c - 'A' + 10;
603     if(c >= 'a' && c <= 'f')
604         return c - 'a' + 10;
605     return 0;
606 }
607 
608 static HRESULT parse_pubkey(IAssemblyNameImpl *name, LPCWSTR pubkey)
609 {
610     int i;
611     BYTE val;
612     static const WCHAR nullstr[] = {'n','u','l','l',0};
613 
614     if(lstrcmpiW(pubkey, nullstr) == 0)
615         return FUSION_E_PRIVATE_ASM_DISALLOWED;
616 
617     if (lstrlenW(pubkey) < CHARS_PER_PUBKEY)
618         return FUSION_E_INVALID_NAME;
619 
620     for (i = 0; i < CHARS_PER_PUBKEY; i++)
621         if (!is_hex(pubkey[i]))
622             return FUSION_E_INVALID_NAME;
623 
624     name->haspubkey = TRUE;
625 
626     for (i = 0; i < CHARS_PER_PUBKEY; i += 2)
627     {
628         val = (hextobyte(pubkey[i]) << 4) + hextobyte(pubkey[i + 1]);
629         name->pubkey[i / 2] = val;
630     }
631 
632     return S_OK;
633 }
634 
635 static HRESULT parse_procarch(IAssemblyNameImpl *name, LPCWSTR procarch)
636 {
637     static const WCHAR msilW[] = {'m','s','i','l',0};
638     static const WCHAR x86W[] = {'x','8','6',0};
639     static const WCHAR ia64W[] = {'i','a','6','4',0};
640     static const WCHAR amd64W[] = {'a','m','d','6','4',0};
641 
642     if (!lstrcmpiW(procarch, msilW))
643         name->pekind = peMSIL;
644     else if (!lstrcmpiW(procarch, x86W))
645         name->pekind = peI386;
646     else if (!lstrcmpiW(procarch, ia64W))
647         name->pekind = peIA64;
648     else if (!lstrcmpiW(procarch, amd64W))
649         name->pekind = peAMD64;
650     else
651     {
652         ERR("unrecognized architecture: %s\n", wine_dbgstr_w(procarch));
653         return FUSION_E_INVALID_NAME;
654     }
655 
656     return S_OK;
657 }
658 
659 static WCHAR *parse_value( const WCHAR *str, unsigned int len )
660 {
661     WCHAR *ret;
662     const WCHAR *p = str;
663     BOOL quoted = FALSE;
664     unsigned int i = 0;
665 
666     if (!(ret = heap_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
667     if (*p == '\"')
668     {
669         quoted = TRUE;
670         p++;
671     }
672     while (*p && *p != '\"') ret[i++] = *p++;
673     if ((quoted && *p != '\"') || (!quoted && *p == '\"'))
674     {
675         heap_free( ret );
676         return NULL;
677     }
678     ret[i] = 0;
679     return ret;
680 }
681 
682 static HRESULT parse_display_name(IAssemblyNameImpl *name, LPCWSTR szAssemblyName)
683 {
684     LPWSTR str, save, ptr, ptr2, value;
685     HRESULT hr = S_OK;
686     BOOL done = FALSE;
687 
688     if (!szAssemblyName)
689         return S_OK;
690 
691     name->displayname = strdupW(szAssemblyName);
692     if (!name->displayname)
693         return E_OUTOFMEMORY;
694 
695     str = strdupW(szAssemblyName);
696     save = str;
697     if (!str)
698     {
699         hr = E_OUTOFMEMORY;
700         goto done;
701     }
702 
703     ptr = strchrW(str, ',');
704     if (ptr) *ptr = '\0';
705 
706     /* no ',' but ' ' only */
707     if( !ptr && strchrW(str, ' ') )
708     {
709         hr = FUSION_E_INVALID_NAME;
710         goto done;
711     }
712 
713     name->name = strdupW(str);
714     if (!name->name)
715     {
716         hr = E_OUTOFMEMORY;
717         goto done;
718     }
719 
720     if (!ptr)
721         goto done;
722 
723     str = ptr + 1;
724     while (!done)
725     {
726         ptr = strchrW(str, '=');
727         if (!ptr)
728         {
729             hr = FUSION_E_INVALID_NAME;
730             goto done;
731         }
732 
733         *(ptr++) = '\0';
734         if (!*ptr)
735         {
736             hr = FUSION_E_INVALID_NAME;
737             goto done;
738         }
739 
740         if (!(ptr2 = strchrW(ptr, ',')))
741         {
742             if (!(ptr2 = strchrW(ptr, '\0')))
743             {
744                 hr = FUSION_E_INVALID_NAME;
745                 goto done;
746             }
747 
748             done = TRUE;
749         }
750 
751         *ptr2 = '\0';
752         if (!(value = parse_value( ptr, ptr2 - ptr )))
753         {
754             hr = FUSION_E_INVALID_NAME;
755             goto done;
756         }
757         while (*str == ' ') str++;
758 
759         if (!lstrcmpiW(str, version))
760             hr = parse_version( name, value );
761         else if (!lstrcmpiW(str, culture))
762             hr = parse_culture( name, value );
763         else if (!lstrcmpiW(str, pubkey))
764             hr = parse_pubkey( name, value );
765         else if (!lstrcmpiW(str, procarch))
766         {
767             name->procarch = value;
768             value = NULL;
769 
770             hr = parse_procarch( name, name->procarch );
771         }
772         heap_free( value );
773 
774         if (FAILED(hr))
775             goto done;
776 
777         str = ptr2 + 1;
778     }
779 
780 done:
781     heap_free(save);
782     if (FAILED(hr))
783     {
784         heap_free(name->displayname);
785         heap_free(name->name);
786         heap_free(name->culture);
787         heap_free(name->procarch);
788     }
789     return hr;
790 }
791 
792 /******************************************************************
793  *  CreateAssemblyNameObject   (FUSION.@)
794  */
795 HRESULT WINAPI CreateAssemblyNameObject(IAssemblyName **ppAssemblyNameObj,
796                                         LPCWSTR szAssemblyName, DWORD dwFlags,
797                                         LPVOID pvReserved)
798 {
799     IAssemblyNameImpl *name;
800     HRESULT hr;
801 
802     TRACE("(%p, %s, %08x, %p)\n", ppAssemblyNameObj,
803           debugstr_w(szAssemblyName), dwFlags, pvReserved);
804 
805     if (!ppAssemblyNameObj)
806         return E_INVALIDARG;
807 
808     if ((dwFlags & CANOF_PARSE_DISPLAY_NAME) &&
809         (!szAssemblyName || !*szAssemblyName))
810         return E_INVALIDARG;
811 
812     if (!(name = heap_alloc_zero(sizeof(*name)))) return E_OUTOFMEMORY;
813 
814     name->IAssemblyName_iface.lpVtbl = &AssemblyNameVtbl;
815     name->ref = 1;
816 
817     hr = parse_display_name(name, szAssemblyName);
818     if (FAILED(hr))
819     {
820         heap_free(name);
821         return hr;
822     }
823 
824     *ppAssemblyNameObj = &name->IAssemblyName_iface;
825 
826     return S_OK;
827 }
828