【问题标题】:Wrapping a static unmanaged library in an instance class在实例类中包装静态非托管库
【发布时间】:2013-02-13 10:54:41
【问题描述】:

我在我的代码 (C#) 中使用了一个非托管库(用 C++ 编写)。库提供的所有方法都是静态的。我使用 P/Invoke 与库进行通信。这是图书馆的工作方式:

  1. 在调用所有其他方法之前,您需要调用一个初始化方法(这需要一个参数,并且该参数只能在初始化期间设置)。
  2. 有一个 settings 方法可以更改库的不同设置,如果您需要更改库的工作方式(更像是微调库),则需要运行此方法。可以随时更改设置。
  3. 有一种方法可以接受一个数字数组,然后返回另一个数组。我们称之为中间操作。这个操作需要一段时间,所以我想缓存它并防止每次都计算它。
  4. 前一个方法(中间操作)返回的数组是创建结果的最后一个方法的输入。我们称之为主要操作

我通常希望有不同的库实例,一个具有设置 A,另一个具有 设置 B。由于库是静态的,我不能。

如何将这个库包装在实例类中?可能,我的类的每个实例都需要在内存中加载库的新实例(因为库本身的每个实例都是静态的,并且您不能在库的一个实例中具有两种不同的设置)。

除了重写库(因为我无法控制它的编写方式)之外,是否有解决方法?

而且我真的很希望能够处理对操作方法的并行调用。

【问题讨论】:

  • 除了传递给主操作之外,还需要中间操作的结果吗?
  • 是的,我需要它来缓存。创建需要一段时间,我想缓存它。
  • 是否可以同步访问库并每次调用SetSettings? (或者至少当有人使用上次使用的不同“实例”时)
  • 是否可以在通话之间更改设置?如果是这样,中间操作和主要操作是否都依赖于这些设置对于相应的调用相同?想到的是一个类,它的对象存储设置并在每次操作之前将它们应用到库中。为了加快速度,您可以检查当前设置(通过存储应用设置的唯一 ID/散列的东西包装库)并仅在需要时应用存储的设置。
  • 这也是我的建议。

标签: c# c++ static pinvoke


【解决方案1】:

以下是 cmets 中针对问题所说的示例实现:

public class Library
{
    private static class Native
    {
        [DllImport("foobar.dll")]
        public static extern void Initialize();

        [DllImport("foobar.dll")]
        public static extern void Settings(int param1, string param2);

        [DllImport("foobar.dll")]
        public static extern float[] Intermediate(float[] input);

        [DllImport("foobar.dll")]
        public static extern void MainMethod(float[] main);
    }

    private static readonly object sync = new object();

    private int param1;
    private string param2;

    static Library()
    {
        Native.Initialize();
    }


    public void Settings(int param1, string param2)
    {
        this.param1 = param1;
        this.param2 = param2;
    }

    public float[] Intermediate(float[] input)
    {
        lock (sync)
        {
            Native.Settings(param1, param2);
            return Native.Intermediate(input);
        }
    }

    public void MainMethod(float[] input)
    {
        lock (sync)
        {
            Native.Settings(param1, param2);
            Native.MainMethod(input);
        }
    }
}

最重要的事情

  • 设置不会立即调用“真实”设置,而是存储值
  • 调用 Intermediate 和 MainMethod 时,它们会应用之前的设置存储
  • 两个调用都在一个静态对象上同步,以避免对在不同线程上运行的库的不同实例的调用产生干扰。

【讨论】:

  • 不错的建议。但首先,initialize 也有一个参数,所以每次调用都需要重新初始化。第二件事是,有没有一种简单的方法可以并行化?据我了解,它只能由一个线程调用。
  • 我不知道initialize带参数的要求,对不起。你是对的,这不会并行化。您可以尝试创建多个 AppDomain。每个 AppDomain 存在一次托管静态变量。我不确定本机 DLL 中的静态变量。不过,可能值得进一步调查。否则,您总是可以求助于多进程解决方案,尽管这当然更难协调。编辑 - 不,根据 stackoverflow.com/questions/6011518/…,AppDomains 将无法工作
  • 您可以多次复制不同名称的DLL,然后动态加载它们。这实际上可以工作。
  • @AndreLoker 这将如何工作? DllImport 属性需要在编译时解析。
【解决方案2】:

正如我在评论中所建议的,如果需要颚化,我能想到的唯一解决方法是采用多进程方法:将每个“设置”包装在一个独立的进程中。当然,这将使进程之间的通信和数据流量成为瓶颈的新候选(无论您将使用 WCF 命名管道、Remoting、WM_COPYDATA 还是套接字)。

【讨论】:

  • 其实可以使用共享内存,对于ipc来说还是不错的。但是,您可以使用 loadlibraryex 多次加载同一个 dll 的可能性很小...
  • @thang:不幸的是(对于 OP)你不能:stackoverflow.com/questions/3497516/…
  • 幸运的是,可能有办法绕过它:stackoverflow.com/questions/4225064/…
  • 谁知道...它被接受了,所以我假设他尝试过。不确定。
猜你喜欢
  • 2011-09-05
  • 2011-08-31
  • 1970-01-01
  • 2011-02-24
  • 2014-01-17
  • 1970-01-01
  • 2012-09-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多