{"record":{"id":"40a97df4fe2e27b2","repo":"JeffreySu/WeiXinMPSDK","slug":"objectdisposedexception-financelibraryhandle","errorCode":null,"errorMessage":"ObjectDisposedException (FinanceLibraryHandle)","messagePattern":"ObjectDisposedException \\(FinanceLibraryHandle\\)","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/MsgAudit/MsgAuditFinanceNativeApi.cs","lineNumber":447,"sourceCode":"            if (_isWindows)\n            {\n                WindowsNative.FreeLibrary(handle);\n            }\n            else if (_useLegacyLibDl)\n            {\n                LinuxLegacyNative.DlClose(handle);\n            }\n            else\n            {\n                LinuxNative.DlClose(handle);\n            }\n        }\n\n        private void ThrowIfDisposed()\n        {\n            if (_handle == IntPtr.Zero)\n            {\n                throw new ObjectDisposedException(nameof(FinanceLibraryHandle));\n            }\n        }\n\n        private static class WindowsNative\n        {\n            [DllImport(\"kernel32\", EntryPoint = \"LoadLibraryW\", CharSet = CharSet.Unicode,\n                SetLastError = true)]\n            internal static extern IntPtr LoadLibrary(string fileName);\n\n            [DllImport(\"kernel32\", EntryPoint = \"GetProcAddress\", CharSet = CharSet.Ansi,\n                SetLastError = true)]\n            internal static extern IntPtr GetProcAddress(IntPtr module, string procedureName);\n\n            [DllImport(\"kernel32\", EntryPoint = \"FreeLibrary\", SetLastError = true)]\n            [return: MarshalAs(UnmanagedType.Bool)]\n            internal static extern bool FreeLibrary(IntPtr module);\n        }\n","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/JeffreySu/WeiXinMPSDK/blob/be573f6f94bdbf718dd5f6cdecb137fbc7ff651e/src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/MsgAudit/MsgAuditFinanceNativeApi.cs#L429-L465","documentation":"FinanceLibraryHandle was disposed (its native handle freed and zeroed) but GetDelegate was still called on it. ThrowIfDisposed detects _handle == IntPtr.Zero and throws ObjectDisposedException('FinanceLibraryHandle'). The native library cannot be used after Dispose — all SDK operations must happen before it.","triggerScenarios":"Calling GetDelegate (or any finance API op) after Dispose() on the FinanceLibraryHandle; using the API from a second scope/thread after an earlier code path disposed the library; DI container disposing a singleton the code still references.","commonSituations":"Host shutdown releasing the library while a background media-download loop is still running; double-dispose patterns where a finally block disposes and a retry path reuses the object.","solutions":["Ensure Dispose is called only after all finance operations (chat pulls, media downloads) complete.","Keep one long-lived library instance per app and dispose only at process shutdown (e.g. IHostedService.StopAsync after all work drained).","Do not reuse the disposed handle — recreate the API instance if you need to operate again.","Synchronize disposal with worker tasks via await on all in-flight media downloads before Dispose."],"exampleFix":"// before\nlibrary.Dispose();\nvar sdk = library.GetDelegate<NewSdkDelegate>(\"NewSdk\"); // ObjectDisposedException\n// after\nvar sdk = library.GetDelegate<NewSdkDelegate>(\"NewSdk\");\n// ... use sdk ...\nlibrary.Dispose(); // dispose only when done","handlingStrategy":"try-catch","validationCode":"if (library == null || library.IsDisposed) throw new InvalidOperationException(\"Finance library not initialized\");","typeGuard":"bool IsUsable(FinanceLibraryHandle h) => h is { } && !h.IsDisposed;","tryCatchPattern":"try { var d = library.GetDelegate<TDelegate>(name); }\ncatch (ObjectDisposedException) { library = FinanceLibraryHandle.Load(path); d = library.GetDelegate<TDelegate>(name); }","preventionTips":["Dispose the library only at application shutdown, after all workers drained.","Expose the library as a singleton and let the DI host own its lifetime.","Never call Dispose in a finally block that can run before retry loops finish."],"tags":["disposed-object","native-library","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"be573f6f94bdbf718dd5f6cdecb137fbc7ff651e","analyzedAt":"2026-09-12T10:01:50.733Z","contentChangedAt":"2026-09-12T10:01:50.733Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}