0

The challenge I'm facing is that I'm writing a WCF service which is by it's nature, multithreaded to work with a C library which I know is not threadsafe and I have no option to influence the library I'm calling. The library I'm calling has an initialisation method to configure the hardware and set a callback handler.

If I execute this process in a console app, it runs absolutely fine as it's on a single thread.

To overcome the challenges I've created a helper class which implements IDisposable to setup the hardware, make the calls and hopefully rip itself down once it's finished.

Sample helper class:

public class MyClass 
{
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern void Setup(ushort var_one, ushort var_two);

    [DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern int Initialise(int port_num, int short_timeout, int long_timeout,
                                                  TXN_CALLBACK callback);

    [DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr GetDeviceStatus();

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void TXN_CALLBACK(int size, [MarshalAs(UnmanagedType.LPStr)] string the_data);

    private int Setup(ushort var_one, ushort var_two);
    {
        Setup(var_one, var_two);
        int foo= Initialise(0, shorttimeout, longtimeout, txn_callback);
        return foo;
    }      

    public MyStruct GetStatus()
    {

        Setup(0, 0);
        return PtrToStruct<MyStruct>(GetDeviceStatus());            
    }

    private static void txn_callback(int size, string the_data)
    {
         // Do something with the data
    }

    private static T PtrToStruct<T>(IntPtr ptr)
    {
        if (ptr == IntPtr.Zero)
        {
            // Invalid pointer returned
            return default(T);
        }

        return Marshal.PtrToStructure<T>(ptr);
    }
}

Calling code (WCF service):

using (MyClass class = new MyClass())
{
    return class.GetStatus();
}

I've left out the IDisposable code as it's been set as the default disposable pattern created by visual studio. As it stands, each time I call GetStatus after the first it knows I've already called it before until I restart the application. I'd like it to behave each time I call it as though it was the first time. Any ideas what I need to include in the disposal code to completely start from scratch each time I create an instance of my helper?

  • 1
    If the dll is not thread-safe implementing a disposal pattern is not going to help much with the possibility of an internal corrupt state. You need to ensure that only one thread is accessing the library either by spawning another process or doing something hacky with multiple renamed dlls – Alex K. Nov 07 '16 at 14:23
  • I'm tried wrapping all the services with lock() so that the library cannot be called from multiple threads however it seems to be wise to the fact it's already been used by the process before. – Matt Brooke Nov 07 '16 at 14:58

1 Answers1

1

I believe the root of your issue is that once "mydll.dll" is loaded into memory, that same instance, if you will, is used by every instance of MyClass.

Given that you cannot modify the underlying library, you either need to be able to call functions in the native dll to restore it to a default state, or you need to be able to unload it from memory between each usage. I'm betting the former option is not possible based on the context of your question. You have a couple options, either use the windows API to dynamically load and unload the DLL: How to dynamically load and unload a native DLL file?

...or wrap this DLL in another exe of your making, so ,on construction, your service will launch the exe, talk to it via any number of means (you could create another layer of WCF service for this, stdin/out, named pipes, whatever is convenient), and close the exe when you are done with it.

I lean towards the exe method to ensure that the DLL is completely isolated and has no way of corrupting your service, plus you also have the ability to spawn multiple instances (for each WCF service instance) if you need to support multiple sessions. In addition, when you create each exe instance, you can get the process ID and monitor it for crashes as well, making your service even more robust.

Community
  • 1
  • 1
Sean K
  • 774
  • 7
  • 10