1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//
5
6//
7// Contains helper types for assembly binding host infrastructure.
8
9#ifndef __CLRPRIVBINDERUTIL_H__
10#define __CLRPRIVBINDERUTIL_H__
11
12#include "holder.h"
13#include "internalunknownimpl.h"
14#include "clrprivbinding.h"
15#include "slist.h"
16#ifdef FEATURE_COMINTEROP
17#include "windowsstring.h"
18#endif // FEATURE_COMINTEROP
19#include "strongnameholders.h"
20
21//=====================================================================================================================
22#define STANDARD_BIND_CONTRACT \
23 CONTRACTL { \
24 NOTHROW; \
25 GC_TRIGGERS; \
26 MODE_PREEMPTIVE; \
27 } CONTRACTL_END
28
29//=====================================================================================================================
30// Forward declarations
31interface ICLRPrivAssembly;
32typedef DPTR(ICLRPrivAssembly) PTR_ICLRPrivAssembly;
33typedef DPTR(ICLRPrivBinder) PTR_ICLRPrivBinder;
34class PEAssembly;
35class AssemblySpec;
36
37//=====================================================================================================================
38#define VALIDATE_CONDITION(condition, fail_op) \
39 do { \
40 _ASSERTE((condition)); \
41 if (!(condition)) \
42 fail_op; \
43 } while (false)
44
45#define VALIDATE_PTR_RET(val) VALIDATE_CONDITION(val != nullptr, return E_POINTER)
46#define VALIDATE_PTR_THROW(val) VALIDATE_CONDITION(val != nullptr, ThrowHR(E_POINTER))
47#define VALIDATE_ARG_RET(condition) VALIDATE_CONDITION(condition, return E_INVALIDARG)
48#define VALIDATE_ARG_THROW(condition) VALIDATE_CONDITION(condition, ThrowHR(E_INVALIDARG))
49
50//=====================================================================================================================
51namespace CLRPrivBinderUtil
52{
53 //=================================================================================================================
54 enum BindFlags
55 {
56 BF_BindIL = 1,
57 BF_BindNI = 2,
58 BF_Default = BF_BindIL | BF_BindNI,
59 };
60
61 //=================================================================================================================
62 template <typename ItfT, typename ObjT>
63 inline ItfT * ToInterface(
64 ObjT * && pObj)
65 {
66 STATIC_CONTRACT_THROWS;
67
68 ItfT * pItf = nullptr;
69 IfFailThrow(pObj->QueryInterface(__uuidof(ItfT), (void **)&pItf));
70 return pItf;
71 }
72
73 //=================================================================================================================
74 template <typename ItfT, typename ObjT>
75 inline ItfT * ToInterface_NoThrow(
76 ObjT * && pObj)
77 {
78 LIMITED_METHOD_CONTRACT;
79
80 ItfT * pItf = nullptr;
81 if (FAILED(pObj->QueryInterface(__uuidof(ItfT), (void **)&pItf)))
82 {
83 return nullptr;
84 }
85 return pItf;
86 }
87
88 //=====================================================================================================================
89
90 //=================================================================================================================
91 // Used to create an identity-only ICLRPrivAssembly from an ICLRPrivBinder. This is currently used when
92 // creating dynamic assemblies as these use the parent assembly's ICLRPrivBinder object to provide binding
93 // functionaltiy.
94
95 class CLRPrivBinderAsAssemblyWrapper :
96 public IUnknownCommon<ICLRPrivAssembly>
97 {
98 public:
99 //-----------------------------------------------------------------------------------------------------------------
100 CLRPrivBinderAsAssemblyWrapper(
101 ICLRPrivBinder *pWrapped)
102 : _pWrapped(clr::SafeAddRef(pWrapped))
103 {
104 STANDARD_VM_CONTRACT;
105 VALIDATE_ARG_THROW(pWrapped);
106 }
107
108 //-----------------------------------------------------------------------------------------------------------------
109 // Forwards to wrapped binder.
110 STDMETHOD(BindAssemblyByName)(
111 IAssemblyName * pAssemblyName,
112 ICLRPrivAssembly ** ppAssembly)
113 {
114 WRAPPER_NO_CONTRACT;
115 return _pWrapped->BindAssemblyByName(pAssemblyName, ppAssembly);
116 }
117
118 //-----------------------------------------------------------------------------------------------------------------
119 // Forwards to wrapped binder.
120 STDMETHOD(GetBinderID)(
121 UINT_PTR *pBinderId)
122 {
123 WRAPPER_NO_CONTRACT;
124 return _pWrapped->GetBinderID(pBinderId);
125 }
126
127 //-----------------------------------------------------------------------------------------------------------------
128 // Forwards to wrapped binder.
129 STDMETHOD(GetLoaderAllocator)(
130 LPVOID * pLoaderAllocator)
131 {
132 WRAPPER_NO_CONTRACT;
133 return _pWrapped->GetLoaderAllocator(pLoaderAllocator);
134 }
135
136 //-----------------------------------------------------------------------------------------------------------------
137 // ICLRPrivAssembly method is unsupported.
138 STDMETHOD(IsShareable)(
139 BOOL * pbIsShareable)
140 {
141 LIMITED_METHOD_CONTRACT;
142 _ASSERTE_MSG(false, "CLRPrivBinderAsAssemblyWrapper does not support ICLRPrivAssembly methods (just ICLRPrivBinder ones)!");
143 VALIDATE_ARG_RET(pbIsShareable);
144 *pbIsShareable = FALSE;
145 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
146 }
147
148 //-----------------------------------------------------------------------------------------------------------------
149 // ICLRPrivAssembly method is unsupported.
150 STDMETHOD(GetAvailableImageTypes)(
151 LPDWORD pdwImageTypes)
152 {
153 LIMITED_METHOD_CONTRACT;
154 _ASSERTE_MSG(false, "CLRPrivBinderAsAssemblyWrapper does not support ICLRPrivAssembly methods (just ICLRPrivBinder ones)!");
155 VALIDATE_ARG_RET(pdwImageTypes);
156 *pdwImageTypes = 0;
157 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
158 }
159
160 //-----------------------------------------------------------------------------------------------------------------
161 // ICLRPrivAssembly method is unsupported.
162 STDMETHOD(GetImageResource)(
163 DWORD dwImageType,
164 DWORD* pdwImageType,
165 ICLRPrivResource ** ppIResource)
166 {
167 LIMITED_METHOD_CONTRACT;
168 _ASSERTE_MSG(false, "CLRPrivBinderAsAssemblyWrapper does not support ICLRPrivAssembly methods (just ICLRPrivBinder ones)!");
169 VALIDATE_ARG_RET(pdwImageType);
170 *pdwImageType = 0;
171 return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
172 }
173
174 private:
175 ReleaseHolder<ICLRPrivBinder> _pWrapped;
176 };
177
178 //=================================================================================================================
179 // Provides a struct that can be accessed at the QWORD, DWORD, or WORD level, and is structured
180
181 struct AssemblyVersion
182 {
183#if BIGENDIAN
184 union
185 {
186 UINT64 qwMajorMinorBuildRevision;
187 struct
188 {
189 union
190 {
191 DWORD dwMajorMinor;
192 struct
193 {
194 WORD wMajor;
195 WORD wMinor;
196 };
197 };
198 union
199 {
200 DWORD dwBuildRevision;
201 struct
202 {
203 WORD wBuild;
204 WORD wRevision;
205 };
206 };
207 };
208 };
209#else
210 union
211 {
212 UINT64 qwMajorMinorBuildRevision;
213 struct
214 {
215 union
216 {
217 DWORD dwBuildRevision;
218 struct
219 {
220 WORD wRevision;
221 WORD wBuild;
222 };
223 };
224 union
225 {
226 DWORD dwMajorMinor;
227 struct
228 {
229 WORD wMinor;
230 WORD wMajor;
231 };
232 };
233 };
234 };
235#endif
236
237 // Default value is 0.0.0.0
238 AssemblyVersion()
239 : qwMajorMinorBuildRevision(static_cast<UINT64>(0))
240 { LIMITED_METHOD_CONTRACT; }
241
242 // Copy constructor
243 AssemblyVersion(AssemblyVersion const & other)
244 : qwMajorMinorBuildRevision(other.qwMajorMinorBuildRevision)
245 { LIMITED_METHOD_CONTRACT; }
246
247 // Initialize version using an IAssemblyName object.
248 HRESULT Initialize(IAssemblyName * pName);
249
250 // Initialize version using an ICLRPrivAssemblyInfo object.
251 HRESULT Initialize(ICLRPrivAssemblyInfo * pInfo);
252
253 // Relative ordering of versions.
254 static inline int Compare(
255 AssemblyVersion const & left,
256 AssemblyVersion const & right)
257 {
258 LIMITED_METHOD_CONTRACT;
259
260 if (left.qwMajorMinorBuildRevision < right.qwMajorMinorBuildRevision)
261 return -1;
262 else if (left.qwMajorMinorBuildRevision == right.qwMajorMinorBuildRevision)
263 return 0;
264 else
265 return 1;
266 }
267 }; // struct AssemblyVersion
268
269 inline bool operator ==(AssemblyVersion const & lhs, AssemblyVersion const & rhs)
270 { LIMITED_METHOD_CONTRACT; return lhs.qwMajorMinorBuildRevision == rhs.qwMajorMinorBuildRevision; }
271
272 inline bool operator !=(AssemblyVersion const & lhs, AssemblyVersion const & rhs)
273 { LIMITED_METHOD_CONTRACT; return lhs.qwMajorMinorBuildRevision != rhs.qwMajorMinorBuildRevision; }
274
275 inline bool operator <(AssemblyVersion const & lhs, AssemblyVersion const & rhs)
276 { LIMITED_METHOD_CONTRACT; return lhs.qwMajorMinorBuildRevision < rhs.qwMajorMinorBuildRevision; }
277
278 inline bool operator <=(AssemblyVersion const & lhs, AssemblyVersion const & rhs)
279 { LIMITED_METHOD_CONTRACT; return lhs.qwMajorMinorBuildRevision <= rhs.qwMajorMinorBuildRevision; }
280
281 inline bool operator >(AssemblyVersion const & lhs, AssemblyVersion const & rhs)
282 { LIMITED_METHOD_CONTRACT; return lhs.qwMajorMinorBuildRevision > rhs.qwMajorMinorBuildRevision; }
283
284 inline bool operator >=(AssemblyVersion const & lhs, AssemblyVersion const & rhs)
285 { LIMITED_METHOD_CONTRACT; return lhs.qwMajorMinorBuildRevision >= rhs.qwMajorMinorBuildRevision; }
286
287 //=================================================================================================================
288 // Encapsulates PublicKey value, can be initialized using a variety of data sources.
289
290 struct PublicKey
291 {
292 // Defaults to empty value.
293 PublicKey()
294 : m_key(nullptr)
295 , m_key_owned(false)
296 , m_size((DWORD)-1)
297 { LIMITED_METHOD_CONTRACT; }
298
299 // Construct directly from existing public key data.
300 PublicKey(PBYTE pbKey, DWORD cbKey)
301 : m_key(pbKey)
302 , m_key_owned(false)
303 , m_size(cbKey)
304 { LIMITED_METHOD_CONTRACT; }
305
306 ~PublicKey()
307 { WRAPPER_NO_CONTRACT; Uninitialize(); }
308
309 // Frees any public key data and resets to default value.
310 void Uninitialize()
311 {
312 LIMITED_METHOD_CONTRACT;
313
314 if (m_key_owned)
315 {
316 delete [] m_key;
317 m_key_owned = false;
318 }
319 m_key = nullptr;
320 m_size = 0;
321 }
322
323 // Initialize PK data form an ICLRPrivAssemblyInfo object.
324 HRESULT Initialize(ICLRPrivAssemblyInfo * pAssemblyInfo);
325
326 // Returns PK data pointer.
327 inline BYTE const * GetKey() const
328 { LIMITED_METHOD_CONTRACT; return m_key; }
329
330 // Returns size in bytes of the PK data.
331 inline DWORD GetSize() const
332 { LIMITED_METHOD_CONTRACT; return m_size; }
333
334 private:
335 PBYTE m_key;
336 bool m_key_owned;
337 DWORD m_size;
338 };
339
340 //=================================================================================================================
341 // Encapsulates PublicKeyToken value, can be initialized using a variety of data sources.
342 //
343 // Constraints: assumes that non-empty PKT data will always be 8 bytes long.
344 //
345
346 struct PublicKeyToken
347 {
348 PublicKeyToken()
349 : m_cbKeyToken(0)
350 { LIMITED_METHOD_CONTRACT; ZeroMemory(&m_rbKeyToken, sizeof(m_rbKeyToken)); }
351
352 PublicKeyToken(PublicKeyToken const & other)
353 : m_cbKeyToken(other.m_cbKeyToken)
354 { LIMITED_METHOD_CONTRACT; CopyMemory(m_rbKeyToken, other.m_rbKeyToken, sizeof(m_rbKeyToken)); }
355
356 // Initialize directly from PKT data.
357 HRESULT Initialize(BYTE * pbKeyToken, DWORD cbKeyToken);
358
359 // Converts PK data to PKT data.
360 HRESULT Initialize(PublicKey const & pk);
361
362 // Initialize using the PKT value contained by pName; returns S_FALSE if there is no associated PKT.
363 HRESULT Initialize(IAssemblyName * pName);
364
365 // Initialize using the PK data contained by pInfo; returns S_FALSE if there is no associated PK.
366 HRESULT Initialize(ICLRPrivAssemblyInfo * pInfo);
367
368 // PKT data.
369 BYTE const * GetToken() const
370 { LIMITED_METHOD_CONTRACT; return m_rbKeyToken; }
371
372 // Size in bytes of the PKT (should always be 0 or 8).
373 DWORD GetSize() const
374 { LIMITED_METHOD_CONTRACT; return m_cbKeyToken; }
375
376 private:
377 static const DWORD PUBLIC_KEY_TOKEN_LEN1 = 8;
378 BYTE m_rbKeyToken[PUBLIC_KEY_TOKEN_LEN1];
379 DWORD m_cbKeyToken;
380 };
381
382 bool operator==(PublicKeyToken const & lhs, PublicKeyToken const & rhs);
383
384 inline bool operator!=(PublicKeyToken const & lhs, PublicKeyToken const & rhs)
385 { WRAPPER_NO_CONTRACT; return !(lhs == rhs); }
386
387 //=================================================================================================================
388 // Encapsulates data required for packaged assembly identity: simple name, version, and public key token.
389 //
390 // Constraints: assumes that the assembly simple name is no longer than _MAX_PATH
391 //
392
393 struct AssemblyIdentity
394 {
395 AssemblyIdentity()
396 { LIMITED_METHOD_CONTRACT; Name[0] = W('\0'); }
397
398 AssemblyIdentity(AssemblyIdentity const & other)
399 : Version(other.Version)
400 , KeyToken(other.KeyToken)
401 { LIMITED_METHOD_CONTRACT; CopyMemory(Name, other.Name, sizeof(Name)); }
402
403 // Initialize from assembly simple name; default version and empty PKT values are used.
404 HRESULT Initialize(LPCWSTR wzName);
405
406 // Initialize from an ICLRPrivAssemblyInfo object.
407 HRESULT Initialize(ICLRPrivAssemblyInfo * pAssemblyInfo);
408
409 // Initialize from an IAssemblyName object.
410 HRESULT Initialize(IAssemblyName * pAssemblyName);
411
412 // Initialize from an AssemblySpec object.
413 HRESULT Initialize(AssemblySpec * pSpec);
414
415 // Assembly simple name
416 WCHAR Name[_MAX_PATH];
417
418 // Assembly version; defaults to 0.0.0.0.
419 CLRPrivBinderUtil::AssemblyVersion Version;
420
421 // Assembly public key token; defaults to none.
422 CLRPrivBinderUtil::PublicKeyToken KeyToken;
423 };
424
425 //=================================================================================================
426 template <typename ItfT>
427 struct CLRPrivResourceBase :
428 public IUnknownCommon<ICLRPrivResource>
429 {
430 //---------------------------------------------------------------------------------------------
431 STDMETHOD(GetResourceType)(
432 IID *pIID)
433 {
434 LIMITED_METHOD_CONTRACT;
435 if (pIID == nullptr)
436 return E_INVALIDARG;
437 *pIID = __uuidof(ItfT);
438 return S_OK;
439 }
440 };
441
442 //=================================================================================================================
443 class CLRPrivResourcePathImpl :
444 public IUnknownCommon< ItfBase< CLRPrivResourceBase< ICLRPrivResourcePath > >,
445 ICLRPrivResourcePath >
446 {
447 public:
448 //---------------------------------------------------------------------------------------------
449 CLRPrivResourcePathImpl(LPCWSTR wzPath);
450
451 //---------------------------------------------------------------------------------------------
452 LPCWSTR GetPath()
453 { return m_wzPath; }
454
455 //
456 // ICLRPrivResourcePath methods
457 //
458
459 //---------------------------------------------------------------------------------------------
460 STDMETHOD(GetPath)(
461 DWORD cchBuffer,
462 LPDWORD pcchBuffer,
463 __inout_ecount_part(cchBuffer, *pcchBuffer) LPWSTR wzBuffer);
464
465 private:
466 //---------------------------------------------------------------------------------------------
467 NewArrayHolder<WCHAR> m_wzPath;
468 };
469
470 //=================================================================================================================
471 class CLRPrivResourceStreamImpl :
472 public IUnknownCommon< ItfBase< CLRPrivResourceBase<ICLRPrivResourceStream > >,
473 ICLRPrivResourceStream>
474 {
475 public:
476 //---------------------------------------------------------------------------------------------
477 CLRPrivResourceStreamImpl(IStream * pStream);
478
479 //---------------------------------------------------------------------------------------------
480 STDMETHOD(GetStream)(
481 REFIID riid,
482 LPVOID * ppvStream);
483
484 private:
485 //---------------------------------------------------------------------------------------------
486 ReleaseHolder<IStream> m_pStream;
487 };
488
489 //=================================================================================================================
490 // Helper to prioritize binder errors. This class will ensure that all other errors have priority over
491 // CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT. This class should be used just like an HRESULT variable.
492 class BinderHRESULT
493 {
494 public:
495 BinderHRESULT()
496 : m_hr(S_OK)
497 {}
498
499 BinderHRESULT(HRESULT hr)
500 : m_hr(hr)
501 {}
502
503 operator HRESULT() const
504 { return m_hr; }
505
506 BinderHRESULT & operator=(HRESULT hr)
507 {
508 // Always record change in success/failure status.
509 if (FAILED(hr) != FAILED(m_hr))
510 m_hr = hr;
511
512 if (FAILED(hr))
513 {
514 if (SUCCEEDED(m_hr))
515 m_hr = hr;
516 else if (m_hr == CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT)
517 m_hr = hr;
518 }
519 else
520 {
521 m_hr = hr;
522 }
523 return *this;
524 }
525
526 private:
527 HRESULT m_hr;
528 }; // class BinderHRESULT
529
530 //=================================================================================================================
531 // Types for WStringList (used in WinRT binders)
532
533 typedef SListElem< PTR_WSTR > WStringListElem;
534 typedef DPTR(WStringListElem) PTR_WStringListElem;
535 typedef SList< WStringListElem, false /* = fHead default value */, PTR_WStringListElem > WStringList;
536 typedef DPTR(WStringList) PTR_WStringList;
537
538 // Destroys list of strings (code:WStringList).
539 void WStringList_Delete(WStringList * pList);
540
541#ifndef DACCESS_COMPILE
542 //=====================================================================================================================
543 // Holder of allocated code:WStringList (helper class for WinRT binders - e.g. code:CLRPrivBinderWinRT::GetFileNameListForNamespace).
544 class WStringListHolder
545 {
546 public:
547 WStringListHolder(WStringList * pList = nullptr)
548 {
549 LIMITED_METHOD_CONTRACT;
550 m_pList = pList;
551 }
552 ~WStringListHolder()
553 {
554 LIMITED_METHOD_CONTRACT;
555 Destroy();
556 }
557
558 void InsertTail(LPCWSTR wszValue)
559 {
560 CONTRACTL
561 {
562 THROWS;
563 GC_NOTRIGGER;
564 MODE_ANY;
565 }
566 CONTRACTL_END;
567
568 NewArrayHolder<WCHAR> wszElemValue = DuplicateStringThrowing(wszValue);
569 NewHolder<WStringListElem> pElem = new WStringListElem(wszElemValue);
570
571 if (m_pList == nullptr)
572 {
573 m_pList = new WStringList();
574 }
575
576 m_pList->InsertTail(pElem.Extract());
577 // The string is now owned by the list
578 wszElemValue.SuppressRelease();
579 }
580
581 WStringList * GetValue()
582 {
583 LIMITED_METHOD_CONTRACT;
584 return m_pList;
585 }
586
587 WStringList * Extract()
588 {
589 LIMITED_METHOD_CONTRACT;
590
591 WStringList * pList = m_pList;
592 m_pList = nullptr;
593 return pList;
594 }
595
596 private:
597 void Destroy()
598 {
599 LIMITED_METHOD_CONTRACT;
600
601 if (m_pList != nullptr)
602 {
603 WStringList_Delete(m_pList);
604 m_pList = nullptr;
605 }
606 }
607
608 private:
609 WStringList * m_pList;
610 }; // class WStringListHolder
611#endif //!DACCESS_COMPILE
612
613#ifdef FEATURE_COMINTEROP
614 //=====================================================================================================================
615 // Holder of allocated array of HSTRINGs (helper class for WinRT binders - e.g. code:CLRPrivBinderWinRT::m_rgAltPaths).
616 class HSTRINGArrayHolder
617 {
618 public:
619 HSTRINGArrayHolder()
620 {
621 LIMITED_METHOD_CONTRACT;
622
623 m_cValues = 0;
624 m_rgValues = nullptr;
625 }
626#ifndef DACCESS_COMPILE
627 ~HSTRINGArrayHolder()
628 {
629 LIMITED_METHOD_CONTRACT;
630 Destroy();
631 }
632
633 // Destroys current array and allocates a new one with cValues elements.
634 void Allocate(DWORD cValues)
635 {
636 STANDARD_VM_CONTRACT;
637
638 Destroy();
639 _ASSERTE(m_cValues == 0);
640
641 if (cValues > 0)
642 {
643 m_rgValues = new HSTRING[cValues];
644 m_cValues = cValues;
645
646 // Initialize the array values
647 for (DWORD i = 0; i < cValues; i++)
648 {
649 m_rgValues[i] = nullptr;
650 }
651 }
652 }
653#endif //!DACCESS_COMPILE
654
655 HSTRING GetAt(DWORD index) const
656 {
657 LIMITED_METHOD_CONTRACT;
658 return m_rgValues[index];
659 }
660
661 HSTRING * GetRawArray()
662 {
663 LIMITED_METHOD_CONTRACT;
664 return m_rgValues;
665 }
666
667 DWORD GetCount()
668 {
669 LIMITED_METHOD_CONTRACT;
670 return m_cValues;
671 }
672
673 private:
674#ifndef DACCESS_COMPILE
675 void Destroy()
676 {
677 LIMITED_METHOD_CONTRACT;
678
679 for (DWORD i = 0; i < m_cValues; i++)
680 {
681 if (m_rgValues[i] != nullptr)
682 {
683 WindowsDeleteString(m_rgValues[i]);
684 }
685 }
686 m_cValues = 0;
687
688 if (m_rgValues != nullptr)
689 {
690 delete [] m_rgValues;
691 m_rgValues = nullptr;
692 }
693 }
694#endif //!DACCESS_COMPILE
695
696 private:
697 DWORD m_cValues;
698 HSTRING * m_rgValues;
699 }; // class HSTRINGArrayHolder
700
701#endif // FEATURE_COMINTEROP
702
703 ////////////////////////////////////////////////////////////////////////////////////////////////////
704 ///// ----------------------------- Questionable stuff -------------------------------------------
705 ////////////////////////////////////////////////////////////////////////////////////////////////////
706
707 /** probably should be exposed on an instance (of something) method rather that magically calling GetAppDomain() **/
708 ICLRPrivAssembly* RaiseAssemblyResolveEvent(IAssemblyName *pAssemblyName, ICLRPrivAssembly* pRequestingAssembly);
709
710 /** Ultimately, only the binder can do ref-def matching, and it should be opaque to CLR.
711 This is not trivial to do, however, since we cannot do data conversion as the function is nofault **/
712 BOOL CompareHostBinderSpecs(AssemblySpec* a1, AssemblySpec* a2);
713
714 /** PLACEHOLDER - the same issue as CompareHostBinderSpecs applies to hashing assemblyspecs **/
715} // namespace CLRPrivBinderUtil
716
717#endif // __CLRPRIVBINDERUTIL_H__
718