diff --git a/src/dnmd/entry.c b/src/dnmd/entry.c index c93d78cb..7a68a84b 100644 --- a/src/dnmd/entry.c +++ b/src/dnmd/entry.c @@ -248,6 +248,119 @@ bool md_create_handle(void const* data, size_t data_len, mdhandle_t* handle) return true; } +// Initialize the minimal set of tables required for a valid metadata image. +// Every image must have a row in the Module table +// for module identity information +// and a row in the TypeDef table for the global type. +static bool initialize_minimal_table_rows(mdcxt_t* cxt) +{ + // Add the Module row for module identity + mdcursor_t module_cursor; + if (!md_append_row(cxt, mdtid_Module, &module_cursor)) + return false; + + // Set the Generation to 0 + uint32_t generation = 0; + if (1 != md_set_column_value_as_constant(module_cursor, mdtModule_Generation, 1, &generation)) + return false; + + // Use the 0 index to specify the NULL guid as the guids for the image. + uint32_t guid_heap_offset = 0; + if (1 != set_column_value_as_heap_offset(module_cursor, mdtModule_Mvid, 1, &guid_heap_offset) + || 1 != set_column_value_as_heap_offset(module_cursor, mdtModule_EncBaseId, 1, &guid_heap_offset) + || 1 != set_column_value_as_heap_offset(module_cursor, mdtModule_EncId, 1, &guid_heap_offset)) + { + return false; + } + + char const* name = ""; + if (1 != md_set_column_value_as_utf8(module_cursor, mdtModule_Name, 1, &name)) + return false; + + // Mark that we're done adding the Module row. + md_commit_row_add(module_cursor); + + // Add a row for the global type. + mdcursor_t global_type_cursor; + if (!md_append_row(cxt, mdtid_TypeDef, &global_type_cursor)) + return false; + + uint32_t flags = 0; + if (1 != md_set_column_value_as_constant(global_type_cursor, mdtTypeDef_Flags, 1, &flags)) + return false; + + char const* global_type_name = ""; // Defined in ECMA-335 II.10.8 + if (1 != md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeName, 1, &global_type_name)) + return false; + + char const* namespace = ""; + if (1 != md_set_column_value_as_utf8(global_type_cursor, mdtTypeDef_TypeNamespace, 1, &namespace)) + return false; + + mdToken nil_typedef = CreateTokenType(mdtTypeDef); + if (1 != md_set_column_value_as_token(global_type_cursor, mdtTypeDef_Extends, 1, &nil_typedef)) + return false; + + // Mark that we're done adding the TypeDef row. + md_commit_row_add(global_type_cursor); + + return true; +} + +mdhandle_t md_create_new_handle() +{ + mdcxt_t cxt; + + memset(&cxt, 0, sizeof(mdcxt_t)); + cxt.magic = MDLIB_MAGIC_NUMBER; + cxt.context_flags = mdc_none; + cxt.major_ver = 1; + cxt.minor_ver = 1; + cxt.flags = 0; + cxt.version = "v4.0.30319"; + cxt.editor = NULL; + cxt.mem = NULL; + + // Allocate and initialize a full context + // with the correctly-sized trailing memory. + mdcxt_t* pcxt = allocate_full_context(&cxt); + if (pcxt == NULL) + return NULL; + + if (!initialize_minimal_table_rows(pcxt)) + { + free(pcxt); + return NULL; + } + + return pcxt; +} + +#ifdef DNMD_PORTABLE_PDB +mdhandle_t md_create_new_pdb_handle() +{ + mdcxt_t cxt; + + memset(&cxt, 0, sizeof(mdcxt_t)); + cxt.magic = MDLIB_MAGIC_NUMBER; + cxt.context_flags = mdc_none; + cxt.major_ver = 1; + cxt.minor_ver = 1; + cxt.flags = 0; + cxt.version = "PDB v1.0"; + cxt.editor = NULL; + cxt.mem = NULL; + + // Allocate and initialize a full context + // with the correctly-sized trailing memory. + mdcxt_t* pcxt = allocate_full_context(&cxt); + if (pcxt == NULL) + return NULL; + + return pcxt; +} +#endif // DNMD_PORTABLE_PDB + bool md_apply_delta(mdhandle_t handle, void const* data, size_t data_len) { mdcxt_t* base = extract_mdcxt(handle); diff --git a/src/inc/dnmd.h b/src/inc/dnmd.h index ee593c45..98361142 100644 --- a/src/inc/dnmd.h +++ b/src/inc/dnmd.h @@ -38,6 +38,21 @@ typedef void* mdhandle_t; // If modifications are made, the data will not be updated in place. bool md_create_handle(void const* data, size_t data_len, mdhandle_t* handle); +// Create a new metadata handle for a new image. +// Returns a handle for the new image, or NULL if the handle could not be created. +// The image will always be in the v1.1 ECMA-355 metadata format, +// use the "v4.0.30319" version string, +// and have an MVID of all zeros. +mdhandle_t md_create_new_handle(); + +#ifdef DNMD_PORTABLE_PDB +// Create a new metadata handle for a new Portable PDB image. +// Returns a handle for the new image, or NULL if the handle could not be created. +// The image will always be in the v1.1 metadata format +// and use the "PDB v1.0" version string. +mdhandle_t md_create_new_pdb_handle(); +#endif // DNMD_PORTABLE_PDB + // Apply delta data to the current metadata. bool md_apply_delta(mdhandle_t handle, void const* data, size_t data_len); @@ -528,6 +543,8 @@ void md_commit_row_add(mdcursor_t row); // Add a user string to the #US heap. mduserstringcursor_t md_add_userstring_to_heap(mdhandle_t handle, char16_t const* userstring); +// Write the metadata represented by the handle to the supplied buffer. +// The metadata is always written with the v2.0 table schema. bool md_write_to_buffer(mdhandle_t handle, uint8_t* buffer, size_t* len); #ifdef __cplusplus } diff --git a/src/interfaces/dispenser.cpp b/src/interfaces/dispenser.cpp index 08671735..8f9802b0 100644 --- a/src/interfaces/dispenser.cpp +++ b/src/interfaces/dispenser.cpp @@ -24,11 +24,50 @@ namespace REFIID riid, IUnknown** ppIUnk) { - UNREFERENCED_PARAMETER(rclsid); - UNREFERENCED_PARAMETER(dwCreateFlags); - UNREFERENCED_PARAMETER(riid); - UNREFERENCED_PARAMETER(ppIUnk); - return E_NOTIMPL; + if (rclsid != CLSID_CLR_v2_MetaData) + { + // DNMD::Interfaces only creating v2 metadata images. + return CLDB_E_FILE_OLDVER; + } + + if (dwCreateFlags != 0) + { + return E_INVALIDARG; + } + + mdhandle_ptr md_ptr { md_create_new_handle() }; + if (md_ptr == nullptr) + return E_OUTOFMEMORY; + + // Initialize the MVID of the new image. + mdcursor_t moduleCursor; + if (!md_token_to_cursor(md_ptr.get(), TokenFromRid(1, mdtModule), &moduleCursor)) + return E_FAIL; + + mdguid_t mvid; + HRESULT hr = PAL_CoCreateGuid(reinterpret_cast(&mvid)); + if (FAILED(hr)) + return hr; + + if (1 != md_set_column_value_as_guid(moduleCursor, mdtModule_Mvid, 1, &mvid)) + return E_OUTOFMEMORY; + + dncp::com_ptr obj; + obj.Attach(new (std::nothrow) ControllingIUnknown()); + if (obj == nullptr) + return E_OUTOFMEMORY; + + try + { + mdhandle_view handle_view{ obj->CreateAndAddTearOff(std::move(md_ptr)) }; + (void)obj->CreateAndAddTearOff(std::move(handle_view)); + } + catch(std::bad_alloc const&) + { + return E_OUTOFMEMORY; + } + + return obj->QueryInterface(riid, (void**)ppIUnk); } STDMETHOD(OpenScope)( diff --git a/src/interfaces/dnmdowner.hpp b/src/interfaces/dnmdowner.hpp index 09977882..a4b6a557 100644 --- a/src/interfaces/dnmdowner.hpp +++ b/src/interfaces/dnmdowner.hpp @@ -87,6 +87,13 @@ class DNMDOwner final : public TearOffBase } public: + DNMDOwner(IUnknown* controllingUnknown, mdhandle_ptr md_ptr) + : TearOffBase(controllingUnknown) + , _handle{ std::move(md_ptr) } + , _malloc_to_free{ nullptr } + , _cotaskmem_to_free{ nullptr } + { } + DNMDOwner(IUnknown* controllingUnknown, mdhandle_ptr md_ptr, malloc_ptr mallocMem, dncp::cotaskmem_ptr cotaskmemMem) : TearOffBase(controllingUnknown) , _handle{ std::move(md_ptr) } diff --git a/src/interfaces/iids.cpp b/src/interfaces/iids.cpp index 7e490ffa..c4dd0b3a 100644 --- a/src/interfaces/iids.cpp +++ b/src/interfaces/iids.cpp @@ -18,5 +18,8 @@ MIDL_DEFINE_GUID(IID_IMetaDataEmit2, 0xf5dd9950, 0xf693, 0x42e6, 0x83, 0xe, 0x7b // Define the ISymUnmanaged* IIDs here - corsym.h provides the declaration. MIDL_DEFINE_GUID(IID_ISymUnmanagedBinder, 0xaa544d42, 0x28cb, 0x11d3, 0xbd, 0x22, 0x00, 0x00, 0xf8, 0x08, 0x49, 0xbd); +// Define option IIDs here - cor.h provides the declaration. +MIDL_DEFINE_GUID(CLSID_CLR_v2_MetaData, 0xefea471a, 0x44fd, 0x4862, 0x92, 0x92, 0xc, 0x58, 0xd4, 0x6e, 0x1f, 0x3a); + // Define an IID for our own marker interface MIDL_DEFINE_GUID(IID_IDNMDOwner, 0x250ebc02, 0x1a92, 0x4638, 0xaa, 0x6c, 0x3d, 0x0f, 0x98, 0xb3, 0xa6, 0xfb);