Ola' daniweb, long time no see.

I've been absent for a while, mostly due to not having any problems to ask about, but also because I got one of those annoying 'job' things ^_^.
Anyhow, I'm not terribly familiar with C# (being from c++ land), however I've got to use it to integrate some of my companies libraries with a VSTA project they are working on. Nothing to complicated, just creating wrapper classes to use the exported functions in their library DLL's in C#.

Anyway, I've been fine marshalling structures before in C# by using the typical

[dllimport("...")]
[marshalas.something] function(x,y,z)

approach, however i've hit a sticky one and wondered if anyone had some advice.

I've got one library that needs to be dynamically loaded (path not known until runtime, etc). To accomplish this I've eaten my google-cookies and come up with this solution (that works for most of the functions).

public class Interface
    {
        //dll static bindings
        [DllImport("kernel32.dll", EntryPoint = "LoadLibraryA")]
        static extern int LoadLibrary(
            [MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);

        [DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
        static extern IntPtr GetProcAddress(int hModule,
            [MarshalAs(UnmanagedType.LPStr)] string lpProcName);

        [DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
        static extern bool FreeLibrary(int hModule);
        int hModule = 0;
        unsafe delegate uint Function_del();
        IntPtr Function_ptr;
        Function_del Function_Deligate;
        public Interface()
        {
//this bit gets the path from windows registry
            CM3Path PathObj = new CM3Path();
            string AppPath = PathObj.CM3PathString;
//end this bit <_<
            string FullPath = "";

            if (AppPath != "")
            {
                FullPath = AppPath + "CPIF_External_Bindings.dll";
                hModule = LoadLibrary(FullPath);
            }
            else
            {
                hModule = 0;
            }

            //capture library handle
            if (hModule != 0)
            {
                Function_ptr = GetProcAddress(hModule, "Function");
                Function_Deligate = (Function_del)Marshal.GetDelegateForFunctionPointer(Function_ptr, typeof(Function_del));
            }
      }
}

So far it's been working fine, however there's one function that is proving to be an issue.

The C++ for this function is as follows (forgive the DLL_PUBLIC macro, it's cross platform see)

DLL_PUBLIC InterChange::DocCheck GetDocData(void*ptr ,unsigned int Data)

and the 'DocCheck' object is a structure defined as follows

struct DocCheck
		{
			uint32_t Size;
			char UUID[36];
			uint32_t InternalCRC;
			uint32_t ExternalCRC;
		};

The problem comes in that I can't simply mashal this return value into the C# structure as I did with static loading, So I'm somewhat confused as the correct way to import this function, or if I can at all.
what I want to do is something like this (though I'm aware this will not work):

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi), Serializable]
    public unsafe struct DocCheck
    {
        public UInt32 Size;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 36)]
        public fixed char UUID[];
        public UInt32 InternalCRC;
        public UInt32 ExternalCRC;
    };
	unsafe delegate [MarshalAs(UnmanagedType.LPStruct)]DocCheck GetDocData_del(void*ptr ,uint Data);
        IntPtr GetDocData_ptr;
        GetDocData_del GetDocData_Deligate;
                GetDocData_ptr = GetProcAddress(hModule, "GetDocData");
                GetDocData_Deligate = (GetDocData_del)Marshal.GetDelegateForFunctionPointer(GetDocData_ptr, typeof(GetDocData_del));

Object = GetDocData_Deligate(DataPointer, Index);

So, any much appreciated advice gal's and guys?

Here's something I wrote a while back to do just this with some data aquisition dll. The reason I had to load/unload it dynamically is because the driver was broken, and would keep the same device handle after the device was unplugged. When you plugged it back in, the handle should have changed but it didnt. Therefore I had to unload then reload the dll from memory at runtime.

It's long, uncommented, and a bit ugly. But it should help you :)

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Security;
using System.Threading;

namespace Mobile4704
{
    internal static class NativeMethods
    {
        [DllImport("kernel32.dll")]
        public static extern IntPtr LoadLibrary(string dllToLoad);

        [DllImport("kernel32.dll")]
        public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

        [DllImport("kernel32.dll")]
        public static extern bool FreeLibrary(IntPtr hModule);
    }
    [Guid("07DEDDC9-5318-46AC-8D6B-CACCA52524BC")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("Mobile4704")]
    unsafe public class Mobile4704
    {
        #region adsapi32.dll Interopp
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_GetAddress([MarshalAs(UnmanagedType.AsAny)] object lpVoid);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_AIConfig(IntPtr DriverHandle, PT_AIGetConfig AIGetConfig);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_DeviceClose(IntPtr DriverHandle);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_DeviceOpen(uint lDeviceNum, ref IntPtr DriverHandle);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_CounterReset(IntPtr DriverHandle, int counter);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_CounterEventStart(IntPtr DriverHandle, ref PT_CounterEventStart CounterEventStart);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_AIVoltageIn(IntPtr DriverHandle, ref PT_AIVoltageIn AIVoltageIn);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_CounterEventRead(IntPtr DriverHandle, ref PT_CounterEventRead CounterEventRead);
        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        private delegate uint DRV_DioReadBit(IntPtr DriverHandle, ref PT_DioReadBit lpDioReadPortByte);

        DRV_GetAddress iopGetAddress;
        DRV_AIConfig iopAIConfig;
        DRV_DeviceClose iopDeviceClose;
        DRV_DeviceOpen iopDeviceOpen;
        DRV_CounterReset iopCounterReset;
        DRV_CounterEventStart iopCounterStart;
        DRV_AIVoltageIn iopAIVoltageIn;
        DRV_CounterEventRead iopCounterRead;
        DRV_DioReadBit iopDIReadBit;

        #region structs/dll members
        volatile private IntPtr DeviceHandle;
        private PT_DioReadBit DiIn;
        private PT_AIConfig lpAIConfig;
        private PT_AIVoltageIn AiVolIn;
        private PT_CounterEventStart lpCounterEventStart;
        private PT_CounterEventRead lpCounterEventRead;

        private struct PT_AIConfig
        {
            public ushort DasChan;
            public ushort DasGain;
        }
        private struct PT_AIVoltageIn
        {
            public ushort chan;
            public ushort gain;
            public ushort TigMode;
            public IntPtr voltage;
        }

        private struct PT_AIGetConfig
        {
            public uint buffer;
            public int Size;
        }
        private struct PT_CounterEventStart
        {
            public int counter;
            public int GateMode;
        }
        private struct PT_CounterEventRead
        {
            public uint counter;
            public uint overflow;
            public IntPtr Count;
        }
        private struct PT_DioReadPortByte
        {
            public int Port;
            public IntPtr value;
        }
        private struct PT_DioReadBit
        {
            public ushort bit;
            public ushort Port;
            public IntPtr state;
        }
        #endregion
        #endregion
        #region Members
        float[] fAnalogIn = new float[16];
        byte[] bDigitalIn = new byte[16];
        long iCounter = 0;
        long lStatus = 0;
        const int MaxEntries = 255;
        const int MODULE_AI_CHANNELS = 8;
        const int MODULE_DI_CHANNELS = 8;
        Thread Updater;
        volatile int iPollRate = 0;
        bool bKillThread = false;
        #endregion
        IntPtr pDll;
        private struct InteropFuncs
        {
            internal IntPtr pGetAddress;
            internal IntPtr pAIConfig;
            internal IntPtr pDeviceClose;
            internal IntPtr pDeviceOpen;
            internal IntPtr pCounterReset;
            internal IntPtr pCounterStart;
            internal IntPtr pAIVoltageIn;
            internal IntPtr pCounterRead;
            internal IntPtr pDIReadBit;
        }
        InteropFuncs iOp = new InteropFuncs();
        public Mobile4704()
        {
        }
        private void LoadDLL()
        {
            pDll = NativeMethods.LoadLibrary(@"C:\windows\system32\adsapi32.dll");
            //Load func pointers
            iOp.pGetAddress = NativeMethods.GetProcAddress(pDll, "DRV_GetAddress");
            iOp.pAIConfig = NativeMethods.GetProcAddress(pDll, "DRV_AIConfig");
            iOp.pAIVoltageIn = NativeMethods.GetProcAddress(pDll, "DRV_AIVoltageIn");
            iOp.pCounterRead = NativeMethods.GetProcAddress(pDll, "DRV_CounterEventRead");
            iOp.pCounterReset = NativeMethods.GetProcAddress(pDll, "DRV_CounterReset");
            iOp.pCounterStart = NativeMethods.GetProcAddress(pDll, "DRV_CounterEventStart");
            iOp.pDeviceClose = NativeMethods.GetProcAddress(pDll, "DRV_DeviceClose");
            iOp.pDeviceOpen = NativeMethods.GetProcAddress(pDll, "DRV_DeviceOpen");
            iOp.pDIReadBit = NativeMethods.GetProcAddress(pDll, "DRV_DioReadBit");

            //Initialize delegates to reference these pointers
            iopAIConfig = (DRV_AIConfig)Marshal.GetDelegateForFunctionPointer(iOp.pAIConfig, typeof(DRV_AIConfig));
            iopAIVoltageIn = (DRV_AIVoltageIn)Marshal.GetDelegateForFunctionPointer(iOp.pAIVoltageIn, typeof(DRV_AIVoltageIn));
            iopCounterRead = (DRV_CounterEventRead)Marshal.GetDelegateForFunctionPointer(iOp.pCounterRead, typeof(DRV_CounterEventRead));
            iopCounterReset = (DRV_CounterReset)Marshal.GetDelegateForFunctionPointer(iOp.pCounterReset, typeof(DRV_CounterReset));
            iopCounterStart = (DRV_CounterEventStart)Marshal.GetDelegateForFunctionPointer(iOp.pCounterStart, typeof(DRV_CounterEventStart));
            iopDeviceClose = (DRV_DeviceClose)Marshal.GetDelegateForFunctionPointer(iOp.pDeviceClose, typeof(DRV_DeviceClose));
            iopDeviceOpen = (DRV_DeviceOpen)Marshal.GetDelegateForFunctionPointer(iOp.pDeviceOpen, typeof(DRV_DeviceOpen));
            iopGetAddress = (DRV_GetAddress)Marshal.GetDelegateForFunctionPointer(iOp.pGetAddress, typeof(DRV_GetAddress));
            iopDIReadBit = (DRV_DioReadBit)Marshal.GetDelegateForFunctionPointer(iOp.pDIReadBit, typeof(DRV_DioReadBit));
        }
        private void UnloadDLL()
        {
            NativeMethods.FreeLibrary(pDll);
        }
        public void Initialize(int _iPollRate)
        {
            LoadDLL();
            iPollRate = _iPollRate;
            Updater = new Thread(new ThreadStart(Refresh));
            doUpdateData = new delVoidaFloataFloat(UpdateData);
            doStatusChange = new delVoidInt(UpdateStatus);
            Updater.Start();
        }
        volatile bool bCounterReset = false;
        private void Refresh()
        {
            uint Err = 0;
            bool bErr = false;
            bool bConnected = true;
            long iPollCount = 0;
            Err = iopDeviceOpen(0, ref DeviceHandle);
            //ResetCounter();
            if (Err != 0)
            {
                doStatusChange.Invoke(1);
                bConnected = false;
            }
            float[] fChannelData = new float[16];
            byte[] bDIData = new byte[16];
            do
            {
                iPollCount++;
                bErr = false;
                if (!bConnected)
                {
                    Marshal.FreeHGlobal(DeviceHandle);
                    DeviceHandle = new IntPtr();
                    Err = iopDeviceOpen(0, ref DeviceHandle);
                    if (Err != 0)
                        bErr = true;
                }
                //get analog inputs
                for (ushort i = 0; i < MODULE_AI_CHANNELS; i++)
                {
                    float[] data = new float[1];
                    AiVolIn.voltage = Marshal.AllocHGlobal(sizeof(float));
                    lpAIConfig.DasChan = i;
                    AiVolIn.chan = i; //only possible channel
                    AiVolIn.gain = 4;//only possible gain on 4704
                    AiVolIn.TigMode = 0;//only possible trigger
                    try
                    {
                        iopDeviceOpen(0, ref DeviceHandle);
                        Err = iopAIVoltageIn(DeviceHandle, ref AiVolIn);
                        iopDeviceClose(DeviceHandle);
                        Marshal.Copy(AiVolIn.voltage, data, 0, 1);
                        Marshal.DestroyStructure(AiVolIn.voltage, AiVolIn.voltage.GetType());
                        fChannelData[i] = data[0];
                    }
                    catch { Err = 1; }
                    if (Err != 0)
                        bErr = true;
                }
                if (bCounterReset == true)
                {
                    Err = iopCounterReset(DeviceHandle, lpCounterEventStart.counter);
                    Err = iopCounterStart(DeviceHandle, ref lpCounterEventStart);
                    bCounterReset = false;
                }
                //get counter
                int[] countData = new int[1];
                IntPtr overflow = new IntPtr();
                lpCounterEventRead.counter = 0;
                lpCounterEventRead.overflow = iopGetAddress(overflow);
                lpCounterEventRead.Count = Marshal.AllocHGlobal(sizeof(int));
                try
                {
                    iopDeviceOpen(0, ref DeviceHandle);
                    Err = iopCounterRead(DeviceHandle, ref lpCounterEventRead);
                    iopDeviceClose(DeviceHandle);
                    Marshal.Copy(lpCounterEventRead.Count, countData, 0, 1);
                    Marshal.DestroyStructure(lpCounterEventRead.Count, lpCounterEventRead.Count.GetType());
                }
                catch { Err = 1; }
                if (Err != 0)
                    bErr = true;
                /*//get digital inputs
                byte[] dinTemp = new byte[1];
                try
                {
                    for (ushort i = 0; i < 1080; i++)
                    {
                        iopDeviceOpen(0, ref DeviceHandle);
                        DiIn.state = Marshal.AllocHGlobal(sizeof(int));
                        DiIn.Port = i;
                        DiIn.bit = 7;
                        Err = iopDIReadBit(DeviceHandle, ref DiIn);
                        iopDeviceClose(DeviceHandle);
                        Marshal.Copy(DiIn.state, dinTemp, 0, 1);
                        Marshal.DestroyStructure(DiIn.state, DiIn.state.GetType());
                       // bDIData[i] = dinTemp[0];
                    }
                }
                catch { Err = 1; }
                if (Err != 0)
                    bErr = true;*/

                try
                {
                    if (bConnected)
                        doUpdateData.Invoke(fChannelData, countData, bDIData);  
                }
                catch { bErr = true; }
                if (!bErr && !bConnected)
                    bCounterReset = true;
                if (bErr)
                {
                    bConnected = false;
                    doStatusChange.Invoke(1);
                }
                else
                {
                    bConnected = true;
                    doStatusChange.Invoke(0);
                }
                Thread.Sleep(iPollRate);
            } while (!bKillThread);
        }
        delegate void delVoidInt(int iStatus);
        delegate void delVoidaFloataFloat(float[] data1, int[] data2, byte[] data3);
        delVoidaFloataFloat doUpdateData;
        delVoidInt doStatusChange;
        private void UpdateData(float[] data1, int[] data2, byte[] data3)
        {
            iCounter = data2[0];
            for (int i = 0; i < data1.Length; i++)
                fAnalogIn[i] = data1[i];
            for (int i = 0; i < data3.Length; i++)
                bDigitalIn[i] = data3[i];

        }
        private void UpdateStatus(int iStatus)
        {
            if (iStatus != 0)
            {
                uint Err = 0;
                if (DeviceHandle != IntPtr.Zero)
                    try
                    {
                        Err = iopDeviceClose(DeviceHandle);
                    }
                    catch { }
                GC.Collect();
                DeviceHandle = IntPtr.Zero;
                //DiIn = new PT_DioReadBit();
                //lpAIConfig = new PT_AIConfig();
                //AiVolIn = new PT_AIVoltageIn();
                //lpCounterEventStart = new PT_CounterEventStart();
                //lpCounterEventRead = new PT_CounterEventRead();
                UnloadDLL();
                LoadDLL();
            }
            lStatus = iStatus;
        }
        public long Status()
        {
            return lStatus;
        }
        public double GetAIChannel(int iChan)
        {
            return (double)fAnalogIn[iChan];
        }
        public long GetCounter()
        {
            return iCounter;
        }
        public void ResetCounter()
        {
            bCounterReset = true;
        }
        public void Kill()
        {
            bKillThread = true;
            do
            { }
            while (Updater.IsAlive);

            try
            {
                iopDeviceClose(DeviceHandle);
                UnloadDLL();
            }
            catch { }
        }

    }
}

Edited 4 Years Ago by skatamatic: Removed the COM interop code that you won't care about

Comments
timely and of great assistance

Here's something I wrote a while back to do just this with some data aquisition dll. The reason I had to load/unload it dynamically is because the driver was broken, and would keep the same device handle after the device was unplugged. When you plugged it back in, the handle should have changed but it didnt. Therefore I had to unload then reload the dll from memory at runtime.

It's long, uncommented, and a bit ugly. But it should help you :)
<snip>

Not exactly what I was looking for, but it's definitely put me on the right track, thanks a bunch ^_^

This article has been dead for over six months. Start a new discussion instead.