{"record":{"id":"46617de7a7905863","repo":"dotnet/runtime","slug":"mono-marshal-safehandles-missing-managed-conv-in","errorCode":null,"errorMessage":"mono/marshal: SafeHandles missing MANAGED_CONV_IN\\n","messagePattern":"mono/marshal: SafeHandles missing MANAGED_CONV_IN\\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/mono/mono/component/marshal-ilgen.c","lineNumber":2038,"sourceCode":"\t\t}\n\t\t/* Store the IntPtr results into a local */\n\t\tintptr_handle_slot = cb_to_mono->methodBuilder.add_local (mb, int_type);\n\t\tcb_to_mono->methodBuilder.emit_stloc (mb, intptr_handle_slot);\n\n\t\t/* Create return value */\n\t\tcb_to_mono->methodBuilder.emit_op (mb, CEE_NEWOBJ, ctor);\n\t\tcb_to_mono->methodBuilder.emit_stloc (mb, 3);\n\n\t\t/* Set the return.handle to the value, am using ldflda, not sure if thats a good idea */\n\t\tcb_to_mono->methodBuilder.emit_ldloc (mb, 3);\n\t\tcb_to_mono->methodBuilder.emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoSafeHandle, handle));\n\t\tcb_to_mono->methodBuilder.emit_ldloc (mb, intptr_handle_slot);\n\t\tcb_to_mono->methodBuilder.emit_byte (mb, CEE_STIND_I);\n\t\tbreak;\n\t}\n\n\tcase MARSHAL_ACTION_MANAGED_CONV_IN:\n\t\tfprintf (stderr, \"mono/marshal: SafeHandles missing MANAGED_CONV_IN\\n\");\n\t\tbreak;\n\n\tcase MARSHAL_ACTION_MANAGED_CONV_OUT:\n\t\tfprintf (stderr, \"mono/marshal: SafeHandles missing MANAGED_CONV_OUT\\n\");\n\t\tbreak;\n\n\tcase MARSHAL_ACTION_MANAGED_CONV_RESULT:\n\t\tfprintf (stderr, \"mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\\n\");\n\t\tbreak;\n\tdefault:\n\t\tprintf (\"Unhandled case for MarshalAction: %d\\n\", action);\n\t}\n\treturn conv_arg;\n}\n\nstatic int\nemit_marshal_handleref_ilgen (EmitMarshalContext *m, int argnum, MonoType *t,\n\t\t\tMonoMarshalSpec *spec, int conv_arg,","sourceCodeStart":2020,"sourceCodeEnd":2056,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/mono/mono/component/marshal-ilgen.c#L2020-L2056","documentation":"In Mono's marshal-ilgen, emit_marshal_safehandle_ilgen handles the SafeHandle marshalling IL emission for P/Invoke stubs. The MARSHAL_ACTION_MANAGED_CONV_IN case (reverse P/Invoke: native calls a managed delegate that takes a SafeHandle) is unimplemented: it only prints this message to stderr and emits no IL. This indicates that SafeHandle handling for native->managed 'in' direction is incomplete in this runtime build.","triggerScenarios":"A managed delegate with a SafeHandle parameter is passed to native code and invoked from native (reverse P/Invoke), or a COM/WinRT interop path triggers the MANAGED_CONV_IN action for a SafeHandle-typed parameter. The marshaller reaches this case and emits a no-op instead of conversion IL.","commonSituations":"Using [DllImport] reverse pinvokes or Marshal.GetDelegateForFunctionPointer with SafeHandle arguments on Mono; COM interop scenarios where a SafeHandle crosses the managed boundary inbound; running code that works on CoreCLR but exercises an unimplemented Mono marshaller path.","solutions":["Avoid SafeHandle parameters on delegates called back from native code; marshal the underlying IntPtr handle and wrap it manually on the managed side.","Use a plain IntPtr or HandleRef for the reverse-P/Invoke boundary, then construct/Release the SafeHandle in managed code.","If you control the native ABI, pass raw handle integers and do lifetime management explicitly.","Upgrade to a .NET runtime (CoreCLR/NativeAOT) that implements the SafeHandle managed-conv path for reverse invocations."],"exampleFix":"// before\npublic delegate int NativeCallback(SafeFileHandle h);\n[DllImport(\"n\")] static extern void register(NativeCallback cb);\n\n// after\npublic delegate int NativeCallback(IntPtr h);\n[DllImport(\"n\")] static extern void register(NativeCallback cb);\n// in the callback, wrap h in a SafeFileHandle manually and own its lifetime","handlingStrategy":"validation","validationCode":"// Validate at startup that no reverse-P/Invoke delegate carries a SafeHandle param,\n// since Mono's MANAGED_CONV_IN for SafeHandle is unimplemented.\nstatic IEnumerable<MethodInfo> CheckReversePInvokes(Assembly asm) {\n    foreach (var t in asm.GetTypes())\n      foreach (var m in t.GetMethods(BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static|BindingFlags.Instance)) {\n        var dlg = m.GetCustomAttribute<UnmanagedFunctionPointerAttribute>();\n        if (dlg == null) continue;\n        foreach (var p in m.GetParameters())\n          if (typeof(SafeHandle).IsAssignableFrom(p.ParameterType))\n            yield return m; // will hit the unimplemented Mono path\n      }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Do not use SafeHandle on delegates invoked from native code on Mono; use IntPtr.","Run an assembly scan at build time to flag reverse-P/Invoke signatures using SafeHandle.","Keep SafeHandle usage on the managed->native direction only.","Target CoreCLR/NativeAOT if you need full SafeHandle reverse marshalling."],"tags":["mono","marshal","safehandle","reverse-pinvoke","interop","ilgen"],"backgroundTag":null,"analyzedSha":"60108ba66eb7d1d12f595480091b4ad80a24b172","analyzedAt":"2026-08-10T18:54:11.478Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}