[Bug 32316] Autodesk 3ds Max 9 32-bit exits silently or crashes on startup with Wine-Mono
WineHQ Bugzilla
wine-bugs at winehq.org
Sun Dec 13 17:22:18 CST 2020
https://bugs.winehq.org/show_bug.cgi?id=32316
--- Comment #18 from Anastasius Focht <focht at gmx.net> ---
Hello folks,
--- snip ---
[00000138:] EXCEPTION handling: System.NotSupportedException: Event
BackgroundImageLayoutChanged is not valid on this ActiveX control.
EXCEPTION: catch found at clause 0 of
<Module>:MXS_dotNet.DotNetObjectWrapper.CreateEventHandlerDelegates
(MXS_dotNet.DotNetObjectWrapper*
modopt(System.Runtime.CompilerServices.IsConst)
modopt(System.Runtime.CompilerServices.IsConst),object,System.Reflection.MethodInfo)
--- snip ---
Turns out the 'System.NotSupportedException' messages of Mono debug trace ought
to be harmless, the exception is expected and *should* be handled in the end.
The relevant piece of code in mixed mode C++ assembly:
--- snip ---
internal static unsafe void
MXS_dotNet::DotNetObjectWrapper::CreateEventHandlerDelegates([In]
DotNetObjectWrapper* obj0, object targetWrapper, MethodInfo mi)
{
...
try
{
DotNetObjectWrapper* netObjectWrapperPtr1 = obj0;
object target = __calli((__FnPtr<object (IntPtr)>) *(int*) *(int*)
netObjectWrapperPtr1)((IntPtr) netObjectWrapperPtr1);
DotNetObjectWrapper* netObjectWrapperPtr2 = obj0;
System.Type type1 = __calli((__FnPtr<System.Type (IntPtr)>) *(int*)
(*(int*) netObjectWrapperPtr2 + 4))((IntPtr) netObjectWrapperPtr2);
if (targetWrapper == null || (object) mi == null || (object) type1 ==
null)
return;
System.Type type2 = targetWrapper.GetType();
System.Type returnType1 = mi.ReturnType;
BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public |
BindingFlags.FlattenHierarchy;
if (target != null)
bindingAttr = BindingFlags.Instance | BindingFlags.Static |
BindingFlags.Public | BindingFlags.FlattenHierarchy;
foreach (EventInfo eventInfo in type1.GetEvents(bindingAttr))
{
System.Type eventHandlerType = eventInfo.EventHandlerType;
System.Type returnType2 =
eventHandlerType.GetMethod("Invoke").ReturnType;
ParameterInfo[] parameters =
eventHandlerType.GetMethod("Invoke").GetParameters();
System.Type[] parameterTypes = new System.Type[parameters.Length + 1];
parameterTypes[0] = type2;
int index1;
for (int index2 = 0; index2 < parameters.Length; index2 = index1)
{
index1 = index2 + 1;
parameterTypes[index1] = parameters[index2].ParameterType;
}
DynamicMethod dynamicMethod = new DynamicMethod(eventInfo.Name,
returnType2, parameterTypes, type2);
ILGenerator ilGenerator = dynamicMethod.GetILGenerator();
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldstr, eventInfo.Name);
LocalBuilder local = ilGenerator.DeclareLocal(typeof (object[]));
ilGenerator.Emit(OpCodes.Ldc_I4, parameters.Length);
ilGenerator.Emit(OpCodes.Newarr, typeof (object));
ilGenerator.Emit(OpCodes.Stloc, local);
int num2;
for (int index2 = 0; index2 < parameters.Length; index2 = num2)
{
ilGenerator.Emit(OpCodes.Ldloc, local);
ilGenerator.Emit(OpCodes.Ldc_I4, index2);
num2 = index2 + 1;
ilGenerator.Emit(OpCodes.Ldarg, num2);
ilGenerator.Emit(OpCodes.Stelem_Ref);
}
ilGenerator.Emit(OpCodes.Ldloc, local);
ilGenerator.Emit(OpCodes.Callvirt, mi);
if ((object) returnType1 == (object) typeof (void))
{
if ((object) returnType2 != (object) typeof (void))
ilGenerator.Emit(OpCodes.Ldnull);
}
else if ((object) returnType2 == (object) typeof (void))
ilGenerator.Emit(OpCodes.Pop);
else
ilGenerator.Emit(OpCodes.Castclass, returnType2);
ilGenerator.Emit(OpCodes.Ret);
Delegate @delegate = dynamicMethod.CreateDelegate(eventHandlerType,
targetWrapper);
try
{
eventInfo.AddEventHandler(target, @delegate);
__calli((__FnPtr<void (IntPtr, event_delegate_pair)>) *(int*)
(*(int*) obj0 + 12))((event_delegate_pair) (IntPtr) obj0, (IntPtr) new
event_delegate_pair(eventInfo.Name, @delegate));
}
catch (Exception ex)
{
if ((object) ex.InnerException.GetType() != (object) typeof
(NotSupportedException))
throw;
}
}
}
--- snip ---
The inner try() / catch() block around 'AddEventHandler' is supposed to swallow
all exceptions of type 'NotSupportedException' and to rethrow any other.
Looking further we find:
--- snip ---
...
[00000138: 1.32092 7] ENTER:c System.Exception:get_InnerException
()(this:0D015828[System.NotSupportedException 3dsmax.exe])
[00000138: 1.32094 7] LEAVE:c System.Exception:get_InnerException
()([OBJECT:00000000]
0138:trace:seh:dispatch_exception code=c0000005 flags=0 addr=0FFE246F
ip=0ffe246f tid=0138
0138:trace:seh:dispatch_exception info[0]=00000000
0138:trace:seh:dispatch_exception info[1]=00000000
0138:trace:seh:dispatch_exception eax=00000000 ebx=00000010 ecx=0031de70
edx=00000000 esi=0e41c900 edi=0000005c
0138:trace:seh:dispatch_exception ebp=0031e168 esp=0031df60 cs=0023 ds=002b
es=002b fs=0063 gs=006b flags=00010202
0138:trace:seh:call_vectored_handlers calling handler at 0CA303D0 code=c0000005
flags=0
0138:trace:seh:call_vectored_handlers handler at 0CA303D0 returned ffffffff
[00000138: 1.32121 7] ENTER:c (wrapper runtime-invoke)
object:runtime_invoke_void__this__
(object,intptr,intptr,intptr)([System.NullReferenceException:0D015928],
00000000, 0031DBE0, 104C7D58)
[00000138: 1.32123 8] ENTER:c System.NullReferenceException:.ctor
()(this:0D015928[System.NullReferenceException 3dsmax.exe])
[00000138: 1.32124 9] ENTER:c System.SystemException:.ctor
(string)(this:0D015928[System.NullReferenceException 3dsmax.exe],
[STRING:0E3DC320:Object reference not set to an instance of an object.])
...
[00000138:] EXCEPTION handling: System.NullReferenceException: Object reference
not set to an instance of an object
[00000138: 1.32148 7] ENTER:c
System.Runtime.InteropServices.Marshal:GetExceptionPointers ()()
[00000138: 1.32154 8] ENTER:c (wrapper runtime-invoke)
object:runtime_invoke_void__this__
(object,intptr,intptr,intptr)([System.NotImplementedException:0D015AA0],
00000000, 0031DB58, 104C84C8)
[00000138: 1.32156 9] ENTER:c System.NotImplementedException:.ctor
()(this:0D015AA0[System.NotImplementedException 3dsmax.exe])
[00000138: 1.32157 10] ENTER:c System.SystemException:.ctor
(string)(this:0D015AA0[System.NotImplementedException 3dsmax.exe],
[STRING:0E3DC3A0:The method or operation is not implemented.])
--- snip ---
ex.InnerException.GetType() causes another exception
'System.NullReferenceException' because the inner exception object is null.
That one is caught by the outer try() / catch() block:
--- snip ---
try
{
...
}
catch (Exception ex1) when (Module.__CxxExceptionFilter((void*)
Marshal.GetExceptionPointers(), (void*) &Module.FAVMAXScriptException, 8,
(void*) 0) != 0)
{
uint num2 = 0;
Module.__CxxRegisterExceptionObject((void*)
Marshal.GetExceptionPointers(), (void*) num1);
try
{
try
{
Module._CxxThrowException((void*) 0, (_s__ThrowInfo*) 0);
return;
}
--- snip ---
Well, not really. Wine-Mono doesn't implement
'System.Runtime.InteropServices.Marshal.GetExceptionPointers' causing a another
exception seen as 'System.NotImplementedException', leaking to the outside.
https://github.com/mono/mono/blob/85056b086f950799b79a8f999e9a3edd406b0785/mcs/class/corlib/System.Runtime.InteropServices/Marshal.cs#L509
Related discussion in:
https://github.com/dotnet/standard/issues/870
But that's a follow-up/secondary problem which becomes a non-issue when the
root cause is fixed.
---
The explanation why the sequence works with native .NET Framework is that there
has to be another try() / catch() clause in between which wraps
'NotSupportedException' in another exception object to become the inner
exception. Wine-Mono doesn't do this.
System.Reflection.EventInfo -> AddEventHandler
System.Reflection.(Runtime)MethodInfo -> Invoke
https://github.com/mono/mono/blob/40372422e7bfa506e6a93fd06e9315513d9341c6/mcs/class/corlib/System.Reflection/RuntimeMethodInfo.cs#L369
--- snip ---
public override Object Invoke (Object obj, BindingFlags invokeAttr, Binder
binder, Object[] parameters, CultureInfo culture)
{
if (!IsStatic) {
if (!DeclaringType.IsInstanceOfType (obj)) {
if (obj == null)
throw new TargetException ("Non-static method requires a
target.");
else
throw new TargetException ("Object does not match target
type.");
}
}
if (binder == null)
binder = Type.DefaultBinder;
/*Avoid allocating an array every time*/
ParameterInfo[] pinfo = GetParametersInternal ();
ConvertValues (binder, parameters, pinfo, culture, invokeAttr);
if (ContainsGenericParameters)
throw new InvalidOperationException ("Late bound operations cannot be
performed on types or methods for which ContainsGenericParameters is true.");
Exception exc;
object o = null;
if ((invokeAttr & BindingFlags.DoNotWrapExceptions) == 0) {
try {
o = InternalInvoke (obj, parameters, out exc);
} catch (ThreadAbortException) {
throw;
#if MOBILE
} catch (MethodAccessException) {
throw;
#endif
#if NETCORE
} catch (Mono.NullByRefReturnException) {
throw new NullReferenceException ();
#endif
} catch (OverflowException) {
throw;
} catch (Exception e) {
throw new TargetInvocationException (e);
}
}
else
{
#if NETCORE
try {
o = InternalInvoke (obj, parameters, out exc);
} catch (Mono.NullByRefReturnException) {
throw new NullReferenceException ();
}
#else
o = InternalInvoke (obj, parameters, out exc);
#endif
}
if (exc != null)
throw exc;
return o;
}
--- snip ---
'InternalInvoke' is wrapped in try() / catch () block which wraps any
non-critical exception in a 'TargetInvocationException'. It calls it with
'TargetInvocationException(System.Exception inner)' ctor which is exactly what
is needed here.
Why isn't 'Invoke' getting called?
https://github.com/mono/mono/blob/f89fdf28a327e8c99e0110f47a1cc17c5bc5eebb/mcs/class/corlib/System.Reflection/EventInfo.cs#L44
--- snip ---
public virtual void AddEventHandler (object target, Delegate handler)
{
// this optimization cause problems with full AOT
// see bug https://bugzilla.xamarin.com/show_bug.cgi?id=3682
#if FULL_AOT_RUNTIME
MethodInfo add = GetAddMethod ();
if (add == null)
throw new InvalidOperationException
(SR.InvalidOperation_NoPublicAddMethod);
if (target == null && !add.IsStatic)
throw new TargetException ("Cannot add a handler to a non static event
with a null target");
add.Invoke (target, new object [] {handler});
#else
if (cached_add_event == null) {
MethodInfo add = GetAddMethod ();
if (add == null)
throw new InvalidOperationException
(SR.InvalidOperation_NoPublicAddMethod);
if (add.DeclaringType.IsValueType) {
if (target == null && !add.IsStatic)
throw new TargetException ("Cannot add a handler to a non
static event with a null target");
add.Invoke (target, new object [] {handler});
return;
}
cached_add_event = CreateAddEventDelegate (add);
}
//if (target == null && is_instance)
// throw new TargetException ("Cannot add a handler to a non static
event with a null target");
cached_add_event (target, handler);
#endif
}
--- snip ---
We are in the "else" part (not FULL_AOT_RUNTIME), and 'CreateAddEventDelegate'
gets called which doesn't do any further exception wrapping on its own.
--- quote ---
The idea behing this optimization is to use a pair of delegates to simulate the
same effect of doing a reflection call. The first delegate performs casting and
boxing, the second dispatch to the add_... method.
--- quote ---
That's about it.
Regards
--
Do not reply to this email, post in Bugzilla using the
above URL to reply.
You are receiving this mail because:
You are watching all bug changes.
More information about the wine-bugs
mailing list