 
            -- v7: dlls/rometadata: Add stub for IMetaDataTables.
 
            From: Vibhav Pant vibhavp@gmail.com
--- dlls/rometadata/Makefile.in | 3 +- dlls/rometadata/main.c | 18 +- dlls/rometadata/mdtables.c | 229 ++++++++++++++ dlls/rometadata/rometadatapriv.h | 24 ++ dlls/rometadata/tests/Makefile.in | 4 +- dlls/rometadata/tests/rometadata.c | 434 ++++++++++++++++++++++++++ dlls/rometadata/tests/rsrc.rc | 20 ++ dlls/rometadata/tests/test-simple.idl | 50 +++ include/Makefile.in | 1 + include/rometadataapi.idl | 69 ++++ 10 files changed, 848 insertions(+), 4 deletions(-) create mode 100644 dlls/rometadata/mdtables.c create mode 100644 dlls/rometadata/rometadatapriv.h create mode 100644 dlls/rometadata/tests/rsrc.rc create mode 100644 dlls/rometadata/tests/test-simple.idl create mode 100644 include/rometadataapi.idl
diff --git a/dlls/rometadata/Makefile.in b/dlls/rometadata/Makefile.in index 7a9940a79d1..52c39ba8f87 100644 --- a/dlls/rometadata/Makefile.in +++ b/dlls/rometadata/Makefile.in @@ -3,4 +3,5 @@ IMPORTLIB = rometadata IMPORTS = combase
SOURCES = \ - main.c + main.c \ + mdtables.c diff --git a/dlls/rometadata/main.c b/dlls/rometadata/main.c index 2530cdf84e0..6cded07c11f 100644 --- a/dlls/rometadata/main.c +++ b/dlls/rometadata/main.c @@ -21,8 +21,11 @@ #include "objbase.h" #include "cor.h" #include "rometadata.h" +#include "rometadataapi.h" #include "wine/debug.h"
+#include "rometadatapriv.h" + WINE_DEFAULT_DEBUG_CHANNEL(rometadata);
struct metadata_dispenser @@ -87,8 +90,19 @@ static HRESULT WINAPI MetaDataDispenser_DefineScope(IMetaDataDispenserEx *iface, static HRESULT WINAPI MetaDataDispenser_OpenScope(IMetaDataDispenserEx *iface, const WCHAR *scope, DWORD open_flags, REFIID riid, IUnknown **obj) { - FIXME("%p %s %lx %s %p\n", iface, debugstr_w(scope), open_flags, debugstr_guid(riid), obj); - return E_NOTIMPL; + IMetaDataTables *tables; + HRESULT hr; + + FIXME("%p %s %lx %s %p semi-stub!\n", iface, debugstr_w(scope), open_flags, debugstr_guid(riid), obj); + + *obj = NULL; + hr = IMetaDataTables_create(&tables); + if (FAILED(hr)) + return hr; + + hr = IMetaDataTables_QueryInterface(tables, riid, (void **)obj); + IMetaDataTables_Release(tables); + return hr; }
static HRESULT WINAPI MetaDataDispenser_OpenScopeOnMemory(IMetaDataDispenserEx *iface, const void *data, diff --git a/dlls/rometadata/mdtables.c b/dlls/rometadata/mdtables.c new file mode 100644 index 00000000000..180dbb09b68 --- /dev/null +++ b/dlls/rometadata/mdtables.c @@ -0,0 +1,229 @@ +/* + * IMetaDataTables implementation + * + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS +#include "objbase.h" +#include "cor.h" +#include "rometadataapi.h" +#include "wine/debug.h" + +#include "rometadatapriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(rometadata); + +struct metadata_tables +{ + IMetaDataTables IMetaDataTables_iface; + LONG ref; +}; + +static inline struct metadata_tables *impl_from_IMetaDataTables(IMetaDataTables *iface) +{ + return CONTAINING_RECORD(iface, struct metadata_tables, IMetaDataTables_iface); +} + +static HRESULT WINAPI tables_QueryInterface(IMetaDataTables *iface, REFIID iid, void **out) +{ + TRACE("(%p, %s, %p)\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(&IID_IUnknown, iid) || IsEqualGUID(&IID_IMetaDataTables, iid)) + { + IMetaDataTables_AddRef((*out = iface)); + return S_OK; + } + + FIXME("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI tables_AddRef(IMetaDataTables *iface) +{ + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + ULONG ref = InterlockedIncrement(&impl->ref); + + TRACE("(%p)\n", impl); + + return ref; +} + +static ULONG WINAPI tables_Release(IMetaDataTables *iface) +{ + struct metadata_tables *impl = impl_from_IMetaDataTables(iface); + ULONG ref = InterlockedDecrement(&impl->ref); + + TRACE("(%p)\n", iface); + + if (!ref) free(impl); + return ref; +} + +static HRESULT WINAPI tables_GetStringHeapSize(IMetaDataTables *iface, ULONG *size) +{ + FIXME("(%p, %p): stub!\n", iface, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetBlobHeapSize(IMetaDataTables *iface, ULONG *size) +{ + FIXME("(%p, %p): stub!\n", iface, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetGuidHeapSize(IMetaDataTables *iface, ULONG *size) +{ + FIXME("(%p, %p): stub!\n", iface, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetUserStringHeapSize(IMetaDataTables *iface, ULONG *size) +{ + FIXME("(%p, %p): stub!\n", iface, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetNumTables(IMetaDataTables *iface, ULONG *size) +{ + FIXME("(%p, %p): stub!\n", iface, size); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetTableIndex(IMetaDataTables *iface, ULONG token, ULONG *idx) +{ + FIXME("(%p %lu %p): stub!\n", iface, token, idx); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetTableInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG *row_size, ULONG *num_rows, + ULONG *num_cols, ULONG *idx_key, const char **name) +{ + FIXME("(%p, %lu, %p, %p, %p, %p, %p): stub!\n", iface, idx_tbl, row_size, num_rows, num_cols, idx_key, name); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetColumnInfo(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG *offset, + ULONG *col_size, ULONG *type, const char **name) +{ + FIXME("(%p, %lu, %lu, %p, %p, %p, %p) stub!\n", iface, idx_tbl, idx_col, offset, col_size, type, name); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetCodedTokenInfo(IMetaDataTables *iface, ULONG type, ULONG *tokens_len, + const ULONG **tokens, const char **name) +{ + FIXME("(%p, %lu, %p, %p, %p) stub!\n", iface, type, tokens_len, tokens, name); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetRow(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_row, const BYTE *row) +{ + FIXME("(%p, %lu, %lu, %p): stub!\n", iface, idx_tbl, idx_row, row); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetColumn(IMetaDataTables *iface, ULONG idx_tbl, ULONG idx_col, ULONG idx_row, ULONG *val) +{ + FIXME("(%p, %lu, %lu, %lu, %p): stub!\n", iface, idx_tbl, idx_col, idx_row, val); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetString(IMetaDataTables *iface, ULONG idx, const char **str) +{ + FIXME("(%p, %lu, %p): stub!\n", iface, idx, str); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetBlob(IMetaDataTables *iface, ULONG idx, ULONG *size, const BYTE **blob) +{ + FIXME("(%p, %lu, %p, %p): stub!\n", iface, idx, size, blob); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetGuid(IMetaDataTables *iface, ULONG idx, const GUID **guid) +{ + FIXME("(%p, %lu, %p): stub!\n", iface, idx, guid); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetUserString(IMetaDataTables *iface, ULONG idx, ULONG *size, const BYTE **string) +{ + FIXME("%p %lu %p %p stub!\n", iface, idx, size, string); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetNextString(IMetaDataTables *iface, ULONG idx, ULONG *next) +{ + FIXME("(%p, %lu, %p): stub!\n", iface, idx, next); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetNextBlob(IMetaDataTables *iface, ULONG idx, ULONG *next) +{ + FIXME("(%p, %lu, %p): stub!\n", iface, idx, next); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetNextGuid(IMetaDataTables *iface, ULONG idx, ULONG *next) +{ + FIXME("(%p, %lu, %p): stub!\n", iface, idx, next); + return E_NOTIMPL; +} + +static HRESULT WINAPI tables_GetNextUserString(IMetaDataTables *iface, ULONG idx, ULONG *next) +{ + FIXME("(%p, %lu, %p): stub!\n", iface, idx, next); + return E_NOTIMPL; +} + +static const struct IMetaDataTablesVtbl tables_vtbl = +{ + tables_QueryInterface, + tables_AddRef, + tables_Release, + tables_GetStringHeapSize, + tables_GetBlobHeapSize, + tables_GetGuidHeapSize, + tables_GetUserStringHeapSize, + tables_GetNumTables, + tables_GetTableIndex, + tables_GetTableInfo, + tables_GetColumnInfo, + tables_GetCodedTokenInfo, + tables_GetRow, + tables_GetColumn, + tables_GetString, + tables_GetBlob, + tables_GetGuid, + tables_GetUserString, + tables_GetNextString, + tables_GetNextBlob, + tables_GetNextGuid, + tables_GetNextUserString, +}; + +HRESULT IMetaDataTables_create(IMetaDataTables **iface) +{ + struct metadata_tables *impl; + + if (!(impl = calloc(1, sizeof(*impl)))) return E_OUTOFMEMORY; + impl->IMetaDataTables_iface.lpVtbl = &tables_vtbl; + impl->ref = 1; + *iface = &impl->IMetaDataTables_iface; + return S_OK; +} diff --git a/dlls/rometadata/rometadatapriv.h b/dlls/rometadata/rometadatapriv.h new file mode 100644 index 00000000000..e6ff2ada18a --- /dev/null +++ b/dlls/rometadata/rometadatapriv.h @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __WINE_ROMETADATA_PRIVATE__ +#define __WINE_ROMETADATA_PRIVATE__ + +extern HRESULT IMetaDataTables_create(IMetaDataTables **iface); + +#endif /* __WINE_ROMETADATA_PRIVATE__ */ diff --git a/dlls/rometadata/tests/Makefile.in b/dlls/rometadata/tests/Makefile.in index 58813aefc54..d70008507b6 100644 --- a/dlls/rometadata/tests/Makefile.in +++ b/dlls/rometadata/tests/Makefile.in @@ -2,4 +2,6 @@ TESTDLL = rometadata.dll IMPORTS = combase rometadata
SOURCES = \ - rometadata.c + rometadata.c \ + rsrc.rc \ + test-simple.idl \ No newline at end of file diff --git a/dlls/rometadata/tests/rometadata.c b/dlls/rometadata/tests/rometadata.c index b4adcd90286..c80714671d6 100644 --- a/dlls/rometadata/tests/rometadata.c +++ b/dlls/rometadata/tests/rometadata.c @@ -22,8 +22,12 @@ #include "cor.h" #include "roapi.h" #include "rometadata.h" +#include "rometadataapi.h" #include "wine/test.h"
+#define WIDL_using_Wine_Test +#include "test-simple.h" + DEFINE_GUID(GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
static void test_MetaDataGetDispenser(void) @@ -57,6 +61,435 @@ static void test_MetaDataGetDispenser(void) IMetaDataDispenserEx_Release(dispenser_ex); }
+static WCHAR *load_resource(const WCHAR *name) +{ + static WCHAR pathW[MAX_PATH]; + DWORD written; + HANDLE file; + HRSRC res; + void *ptr; + + GetTempPathW(ARRAY_SIZE(pathW), pathW); + wcscat(pathW, name); + + file = CreateFileW(pathW, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + ok(file != INVALID_HANDLE_VALUE, "Failed to create file %s, error %lu.\n", + wine_dbgstr_w(pathW), GetLastError()); + + res = FindResourceW(NULL, name, (LPCWSTR)RT_RCDATA); + ok(!!res, "Failed to load resource, error %lu.\n", GetLastError()); + ptr = LockResource(LoadResource(GetModuleHandleA(NULL), res)); + WriteFile(file, ptr, SizeofResource( GetModuleHandleA(NULL), res), &written, NULL); + ok(written == SizeofResource(GetModuleHandleA(NULL), res), "Failed to write resource.\n"); + CloseHandle(file); + + return pathW; +} + +#pragma pack(push,1) +struct row_module +{ + WORD Generation; + WORD Name; + WORD Mvid; + WORD EncId; + WORD EncBaseId; +}; + +struct row_typeref +{ + WORD ResolutionScope; + WORD Name; + WORD Namespace; +}; + +struct row_typedef +{ + DWORD Flags; + WORD Name; + WORD Namespace; + WORD Extends; + WORD FieldList; + WORD MethodList; +}; + +struct row_memberref +{ + WORD Class; + WORD Name; + WORD Signature; +}; + +struct row_custom_attribute +{ + WORD Parent; + WORD Type; + WORD Value; +}; + +#pragma pack(pop) + +enum +{ + TYPE_ATTR_PUBLIC = 0x000001, + TYPE_ATTR_NESTEDPUBLIC = 0x000002, + TYPE_ATTR_NESTEDPRIVATE = 0x000003, + TYPE_ATTR_NESTEDFAMILY = 0x000004, + TYPE_ATTR_NESTEDASSEMBLY = 0x000005, + TYPE_ATTR_NESTEDFAMANDASSEM = 0x000006, + TYPE_ATTR_NESTEDFAMORASSEM = 0x000007, + TYPE_ATTR_SEQUENTIALLAYOUT = 0x000008, + TYPE_ATTR_EXPLICITLAYOUT = 0x000010, + TYPE_ATTR_INTERFACE = 0x000020, + TYPE_ATTR_ABSTRACT = 0x000080, + TYPE_ATTR_SEALED = 0x000100, + TYPE_ATTR_SPECIALNAME = 0x000400, + TYPE_ATTR_RTSPECIALNAME = 0x000800, + TYPE_ATTR_IMPORT = 0x001000, + TYPE_ATTR_SERIALIZABLE = 0x002000, + TYPE_ATTR_UNKNOWN = 0x004000, + TYPE_ATTR_UNICODECLASS = 0x010000, + TYPE_ATTR_AUTOCLASS = 0x020000, + TYPE_ATTR_CUSTOMFORMATCLASS = 0x030000, + TYPE_ATTR_HASSECURITY = 0x040000, + TYPE_ATTR_BEFOREFIELDINIT = 0x100000 +}; + +enum table +{ + TABLE_MODULE = 0x00, + TABLE_TYPEREF = 0x01, + TABLE_TYPEDEF = 0x02, + TABLE_FIELD = 0x04, + TABLE_METHODDEF = 0x06, + TABLE_PARAM = 0x08, + TABLE_INTERFACEIMPL = 0x09, + TABLE_MEMBERREF = 0x0a, + TABLE_CONSTANT = 0x0b, + TABLE_CUSTOMATTRIBUTE = 0x0c, + TABLE_FIELDMARSHAL = 0x0d, + TABLE_DECLSECURITY = 0x0e, + TABLE_CLASSLAYOUT = 0x0f, + TABLE_FIELDLAYOUT = 0x10, + TABLE_STANDALONESIG = 0x11, + TABLE_EVENTMAP = 0x12, + TABLE_EVENT = 0x14, + TABLE_PROPERTYMAP = 0x15, + TABLE_PROPERTY = 0x17, + TABLE_METHODSEMANTICS = 0x18, + TABLE_METHODIMPL = 0x19, + TABLE_MODULEREF = 0x1a, + TABLE_TYPESPEC = 0x1b, + TABLE_IMPLMAP = 0x1c, + TABLE_FIELDRVA = 0x1d, + TABLE_ASSEMBLY = 0x20, + TABLE_ASSEMBLYPROCESSOR = 0x21, + TABLE_ASSEMBLYOS = 0x22, + TABLE_ASSEMBLYREF = 0x23, + TABLE_ASSEMBLYREFPROCESSOR = 0x24, + TABLE_ASSEMBLYREFOS = 0x25, + TABLE_FILE = 0x26, + TABLE_EXPORTEDTYPE = 0x27, + TABLE_MANIFESTRESOURCE = 0x28, + TABLE_NESTEDCLASS = 0x29, + TABLE_GENERICPARAM = 0x2a, + TABLE_METHODSPEC = 0x2b, + TABLE_GENERICPARAMCONSTRAINT = 0x2c, + TABLE_MAX = 0x2d +}; + +struct table_info +{ + ULONG exp_row_size; + ULONG exp_rows; + ULONG exp_cols; + ULONG exp_key_idx; + const char *exp_name; +}; + +struct type_info +{ + DWORD exp_flags; + const char *exp_name; + const char *exp_namespace; +}; + +static void test_MetaDataDispenser_OpenScope(void) +{ + static const struct table_info tables[TABLE_MAX] = { + {sizeof(struct row_module), 1, 5, -1, "Module"}, + {sizeof(struct row_typeref), 10, 3, -1, "TypeRef"}, + {sizeof(struct row_typedef), 3, 6, -1, "TypeDef"}, + {2, 0, 1, -1, "FieldPtr"}, + {6, 0, 3, -1, "Field"}, + {2, 0, 1, -1, "MethodPtr"}, + {14, 2, 6, -1, "Method"}, + {2, 0, 1, -1, "ParamPtr"}, + {6, 0, 3, -1, "Param"}, + {4, 1, 2, 0, "InterfaceImpl"}, + {sizeof(struct row_memberref), 6, 3, -1, "MemberRef"}, + {6, 0, 3, 1, "Constant"}, + {sizeof(struct row_custom_attribute), 7, 3, 0, "CustomAttribute"}, + {4, 0, 2, 0, "FieldMarshal"}, + {6, 0, 3, 1, "DeclSecurity"}, + {8, 0, 3, 2, "ClassLayout"}, + {6, 0, 2, 1, "FieldLayout"}, + {2, 0, 1, -1, "StandAloneSig"}, + {4, 0, 2, -1, "EventMap"}, + {2, 0, 1, -1, "EventPtr"}, + {6, 0, 3, -1, "Event"}, + {4, 0, 2, -1, "PropertyMap"}, + {2, 0, 1, -1, "PropertyPtr"}, + {6, 0, 3, -1, "Property"}, + {6, 0, 3, 2, "MethodSemantics"}, + {6, 1, 3, 0, "MethodImpl"}, + {2, 0, 1, -1, "ModuleRef"}, + {2, 0, 1, -1, "TypeSpec"}, + {8, 0, 4, 1, "ImplMap"}, + {6, 0, 2, 1, "FieldRVA"}, + {8, 0, 2, -1, "ENCLog"}, + {4, 0, 1, -1, "ENCMap"}, + {22, 1, 9, -1, "Assembly"}, + {4, 0, 1, -1, "AssemblyProcessor"}, + {12, 0, 3, -1, "AssemblyOS"}, + {20, 3, 9, -1, "AssemblyRef"}, + {6, 0, 2, -1, "AssemblyRefProcessor"}, + {14, 0, 4, -1, "AssemblyRefOS"}, + {8, 0, 3, -1, "File"}, + {14, 0, 5, -1, "ExportedType"}, + {12, 0, 4, -1, "ManifestResource"}, + {4, 0, 2, 0, "NestedClass"}, + {8, 0, 4, 2, "GenericParam"}, + {4, 0, 2, -1, "MethodSpec"}, + {4, 0, 2, 0, "GenericParamConstraint"}, + }; + static const struct type_info type_defs[3] = + { + { 0, "<Module>", NULL }, + { TYPE_ATTR_INTERFACE | TYPE_ATTR_ABSTRACT | TYPE_ATTR_UNKNOWN, "ITest1", "Wine.Test" }, + { TYPE_ATTR_PUBLIC | TYPE_ATTR_SEALED | TYPE_ATTR_UNKNOWN, "Test1", "Wine.Test" }, + }; + const WCHAR *filename = load_resource(L"test-simple.winmd"); + ULONG val = 0, i, guid_ctor_idx = 0, itest1_def_idx = 0; + const struct row_typedef *type_def = NULL; + const struct row_module *module = NULL; + IMetaDataDispenser *dispenser; + IMetaDataTables *md_tables; + const GUID *guid = NULL; + const char *str; + HRESULT hr; + + hr = MetaDataGetDispenser(&CLSID_CorMetaDataDispenser, &IID_IMetaDataDispenser, (void **)&dispenser); + ok(hr == S_OK, "got hr %#lx\n", hr); + + hr = IMetaDataDispenser_OpenScope(dispenser, filename, 0, &IID_IMetaDataTables, (IUnknown **)&md_tables); + ok(hr == S_OK, "got hr %#lx\n", hr); + IMetaDataDispenser_Release(dispenser); + + hr = IMetaDataTables_GetStringHeapSize(md_tables, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == 329, "got val %lu\n", val); + + val = 0; + hr = IMetaDataTables_GetBlobHeapSize(md_tables, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == 156, "got val %lu\n", val); + + val = 0; + hr = IMetaDataTables_GetGuidHeapSize(md_tables, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == 16, "got val %lu\n", val); + + val = 0; + hr = IMetaDataTables_GetUserStringHeapSize(md_tables, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == 8, "got val %lu\n", val); + + val = 0; + hr = IMetaDataTables_GetNumTables(md_tables, &val); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(val == TABLE_MAX, "got val %lu\n", val); + + for (i = 0; i < TABLE_MAX; i++) + { + const struct table_info *table = &tables[i]; + ULONG row_size, rows, cols, key_idx; + const char *name; + + winetest_push_context("tables[%lu]", i); + + hr = IMetaDataTables_GetTableInfo(md_tables, i, &row_size, &rows, &cols, &key_idx, &name); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + if (FAILED(hr)) + { + winetest_pop_context(); + continue; + } + + todo_wine + ok(row_size == table->exp_row_size, "got row_size %lu != %lu\n", row_size, table->exp_row_size); + todo_wine + ok(rows == table->exp_rows, "got rows %lu != %lu\n", rows, table->exp_rows); + todo_wine + ok(cols == table->exp_cols, "got cols %lu != %lu\n", cols, table->exp_cols); + todo_wine + ok(key_idx == table->exp_key_idx, "got key_idx %lu != %lu\n", key_idx, table->exp_key_idx); + todo_wine + ok(!strcmp(name, table->exp_name), "got name %s != %s\n", debugstr_a(name), debugstr_a(table->exp_name)); + + winetest_pop_context(); + } + + /* Read module information */ + hr = IMetaDataTables_GetRow(md_tables, TABLE_MODULE, 1, (BYTE *)&module); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(!!module, "got module=%p\n", module); + + if (SUCCEEDED(hr)) + { + str = NULL; + hr = IMetaDataTables_GetString(md_tables, module->Name, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(str &&!strcmp(str, "dlls/rometadata/tests/test-simple.winmd"), "got str %s\n", debugstr_a(str)); + + hr = IMetaDataTables_GetGuid(md_tables, module->Mvid, &guid); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(!!guid, "got guid %p\n", guid); + } + + /* Read defined types. */ + for (i = 0; i < ARRAY_SIZE(type_defs); i++) + { + const struct type_info *type_info = &type_defs[i]; + + winetest_push_context("type_info[%lu]", i); + + type_def = NULL; + hr = IMetaDataTables_GetRow(md_tables, TABLE_TYPEDEF, i + 1, (BYTE *)&type_def); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + todo_wine + ok(!!type_def, "got type_def=%p\n", type_def); + if (FAILED(hr)) + { + winetest_pop_context(); + continue; + } + + ok(type_def->Flags == type_info->exp_flags, "got Flags %#lx != %#lx\n", type_def->Flags, type_info->exp_flags); + str = NULL; + hr = IMetaDataTables_GetString(md_tables, type_def->Name, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(str && !strcmp(str, type_info->exp_name), "got str %s != %s\n", debugstr_a(str), + debugstr_a(type_info->exp_name)); + if (type_info->exp_namespace) + { + str = NULL; + hr = IMetaDataTables_GetString(md_tables, type_def->Namespace, &str); + ok(str && !strcmp(str, type_info->exp_namespace), "got str %s != %s\n", debugstr_a(str), + debugstr_a(type_info->exp_namespace)); + if (!strcmp(type_info->exp_name, "ITest1") && !strcmp(type_info->exp_namespace, "Wine.Test")) + itest1_def_idx = ((i + 1) << 5) | 3; + } + + winetest_pop_context(); + } + + /* Get the MemberRef row for the GuidAttribute constructor */ + for (i = 0; i < tables[TABLE_MEMBERREF].exp_rows; i++) + { + const struct row_memberref *ref = NULL; + + winetest_push_context("i=%lu", i); + + hr = IMetaDataTables_GetRow(md_tables, TABLE_MEMBERREF, i + 1, (BYTE *)&ref); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + if (FAILED(hr)) + { + winetest_pop_context(); + continue; + } + + hr = IMetaDataTables_GetString(md_tables, ref->Name, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + if (str && !strcmp(str, ".ctor")) + { + const struct row_typeref *typeref = NULL; + + /* All MemberRefParent coded indices in test-simple.winmd point to TypeRef entries. */ + hr = IMetaDataTables_GetRow(md_tables, TABLE_TYPEREF, (ref->Class & ~7) >> 3, (BYTE *)&typeref); + ok(hr == S_OK, "got hr %#lx\n", hr); + hr = IMetaDataTables_GetString(md_tables, typeref->Name, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + if (!strcmp(str, "GuidAttribute")) + { + hr = IMetaDataTables_GetString(md_tables, typeref->Namespace, &str); + ok(hr == S_OK, "got hr %#lx\n", hr); + if (!strcmp(str, "Windows.Foundation.Metadata")) + { + guid_ctor_idx = ((i + 1) << 3) | 3; + winetest_pop_context(); + break; + } + } + } + + winetest_pop_context(); + } + + todo_wine + ok(!!guid_ctor_idx, "got guid_ctor_coded_idx %lu\n", guid_ctor_idx); + + /* Verify ITest1 has the correct GuidAttribute value. */ + for (i = 0; i < tables[TABLE_CUSTOMATTRIBUTE].exp_rows; i++) + { + const struct row_custom_attribute *attr = NULL; + + winetest_push_context("i=%lu", i); + + attr = NULL; + hr = IMetaDataTables_GetRow(md_tables, TABLE_CUSTOMATTRIBUTE, i + 1, (BYTE *)&attr); + todo_wine + ok(hr == S_OK, "got hr %#lx\n", hr); + if (FAILED(hr)) + { + winetest_pop_context(); + continue; + } + + if (attr->Type == guid_ctor_idx && attr->Parent == itest1_def_idx) + { + const BYTE *value = NULL; + const GUID *guid; + ULONG size = 0; + + hr = IMetaDataTables_GetBlob(md_tables, attr->Value, &size, &value); + ok(hr == S_OK, "got hr %#lx\n", hr); + ok(size == sizeof(GUID) + 4, "got size %lu\n", size); + guid = (GUID *)&value[2]; + ok(IsEqualGUID(guid, &IID_ITest1), "got guid %s\n", debugstr_guid(guid)); + } + + winetest_pop_context(); + } + + IMetaDataTables_Release(md_tables); +} + START_TEST(rometadata) { HRESULT hr; @@ -65,6 +498,7 @@ START_TEST(rometadata) ok(hr == S_OK, "RoInitialize failed, hr %#lx\n", hr);
test_MetaDataGetDispenser(); + test_MetaDataDispenser_OpenScope();
RoUninitialize(); } diff --git a/dlls/rometadata/tests/rsrc.rc b/dlls/rometadata/tests/rsrc.rc new file mode 100644 index 00000000000..9b2c6daded3 --- /dev/null +++ b/dlls/rometadata/tests/rsrc.rc @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* @makedep: test-simple.winmd */ +test-simple.winmd RCDATA "test-simple.winmd" diff --git a/dlls/rometadata/tests/test-simple.idl b/dlls/rometadata/tests/test-simple.idl new file mode 100644 index 00000000000..bb47160ea8b --- /dev/null +++ b/dlls/rometadata/tests/test-simple.idl @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef __WIDL__ +#pragma makedep header winmd +#pragma winrt ns_prefix +#endif + +import "inspectable.idl"; +import "windowscontracts.idl"; + +namespace Wine.Test { + interface ITest1; + + runtimeclass Test1; + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + exclusiveto(Wine.Test.Test1), + uuid(deadbeef-dead-beef-dead-deadbeef0001) + ] + interface ITest1 : IInspectable + { + HRESULT Foo(); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 1.0), + marshaling_behavior(agile) + ] + runtimeclass Test1 + { + [default] interface Wine.Test.ITest1; + } +} diff --git a/include/Makefile.in b/include/Makefile.in index 351de1be8a1..a43168baf1f 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -689,6 +689,7 @@ SOURCES = \ robuffer.idl \ roerrorapi.h \ rometadata.h \ + rometadataapi.idl \ rometadataresolution.h \ roparameterizediid.idl \ row.idl \ diff --git a/include/rometadataapi.idl b/include/rometadataapi.idl new file mode 100644 index 00000000000..22e7a0d2085 --- /dev/null +++ b/include/rometadataapi.idl @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Vibhav Pant + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import "unknwn.idl"; +import "cor.idl"; + +[ + local, + object, + uuid(d8f579ab-402d-4b8e-82d9-5d63b1065c68) +] +interface IMetaDataTables : IUnknown +{ + HRESULT GetStringHeapSize([out] ULONG *size); + + HRESULT GetBlobHeapSize([out] ULONG *size); + + HRESULT GetGuidHeapSize([out] ULONG *size); + + HRESULT GetUserStringHeapSize([out] ULONG *size); + + HRESULT GetNumTables([out] ULONG *tables); + + HRESULT GetTableIndex([in] ULONG token, [out] ULONG *idx); + + HRESULT GetTableInfo([in] ULONG idx, [out] ULONG *row_size, [out] ULONG *rows, [out] ULONG *cols, + [out] ULONG *key_idx, [out, string] const char **name); + + HRESULT GetColumnInfo([in] ULONG idx_tbl, [in] ULONG idx_col, [out] ULONG *offset, [out] ULONG *col_size, + [out] ULONG *type, [out, string] const char **name); + + HRESULT GetCodedTokenInfo([in] ULONG type, [out] ULONG *tokens_len, [out] const ULONG **tokens, + [out, string] const char **name); + + HRESULT GetRow([in] ULONG idx_tbl, [in] ULONG idx_row, [out] const BYTE *row); + + HRESULT GetColumn([in] ULONG idx_tbl, [in] ULONG idx_col, [in] ULONG idx_row, [out] ULONG *val); + + HRESULT GetString([in] ULONG idx, [out] const char **str); + + HRESULT GetBlob([in] ULONG idx, [out] ULONG *size, [out] const BYTE **blob); + + HRESULT GetGuid([in] ULONG idx, [out] const GUID **guid); + + HRESULT GetUserString([in] ULONG idx, [out] ULONG *size, [out] const BYTE **blob); + + HRESULT GetNextString([in] ULONG idx, [out] ULONG *next); + + HRESULT GetNextBlob([in] ULONG idx, [out] ULONG *next); + + HRESULT GetNextGuid([in] ULONG idx, [out] ULONG *next); + + HRESULT GetNextUserString([in] ULONG idx, [out] ULONG *next); +}
 
            On Mon Oct 6 10:33:54 2025 +0000, Hans Leidekker wrote:
This is a lot of code to verify just one aspect of metadata. I think it's fine as a way to exercise the IMetadata interface but it doesn't really scale to verifying all aspects of metadata files.
Yes, IMetaDataTables is too verbose to enumerate through all metadata. I'll add separate tests for `IMetaDataImport`.
 
            On Tue Oct 7 19:49:56 2025 +0000, Vibhav Pant wrote:
changed this line in [version 7 of the diff](/wine/wine/-/merge_requests/9094/diffs?diff_id=214872&start_sha=12aec772c71db717007070fd92e30d017bf43f17#b176efa0f1948e9f53b7d2197803bfdd0a75db1e_1516_1516)
Oops, thanks!
 
            This MR now needs a rebase to get the makedep support but otherwise it looks good.
 
            This merge request was approved by Hans Leidekker.


