【问题标题】:How do I managing usage of multiple versions of unmanaged DLL calls in C#?如何在 C# 中管理多个版本的非托管 DLL 调用的使用?
【发布时间】:2012-06-19 17:37:08
【问题描述】:

我想知道是否有人能指出我正确的方向我对 c# 还很陌生,所以请放轻松。

我的代码使用非托管 DLL,它是提供的用于与智能卡读卡器交互的 API,我无法控制 DLL,我只想将其包装在 c# 中以使其更易于使用。到目前为止,我已经设法做到了这一点,但我现在发现需要测试这个 DLL 的多个版本,这些版本的入口点相同,但 可以 不同的参数。 p>

我尝试了一些类似的方法来组织我的包装类;

internal static class UnSafeNativeMethods
{
    internal static class READER
    {
        internal static class SDK20
        {
            [DllImport(dllpath, EntryPoint = "CV_SetCommunicationType")]
            internal static extern int CV_SetCommunicationType(byte type);
            ...
        }

        internal static class SDK21
        {
            [DllImport(dllpath, EntryPoint = "CV_SetCommunicationType")]
            internal static extern int CV_SetCommunicationType(byte type);
            ...
        }
    }
}

但是在检查要使用的调用时,它会产生非常难看的代码;

ReaderSDK sdk = ReaderSDK.SDK20  //Could come from a argument passed in or set in an 
                                 //instantiated class
...
switch (sdk)
{
    case ReaderSDK.SDK20:
        UnSafeNativeMethods.READER.SDK20.CV_SetCommunicationType(0x0);
        break;
    case ReaderSDK.SDK21:
        UnSafeNativeMethods.READER.SDK21.CV_SetCommunicationType(0x0);
        break;
    ...
}

这对我来说似乎很乱,想知道是否有人能指出我正确的方向......


编辑:离开下面的 cmets,我想出了一些示例代码,但我仍然不确定我是否走在正确的轨道上,因为我的开关仍然存在,但它现在是混凝土的一部分工厂类。

public enum ConnectionType
{
    RS232 = 0x0,
    USB = 0x1,
    UDP = 0x2
}

interface INativeMethods
{
    string Name();
    void SetCommunicationType(ConnectionType type);
}

class SDK20 : INativeMethods
{
    public string Name()
    {
        return "SDK Version 2.0";
    }

    // Thanks to @dzendras for this!!
    public void SetCommunicationType(ConnectionType type)
    {
        int result = UnSafeNativeMethods.READER.SDK20.CV_SetCommunicationType((byte)type);
        switch (result)
        {
            case 0:
                return;
            case 1:
                throw new NotSupportedException("Communication type not supported");
            case 2:
                throw AnyOtherMeaningfulException("Its message");
        }
    }


}

class SDK21 : INativeMethods
{
    public string Name()
    {
        return "SDK Version 2.1";
    }

    // Thanks to @dzendras for this!!
    public void SetCommunicationType(ConnectionType type)
    {
        int result = UnSafeNativeMethods.READER.SDK21.CV_SetCommunicationType((byte)type);
        switch (result)
        {
            case 0:
                return;
            case 1:
                throw new NotSupportedException("Communication type not supported");
            case 2:
                throw AnyOtherMeaningfulException("Its message");
        }
    }
}

class NativeMethodsFactory
{
    private static NativeMethodsFactory instance = new NativeMethodsFactory();
    private NativeMethodsFactory()
    {

    }

    public static NativeMethodsFactory Instance
    {
         get { return NativeMethodsFactory.instance; }
    }
    public INativeMethods Get(ReaderSDK version)
    {
        switch (version)
        {
            case ReaderSDK.SDK20:
                return new SDK20();

            case ReaderSDK.SDK21:
                return new SDK21();

            default:
                return new SDK20();
        }
    }
}

我在正确的轨道上吗?

这就是我现在实现对 SDK 的调用的方式...

