How do I managing usage of multiple versions of unmanaged DLL calls in C#?

I wonder if anyone can point me in the right direction I'm fairly new to c# so go easy on me.

My code uses an unmanaged DLL that is a provided API for interfacing with a SmartCard Reader, I don't have control of the DLL I just want to wrap it in c# to make it easier to use. So far I've managed to do this but I'm finding now that there is a requirement to test multiple versions of this DLL which has EntryPoints that are the same but parameters that can be different.

I tried something along these lines to organise my wrapper classes;

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);
            ...
        }
    }
}

But it makes for really unsightly code when checking which call to use;

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;
    ...
}

This seems messy to me and wondering if anyone could point me in the right direction...


EDIT : Going off comments below, I've come up with some sample code still not sure if I'm on the right track as my switch still remains but it is now part of a Concrete Factory Class.

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();
        }
    }
}

Am I on the right track with this?

This is how I implement calls to the SDKs now...

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

Answers


Use pattern Strategy

Some links about pattern:

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

Real World Example of the Strategy Pattern

Some sample of how it could be in final with Strategy pattern with help of Factory pattern.

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

Advantages:

  1. Decoupling through interface and factory
  2. No switches
  3. Easy to add new version, all other code is independent from wich version to use.

I'd suggest changing your managed API. Calling C code (from the DLL) shouldn't force you to use structural paradigm. Your wrappers should be fully objective. For instance:

internal static extern int CV_SetCommunicationType(byte aType);
  1. Returned int is 0 in case of success, and any other value indicating error, I suppose. Return void and internally translate the code from the DLL call to exceptions.

    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. Create an enum for aType.

  3. Seek for classes. Static methods are pure evil. Analyze your domain and seek for objects and behaviours. For instance this could be the method of (stupid name, I know...) ConnectionManager.

  4. Depend on contracts. Let your classes (for different SDK) implement some common interfaces.

  5. Do not use hungarian notation in the way you've shown in the example method.


Need Your Help

Queryset Cannot reorder a query once a slice has been taken

python django django-queryset

I am having some trouble with a queryset in Django. Unfortunately I cannot use the Django REST Framework built in pagination because it adds unwated JSON fields such as "next", "previous" and "coun...

Allowing anyone to View a webpage in ASP.NET MVC

c# asp.net-mvc-4

I have a website designed using ASP.NET MVC4 which has a specific Controller 'ProductionLogs' which will display the top 25 logs from the Production Server on typing the URL https://(myapp.com)/