@nativescript/windows-hermes 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -0
- package/framework/__PROJECT_NAME__/App.xaml +13 -0
- package/framework/__PROJECT_NAME__/App.xaml.cs +240 -0
- package/framework/__PROJECT_NAME__/Assets/SplashScreen.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/Square150x150Logo.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/Square44x44Logo.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/StoreLogo.png +0 -0
- package/framework/__PROJECT_NAME__/Assets/Wide310x150Logo.png +0 -0
- package/framework/__PROJECT_NAME__/CrashDiagnostics.cs +281 -0
- package/framework/__PROJECT_NAME__/Directory.Build.props +15 -0
- package/framework/__PROJECT_NAME__/Directory.Build.targets +106 -0
- package/framework/__PROJECT_NAME__/Package.appxmanifest +35 -0
- package/framework/__PROJECT_NAME__/Program.cs +34 -0
- package/framework/__PROJECT_NAME__/Properties/Default.rd.xml +9 -0
- package/framework/__PROJECT_NAME__/RuntimeHost.cs +359 -0
- package/framework/__PROJECT_NAME__/__PROJECT_NAME__.csproj +263 -0
- package/framework/dotnet-bridge/BinaryProtocol.cs +161 -0
- package/framework/dotnet-bridge/Bridge.BinaryDispatch.cs +373 -0
- package/framework/dotnet-bridge/Bridge.Dispatch.cs +597 -0
- package/framework/dotnet-bridge/Bridge.JsDelegate.cs +244 -0
- package/framework/dotnet-bridge/Bridge.Proxy.cs +573 -0
- package/framework/dotnet-bridge/Bridge.TaskHelper.cs +207 -0
- package/framework/dotnet-bridge/Bridge.cs +724 -0
- package/framework/dotnet-bridge/DispatchTypes.cs +333 -0
- package/framework/dotnet-bridge/DotNetBridge.csproj +30 -0
- package/framework/dotnet-bridge/publish_build/DotNetBridge.deps.json +23 -0
- package/framework/dotnet-bridge/publish_build/DotNetBridge.dll +0 -0
- package/framework/dotnet-bridge/publish_build/DotNetBridge.pdb +0 -0
- package/framework/dotnet-bridge/publish_build/DotNetBridge.runtimeconfig.json +14 -0
- package/framework/libs/x64/hermes-icu.dll +0 -0
- package/framework/libs/x64/hermes.dll +0 -0
- package/framework/libs/x64/nativescript.dll +0 -0
- package/framework/tools/ManifestMerger/ManifestMerger.deps.json +24 -0
- package/framework/tools/ManifestMerger/ManifestMerger.dll +0 -0
- package/framework/tools/ManifestMerger/ManifestMerger.pdb +0 -0
- package/framework/tools/dotnet-tool-arm64.exe +0 -0
- package/framework/tools/dotnet-tool-x64.exe +0 -0
- package/framework/tools/dotnet-tool.exe +0 -0
- package/framework/tools/dotnet-typings-gen/dotnet-typings-gen.deps.json +23 -0
- package/framework/tools/dotnet-typings-gen/dotnet-typings-gen.dll +0 -0
- package/framework/tools/dotnet-typings-gen/dotnet-typings-gen.exe +0 -0
- package/framework/tools/dotnet-typings-gen/dotnet-typings-gen.pdb +0 -0
- package/framework/tools/dotnet-typings-gen/dotnet-typings-gen.runtimeconfig.json +13 -0
- package/framework/tools/sbg-arm64.exe +0 -0
- package/framework/tools/sbg-x64.exe +0 -0
- package/framework/tools/sbg.exe +0 -0
- package/framework/tools/typings-generator-arm64.exe +0 -0
- package/framework/tools/typings-generator-x64.exe +0 -0
- package/framework/tools/typings-generator.exe +0 -0
- package/package.json +43 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
using System;
|
|
2
|
+
using System.Buffers;
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
using System.Reflection;
|
|
6
|
+
using System.Runtime.InteropServices;
|
|
7
|
+
|
|
8
|
+
namespace NativeScriptBridge;
|
|
9
|
+
|
|
10
|
+
public static partial class Bridge
|
|
11
|
+
{
|
|
12
|
+
// Safe to reuse per thread: the response is copied to unmanaged memory
|
|
13
|
+
// before InvokeBinary returns.
|
|
14
|
+
[ThreadStatic]
|
|
15
|
+
private static ArrayBufferWriter<byte>? t_responseWriter;
|
|
16
|
+
|
|
17
|
+
[UnmanagedCallersOnly(EntryPoint = "InvokeBinary",
|
|
18
|
+
CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])]
|
|
19
|
+
public static unsafe int InvokeBinary(
|
|
20
|
+
byte* requestPtr, int requestLen,
|
|
21
|
+
byte** responsePtr, int* responseLenPtr)
|
|
22
|
+
{
|
|
23
|
+
try
|
|
24
|
+
{
|
|
25
|
+
var r = new BinReader(new ReadOnlySpan<byte>(requestPtr, requestLen));
|
|
26
|
+
var res = DispatchBin(ref r);
|
|
27
|
+
var buf = t_responseWriter ??= new ArrayBufferWriter<byte>(256);
|
|
28
|
+
buf.ResetWrittenCount();
|
|
29
|
+
res.WriteAsBin(buf);
|
|
30
|
+
WriteUnmanaged(buf.WrittenSpan, responsePtr, responseLenPtr);
|
|
31
|
+
}
|
|
32
|
+
catch (Exception ex)
|
|
33
|
+
{
|
|
34
|
+
WriteBinError(Unwrap(ex).Message, responsePtr, responseLenPtr);
|
|
35
|
+
}
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
internal static DispatchResult DispatchBin(ref BinReader r)
|
|
40
|
+
{
|
|
41
|
+
var op = r.ReadByte();
|
|
42
|
+
|
|
43
|
+
if (op == 0x04) // release
|
|
44
|
+
{
|
|
45
|
+
var handleToRemove = r.ReadI32();
|
|
46
|
+
s_handles.TryRemove(handleToRemove, out _);
|
|
47
|
+
if (s_nativePtrs.TryRemove(handleToRemove, out var nativePtr))
|
|
48
|
+
{
|
|
49
|
+
RemoveNativePtr(handleToRemove, nativePtr);
|
|
50
|
+
try
|
|
51
|
+
{
|
|
52
|
+
Marshal.Release(nativePtr);
|
|
53
|
+
}
|
|
54
|
+
catch
|
|
55
|
+
{
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return DispatchResult.Void;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (op == 0x05) // members by handle
|
|
62
|
+
{
|
|
63
|
+
var h = r.ReadI32();
|
|
64
|
+
if (!s_handles.TryGetValue(h, out var obj))
|
|
65
|
+
throw new KeyNotFoundException($"Invalid handle {h}");
|
|
66
|
+
return BuildMembersResult(
|
|
67
|
+
obj?.GetType() ?? throw new InvalidOperationException("Handle is null"));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (op == 0x06) // members by type
|
|
71
|
+
{
|
|
72
|
+
var typeName = r.ReadString16();
|
|
73
|
+
var assembly = r.ReadString16();
|
|
74
|
+
var type = ResolveType(NullIfEmpty(assembly), typeName)
|
|
75
|
+
?? throw new TypeLoadException($"Type not found: {typeName} (assembly: {assembly})");
|
|
76
|
+
return BuildMembersResult(type);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (op == 0x01) // instance call
|
|
80
|
+
{
|
|
81
|
+
var handle = r.ReadI32();
|
|
82
|
+
if (!s_handles.TryGetValue(handle, out var target))
|
|
83
|
+
throw new KeyNotFoundException($"Invalid handle {handle}");
|
|
84
|
+
var type = target?.GetType() ?? throw new InvalidOperationException("Handle is null");
|
|
85
|
+
var method = r.ReadString16();
|
|
86
|
+
var args = r.ReadArgs();
|
|
87
|
+
|
|
88
|
+
if (method == "__dotnet_await__" && args.Length == 2
|
|
89
|
+
&& args[0] is int resolveId && args[1] is int rejectId)
|
|
90
|
+
{
|
|
91
|
+
ScheduleTaskContinuation(handle, resolveId, rejectId);
|
|
92
|
+
return DispatchResult.Void;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return DispatchCallBin(target, type, method, args, isStatic: false);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (op == 0x09) // create JS delegate
|
|
99
|
+
{
|
|
100
|
+
var delTypeName = r.ReadString16(); // "" → System.Action
|
|
101
|
+
var callbackId = r.ReadI32();
|
|
102
|
+
return CreateJsDelegate(delTypeName, callbackId);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (op == 0x0A) // create JS-backed subclass instance
|
|
106
|
+
{
|
|
107
|
+
var assembly = r.ReadString16();
|
|
108
|
+
var typeName = r.ReadString16();
|
|
109
|
+
|
|
110
|
+
var interfaceCount = r.ReadI32();
|
|
111
|
+
var interfaceNames = interfaceCount > 0 ? new string[interfaceCount] : [];
|
|
112
|
+
for (int i = 0; i < interfaceCount; i++) interfaceNames[i] = r.ReadString16();
|
|
113
|
+
|
|
114
|
+
var memberCount = r.ReadI32();
|
|
115
|
+
var memberNames = memberCount > 0 ? new string[memberCount] : [];
|
|
116
|
+
for (int i = 0; i < memberCount; i++) memberNames[i] = r.ReadString16();
|
|
117
|
+
|
|
118
|
+
var callbackId = r.ReadI32();
|
|
119
|
+
return CreateJsSubclass(NullIfEmpty(assembly), typeName, callbackId, interfaceNames, memberNames);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (op == 0x0B) // get CLR-only property by raw IInspectable ptr (CLR reflection fallback)
|
|
123
|
+
{
|
|
124
|
+
var instancePtr = new IntPtr(r.ReadI64());
|
|
125
|
+
var propName = r.ReadString16();
|
|
126
|
+
return ClrGetProperty(instancePtr, propName);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Static ops: 0x02 = call, 0x03 = constructor
|
|
130
|
+
var typeNameS = r.ReadString16();
|
|
131
|
+
var assemblyS = r.ReadString16();
|
|
132
|
+
var typeS = ResolveType(NullIfEmpty(assemblyS), typeNameS)
|
|
133
|
+
?? throw new TypeLoadException($"Type not found: {typeNameS} (assembly: {assemblyS})");
|
|
134
|
+
|
|
135
|
+
if (op == 0x03) // constructor
|
|
136
|
+
{
|
|
137
|
+
var args = r.ReadArgs();
|
|
138
|
+
var entry = GetCachedCtor(typeS, args.Length);
|
|
139
|
+
if (entry.Ctor is null)
|
|
140
|
+
throw new MissingMethodException(
|
|
141
|
+
$"No public ctor on {typeS.FullName} for {args.Length} args");
|
|
142
|
+
// ConstructorInfo.Invoke requires exact arg count — build a precise array.
|
|
143
|
+
var ctorArgs = BuildArgsBinExact(args, entry.Parameters);
|
|
144
|
+
return Box(entry.Ctor.Invoke(ctorArgs));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// op == 0x02: static call
|
|
148
|
+
var methodS = r.ReadString16();
|
|
149
|
+
var argsS = r.ReadArgs();
|
|
150
|
+
return DispatchCallBin(null, typeS, methodS, argsS, isStatic: true);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private static DispatchResult DispatchCallBin(
|
|
154
|
+
object? target, Type type, string method, object?[] args, bool isStatic)
|
|
155
|
+
{
|
|
156
|
+
var flags = (isStatic ? BindingFlags.Static : BindingFlags.Instance) | BindingFlags.Public;
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
if (method.Length > 4
|
|
161
|
+
&& method[0] == 'g' && method[1] == 'e' && method[2] == 't' && method[3] == '_')
|
|
162
|
+
{
|
|
163
|
+
var prop = GetCachedProp(type, method, 4, flags);
|
|
164
|
+
if (prop is not null) return Box(prop.GetValue(target));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (method.Length > 4
|
|
168
|
+
&& method[0] == 's' && method[1] == 'e' && method[2] == 't' && method[3] == '_'
|
|
169
|
+
&& args.Length == 1)
|
|
170
|
+
{
|
|
171
|
+
var prop = GetCachedProp(type, method, 4, flags);
|
|
172
|
+
if (prop is not null)
|
|
173
|
+
{
|
|
174
|
+
prop.SetValue(target, CoerceBin(args[0], prop.PropertyType));
|
|
175
|
+
return DispatchResult.Void;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
var entry = GetCachedMethod(type, method, args.Length, flags);
|
|
180
|
+
if (entry.Invoke is null)
|
|
181
|
+
{
|
|
182
|
+
var candidates = type.GetMethods(flags).Where(m => m.Name == method && !m.IsSpecialName);
|
|
183
|
+
foreach (var m in candidates)
|
|
184
|
+
{
|
|
185
|
+
var parameters = m.GetParameters();
|
|
186
|
+
var built = BuildArgsBin(args, parameters);
|
|
187
|
+
try
|
|
188
|
+
{
|
|
189
|
+
var res = AwaitIfTask(m.Invoke(target, built));
|
|
190
|
+
return Box(res);
|
|
191
|
+
}
|
|
192
|
+
catch (TargetInvocationException tie) when (IsMarshaledForDifferentThread(tie.InnerException))
|
|
193
|
+
{
|
|
194
|
+
if (Bridge.IsLogToConsole()) Console.Error.WriteLine($"[Bridge] Detected wrong-thread COM error; retrying {type.FullName}.{m.Name} on UI thread");
|
|
195
|
+
try
|
|
196
|
+
{
|
|
197
|
+
var res = InvokeOnUIThread(() => AwaitIfTask(m.Invoke(target, built)));
|
|
198
|
+
return Box(res);
|
|
199
|
+
}
|
|
200
|
+
catch { /* retry failed — try next candidate */ }
|
|
201
|
+
}
|
|
202
|
+
catch (System.Runtime.InteropServices.COMException ce) when (IsMarshaledForDifferentThread(ce))
|
|
203
|
+
{
|
|
204
|
+
if (Bridge.IsLogToConsole()) Console.Error.WriteLine($"[Bridge] Detected COMException wrong-thread; retrying {type.FullName}.{m.Name} on UI thread");
|
|
205
|
+
try
|
|
206
|
+
{
|
|
207
|
+
var res = InvokeOnUIThread(() => AwaitIfTask(m.Invoke(target, built)));
|
|
208
|
+
return Box(res);
|
|
209
|
+
}
|
|
210
|
+
catch { /* retry failed — try next candidate */ }
|
|
211
|
+
}
|
|
212
|
+
finally { if (built.Length > 0) ReturnArgs(built); }
|
|
213
|
+
}
|
|
214
|
+
throw new MissingMethodException(
|
|
215
|
+
$"Method '{method}' ({args.Length} args) not found on {type.FullName}");
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
var builtArgs = BuildArgsBin(args, entry.Parameters);
|
|
219
|
+
try {
|
|
220
|
+
try
|
|
221
|
+
{
|
|
222
|
+
var res = AwaitIfTask(entry.Invoke(target, builtArgs));
|
|
223
|
+
return Box(res);
|
|
224
|
+
}
|
|
225
|
+
catch (TargetInvocationException tie) when (IsMarshaledForDifferentThread(tie.InnerException))
|
|
226
|
+
{
|
|
227
|
+
if (Bridge.IsLogToConsole()) Console.Error.WriteLine($"[Bridge] Detected wrong-thread COM error; retrying {type.FullName}.{method} on UI thread");
|
|
228
|
+
var res = InvokeOnUIThread(() => AwaitIfTask(entry.Invoke(target, builtArgs)));
|
|
229
|
+
return Box(res);
|
|
230
|
+
}
|
|
231
|
+
catch (System.Runtime.InteropServices.COMException ce) when (IsMarshaledForDifferentThread(ce))
|
|
232
|
+
{
|
|
233
|
+
if (Bridge.IsLogToConsole()) Console.Error.WriteLine($"[Bridge] Detected COMException wrong-thread; retrying {type.FullName}.{method} on UI thread");
|
|
234
|
+
var res = InvokeOnUIThread(() => AwaitIfTask(entry.Invoke(target, builtArgs)));
|
|
235
|
+
return Box(res);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
finally { if (builtArgs.Length > 0) ReturnArgs(builtArgs); }
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
private static object?[] BuildArgsBin(object?[] binArgs, ParameterInfo[] parameters)
|
|
242
|
+
{
|
|
243
|
+
if (parameters.Length == 0) return [];
|
|
244
|
+
var result = ArrayPool<object?>.Shared.Rent(parameters.Length);
|
|
245
|
+
for (int i = 0; i < parameters.Length && i < binArgs.Length; i++)
|
|
246
|
+
result[i] = CoerceBin(binArgs[i], parameters[i].ParameterType);
|
|
247
|
+
return result;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private static object?[] BuildArgsBinExact(object?[] binArgs, ParameterInfo[] parameters)
|
|
251
|
+
{
|
|
252
|
+
if (parameters.Length == 0) return [];
|
|
253
|
+
var result = new object?[parameters.Length];
|
|
254
|
+
for (int i = 0; i < parameters.Length && i < binArgs.Length; i++)
|
|
255
|
+
result[i] = CoerceBin(binArgs[i], parameters[i].ParameterType);
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private static object? CoerceBin(object? value, Type targetType)
|
|
260
|
+
{
|
|
261
|
+
if (value is null) return null;
|
|
262
|
+
if (value is HandleRef hr)
|
|
263
|
+
{
|
|
264
|
+
s_handles.TryGetValue(hr.Id, out var obj);
|
|
265
|
+
return obj;
|
|
266
|
+
}
|
|
267
|
+
if (value is WinRtRef wr)
|
|
268
|
+
{
|
|
269
|
+
if (wr.Ptr == 0) return null;
|
|
270
|
+
var nativePtr = new IntPtr((long)wr.Ptr);
|
|
271
|
+
// If this native pointer is one that we previously exported (stored
|
|
272
|
+
// in s_nativePtrs), prefer returning the original managed object
|
|
273
|
+
// to preserve identity and avoid creating a new RCW which can
|
|
274
|
+
// fail for CCW pointers. This addresses cases where the runtime
|
|
275
|
+
// round-trips a managed object's canonical IUnknown pointer.
|
|
276
|
+
if (s_nativePtrsReverse.TryGetValue(nativePtr, out var ownerHandle)
|
|
277
|
+
&& s_handles.TryGetValue(ownerHandle, out var original))
|
|
278
|
+
{
|
|
279
|
+
return original;
|
|
280
|
+
}
|
|
281
|
+
// 1. Typed QI first: works for COM/CsWinRT interface types that carry a
|
|
282
|
+
// [Guid] attribute. More precise than a generic RCW for strongly-typed
|
|
283
|
+
// parameters such as Windows.UI.Xaml.UIElement.
|
|
284
|
+
if (targetType != typeof(object) && targetType.GUID != Guid.Empty)
|
|
285
|
+
{
|
|
286
|
+
try
|
|
287
|
+
{
|
|
288
|
+
return Marshal.GetTypedObjectForIUnknown(nativePtr, targetType);
|
|
289
|
+
}
|
|
290
|
+
catch (Exception ex)
|
|
291
|
+
{
|
|
292
|
+
if (Bridge.IsLogToConsole())
|
|
293
|
+
{
|
|
294
|
+
try { Console.Error.WriteLine($"[Bridge] GetTypedObjectForIUnknown failed ptr=0x{nativePtr.ToInt64():x} targetType={targetType.FullName}: {ex}"); } catch { }
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
try
|
|
300
|
+
{
|
|
301
|
+
return Marshal.GetObjectForIUnknown(nativePtr);
|
|
302
|
+
}
|
|
303
|
+
catch (Exception ex)
|
|
304
|
+
{
|
|
305
|
+
if (Bridge.IsLogToConsole())
|
|
306
|
+
{
|
|
307
|
+
try
|
|
308
|
+
{
|
|
309
|
+
Console.Error.WriteLine($"[Bridge] GetObjectForIUnknown failed ptr=0x{nativePtr.ToInt64():x} targetType={targetType.FullName} ({targetType.GUID}): {ex}");
|
|
310
|
+
try
|
|
311
|
+
{
|
|
312
|
+
var found = s_nativePtrs.FirstOrDefault(kvp => kvp.Value == nativePtr);
|
|
313
|
+
Console.Error.WriteLine($"[Bridge] s_nativePtrs match: key={found.Key} ptr=0x{found.Value.ToInt64():x}");
|
|
314
|
+
}
|
|
315
|
+
catch { }
|
|
316
|
+
}
|
|
317
|
+
catch { }
|
|
318
|
+
}
|
|
319
|
+
throw;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (value.GetType() == targetType) return value;
|
|
323
|
+
try { return Convert.ChangeType(value, targetType); }
|
|
324
|
+
catch { return value; }
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// CLR reflection fallback for properties that exist only in managed code and are
|
|
328
|
+
// therefore invisible to the WinRT metadata layer (e.g. App.MainWindow on a class
|
|
329
|
+
// that derives from Microsoft.UI.Xaml.Application but adds CLR-only members).
|
|
330
|
+
// Called by binary opcode 0x0B from the Rust runtime's property interceptor.
|
|
331
|
+
// Throws MissingMemberException when the property doesn't exist so the Rust side
|
|
332
|
+
// can distinguish "not found" (0xFF error → kNo) from "found but null" (0x00 → JS null).
|
|
333
|
+
private static DispatchResult ClrGetProperty(IntPtr instancePtr, string propName)
|
|
334
|
+
{
|
|
335
|
+
if (instancePtr == IntPtr.Zero)
|
|
336
|
+
throw new ArgumentException("Null instance pointer");
|
|
337
|
+
|
|
338
|
+
object? target;
|
|
339
|
+
// Prefer the existing managed wrapper (CCW/RCW identity) over creating a new RCW.
|
|
340
|
+
if (s_nativePtrsReverse.TryGetValue(instancePtr, out var ownerHandle)
|
|
341
|
+
&& s_handles.TryGetValue(ownerHandle, out target))
|
|
342
|
+
{ /* use cached */ }
|
|
343
|
+
else
|
|
344
|
+
{
|
|
345
|
+
try { target = Marshal.GetObjectForIUnknown(instancePtr); }
|
|
346
|
+
catch (Exception e) { throw new InvalidOperationException($"GetObjectForIUnknown failed: {e.Message}"); }
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (target is null)
|
|
350
|
+
throw new InvalidOperationException("Instance resolved to null");
|
|
351
|
+
|
|
352
|
+
var type = target.GetType();
|
|
353
|
+
var flags = BindingFlags.Public | BindingFlags.Instance;
|
|
354
|
+
var prop = GetCachedProp(type, propName, 0, flags);
|
|
355
|
+
|
|
356
|
+
if (prop is null)
|
|
357
|
+
throw new MissingMemberException($"CLR property '{propName}' not found on {type.FullName}");
|
|
358
|
+
|
|
359
|
+
return Box(prop.GetValue(target));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private static string? NullIfEmpty(string s) => s.Length == 0 ? null : s;
|
|
363
|
+
|
|
364
|
+
private static unsafe void WriteBinError(string msg, byte** outPtr, int* outLen)
|
|
365
|
+
{
|
|
366
|
+
var msgBytes = System.Text.Encoding.UTF8.GetByteCount(msg);
|
|
367
|
+
var buf = new ArrayBufferWriter<byte>(5 + msgBytes);
|
|
368
|
+
var w = new BinWriter(buf);
|
|
369
|
+
w.WriteByte(0xFF);
|
|
370
|
+
w.WriteString32(msg);
|
|
371
|
+
WriteUnmanaged(buf.WrittenSpan, outPtr, outLen);
|
|
372
|
+
}
|
|
373
|
+
}
|