{"record":{"id":"9d2e84d0f65fdf87","repo":"dotnet/runtime","slug":"mono-marshal-safehandles-missing-managed-conv-res","errorCode":null,"errorMessage":"mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\\n","messagePattern":"mono/marshal: SafeHandles missing MANAGED_CONV_RESULT\\\\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/mono/mono/component/marshal-ilgen.c","lineNumber":2046,"sourceCode":"\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,\n\t\t\tMonoType **conv_arg_type, MarshalAction action)\n{\n\tMonoMethodBuilder *mb = m->mb;\n\n\tMonoType *int_type = cb_to_mono->get_int_type ();\n\tswitch (action){\n\tcase MARSHAL_ACTION_CONV_IN: {\n\t\tconv_arg = cb_to_mono->methodBuilder.add_local (mb, int_type);","sourceCodeStart":2028,"sourceCodeEnd":2064,"githubUrl":"https://github.com/dotnet/runtime/blob/60108ba66eb7d1d12f595480091b4ad80a24b172/src/mono/mono/component/marshal-ilgen.c#L2028-L2064","documentation":"Third unimplemented branch in emit_marshal_safehandle_ilgen: MARSHAL_ACTION_MANAGED_CONV_RESULT covers the case where a reverse P/Invoke (native->managed) managed method returns a SafeHandle that must be converted to a native handle for the caller. Mono prints this to stderr and emits nothing, so the return-value conversion is missing.","triggerScenarios":"A managed delegate invoked from native code returns a SafeHandle; the marshaller needs to emit IL extracting/returning the raw handle but this case is a no-op.","commonSituations":"Reverse P/Invoke whose managed handler returns a SafeHandle (e.g. returning a file/socket handle to native); COM interop with SafeHandle return values on Mono; code that behaves on CoreCLR but hits the unimplemented Mono path.","solutions":["Change the managed callback's return type from SafeHandle to IntPtr, returning handle.DangerousGetHandle(), and let the native caller treat it as a raw handle.","Return an integer id and expose a separate managed API to resolve it to a SafeHandle on demand.","Keep all SafeHandle lifetime inside managed code and never return one across a reverse-P/Invoke boundary.","Target a runtime that implements MANAGED_CONV_RESULT for SafeHandle."],"exampleFix":"// before\npublic delegate SafeFileHandle OpenCallback(string path);\n\n// after\npublic delegate IntPtr OpenCallback(string path);\n// managed helper wraps the returned IntPtr into a SafeFileHandle when needed","handlingStrategy":"validation","validationCode":"// Flag reverse-P/Invoke delegates that return SafeHandle (Mono MANAGED_CONV_RESULT unsupported).\nstatic IEnumerable<MethodInfo> CheckReturningSafeHandles(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        if (m.GetCustomAttribute<UnmanagedFunctionPointerAttribute>() == null) continue;\n        if (typeof(SafeHandle).IsAssignableFrom(m.ReturnType))\n          yield return m;\n      }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Return IntPtr, not SafeHandle, from delegates called by native code.","Expose a managed helper to wrap the returned handle later.","Scan interop assemblies for SafeHandle-returning reverse delegates at build time.","Target a runtime with full SafeHandle managed-conv support if unavoidable."],"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-14T00:17:10.932Z"}