JeffreySu/WeiXinMPSDK · critical · DllNotFoundException

无法加载企业微信 Finance 原生库:

Error message

无法加载企业微信 Finance 原生库:{path}。{detail}

What it means

On Linux, dlopen failed to load the WeChat Work Finance shared library: both libdl.so.2 and the legacy libdl.so attempts returned IntPtr.Zero. The wrapper throws DllNotFoundException with the path plus the dlerror() detail string, which names the actual OS-level cause.

Solutions

  1. Read the dlerror detail in the message — 'No such file or directory' = fix path/publish the .so, 'cannot open shared object file: libssl.so.1.1' = install that dependency.
  2. Verify the .so was published (RuntimeIdentifier linux-x64, native lib copied to output) and use an absolute path.
  3. Check architecture: the .so must match the process (x64). Run `file libWeWorkFinanceSdk.so`.
  4. On alpine/musl images, switch to a glibc-based image (e.g. mcr.microsoft.com/dotnet/aspnet:8.0) or add gcompat.

Example fix

// before
var api = MsgAuditFinanceNativeApi.Create("libWeWorkFinanceSdk.so"); // dlopen fails, path not in search dirs
// after
var path = Path.Combine(AppContext.BaseDirectory, "runtimes", "linux-x64", "native", "libWeWorkFinanceSdk.so");
Environment.SetEnvironmentVariable("LD_LIBRARY_PATH", Path.GetDirectoryName(path));
var api = MsgAuditFinanceNativeApi.Create(path);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!File.Exists(soPath)) throw new FileNotFoundException("Finance .so not published", soPath);
if (!RuntimeInformation.ProcessArchitecture.ToString().Contains("X64")) throw new PlatformNotSupportedException("x64 required");

Type guard

static bool LinuxLibraryReady(string path) => File.Exists(path) && RuntimeInformation.IsOSPlatform(OSPlatform.Linux);

Try / catch

try { return FinanceLibraryHandle.Load(path); }
catch (DllNotFoundException ex) { logger.LogCritical(ex, "dlopen failed: {Detail}", ex.Message); throw; }

Prevention

When it happens

Trigger: Loading the finance .so with a wrong or relative path; the .so has unresolved dependencies (missing libssl/libcrypto versions, missing libcurl); attempting to load an x86 .so into an x64 process; glibc/dl implementation too old.

Common situations: Docker linux containers missing the SDK's dependency chain (e.g. OpenSSL 1.1 not installed); publishing only managed dlls without the native .so; alpine/musl images where the glibc-built .so cannot load.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


AI-assisted analysis of JeffreySu/WeiXinMPSDK@be573f6f94 (2026-09-12). Data as JSON: /api/errors/764da9a2e6a93678. Report an issue: GitHub.

Appendix: source

Thrown at src/Senparc.Weixin.Work/Senparc.Weixin.Work/AdvancedAPIs/MsgAudit/MsgAuditFinanceNativeApi.cs:380

                    "企业微信 Finance 会话内容存档原生库仅支持 Windows 和 Linux。");
            }

            IntPtr linuxHandle;
            var useLegacy = false;
            try
            {
                linuxHandle = LinuxNative.DlOpen(path, RtldNow);
            }
            catch (DllNotFoundException)
            {
                useLegacy = true;
                linuxHandle = LinuxLegacyNative.DlOpen(path, RtldNow);
            }

            if (linuxHandle == IntPtr.Zero)
            {
                var detail = useLegacy ? LinuxLegacyNative.GetError() : LinuxNative.GetError();
                throw new DllNotFoundException(
                    $"无法加载企业微信 Finance 原生库:{path}。{detail}");
            }

            return new FinanceLibraryHandle(linuxHandle, false, useLegacy);
        }

        /// <summary>
        /// 获取动态库导出函数并转换为指定的 Cdecl 委托。
        /// </summary>
        /// <typeparam name="TDelegate">带有 <see cref="UnmanagedFunctionPointerAttribute"/> 的委托类型。</typeparam>
        /// <param name="exportName">官方 C ABI 导出函数名称。</param>
        /// <returns>绑定到导出函数的托管委托。</returns>
        public TDelegate GetDelegate<TDelegate>(string exportName) where TDelegate : Delegate
        {
            ThrowIfDisposed();
            IntPtr symbol;
            if (_isWindows)
            {

View on GitHub (pinned to be573f6f94)