JeffreySu/WeiXinMPSDK · error · ObjectDisposedException
ObjectDisposedException (MsgAuditFinanceClient)
Error message
ObjectDisposedException (MsgAuditFinanceClient)
What it means
MsgAuditFinanceClient wraps a native SDK handle; ThrowIfDisposed throws ObjectDisposedException naming MsgAuditFinanceClient when GetChatData, DecryptData, or GetMediaData is called after Dispose. Native resources cannot be used after the SDK instance is destroyed.
Solutions
- Keep the client alive for the full duration of its use; move it out of the using scope or use explicit lifecycle management
- Register the client in DI as a singleton/scoped service and let the container dispose it
- Recreate a new MsgAuditFinanceClient if the previous one was disposed
- Guard background tasks against using clients whose lifetime ended
Example fix
// before
MsgAuditFinanceClient client;
using (client = new MsgAuditFinanceClient(options)) { }
client.GetChatData(...); // disposed
// after
using var client = new MsgAuditFinanceClient(options);
client.GetChatData(...); // all usage inside lifetime Defensive patterns
Strategy: type-guard
Validate before calling
if (_client is null || _clientDisposed) throw new InvalidOperationException("client lifetime ended"); Type guard
bool Usable(MsgAuditFinanceClient? c) => c is not null && !c.Disposed; // track disposal in your wrapper if the client lacks a public flag
Try / catch
try { var data = client.GetChatData(...); }
catch (ObjectDisposedException) { client = new MsgAuditFinanceClient(options); /* recreate and retry once */ } Prevention
- Scope client usage strictly inside using blocks or a DI-managed lifetime
- Avoid storing using-scoped clients in fields or closures that outlive the scope
- Let DI containers own disposal for singletons/background services
- Recreate clients lazily via a factory instead of caching instances
When it happens
Trigger: Calling any data API after client.Dispose() — e.g. reusing a client stored in a field after a using block ended, or a long-lived background job holding a disposed client.
Common situations: 'using var client = ...' scope ending while async continuations still use the client, singleton service disposed by DI container while background threads still reference it, or accidental early Dispose.
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
- ArgumentNullException (destination is null)
- 目标流必须可写。
- Finance SDK 尚未完成媒体下载,但没有返回可继续使用的新索引缓冲区。
- ArgumentNullException (options is null)
- 原生 SDK 网络请求超时时间必须大于 0 秒。
AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12).
Data as JSON: /api/errors/b206a4da6fa957f9.
Report an issue: GitHub.
Appendix: source
Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/MsgAudit/MsgAuditFinanceClient.cs:317
if (handle == IntPtr.Zero)
{
throw new InvalidOperationException($"Finance SDK 的 {operation} 操作返回了空指针。");
}
}
private static void ThrowIfNativeError(int errorCode, string operation)
{
if (errorCode != 0)
{
throw new MsgAuditFinanceException(errorCode, operation);
}
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(MsgAuditFinanceClient));
}
}
private void ReleaseResources(bool disposing)
{
if (_disposed)
{
return;
}
_disposed = true;
var nativeApi = _nativeApi;
_nativeApi = null;
if (nativeApi == null)
{
return;
}
View on GitHub (pinned to be573f6f94)