// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. /*************************************************************** * * Portions of this header fall under the following * copyrights and/or licenses: * * rfc4122 and supporting functions * * Algorithm from RFC 4122 - A Universally Unique IDentifier (UUID) URN Namespace * * By Paul J. Leach, Michael Mealling and Rich Sals, July 2005. * * * * This function is adapted from the routines in the document * * uuid_create_sha1_from_name and format_uuid_v3or5 * * * * * * Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. * * Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & * * Digital Equipment Corporation, Maynard, Mass. * * Copyright (c) 1998 Microsoft. * * To anyone who acknowledges that this file is provided "AS IS" * * without any express or implied warranty: permission to use, copy, * * modify, and distribute this file for any purpose is hereby * * granted without fee, provided that the above copyright notices and * * this notice appears in all source code copies, and that none of * * the names of Open Software Foundation, Inc., Hewlett-Packard * * Company, Microsoft, or Digital Equipment Corporation be used in * * advertising or publicity pertaining to distribution of the software * * without specific, written prior permission. Neither Open Software * * Foundation, Inc., Hewlett-Packard Company, Microsoft, nor Digital * * Equipment Corporation makes any representations about the * * suitability of this software for any purpose. * * */ #ifdef _MSC_VER #pragma once #endif /* _MSC_VER */ #ifndef WINRT_PARAMINSTANCEAPI_H #define WINRT_PARAMINSTANCEAPI_H #ifdef __cplusplus #ifdef _MSC_VER #pragma warning( push ) #pragma warning( disable : 4180 ) // qualifier applied to function type has no meaning; ignored #endif #include #include #include #include #include #include #include //#ifdef _MSC_VER //#include //#else //#include //#endif #ifndef WINRT_PARAMINSTANCE_NOCRYPT_SHA1 #include #endif #ifdef _MSC_VER #pragma push_macro("CHKHR") #pragma push_macro("CHKNT") #endif namespace Ro { namespace detail { // // Debugging aide. Set breakpoint on _FailedHR // to see HRESULT propagation. // #ifdef DEBUG inline HRESULT __declspec(noinline) _FailedHR(HRESULT hr) { static HRESULT _hr = hr; return hr; } #else inline HRESULT _FailedHR(HRESULT hr) { return hr; } #endif }} #undef CHKHR // // Call HRESULT returning code and propagate any errors. // Note: only use in code that is exception-safe / uses RAII. // #define CHKHR(expr) \ { HRESULT _zzhr; \ _zzhr = expr; \ if (FAILED(_zzhr)) return Ro::detail::_FailedHR(_zzhr); } #undef CHKNT // // Call NTSTATUS returning code and propagate any errors, as HRESULTs. // Note: // - only use in code that is exception-safe / uses RAII / RRID. // - HRESULT_FROM_NT does safely convert STATUS_SUCCESS into // a SUCCEEDED hr. // #define CHKNT(expr) \ CHKHR( HRESULT_FROM_NT( expr ) ) namespace Ro { namespace detail { // // Runtime check for an invariant. This check executes in release builds. // inline HRESULT Verify(bool invariant, HRESULT defaultHr = E_UNEXPECTED) { if (!invariant) { CHKHR(defaultHr); } return S_OK; } }} extern "C" { // sha1 adaptor // create hash instance HRESULT _RoSha1Create( __out void** handle); // sha1 adaptor // append more data to the input stream HRESULT _RoSha1AppendData( __in void* handle, __in size_t numBytes, __in_bcount(numBytes) const void* data); // sha1 adaptor // return the first 16 bytes of SHA1 hash HRESULT _RoSha1Finish( __in void* handle, __out BYTE (*hashValue)[20]); // sha1 adaptor // free this instance void _RoSha1Release(__in void* handle); } struct IRoSimpleMetaDataBuilder; struct IRoMetaDataLocator; // The 'detail' namespace includes implementation details that // are subject to change without notice. namespace Ro { namespace detail { struct SimpleMetaDataBuffer; }} // // Purpose: // Given a parameterized type instance name and metadata, // computes the IID for that instance. // // Parameters: // // nameElementCount // number of elements in nameElements // nameElements // a parsed WinRt type name, as would be returned by RoParseTypeName. // Eg: ["W.F.C.IVector`1", "N1.N2.IFoo"] // metaDataLocator // A callback to use for resolving metadata. // // An implementation could, for example, forward all calls // to RoGetMetaData, then passing the results to // RoWriteImporterToPushSimpleMetaData. As RoGetMetadata does // not cache results, such an implementation would be inefficient. // A better implementation will cache the results to RoGetMetaData, // as appropriate. // // The Locator helper function can be used to wrap a lambda // expression, or function pointer. eg: // RoGetParameterizedTypeInstanceIID( // ..., // Locate([&](PCWSTR* name, IRoSimpleMetaDataBuilder& push){...}), // ...); // iid // out param. Returns the iid for the parameterized type specified // by nameElements // extra // out param. returns a handle that holds extra information about the // IID result, for diagnostic purposes. If this handle is not desired, // provide nullptr instead. // // Notes: // - This function is stateless. IRoMetaDataLocator will not be preserved // between calls. // - This function does not perform deep semantic analysis. For instance, // if IRoSimpleMetaDataBuilder specifies that a struct contains an interface pointer, // this API will return success, even though such metadata is semantically // invalid. The value of the IID returned is unspecified in such cases. // - This function does introduce reentrancy. Its implementation // of IRoSimpleMetaDataBuilder may make reentrant calls to IRoMetaDataLocator. // - If a call to IRoSimpleMetaDataBuilder fails, this function will return that // failure code. // DECLARE_HANDLE(ROPARAMIIDHANDLE); inline HRESULT RoGetParameterizedTypeInstanceIID( UINT32 nameElementCount, __in_ecount(nameElementCount) PCWSTR* nameElements, __in const IRoMetaDataLocator& metaDataLocator, __out GUID* iid, __deref_opt_out ROPARAMIIDHANDLE* pExtra = nullptr); // Frees the 'extra' handle allocated // by RoGetParameterizedTypeInstanceIID inline void RoFreeParameterizedTypeExtra(__in ROPARAMIIDHANDLE extra); // Fetches the TypeSignature used to compute the IID by the last // call to RoGetParameterizedTypeInstanceIID on this extra handle. // The string contains ASCII code only, and the string is valid // until RoFreeParameterizedTypeExtra is called on the extra pointer. inline PCSTR RoParameterizedTypeExtraGetTypeSignature(__in ROPARAMIIDHANDLE extra); namespace Ro { namespace detail { // private type used in helper function template struct _Locator; }} // namespace Ro::detail namespace Ro { // helper function to create IRoMetaDataLocator from lambda expression template Ro::detail::_Locator Locator(const Fn& fn); } // namespace Ro // // Purpose: // Destination for IRoMetaDataLocator::Locate to write parsed metadata to. // 'Locate' should set the appropriate Windows Runtime metadata information gleaned // from Windows Runtime metadata file, or other appropriate source. // // Notes: // - Methods for base types and COM interfaces (eg, Int32 and IInspectable // respectively) are not needed -- RoGetParameterizedTypeInstanceIID already // knows the WinRT base type names, so will not invoke IMetDataLocator // to discover them. // - This is not a COM interface. It does not derive from IUnknown. // struct IRoSimpleMetaDataBuilder { // Notes: // IInspectable and other non-WinRT interfaces are not permissible. // Not for use with parameterized type instances. See SetParameterizedInterface STDMETHOD(SetWinRtInterface)( GUID iid) = 0; // Notes: // Not for use with parameterized type instances. See SetParameterizedDelegate STDMETHOD(SetDelegate)( GUID iid) = 0; // Notes: // Call this method when an interface group has a default interface // that is a non-parametric type. STDMETHOD(SetInterfaceGroupSimpleDefault)( PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID* defaultInterfaceIID) = 0; // Notes: // Call this method when an interface group has a parameterized // interface as its default interface. STDMETHOD(SetInterfaceGroupParameterizedDefault)( PCWSTR name, UINT32 elementCount, __in_ecount(elementCount) PCWSTR* defaultInterfaceNameElements) = 0; STDMETHOD(SetRuntimeClassSimpleDefault)( PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID* defaultInterfaceIID) = 0; STDMETHOD(SetRuntimeClassParameterizedDefault)( PCWSTR name, UINT32 elementCount, __in_ecount(elementCount) PCWSTR* defaultInterfaceNameElements) = 0; STDMETHOD(SetStruct)( PCWSTR name, UINT32 numFields, __in_ecount(numFields) PCWSTR* fieldTypeNames) = 0; STDMETHOD(SetEnum)( PCWSTR name, PCWSTR baseType) = 0; // Notes: // This is only for the 'non-instantiated' parameterized interface itself - // instances are handled by RoGetParameterizedTypeInstanceIID, and the // caller need not parse them. STDMETHOD(SetParameterizedInterface)( GUID piid, UINT32 numArgs) = 0; STDMETHOD(SetParameterizedDelegate)( GUID piid, UINT32 numArgs) = 0; }; // // Purpose: // Callback for resolving metadata. // struct IRoMetaDataLocator { // // Parameters: // nameElement // a metadata typeref name to resolve. // Eg: "N1.N2.IFoo", or "W.F.C.IVector`1". // pushMetaData // data sink for providing information about the // type information for nameElement // STDMETHOD(Locate)( PCWSTR nameElement, __in IRoSimpleMetaDataBuilder& metaDataDestination ) const = 0; }; namespace Ro { namespace detail { // // helper function, moves range of elements // template void _VecMoveRange( __in_ecount(size) T* dst, __in_ecount(size) T* src, size_t size) { for (size_t i = 0; i != size; ++i) { dst[i] = static_cast(src[i]); } } // // specializations to move strings more efficiently // inline void _VecMoveRange( __in_ecount(size) char* dst, __in_ecount(size) char* src, size_t size) { errno_t err = memcpy_s(dst, size*sizeof(*dst), src, size*sizeof(*dst)); NT_ASSERT(!err); (void)err; } inline void _VecMoveRange( __in_ecount(size) wchar_t* dst, __in_ecount(size) wchar_t* src, size_t size) { errno_t err = memcpy_s(dst, size*sizeof(*dst), src, size*sizeof(*dst)); NT_ASSERT(!err); (void)err; } // // helper function, moves range of elements // template void _VecCopyRange( __in_ecount(size) T* dst, __in_ecount(size) const T* src, size_t size) { for (size_t i = 0; i != _size; ++i) { dst[i] = src[i]; } } // // specializations to move strings more efficiently // inline void _VecCopyRange( __in_ecount(size) char* dst, __in_ecount(size) const char* src, size_t size) { errno_t err = memcpy_s(dst, size*sizeof(*dst), const_cast(src), size*sizeof(*dst)); NT_ASSERT(!err); (void)err; } inline void _VecCopyRange( __in_ecount(size) wchar_t* dst, __in_ecount(size) const wchar_t* src, size_t size) { errno_t err = memcpy_s(dst, size*sizeof(*dst), const_cast(src), size*sizeof(*dst)); NT_ASSERT(!err); (void)err; } // // Single-owner smart pointer for arrays // template struct ArrayHolder { ArrayHolder() : _value(NULL) { } T* Value() const { return _value; } T*& Value() { return _value; } T* Detach() { T* tmp = _value; _value = NULL; return tmp; } ~ArrayHolder() { delete[] _value; } private: T* _value; }; // // Single-owner smart pointer for object pointer // template struct ElementHolder { ElementHolder() : _value(NULL) { } T* operator->() const { return _value; } T* Value() const { return _value; } T*& Value() { return _value; } T* Detach() { T* tmp = _value; _value = NULL; return tmp; } ~ElementHolder() { delete _value; } private: T* _value; }; // // simple vector, with small vector optimization // T - must be default constructable and movable. // const input overload of AppendN requires copyable. // FixedBufSize - number of bytes to use for small array // optimization, to avoid heap allocation in case of // small vectors. Defaults to at least one element, // otherwise the largest value such that <= 64 bytes // are used. // template < typename T, size_t FixedBufSize = 0 > class Vec { private: static const size_t _fixedBufSize = FixedBufSize/sizeof(T) ? FixedBufSize/sizeof(T) : (((64/sizeof(T)) > 0) ? (64/sizeof(T)) : 1); public: Vec() : _size(0), _cap(_countof(_fixedBuf)), _buf(_fixedBuf) { } // Appends an element, or a default value if one // it not specified. If called with an rvalue, // it uses move assignment instead of copy. HRESULT Append(T value = T()) { if (_cap - _size < 1) { CHKHR(_Grow()); } _buf[_size] = static_cast(value); ++_size; return S_OK; } // Moves elements (move assignment) into array. HRESULT MoveN(__in_ecount(n) T* values, size_t n) { if (_cap - _size < n) { CHKHR(_Grow(n - (_cap - _size))); } _VecMoveRange(_buf + _size, values, n); _size += n; return S_OK; } // Appends elements. Does not invoke move assignment. HRESULT AppendN(__in_ecount(n) const T* values, size_t n) { if (_cap - _size < n) { CHKHR(_Grow(n - (_cap - _size))); } _VecCopyRange(_buf + _size, values, n); _size += n; return S_OK; } HRESULT Pop() { CHKHR(Verify( _size > 0 )); --_size; return S_OK; } HRESULT Resize(size_t newSize) { if (_cap < newSize) { CHKHR(_Grow(newSize - _cap)); } _size = newSize; return S_OK; } size_t Size() const { return _size; } T& operator[](size_t index) { NT_ASSERT(index < _size); return _buf[index]; } T& Last() { return (*this)[_size-1]; } ~Vec() { if (_buf != _fixedBuf) { delete[] _buf; } } private: // // growth factor (does not check for overflow) -- returns amount to grow by // static size_t _GrowthIncrement(size_t n) { return n / 2; } HRESULT _Grow(size_t byAtLeast = 4) { size_t increase = _GrowthIncrement(_cap); if (increase < byAtLeast) { increase = byAtLeast; } size_t newCap = _cap + increase; if (newCap <= _cap) { CHKHR(E_OUTOFMEMORY); } ArrayHolder newBuf; void* p = (newBuf.Value() = new (std::nothrow) T[newCap]); if (!p) { CHKHR(E_OUTOFMEMORY); } _VecMoveRange( newBuf.Value(), _buf, _size ); if (_buf != _fixedBuf) { delete _buf; } _buf = newBuf.Detach(); _cap = newCap; return S_OK; } size_t _size; size_t _cap; T* _buf; T _fixedBuf[_fixedBufSize]; }; struct SimpleMetaDataBuilder : IRoSimpleMetaDataBuilder { public: SimpleMetaDataBuilder(SimpleMetaDataBuffer& buffer, const IRoMetaDataLocator& locator) : _buffer(&buffer), _locator(&locator), _invoked(false) { } IFACEMETHOD(SetWinRtInterface)(GUID iid); IFACEMETHOD(SetDelegate)(GUID iid); IFACEMETHOD(SetInterfaceGroupSimpleDefault)(PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID *defaultInterfaceIID); IFACEMETHOD(SetInterfaceGroupParameterizedDefault)(PCWSTR name, UINT32 elementCount, __in_ecount(elementCount) PCWSTR *defaultInterfaceNameElements); IFACEMETHOD(SetRuntimeClassSimpleDefault)(PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID *defaultInterfaceIID); IFACEMETHOD(SetRuntimeClassParameterizedDefault)(PCWSTR name, UINT32 elementCount, __in_ecount(elementCount) PCWSTR *defaultInterfaceNameElements); IFACEMETHOD(SetStruct)(PCWSTR name, UINT32 numFields, __in_ecount(numFields) PCWSTR *fieldTypeNames); IFACEMETHOD(SetEnum)(PCWSTR name, PCWSTR baseType); IFACEMETHOD(SetParameterizedInterface)(GUID piid, UINT32 numArgs); IFACEMETHOD(SetParameterizedDelegate)(GUID piid, UINT32 numArgs); // Runs the locating process for a parameterized type. // Notes: // _buffer->_nestingLevel is used to determine the number of // arguments left to consume for nested parameterized types. HRESULT SendArguments(UINT32 nameElementCount, __in_ecount(nameElementCount) PCWSTR *nameElements); private: // Writes the type signature for the type 'name' // Notes: // - If a builtin type, writes the type directly. // - Otherwise, uses the IRoMetaDataLocator to // write the type signature into _buffer // - As the sole function to call // IRoMetaDataLocator, it also performs the check // on recursion depth bounds. HRESULT _WriteType(PCWSTR name); // The tail portion of IG and RC formats is the same. This // function implements the shared portion of that format. HRESULT _CommonInterfaceGroupSimple(PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID *defaultInterfaceIID); // Called at the beginning of every 'Set' method. Set must only be called once. HRESULT _OnSet(); // Called at the end of every 'Set' method, only if successful. void _Completed(); static char _AsciiLower(char ch) { if ('A' <= ch && ch <= 'Z') { return ch + ('a' - 'A'); } else { return ch; } } // Writes a guid into the type signature being built, in lower case. HRESULT _WriteGuid(const GUID& iid); HRESULT _WriteString(PCSTR str); HRESULT _WriteChar(char c); HRESULT _WriteWideString(PCWSTR str); SimpleMetaDataBuilder(); SimpleMetaDataBuilder(const SimpleMetaDataBuilder&); void operator=(const SimpleMetaDataBuilder&); SimpleMetaDataBuffer* _buffer; const IRoMetaDataLocator* _locator; bool _invoked; }; // If the type string describes a built-in type, modifies // this instance to use builtin type table entry instead of name. inline bool _IsBuiltin(__in PCWSTR name, __out PCSTR * typeSignature) { *typeSignature = nullptr; struct BuiltinEntry { PCWSTR name; PCSTR typeSignature; }; static const BuiltinEntry entries[] = { { L"UInt8", "u1" }, { L"Int16", "i2" }, { L"UInt16", "u2" }, { L"Int32", "i4" }, { L"UInt32", "u4" }, { L"Int64", "i8" }, { L"UInt64", "u8" }, { L"Single", "f4" }, { L"Double", "f8" }, { L"Boolean", "b1" }, { L"Char16", "c2" }, { L"String", "string" }, { L"Guid", "g16" }, { L"Object", "cinterface(IInspectable)" }, }; for (const BuiltinEntry* tip = entries; tip != &entries[_countof(entries)]; ++tip) { if (wcscmp(tip->name, name) == 0) { *typeSignature = tip->typeSignature; return true; } } // if not found, assume is a normal type name return false; } // Linked list (stack allocated) of type resolution calls, // used to detect if an InterfaceGroup/RuntimeClass type // signature depends on itself. In that case, we use "*" // in the type signature instead of recurring further. struct ResolutionPathEntry { ResolutionPathEntry* _next; PCWSTR _typeName; ResolutionPathEntry(PCWSTR typeName) : _next(nullptr) , _typeName(typeName) { } }; inline void Push(ResolutionPathEntry*& top, ResolutionPathEntry* item) { item->_next = top; top = item; } inline HRESULT Pop(ResolutionPathEntry*& top) { if (!top) { return E_UNEXPECTED; } top = top->_next; return S_OK; } // Holds metadata state that is shared between RoGetParamInstanceIID and SimpleMetaDataBuilder struct SimpleMetaDataBuffer { SimpleMetaDataBuffer() { Clear(); } // reset all tables void Clear() { _recursionDepth = 0; _topLevelTypes = 0; _resolutionPath = nullptr; _outputStream.Resize(0); } static const size_t _maxTypeName = 256; // Estimate of 'reasonable' level of Interface Group / Runtime // Class / Parameterized Type nesting. static const size_t _maxRecursionDepth = 64; Vec _outputStream; ResolutionPathEntry* _resolutionPath; // RAII object, places an item on the resolution path, and pops it on destruction class ResolutionPathGuard { private: ResolutionPathEntry _entry; SimpleMetaDataBuffer* _buffer; public: ResolutionPathGuard(PCWSTR typeName, SimpleMetaDataBuffer* buffer) : _buffer(buffer) , _entry(typeName) { Push(buffer->_resolutionPath, &_entry); } ~ResolutionPathGuard() { HRESULT hr = Pop(_buffer->_resolutionPath); NT_ASSERT(SUCCEEDED(hr)); (void)hr; } }; // Searches the resolution path for 'name' returning true if exists bool ExistsCycle(PCWSTR typeName) { for (auto pTip = _resolutionPath; pTip; pTip = pTip->_next) { if (wcscmp(typeName, pTip->_typeName) == 0) { return true; } } return false; } // Indicates the nesting level of compound types, used // to properly balance parenthesis on parameterized types, // and used to bound recursion depth. // // - Pinterfaces // : push 'numArgs' on to _nestingLevel // - A compound type that doesn't know number of arguments // eg, RoGetParameterizedInstanceIID arguments, or // SetIG..Parameterized // : will 0) note nesting level // 1) iterate calling Locate on the compound arguments. // 2) the above should cause exactly one push of _nestingLevel // 3) reduce nesting level back to original nesting level, // inserting the difference in closing parens // - Compound types that do know number of arguments (eg SetStruct) // : will 1) increase nesting level by 1 // 2) iterate calling Locate on arguments // 3) decrease nesting level again // // Vec _nestedArgs; // topLevelTypes should be incremented once, by the initial // parameterized type, then never again. size_t _topLevelTypes; size_t _recursionDepth; }; }} // namespace Ro::detail namespace Ro { namespace detail { template struct _Locator : IRoMetaDataLocator { Fn _fn; _Locator(const Fn& fn) : _fn(fn) { } IFACEMETHOD(Locate)( PCWSTR name, IRoSimpleMetaDataBuilder& pushMetaData) const { return _fn(name, pushMetaData); } }; }} // namespace Ro::detail namespace Ro { template Ro::detail::_Locator Locator(const Fn& fn) { return Ro::detail::_Locator(fn); } } namespace Ro { namespace detail { // Figure out if we're compiling for a big- or little-endian machine. inline bool BigEndian() { unsigned long n = 0xff000000L; return 0 != *reinterpret_cast(&n); } // HostToNetworkLong converts a 32-bit long to network byte order inline ULONG HostToNetworkLong(ULONG hostlong) { if (BigEndian()) return hostlong; else return ( (hostlong >> 24) & 0x000000FFL) | ( (hostlong >> 8) & 0x0000FF00L) | ( (hostlong << 8) & 0x00FF0000L) | ( (hostlong << 24) & 0xFF000000L); } // HostToNetworkLong converts a 16-bit short to network byte order inline USHORT HostToNetworkShort(USHORT hostshort) { if (BigEndian()) return hostshort; else return ((hostshort >> 8) & 0x00FF) | ((hostshort << 8) & 0xFF00); } // NetworkToHostLong converts a 32-bit long to local host byte order inline ULONG NetworkToHostLong(ULONG netlong) { if (BigEndian()) return netlong; else return ( (netlong >> 24) & 0x000000FFL) | ( (netlong >> 8) & 0x0000FF00L) | ( (netlong << 8) & 0x00FF0000L) | ( (netlong << 24) & 0xFF000000L); } // NetworkToHostShort converts a 16-bit short to local host byte order inline USHORT NetworkToHostShort(USHORT netshort) { if (BigEndian()) return netshort; else return ((netshort >> 8) & 0x00FF) | ((netshort << 8) & 0xFF00); } // smart pointer for Sha1 handle struct Sha1Holder { Sha1Holder() : _handle(nullptr) { } void*& Value() { return _handle; } ~Sha1Holder() { if (_handle) { _RoSha1Release(_handle); } } private: void* _handle; }; // // Computes the rfc4122 v5 UUID from GUID,name pair. // // Notes: // - see copyright at beginning of file. // inline HRESULT GuidFromName( __in const GUID& guidNamespace, __in_bcount(dwcbSize) const void* pbName, __in DWORD dwcbSize, __out GUID* pGuid) { Sha1Holder sha1; CHKHR( _RoSha1Create(&sha1.Value()) ); { GUID networkOrderGuidNamespace = guidNamespace; // Put name space ID in network byte order so it hashes the same // no matter what endian machine we're on if (!BigEndian()) { networkOrderGuidNamespace.Data1 = HostToNetworkLong (networkOrderGuidNamespace.Data1); networkOrderGuidNamespace.Data2 = HostToNetworkShort(networkOrderGuidNamespace.Data2); networkOrderGuidNamespace.Data3 = HostToNetworkShort(networkOrderGuidNamespace.Data3); } CHKHR( _RoSha1AppendData(sha1.Value(), sizeof(networkOrderGuidNamespace), reinterpret_cast(&networkOrderGuidNamespace)) ); } CHKHR( _RoSha1AppendData(sha1.Value(), dwcbSize, pbName) ); { BYTE sha1Result[20]; CHKHR( _RoSha1Finish(sha1.Value(), &sha1Result) ); errno_t err = memcpy_s(pGuid, sizeof(GUID), &sha1Result[0], sizeof(GUID)); CHKHR(Verify( 0 == err )); // Restore the byte order if (!BigEndian()) { pGuid->Data1 = NetworkToHostLong (pGuid->Data1); pGuid->Data2 = NetworkToHostShort(pGuid->Data2); pGuid->Data3 = NetworkToHostShort(pGuid->Data3); } // set version number // 1: clear version number nibble // 2: set version 5 = name-based SHA1 pGuid->Data3 &= 0x0FFF; pGuid->Data3 |= (5 << 12); // set variant field by clearing variant bits. pGuid->Data4[0] &= 0x3F; pGuid->Data4[0] |= 0x80; } return S_OK; } }} // namespace Ro::detail inline HRESULT RoGetParameterizedTypeInstanceIID( UINT32 nameElementCount, __in_ecount(nameElementCount) PCWSTR* nameElements, __in const IRoMetaDataLocator& metaDataLocator, __out GUID* iid, __deref_opt_out ROPARAMIIDHANDLE* pExtra) { using namespace Ro::detail; memset(iid, 0, sizeof(*iid)); SimpleMetaDataBuffer reserveBuffer; SimpleMetaDataBuffer *pBuffer = &reserveBuffer; // if user wishes to hold on to the result value, // dynamically allocate this buffer. if (pExtra) { pBuffer = new (std::nothrow) SimpleMetaDataBuffer; *pExtra = static_cast(static_cast(pBuffer)); } SimpleMetaDataBuffer& buffer = *pBuffer; SimpleMetaDataBuilder builder(*pBuffer, metaDataLocator); // send initial arguments CHKHR(builder.SendArguments(nameElementCount, nameElements)); // verify that precisely one type was resolved, to completion. CHKHR(Verify(buffer._topLevelTypes == 1 && buffer._nestedArgs.Size() == 0, E_INVALIDARG)); // compute type signature hash static const GUID guidPinterfaceNamespace = { 0x11f47ad5, 0x7b73, 0x42c0, { 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee }}; CHKHR(Ro::detail::Verify( buffer._outputStream.Size() <= DWORD(-1) )); // null terminate CHKHR( buffer._outputStream.Append('\0') ); // // Unit test logging, to verify proper signatures // #ifdef UNITTEST_TRACE { CHKHR( UNITTEST_TRACE("type signature", &buffer._outputStream[0]) ); } #endif CHKHR( GuidFromName(guidPinterfaceNamespace, &buffer._outputStream[0], DWORD(buffer._outputStream.Size() - 1), // does not include terminator iid) ); return S_OK; } inline void RoFreeParameterizedTypeExtra(__in ROPARAMIIDHANDLE extra) { using namespace Ro::detail; delete static_cast(static_cast(extra)); } inline PCSTR RoParameterizedTypeExtraGetTypeSignature(__in ROPARAMIIDHANDLE extra) { using namespace Ro::detail; SimpleMetaDataBuffer* pBuffer = static_cast(static_cast(extra)); return &pBuffer->_outputStream[0]; } namespace Ro { namespace detail { inline HRESULT SimpleMetaDataBuilder::_WriteType(PCWSTR name) { PCSTR builtInName = nullptr; SimpleMetaDataBuilder builder(*_buffer, *_locator); if (_IsBuiltin(name, &builtInName)) { CHKHR(builder._OnSet()); CHKHR(builder._WriteString(builtInName)); builder._Completed(); } else { size_t newDepth = ++_buffer->_recursionDepth; size_t pinterfaceNesting = _buffer->_nestedArgs.Size(); if (newDepth + pinterfaceNesting > _buffer->_maxRecursionDepth) { // Terminate recursion; bounds call stack consumption CHKHR(E_UNEXPECTED); } CHKHR(_locator->Locate(name, builder)); // Note, buffers aren't reusable, so it's fine that we don't // unwind this value on return. Also note, we do not unwind // this value if the user provides inconsistent data either // (eg, if they provide only 1 argument to a 2 parameter // parameterized type). --_buffer->_recursionDepth; } return S_OK; } inline HRESULT SimpleMetaDataBuilder::_OnSet() { if (_invoked) { CHKHR(E_INVALIDARG); } _invoked = true; // Reduce the number of arguments left for this compound type. if(_buffer->_nestedArgs.Size() > 0) { --(_buffer->_nestedArgs.Last()); } else { // Increase number of top level types in signature // string. (should never exceed one) ++_buffer->_topLevelTypes; } return S_OK; } inline void SimpleMetaDataBuilder::_Completed() { } inline HRESULT SimpleMetaDataBuilder::SendArguments(UINT32 nameElementCount, __in_ecount(nameElementCount) PCWSTR *nameElements) { CHKHR(Verify(nameElementCount > 0)); CHKHR(Verify(_buffer->_nestedArgs.Size() <= UINT32(-1))); UINT32 previousLevel = UINT32(_buffer->_nestedArgs.Size()); for (UINT32 i = 0; i < nameElementCount; ++i) { CHKHR(_WriteType(nameElements[i])); // Close any nested parameterized types that are complete while (_buffer->_nestedArgs.Size() > previousLevel && _buffer->_nestedArgs.Last() == 0) { CHKHR(_buffer->_nestedArgs.Pop()); CHKHR(_WriteChar(')')); } // insert separator between parameterized type arguments CHKHR(_WriteChar(';')); } // remove final separator CHKHR(_buffer->_outputStream.Pop()); // Verify that all the arguments were consumed. CHKHR(Verify(_buffer->_nestedArgs.Size() == previousLevel, E_INVALIDARG)); return S_OK; } inline HRESULT SimpleMetaDataBuilder::_WriteGuid(const GUID& iid) { static const size_t guidStringLength = _countof("{11223344-1122-1122-1122-334455667788}") - 1; WCHAR tmpString[guidStringLength+1]; int numWritten = StringFromGUID2(iid, tmpString, guidStringLength + 1); CHKHR(Verify( numWritten == guidStringLength + 1 )); NT_ASSERT( numWritten == guidStringLength + 1 ); size_t offset = _buffer->_outputStream.Size(); CHKHR(Verify( offset + guidStringLength > offset )) CHKHR( _buffer->_outputStream.Resize(offset + guidStringLength) ); char* writePtr = &_buffer->_outputStream[offset]; // All characters are ascii. Just truncate. for(size_t i = 0; i < guidStringLength; ++i) { writePtr[i] = _AsciiLower(char(tmpString[i])); } return S_OK; } inline HRESULT SimpleMetaDataBuilder::_WriteString(PCSTR str) { CHKHR( _buffer->_outputStream.AppendN(str, strlen(str)) ); return S_OK; } inline HRESULT SimpleMetaDataBuilder::_WriteChar(char c) { CHKHR( _buffer->_outputStream.Append(c) ); return S_OK; } inline HRESULT SimpleMetaDataBuilder::_WriteWideString(PCWSTR str) { size_t len = wcslen(str); size_t offset = _buffer->_outputStream.Size(); int written; // provision enough space for conversion to take place size_t provision = len + 1; for(;;) { CHKHR( _buffer->_outputStream.Resize(offset+provision)); char* writePtr = &_buffer->_outputStream[offset]; CHKHR(Verify(len <= INT_MAX)); CHKHR(Verify(provision <= INT_MAX)); written = WideCharToMultiByte( CP_UTF8, 0, str, int(len), writePtr, int(provision), nullptr, nullptr ); if (written > 0) { break; } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { CHKHR(HRESULT_FROM_WIN32(GetLastError())); } else { provision *= 2; CHKHR(Verify( offset + provision > offset )); } } // reduce size to reflect number of characters actually written. // Note that since we specified string length, no null terminator // was injected, so we don't have to remove it. CHKHR( _buffer->_outputStream.Resize(offset+written) ); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetWinRtInterface( GUID iid) { CHKHR(_OnSet()); CHKHR(_WriteGuid(iid)); _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetDelegate( GUID iid) { CHKHR(_OnSet()); CHKHR(_WriteString("delegate(")); CHKHR(_WriteGuid(iid)); CHKHR(_WriteChar(')')); _Completed(); return S_OK; } inline HRESULT SimpleMetaDataBuilder::_CommonInterfaceGroupSimple( PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID * defaultInterfaceIID) { CHKHR(_WriteWideString(name)); CHKHR(_WriteChar(';')); // InterfaceGroups and RuntimeClasses take one nested argument CHKHR(_buffer->_nestedArgs.Append(1)); if (!defaultInterfaceIID) { CHKHR(_WriteType(defaultInterfaceName)); } else { // complete the type signature immediately; no nested // call needed to resolve the interface. SimpleMetaDataBuilder builder(*_buffer, *_locator); CHKHR(builder.SetWinRtInterface(*defaultInterfaceIID)) } CHKHR(_WriteChar(')')); CHKHR(_buffer->_nestedArgs.Pop()); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetInterfaceGroupSimpleDefault( PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID* defaultInterfaceIID) { CHKHR(_OnSet()); CHKHR(_WriteString("ig(")); CHKHR(_CommonInterfaceGroupSimple(name, defaultInterfaceName, defaultInterfaceIID)); _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetInterfaceGroupParameterizedDefault( PCWSTR name, UINT32 elementCount, __in_ecount(elementCount) PCWSTR* defaultInterfaceNameElements) { CHKHR(_OnSet()); // If an interface group or runtime class has a compound type as its default, and that // type directly or indirectly refers to itself, the second occurrence instead used '*' // to signal that the default interface has already been specified earlier up the call // stack. This prevents unbounded recursion. if (_buffer->ExistsCycle(name)) { CHKHR( _WriteString("ig(") ); CHKHR( _WriteWideString(name) ); CHKHR( _WriteString(";*)") ); } else { SimpleMetaDataBuffer::ResolutionPathGuard guard(name, _buffer); CHKHR( _WriteString("ig(") ); CHKHR( _WriteWideString(name) ); CHKHR( _WriteChar(';') ); // InterfaceGroups and RuntimeClasses take one nested argument CHKHR( _buffer->_nestedArgs.Append(1) ); CHKHR( SendArguments(elementCount, defaultInterfaceNameElements) ); CHKHR( _buffer->_nestedArgs.Pop() ); CHKHR( _WriteChar(')') );; } _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetRuntimeClassSimpleDefault( PCWSTR name, PCWSTR defaultInterfaceName, __in_opt const GUID* defaultInterfaceIID) { CHKHR(_OnSet()); CHKHR(_WriteString("rc(")); CHKHR(_CommonInterfaceGroupSimple(name, defaultInterfaceName, defaultInterfaceIID)); _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetRuntimeClassParameterizedDefault( PCWSTR name, UINT32 elementCount, __in_ecount(elementCount) PCWSTR* defaultInterfaceNameElements) { CHKHR(_OnSet()); if (_buffer->ExistsCycle(name)) { CHKHR(_WriteString("rc(")); CHKHR(_WriteWideString(name)); CHKHR(_WriteString(";*)")); } else { SimpleMetaDataBuffer::ResolutionPathGuard guard(name, _buffer); CHKHR(_WriteString("rc(")); CHKHR(_WriteWideString(name)); CHKHR(_WriteChar(';')); // InterfaceGroups and RuntimeClasses take one nested argument CHKHR(_buffer->_nestedArgs.Append(1)); CHKHR(SendArguments(elementCount, defaultInterfaceNameElements)); CHKHR(_buffer->_nestedArgs.Pop()); CHKHR(_WriteChar(')')); } _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetStruct( PCWSTR name, UINT32 numFields, __in_ecount(numFields) PCWSTR* fieldTypeNames) { CHKHR(_OnSet()); CHKHR(_WriteString("struct(")); CHKHR(_WriteWideString(name)); CHKHR(_WriteChar(';')); CHKHR(_buffer->_nestedArgs.Append(1)); CHKHR(SendArguments(numFields, fieldTypeNames)); CHKHR(_buffer->_nestedArgs.Pop()); CHKHR(_WriteChar(')')); _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetEnum( PCWSTR name, PCWSTR baseType) { CHKHR(_OnSet()); CHKHR(_WriteString("enum(")); CHKHR(_WriteWideString(name)); CHKHR(_WriteChar(';')); CHKHR(_buffer->_nestedArgs.Append(1)); CHKHR(_WriteType(baseType)); CHKHR(_buffer->_nestedArgs.Pop()); CHKHR(_WriteChar(')')); _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetParameterizedInterface( GUID piid, UINT32 numArgs) { CHKHR(_OnSet()); CHKHR(_WriteString("pinterface(")); CHKHR(_WriteGuid(piid)); // Note the number of arguments. The SendArguments // function will append the ')' after that number of // arguments are consumed. CHKHR(_buffer->_nestedArgs.Append(numArgs)); _Completed(); return S_OK; } inline __override HRESULT STDMETHODCALLTYPE SimpleMetaDataBuilder::SetParameterizedDelegate( GUID piid, UINT32 numArgs) { // Parameterized interfaces and parameterized delegates use the same signature scheme. return SetParameterizedInterface(piid, numArgs); } }} // namespace Ro::detail #ifndef WINRT_PARAMINSTANCE_NOCRYPT_SHA1 namespace Ro { namespace detail { class Sha1 { public: Sha1() : _hAlg(nullptr) , _hHash(nullptr) { } HRESULT Initialize() { DWORD dwcb; DWORD dwcbResult; CHKNT(BCryptOpenAlgorithmProvider(&_hAlg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0)); CHKNT(BCryptGetProperty(_hAlg, BCRYPT_OBJECT_LENGTH, reinterpret_cast(&dwcb), sizeof(dwcb), &dwcbResult, 0)); _ahBuf.Value() = new (std::nothrow) BYTE[dwcb]; if (nullptr == _ahBuf.Value()) { CHKHR( E_OUTOFMEMORY ); } CHKNT(BCryptCreateHash(_hAlg, &_hHash, _ahBuf.Value(), dwcb, NULL, 0, 0)); return S_OK; } HRESULT AppendData(size_t numBytes, __in_bcount(numBytes) const void* bytes) { CHKHR(Verify(numBytes <= DWORD(-1))); CHKNT(BCryptHashData(_hHash, reinterpret_cast(const_cast(bytes)), DWORD(numBytes), 0)); return S_OK;; } HRESULT GetResult(__out BYTE (*hashValue)[20]) { // Sha1 hash result is fixed size, at 20 bytes. CHKNT(BCryptFinishHash(_hHash, reinterpret_cast(&hashValue[0]), _countof(*hashValue), 0)); return S_OK; } ~Sha1() { if (_hHash) { BCryptDestroyHash(_hHash); } if (_hAlg) { BCryptCloseAlgorithmProvider(_hAlg, 0); } } private: ArrayHolder _ahBuf; BCRYPT_ALG_HANDLE _hAlg; BCRYPT_HASH_HANDLE _hHash; }; }} // namespace Ro::detail extern "C" { inline HRESULT _RoSha1Create( __out void** handle) { *handle = nullptr; Ro::detail::ElementHolder sha1Instance; sha1Instance.Value() = new (std::nothrow) Ro::detail::Sha1; if (!sha1Instance.Value()) { CHKHR(E_OUTOFMEMORY); } CHKHR(sha1Instance->Initialize()); *handle = sha1Instance.Detach(); return S_OK; } inline HRESULT _RoSha1AppendData( __in void* handle, __in size_t numBytes, __in_bcount(numBytes) const void* data) { Ro::detail::Sha1* sha1Instance = static_cast(handle); CHKHR(sha1Instance->AppendData(numBytes, data)); return S_OK; } inline HRESULT _RoSha1Finish( __in void* handle, __out BYTE (*hashValue)[20]) { Ro::detail::Sha1* sha1Instance = static_cast(handle); CHKHR(sha1Instance->GetResult(hashValue)); return S_OK; } inline void _RoSha1Release(__in void* handle) { Ro::detail::Sha1* sha1Instance = static_cast(handle); delete sha1Instance; } } #endif /* ifdef WINRT_PARAMINSTANCE_NOCRYPT_SHA1 */ #ifdef _MSC_VER #pragma pop_macro("CHKNT") #pragma pop_macro("CHKHR") #pragma warning( pop ) #endif #endif /* ifdef __cplusplus */ #endif /* ifndef WINRT_PARAMINSTANCEAPI_H */