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