-- v3: dlls/rometadata: Add stub for IMetaDataTables.
From: Vibhav Pant vibhavp@gmail.com
--- tools/makedep.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/tools/makedep.c b/tools/makedep.c index 7e33df2432a..99a33f08dfe 100644 --- a/tools/makedep.c +++ b/tools/makedep.c @@ -1513,6 +1513,7 @@ static struct file *open_include_file( const struct makefile *make, struct incl_ if ((file = open_local_generated_file( make, pFile, ".ico", ".svg" ))) return file; } if ((file = open_local_generated_file( make, pFile, "-client-protocol.h", ".xml" ))) return file; + if ((file = open_local_generated_file( make, pFile, ".winmd", ".idl" ))) return file;
/* check for extra targets */ if (strarray_exists( make->extra_targets, pFile->name ))
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 | 380 ++++++++++++++++++++++++++ dlls/rometadata/tests/rsrc.rc | 20 ++ dlls/rometadata/tests/test-simple.idl | 51 ++++ include/Makefile.in | 1 + include/rometadataapi.idl | 69 +++++ 10 files changed, 795 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..53fc2c415d4 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,381 @@ 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; +} + +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; +}; + +#define TYPE_VISIBILITY_PUBLIC 0x00000001 + +#define TYPE_SEMANTICS_CLASS 0x00000000 +#define TYPE_SEMANTICS_INTERFACE 0x00000020 + +#define TYPE_ABSTRACT 0x00000080 +#define TYPE_SEALED 0x00000100 +#define TYPE_IMPORTED 0x00001000 +#define TYPE_WINRT 0x00004000 + +enum class_semantics +{ + CLASS_SEMANTICS_CLASS = 0, + CLASS_SEMANTICS_INTERFACE = 0x00000020, +}; + +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; +}; + +#define TableFromToken(tk) (TypeFromToken(tk) >> 24) + +static void test_MetaDataDispenser_OpenScope(void) +{ + static const struct table_info tables[45] = { + {10, 1, 5, -1, "Module"}, + {6, 10, 3, -1, "TypeRef"}, + {14, 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"}, + {6, 6, 3, -1, "MemberRef"}, + {6, 0, 3, 1, "Constant"}, + {6, 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_SEMANTICS_INTERFACE | TYPE_ABSTRACT | TYPE_WINRT, "ITest1", "Wine.Test" }, + { TYPE_VISIBILITY_PUBLIC | TYPE_SEALED | TYPE_WINRT, "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 == 45, "got val %lu\n", val); + + for (i = 0; i < 45; 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, 0, 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, 2, 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[TableFromToken(mdtMemberRef)].exp_rows; i++) + { + const struct row_memberref *ref = NULL; + + winetest_push_context("i=%lu", i); + + hr = IMetaDataTables_GetRow(md_tables, TableFromToken(mdtMemberRef), 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, TableFromToken(mdtTypeRef), (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[TableFromToken(mdtCustomAttribute)].exp_rows; i++) + { + const struct row_custom_attribute *attr = NULL; + + winetest_push_context("i=%lu", i); + + attr = NULL; + hr = IMetaDataTables_GetRow(md_tables, 12, 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 +444,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..8722945f8df --- /dev/null +++ b/dlls/rometadata/tests/test-simple.idl @@ -0,0 +1,51 @@ +/* + * 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 +#pragma makedep 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); +}
Hans Leidekker (@hans) commented about tools/makedep.c:
if ((file = open_local_generated_file( make, pFile, ".ico", ".svg" ))) return file; } if ((file = open_local_generated_file( make, pFile, "-client-protocol.h", ".xml" ))) return file;
- if ((file = open_local_generated_file( make, pFile, ".winmd", ".idl" ))) return file;
Please put this in a separate MR.
Hans Leidekker (@hans) commented about dlls/rometadata/tests/rometadata.c:
- WORD Class;
- WORD Name;
- WORD Signature;
+};
+struct row_custom_attribute +{
- WORD Parent;
- WORD Type;
- WORD Value;
+};
+#define TYPE_VISIBILITY_PUBLIC 0x00000001
+#define TYPE_SEMANTICS_CLASS 0x00000000 +#define TYPE_SEMANTICS_INTERFACE 0x00000020
You may want to copy the TYPE_ATTR_ enum from tools/widl/metadata.c which is based on the ECMA standard, paragraph II.23.1.15.
Hans Leidekker (@hans) commented about dlls/rometadata/tests/rometadata.c:
+#define TYPE_VISIBILITY_PUBLIC 0x00000001
+#define TYPE_SEMANTICS_CLASS 0x00000000 +#define TYPE_SEMANTICS_INTERFACE 0x00000020
+#define TYPE_ABSTRACT 0x00000080 +#define TYPE_SEALED 0x00000100 +#define TYPE_IMPORTED 0x00001000 +#define TYPE_WINRT 0x00004000
+enum class_semantics +{
- CLASS_SEMANTICS_CLASS = 0,
- CLASS_SEMANTICS_INTERFACE = 0x00000020,
+};
This appears to duplicate the defines above and it's unused.
Hans Leidekker (@hans) commented about dlls/rometadata/tests/rometadata.c:
+#define TYPE_WINRT 0x00004000
+enum class_semantics +{
- CLASS_SEMANTICS_CLASS = 0,
- CLASS_SEMANTICS_INTERFACE = 0x00000020,
+};
+struct table_info +{
- ULONG exp_row_size;
- ULONG exp_rows;
- ULONG exp_cols;
- ULONG exp_key_idx;
- const char *exp_name;
+};
Why the exp_ prefix?
Hans Leidekker (@hans) commented about dlls/rometadata/tests/rometadata.c:
- ULONG exp_key_idx;
- const char *exp_name;
+};
+struct type_info +{
- DWORD exp_flags;
- const char *exp_name;
- const char *exp_namespace;
+};
+#define TableFromToken(tk) (TypeFromToken(tk) >> 24)
+static void test_MetaDataDispenser_OpenScope(void) +{
- static const struct table_info tables[45] = {
It would be nice to use symbolic constants for the table indexes, so 0 becomes e.g. TABLE_MODULE, TableFromToken(mdtMemberRef) becomes TABLE_MEMBERREF and 45 would be TABLE_MAX.
Hans Leidekker (@hans) commented about dlls/rometadata/tests/rometadata.c:
+struct type_info +{
- DWORD exp_flags;
- const char *exp_name;
- const char *exp_namespace;
+};
+#define TableFromToken(tk) (TypeFromToken(tk) >> 24)
+static void test_MetaDataDispenser_OpenScope(void) +{
- static const struct table_info tables[45] = {
{10, 1, 5, -1, "Module"},
{6, 10, 3, -1, "TypeRef"},
{14, 3, 6, -1, "TypeDef"},
If you use sizeof(struct row_typedef) instead of hard coding 14 here you'll notice that the test will fail. It shows that the row structures should be packed.
Hans Leidekker (@hans) commented about dlls/rometadata/tests/rometadata.c:
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. */
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.
Hans Leidekker (@hans) commented about dlls/rometadata/tests/test-simple.idl:
- 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 +#pragma makedep winmd
Note that you can also write this as `#pragma makedep header winmd`.
On Mon Oct 6 10:33:53 2025 +0000, Hans Leidekker wrote:
Please put this in a separate MR.
Sure, spun this out into !9110
On Mon Oct 6 10:33:53 2025 +0000, Hans Leidekker wrote:
If you use sizeof(struct row_typedef) instead of hard coding 14 here you'll notice that the test will fail. It shows that the row structures should be packed.
Oops, thanks! I'll also use `sizeof` expressions instead of hard-coding the row-size for the structures we're testing.
On Mon Oct 6 10:33:53 2025 +0000, Hans Leidekker wrote:
Why the exp_ prefix?
For "expected".
On Mon Oct 6 13:56:56 2025 +0000, Hans Leidekker wrote:
You may want to copy the TYPE_ATTR_ enum from tools/widl/metadata.c which is based on the ECMA standard, paragraph II.23.1.15.
Thanks.