// sdk passed in as enum, NativeMethods stored as class member.
NativeMethods = NativeMethodsFactory.Instance.Get(sdk);
...
NativeMethods.SetCommunicationType(ConnectionType.USB);

【问题讨论】:

  • 在我看来,对于我所做的每个 API 调用都必须使用 switch 语句真的很不整洁,我的问题是如何整理我的代码,所以我存储说类类型的引用并调用来自那里的非托管电话,事情不确定我该怎么做。尝试使用单例类,但问题太多。

标签: c# dll unmanaged dllimport multiple-versions


【解决方案1】:

使用模式策略

一些关于模式的链接:

http://www.oodesign.com/strategy-pattern.html

Real World Example of the Strategy Pattern

在工厂模式的帮助下,使用策略模式最终如何实现的一些示例。

INativeMethods nativeMethods = NativeMethodsFactory.Get(UnsafeSdkVersion.V1);
nativeMethods.CV_SetCommunicationType(aType);

优点:

  1. 通过接口和工厂解耦
  2. 没有开关
  3. 易于添加新版本,所有其他代码都独立于要使用的版本。

【讨论】:

  • 但我的意思是 c#。在 C# 上实现模式策略。您将有 2 个策略 - 对于每个版本,您也可以避免不必要的切换。并且执行的版本可以存储在 app.config 中
  • 我在 C# 中找到了关于抽象工厂模式实现的这个 - link
  • 您向我展示了难题的一部分,它看起来很有希望,但似乎我在您的示例中缺少关键步骤,我如何从 NativeMethodFactory 到我的 SDK 实例?
  • NativeMethodsFactory 是一个静态类吗?因为我遇到了无法从静态类返回实例成员的问题。
  • 我查看了更改。是的,这与我所说的变化非常相似。与 Factory 的主要区别在于,您只能在一个地方进行切换,而您的所有其他代码将取决于界面。并执行相同的方法,但根据您的需要使用不同的实现。 NativeMethodsFactory 可以是静态的,也可以不是静态的。根据您的意愿和需要实施工厂。工厂的主要目标是根据版本构造对象。只有工厂应该知道具体的实现,所有其他代码都应该使用工厂和接口。
【解决方案2】:

我建议更改您的托管 API。调用 C 代码(从 DLL)不应该强迫你使用结构范式。你的包装应该是完全客观的。例如:

internal static extern int CV_SetCommunicationType(byte aType);
  1. 如果成功,返回的 int 为 0,我想任何其他指示错误的值。返回 void 并在内部将代码从 DLL 调用转换为异常。

    public void SetCommunicationType(byte type) 
    {
         int result = CV_SetCommunicationType(type);
         switch (result)
         {
             case 0:
                 return;
             case 1:
                 throw new NotSupportedException("Communication type not supported");
             case 2:
                 throw AnyOtherMeaningfulException("Its message");
         }
     }
    
  2. 为 aType 创建一个枚举。

  3. 寻找课程。静态方法是纯粹的邪恶。分析您的领域并寻找对象和行为。例如,这可能是(愚蠢的名字,我知道...)ConnectionManager 的方法。

  4. 取决于合同。让你的类(针对不同的 SDK)实现一些通用接口。

  5. 不要以示例方法中所示的方式使用hungarian notation

【讨论】:

  • 感谢 dzendras,非常有帮助。我通常不使用匈牙利符号,但在这种情况下,我从来没有改变 SDK 示例附带的声明(有很多)。
  • 这如何帮助我包装多个版本的调用?我知道我遗漏了一些东西,你能帮我把这些点联系起来吗?您能否扩展 3. 寻求课程...
  • 将返回值设置为异常将是一项艰巨的工作,这些值以单个字节的形式返回并且有很多,但我明白你的意思,谢谢。
  • 你总是可以支持我的回答 :) 你想出的解决方案很好。但是,如果 API 发生更改(某些参数或新方法),您的代码将会降级......没有更广泛的上下文,我找不到任何解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-24
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-07
相关资源
最近更新 更多