This MR introduces a few minor changes to widl to support generating proxy code for WinRT's parameterized interfaces:
* Use the mangled, C-friendly interface name for naming variables `FormatStringOffsetTable`, `StubThunkTable`, etc) in proxy and stub code. * Use the default interface of a runtime class while creating type format strings in `write_ip_tfs`. * When proxy generation is enabled, also compute UUIDs for all specialized parameterized interface declarations in imported files.
Finally, I have also enabled proxy generation and registration for wintypes.dll as an example. We use the `PROXY_CLSID_IS` define as WinRT IDLs cannot use `coclass` to declare the `PSFactoryBuffer` class for the DLL.
-- v6: wintypes: Register proxies for interfaces. include: Add size and length annotations for IVector(View) and IIterator method params. include: Remove duplicate interface declarations. widl: Ignore all #included runtimeclass statements while generating registry entries.
From: Vibhav Pant vibhavp@gmail.com
This allows widl to generate legal C names for interfaces that have a namespace and/or are parameterized. --- tools/widl/proxy.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-)
diff --git a/tools/widl/proxy.c b/tools/widl/proxy.c index 9982efc635e..41763cf4fac 100644 --- a/tools/widl/proxy.c +++ b/tools/widl/proxy.c @@ -594,6 +594,7 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) int first_func = 1; int needs_stub_thunks = 0; int needs_inline_stubs = need_inline_stubs( iface ) || need_delegation( iface ); + const char *name = type_get_name( iface, NAME_C, false );
STATEMENTS_FOR_EACH_FUNC(stmt, type_iface_get_stmts(iface)) { var_t *func = stmt->u.var; @@ -632,7 +633,7 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset)
count = count_methods(iface);
- print_proxy( "static const unsigned short %s_FormatStringOffsetTable[] =\n", iface->name ); + print_proxy( "static const unsigned short %s_FormatStringOffsetTable[] =\n", name ); print_proxy( "{\n" ); indent++; write_proxy_procformatstring_offsets( iface, 0 ); @@ -642,12 +643,12 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) /* proxy info */ if (interpreted_mode) { - print_proxy( "static const MIDL_STUBLESS_PROXY_INFO %s_ProxyInfo =\n", iface->name ); + print_proxy( "static const MIDL_STUBLESS_PROXY_INFO %s_ProxyInfo =\n", name ); print_proxy( "{\n" ); indent++; print_proxy( "&Object_StubDesc,\n" ); print_proxy( "__MIDL_ProcFormatString.Format,\n" ); - print_proxy( "&%s_FormatStringOffsetTable[-3],\n", iface->name ); + print_proxy( "&%s_FormatStringOffsetTable[-3],\n", name ); print_proxy( "0,\n" ); print_proxy( "0,\n" ); print_proxy( "0\n" ); @@ -658,13 +659,13 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) /* proxy vtable */ print_proxy( "static %sCINTERFACE_PROXY_VTABLE(%d) _%sProxyVtbl =\n", (interpreted_mode || need_delegation_indirect(iface)) ? "" : "const ", - count, iface->name); + count, name); print_proxy( "{\n"); indent++; print_proxy( "{\n"); indent++; - if (interpreted_mode) print_proxy( "&%s_ProxyInfo,\n", iface->name ); - print_proxy( "&IID_%s,\n", iface->name); + if (interpreted_mode) print_proxy( "&%s_ProxyInfo,\n", name ); + print_proxy( "&IID_%s,\n", name); indent--; print_proxy( "},\n"); print_proxy( "{\n"); @@ -678,7 +679,7 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) /* stub thunk table */ if (needs_stub_thunks) { - print_proxy( "static const STUB_THUNK %s_StubThunkTable[] =\n", iface->name); + print_proxy( "static const STUB_THUNK %s_StubThunkTable[] =\n", name); print_proxy( "{\n"); indent++; write_thunk_methods( iface, 0 ); @@ -687,15 +688,15 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) }
/* server info */ - print_proxy( "static const MIDL_SERVER_INFO %s_ServerInfo =\n", iface->name ); + print_proxy( "static const MIDL_SERVER_INFO %s_ServerInfo =\n", name ); print_proxy( "{\n" ); indent++; print_proxy( "&Object_StubDesc,\n" ); print_proxy( "0,\n" ); print_proxy( "__MIDL_ProcFormatString.Format,\n" ); - print_proxy( "&%s_FormatStringOffsetTable[-3],\n", iface->name ); + print_proxy( "&%s_FormatStringOffsetTable[-3],\n", name ); if (needs_stub_thunks) - print_proxy( "&%s_StubThunkTable[-3],\n", iface->name ); + print_proxy( "&%s_StubThunkTable[-3],\n", name ); else print_proxy( "0,\n" ); print_proxy( "0,\n" ); @@ -707,7 +708,7 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) /* stub vtable */ if (needs_inline_stubs) { - print_proxy( "static const PRPC_STUB_FUNCTION %s_table[] =\n", iface->name); + print_proxy( "static const PRPC_STUB_FUNCTION %s_table[] =\n", name); print_proxy( "{\n"); indent++; write_stub_methods(iface, FALSE); @@ -716,15 +717,15 @@ static void write_proxy(type_t *iface, unsigned int *proc_offset) fprintf(proxy, "};\n\n"); } print_proxy( "static %sCInterfaceStubVtbl _%sStubVtbl =\n", - need_delegation_indirect(iface) ? "" : "const ", iface->name); + need_delegation_indirect(iface) ? "" : "const ", name); print_proxy( "{\n"); indent++; print_proxy( "{\n"); indent++; - print_proxy( "&IID_%s,\n", iface->name); - print_proxy( "&%s_ServerInfo,\n", iface->name ); + print_proxy( "&IID_%s,\n", name); + print_proxy( "&%s_ServerInfo,\n", name ); print_proxy( "%d,\n", count); - if (needs_inline_stubs) print_proxy( "&%s_table[-3]\n", iface->name ); + if (needs_inline_stubs) print_proxy( "&%s_table[-3]\n", name ); else print_proxy( "0\n" ); indent--; print_proxy( "},\n"); @@ -945,7 +946,7 @@ static void write_proxy_routines(const statement_list_t *stmts) fprintf(proxy, "static const CInterfaceProxyVtbl* const _%s_ProxyVtblList[] =\n", file_id); fprintf(proxy, "{\n"); for (i = 0; i < count; i++) - fprintf(proxy, " (const CInterfaceProxyVtbl*)&_%sProxyVtbl,\n", interfaces[i]->name); + fprintf(proxy, " (const CInterfaceProxyVtbl*)&_%sProxyVtbl,\n", type_get_name(interfaces[i], NAME_C, false)); fprintf(proxy, " 0\n"); fprintf(proxy, "};\n"); fprintf(proxy, "\n"); @@ -953,7 +954,7 @@ static void write_proxy_routines(const statement_list_t *stmts) fprintf(proxy, "static const CInterfaceStubVtbl* const _%s_StubVtblList[] =\n", file_id); fprintf(proxy, "{\n"); for (i = 0; i < count; i++) - fprintf(proxy, " &_%sStubVtbl,\n", interfaces[i]->name); + fprintf(proxy, " &_%sStubVtbl,\n", type_get_name(interfaces[i], NAME_C, false)); fprintf(proxy, " 0\n"); fprintf(proxy, "};\n"); fprintf(proxy, "\n"); @@ -961,7 +962,7 @@ static void write_proxy_routines(const statement_list_t *stmts) fprintf(proxy, "static PCInterfaceName const _%s_InterfaceNamesList[] =\n", file_id); fprintf(proxy, "{\n"); for (i = 0; i < count; i++) - fprintf(proxy, " "%s",\n", interfaces[i]->name); + fprintf(proxy, " "%s",\n", type_get_name(interfaces[i], NAME_C, false)); fprintf(proxy, " 0\n"); fprintf(proxy, "};\n"); fprintf(proxy, "\n"); @@ -976,7 +977,8 @@ static void write_proxy_routines(const statement_list_t *stmts) for (i = 0; i < count; i++) { if (get_delegation_indirect(interfaces[i], &delegate_to)) - fprintf( proxy, " &IID_%s, /* %s */\n", delegate_to->name, interfaces[i]->name ); + fprintf( proxy, " &IID_%s, /* %s */\n", type_get_name( delegate_to, NAME_C, false ), + interfaces[i]->name ); else fprintf( proxy, " 0,\n" ); }
From: Vibhav Pant vibhavp@gmail.com
--- tools/widl/typegen.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/tools/widl/typegen.c b/tools/widl/typegen.c index 76d7a2bde21..41cd3884431 100644 --- a/tools/widl/typegen.c +++ b/tools/widl/typegen.c @@ -3748,7 +3748,12 @@ static unsigned int write_ip_tfs(FILE *file, const attr_list_t *attrs, type_t *t else { const type_t *base = is_ptr(type) ? type_pointer_get_ref_type(type) : type; - const struct uuid *uuid = get_attrp(base->attrs, ATTR_UUID); + const struct uuid *uuid; + + if (base->type_type == TYPE_RUNTIMECLASS) + base = type_runtimeclass_get_default_iface(base, 1); + + uuid = get_attrp(base->attrs, ATTR_UUID);
if (! uuid) error("%s: interface %s missing UUID\n", __FUNCTION__, base->name);
From: Vibhav Pant vibhavp@gmail.com
--- tools/widl/parser.y | 20 +++++++++++++++++++- tools/widl/typetree.c | 13 +++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/tools/widl/parser.y b/tools/widl/parser.y index e57b1412a80..a28fe8c3fee 100644 --- a/tools/widl/parser.y +++ b/tools/widl/parser.y @@ -102,6 +102,7 @@ static struct namespace global_namespace = { static struct namespace *current_namespace = &global_namespace; static struct namespace *parameters_namespace = NULL; static statement_list_t *parameterized_type_stmts = NULL; +static statement_list_t *imp_parameterized_type_stmts = NULL;
static typelib_t *current_typelib;
@@ -405,7 +406,9 @@ decl_block: tDECLARE '{' decl_statements '}' { $$ = $3; } imp_decl_statements : %empty { $$ = NULL; } | imp_decl_statements tINTERFACE qualified_type '<' parameterized_type_args '>' ';' - { $$ = append_statement($1, make_statement_reference( @$, type_parameterized_type_specialize_declare($3, $5) )); } + { imp_parameterized_type_stmts = append_statement( imp_parameterized_type_stmts, make_statement_parameterized_type(@$, $3, $5) ); + $$ = append_statement($1, make_statement_reference( @$, type_parameterized_type_specialize_declare($3, $5) )); + } ;
imp_decl_block @@ -2898,6 +2901,21 @@ static statement_list_t *append_parameterized_type_stmts(statement_list_t *stmts { statement_t *stmt, *next;
+ if (imp_parameterized_type_stmts) + LIST_FOR_EACH_ENTRY(stmt, imp_parameterized_type_stmts, statement_t, entry) + { + switch(stmt->type) + { + case STMT_TYPE: + stmt->u.type = type_parameterized_type_specialize_define(stmt->u.type); + stmt->is_defined = 1; + break; + default: + assert(0); + break; + } + } + if (stmts && parameterized_type_stmts) LIST_FOR_EACH_ENTRY_SAFE(stmt, next, parameterized_type_stmts, statement_t, entry) { switch(stmt->type) diff --git a/tools/widl/typetree.c b/tools/widl/typetree.c index c77efbc834f..0f917123ecf 100644 --- a/tools/widl/typetree.c +++ b/tools/widl/typetree.c @@ -1521,11 +1521,20 @@ static void type_parameterized_delegate_specialize(type_t *tmpl, type_t *delegat
type_t *type_parameterized_type_specialize_declare(type_t *type, typeref_list_t *params) { + char *name = format_parameterized_type_name(type, params); type_t *tmpl = type->details.parameterized.type; - type_t *new_type = duptype(tmpl, 0); + type_t *new_type;
+ /* If this specialization has already been registered/declared, reuse it. */ + if ((new_type = find_type(name, type->namespace, 0))) + { + free(name); + return new_type; + } + + new_type = duptype(tmpl, 0); new_type->namespace = type->namespace; - new_type->name = format_parameterized_type_name(type, params); + new_type->name = name; reg_type(new_type, new_type->name, new_type->namespace, 0); new_type->c_name = format_parameterized_type_c_name(type, params, "", "_C"); new_type->short_name = format_parameterized_type_short_name(type, params, "");
From: Vibhav Pant vibhavp@gmail.com
--- tools/widl/register.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/widl/register.c b/tools/widl/register.c index 7e142b5e4f1..0089ec22e1a 100644 --- a/tools/widl/register.c +++ b/tools/widl/register.c @@ -195,7 +195,7 @@ static void write_runtimeclasses_registry( const statement_list_t *stmts ) if (stmts) LIST_FOR_EACH_ENTRY( stmt, stmts, const statement_t, entry ) { if (stmt->type != STMT_TYPE && stmt->type != STMT_TYPEREF) continue; - if (stmt->type == STMT_TYPEREF && strcmp( stmt->where.input_name, input_name )) continue; /* ignore #included statements */ + if (strcmp( stmt->where.input_name, input_name )) continue; /* ignore #included statements */ if (type_get_type((type = stmt->u.type)) != TYPE_RUNTIMECLASS) continue; if (!get_attrp(type->attrs, ATTR_ACTIVATABLE) && !get_attrp(type->attrs, ATTR_STATIC)) continue; put_str( indent, "ForceRemove %s\n", format_namespace( type->namespace, "", ".", type->name, NULL ) );
From: Vibhav Pant vibhavp@gmail.com
--- include/windows.foundation.idl | 1 - 1 file changed, 1 deletion(-)
diff --git a/include/windows.foundation.idl b/include/windows.foundation.idl index f4c1ea2800e..5cfe0f36fab 100644 --- a/include/windows.foundation.idl +++ b/include/windows.foundation.idl @@ -142,7 +142,6 @@ namespace Windows.Foundation { interface Windows.Foundation.AsyncOperationCompletedHandler<HSTRING>; interface Windows.Foundation.AsyncOperationCompletedHandler<IInspectable *>; interface Windows.Foundation.AsyncOperationCompletedHandler<boolean>; - interface Windows.Foundation.AsyncOperationCompletedHandler<HSTRING>; interface Windows.Foundation.AsyncOperationCompletedHandler<UINT32>; interface Windows.Foundation.AsyncOperationCompletedHandler<Windows.Foundation.Uri *>; interface Windows.Foundation.AsyncOperationProgressHandler<UINT32, UINT32>;
From: Vibhav Pant vibhavp@gmail.com
--- include/windows.foundation.collections.idl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/windows.foundation.collections.idl b/include/windows.foundation.collections.idl index 2a8fd55be7d..1c381d087f3 100644 --- a/include/windows.foundation.collections.idl +++ b/include/windows.foundation.collections.idl @@ -150,7 +150,7 @@ cpp_quote("#endif") [propget] HRESULT Current([out, retval] T *value); [propget] HRESULT HasCurrent([out, retval] boolean *value); HRESULT MoveNext([out, retval] boolean *value); - HRESULT GetMany([in] UINT32 items_size, [out] T *items, [out, retval] UINT32 *value); + HRESULT GetMany([in] UINT32 items_size, [out, size_is(items_size), length_is(*value)] T *items, [out, retval] UINT32 *value); }
[ @@ -252,7 +252,7 @@ cpp_quote("#endif") HRESULT GetAt([in] UINT32 index, [out, retval] T *value); [propget] HRESULT Size([out, retval] UINT32 *value); HRESULT IndexOf([in, optional] T element, [out] UINT32 *index, [out, retval] BOOLEAN *value); - HRESULT GetMany([in] UINT32 start_index, [in] UINT32 items_size, [out] T *items, [out, retval] UINT32 *value); + HRESULT GetMany([in] UINT32 start_index, [in] UINT32 items_size, [out, size_is(items_size), length_is(*value)] T *items, [out, retval] UINT32 *value); }
[ @@ -272,8 +272,8 @@ cpp_quote("#endif") HRESULT Append([in, optional] T value); HRESULT RemoveAtEnd(); HRESULT Clear(); - HRESULT GetMany([in] UINT32 start_index, [in] UINT32 items_size, [out] T *items, [out, retval] UINT32 *value); - HRESULT ReplaceAll([in] UINT32 count, [in] T *items); + HRESULT GetMany([in] UINT32 start_index, [in] UINT32 items_size, [out, size_is(items_size), length_is(*value)] T *items, [out, retval] UINT32 *value); + HRESULT ReplaceAll([in] UINT32 count, [in, size_is(count)] T *items); }
[
From: Vibhav Pant vibhavp@gmail.com
--- dlls/wintypes/Makefile.in | 8 ++++++-- dlls/wintypes/main.c | 6 ------ dlls/wintypes/wintypes.spec | 3 +++ dlls/wintypes/wintypes_proxy.idl | 25 +++++++++++++++++++++++++ loader/wine.inf.in | 1 + 5 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 dlls/wintypes/wintypes_proxy.idl
diff --git a/dlls/wintypes/Makefile.in b/dlls/wintypes/Makefile.in index fd370133cf8..2ad52b0480e 100644 --- a/dlls/wintypes/Makefile.in +++ b/dlls/wintypes/Makefile.in @@ -1,6 +1,6 @@ MODULE = wintypes.dll IMPORTLIB = wintypes -IMPORTS = combase +IMPORTS = combase rpcrt4
SOURCES = \ buffer.c \ @@ -8,4 +8,8 @@ SOURCES = \ main.c \ propertyset.c \ storage.c \ - wintypes_private.idl + wintypes_private.idl \ + wintypes_proxy.idl + +dlldata_EXTRADEFS = -DPROXY_DELEGATION -DREGISTER_PROXY_DLL \ + -DPROXY_CLSID_IS="{0x11659A23,0x5884,0x4D1B,{0x9C,0xF6,0x67,0xD6,0xF4,0xF9,0x0B,0x36}}" diff --git a/dlls/wintypes/main.c b/dlls/wintypes/main.c index 02121194956..72d6e2da59c 100644 --- a/dlls/wintypes/main.c +++ b/dlls/wintypes/main.c @@ -1712,12 +1712,6 @@ static struct property_value_statics property_value_statics = 1 };
-HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) -{ - FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); - return CLASS_E_CLASSNOTAVAILABLE; -} - HRESULT WINAPI DllGetActivationFactory(HSTRING classid, IActivationFactory **factory) { const WCHAR *buffer = WindowsGetStringRawBuffer(classid, NULL); diff --git a/dlls/wintypes/wintypes.spec b/dlls/wintypes/wintypes.spec index a1eb0f3830d..bf5f4741281 100644 --- a/dlls/wintypes/wintypes.spec +++ b/dlls/wintypes/wintypes.spec @@ -1,6 +1,9 @@ +3 stdcall -private GetProxyDllInfo(ptr ptr) @ stdcall -private DllCanUnloadNow() @ stdcall -private DllGetActivationFactory(ptr ptr) @ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() @ stub RoCreateNonAgilePropertySet @ stub RoCreatePropertySetSerializer @ stub RoGetBufferMarshaler diff --git a/dlls/wintypes/wintypes_proxy.idl b/dlls/wintypes/wintypes_proxy.idl new file mode 100644 index 00000000000..9463b2d4d05 --- /dev/null +++ b/dlls/wintypes/wintypes_proxy.idl @@ -0,0 +1,25 @@ +/* + * Proxies for wintypes.dll + * + * 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 + */ + +#pragma makedep proxy +#pragma makedep register +#pragma winrt ns_prefix + +#include "windows.foundation.idl" diff --git a/loader/wine.inf.in b/loader/wine.inf.in index 892078a4a96..6e8369fc7ab 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -705,6 +705,7 @@ HKLM,SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86,"Version",2,"14.42.344 11,,wmadmod.dll,1 11,,wmvdecod.dll,1 11,,iexplore.exe,1 +11,,wintypes.dll,1
; 32bit-only fake dlls [FakeDllsWin32]
v5:
* Ignore all #included runtimeclass statements while generating registry entries (fixes iertutil.dll registration breakage).
Rémi Bernon (@rbernon) commented about tools/widl/parser.y:
imp_decl_statements : %empty { $$ = NULL; } | imp_decl_statements tINTERFACE qualified_type '<' parameterized_type_args '>' ';'
{ $$ = append_statement($1, make_statement_reference( @$, type_parameterized_type_specialize_declare($3, $5) )); }
{ imp_parameterized_type_stmts = append_statement( imp_parameterized_type_stmts, make_statement_parameterized_type(@$, $3, $5) );
$$ = append_statement($1, make_statement_reference( @$, type_parameterized_type_specialize_declare($3, $5) ));
}
I think removing the mostly duplicate logic like in https://gitlab.winehq.org/rbernon/wine/-/commit/1f91758a2b05deac6741c77bb26a... would be better.
Finally, I have also enabled proxy generation and registration for wintypes.dll as an example. We use the PROXY_CLSID_IS define as WinRT IDLs cannot use coclass to declare the PSFactoryBuffer class for the DLL.
We can add extensions if necessary, but we'd want proxies to be registered through the regscript resource.
We can add extensions if necessary
Sure. I can think of two ways:
* Specifically allow `coclass PSFactoryBuffer` statements in WinRT mode. * A pragma directive like `#pragma proxy_clsid <guid>`
On Wed Oct 1 15:38:24 2025 +0000, Rémi Bernon wrote:
I think removing the mostly duplicate logic like in https://gitlab.winehq.org/rbernon/wine/-/commit/1f91758a2b05deac6741c77bb26a... would be better.
Ah, that's way better, I'll incorporate this into the MR, thanks!