JeffreySu/WeiXinMPSDK · error · ObjectDisposedException
ObjectDisposedException (FinanceLibraryHandle)
Error message
ObjectDisposedException (FinanceLibraryHandle)
What it means
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.
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.
Example fix
// before
library.Dispose();
var sdk = library.GetDelegate<NewSdkDelegate>("NewSdk"); // ObjectDisposedException
// after
var sdk = library.GetDelegate<NewSdkDelegate>("NewSdk");
// ... use sdk ...
library.Dispose(); // dispose only when done Defensive patterns
Strategy: try-catch
Validate before calling
if (library == null || library.IsDisposed) throw new InvalidOperationException("Finance library not initialized"); Type guard
bool IsUsable(FinanceLibraryHandle h) => h is { } && !h.IsDisposed; Try / catch
try { var d = library.GetDelegate<TDelegate>(name); }
catch (ObjectDisposedException) { library = FinanceLibraryHandle.Load(path); d = library.GetDelegate<TDelegate>(name); } Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- 企业微信官方未提供当前操作系统可用的 Finance 会话内容存档原生库。请在 Windows 或 Linux…
- 无法加载企业微信 Finance 原生库:
- 企业微信 Finance 会话内容存档原生库仅支持 Windows 和 Linux。
- 无法加载企业微信 Finance 原生库:
- 企业微信 Finance 原生库缺少导出函数:
AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12).
Data as JSON: /api/errors/40a97df4fe2e27b2.
Report an issue: GitHub.
Appendix: source
Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/MsgAudit/MsgAuditFinanceNativeApi.cs:447
if (_isWindows)
{
WindowsNative.FreeLibrary(handle);
}
else if (_useLegacyLibDl)
{
LinuxLegacyNative.DlClose(handle);
}
else
{
LinuxNative.DlClose(handle);
}
}
private void ThrowIfDisposed()
{
if (_handle == IntPtr.Zero)
{
throw new ObjectDisposedException(nameof(FinanceLibraryHandle));
}
}
private static class WindowsNative
{
[DllImport("kernel32", EntryPoint = "LoadLibraryW", CharSet = CharSet.Unicode,
SetLastError = true)]
internal static extern IntPtr LoadLibrary(string fileName);
[DllImport("kernel32", EntryPoint = "GetProcAddress", CharSet = CharSet.Ansi,
SetLastError = true)]
internal static extern IntPtr GetProcAddress(IntPtr module, string procedureName);
[DllImport("kernel32", EntryPoint = "FreeLibrary", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool FreeLibrary(IntPtr module);
}
View on GitHub (pinned to be573f6f94)