Click here to Skip to main content
15,891,033 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
<lang>Environment:
------------
Microsoft Visual Studio 2010
.NET 4, .NET 2
external libraries (.dll's) based on .NET 4

.NET 4 DLL "communication.dll":
-------------------------------
I have implemented a .NET 4 library which wraps technical information how to carry out and control communication between PC and individual microprocessor based devices. Different communication protocols are supported. My library uses external libraries to supoort these protocols. The external libraries are based on .NET 4.
Amongst others files will be transmitted from PC to a connected device and viceversa. Operating the file transfer the "communication.dll" owns the information on the file size, the bytes actually transmitted and the file name. These information should be signalled to the application which uses the "communication.dll" by a callback function.

This "communication.dll" will be used by different engineering programs (applications). It is already sucessfully tested with applications based on .NET 4 as well as a C++ project based on Visual Studio 6.0. Communication functions works correctly as well as the callback function to support a progress bar.

.NET 2 Application:
-------------------
Additionally I need to provide a interface to an application based on .NET 2. (There are some reasons why this application cannot be converted to .NET 4 at time.)
The integration of the .NET 4 DLL in the .NET 2 application is done according to the article "Using a .NET 4 Based DLL From a .NET 2 Based Application" from Arik Poznanski:
http://code.msdn.microsoft.com/Using-a-NET-4-Based-DLL-bb141db3
and
http://www.codeproject.com/Articles/204406/How-To-Use-a-NET-4-Based-DLL-From-NET-2-Based-Appl
Very much thanks to Arik for this article!

I.e. the .NET 2 Application inherits the "communication.dll" and calls communication functions. This is working sucessfully.
The interface between the .NET 2 application and the .NET 4 DLL is done via the declaration "public interface IMyClassAdapter".

The outstanding problem is how to route a callback function from the .NET 2 application through the interface into the .NET 4 DLL in order to implement in the .NET 2 application a progress bar using the actual data provided by the .NET 4 DLL.

I checked an tested several implementations which compiles sucessfully. But on runtime I got exceptions like "generic types cannot be marshaled" and System.InvalidCastException etc.
I tried to use delegates, generic delegates, common modules, objects - but I got runtime exceptions as mentioned above each time.

The question is:
How to pass the function pointer of the .NET 2 method
public static int MyCallbackFunction(int iFileSize, int iNrOfBytesTransmitted,
                                     int iStop, int iActualActionID, string strFileName)
{
   System.Console.WriteLine("<" + imaxCount.ToString() + "> <" +
                            iNrOfBytesTransmitted.ToString() + "> <" +
                            iStop.ToString() + "> <" +
                            getActualAction(iActualActionID) + "> <" +
                            strFileName + ">");
   return 0;
}

through the interface declaration into the .NET 4 DLL in order to realize there the callback?
The problem seems to be how to display a type declaration from one side of the interface to the other side. Whereas the interface only supports instances.

I used the sources of Arik Poznanski as test base and added the attempt to implement the callback function using a generic delegate declaration. So you can see what I aspect to work. I attached the code snippets below. On runtime the attempt to assign the passed function to pointer to the local .NET 4 delegate instance results in the exception "...generic types cannot be marshaled...".



Net4ToNet2Adapter - IMyClassAdapter.cs:
using System;
using System.Runtime.InteropServices;

namespace Net4ToNet2Adapter
{
#if (__dotNet2__) 
   // meaning of parameters:
   // T1: int iFileSize, 
   // T2: int iNrOfBytesTransmitted,
   // T3: int iStop: 0=transmission in progress, 1=transmission stopped
   // T4: int iActualActionID, 
   // T5: string strFileName
   // TResult: 0=continue, 1=cancel transmission
   public delegate TResult Func<in T1, in T2, in T3, in T4, in T5, out TResult>
   ( T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5 );
#endif

   [ComVisible(true)]
    [Guid("E36BBF07-591E-4959-97AE-D439CBA392FB")]
    public interface IMyClassAdapter
    {
        void DoNet4Action();
        void setCallbackFunc(Func<int, int, int, int, string, int> ProgressFunc);
    }
}


Net4ToNet2Adapter - MyClassAdapter.cs
using System;
using System.Runtime.InteropServices;
using Net4Assembly;

namespace Net4ToNet2Adapter
{
    [ComVisible(true)]
    [Guid("A6574755-925A-4E41-A01B-B6A0EEF72DF0")]
    public class MyClassAdapter : IMyClassAdapter
    {
        private MyClass _myClass = new MyClass();

        public void DoNet4Action()
        {
            _myClass.DoNet4Action();
        }

        public void setCallbackFunc(Func<int, int, int, int, string, int> ProgressFunc)
        {

           _myClass.setCallbackFunc(ProgressFunc);
        }

    }
}


Net4Assembly - MyClass.cs
using System;

namespace Net4Assembly
{
    public class MyClass
    {
        public void DoNet4Action()
        {
           Console.WriteLine("CLR version from DLL: {0}", Environment.Version);
           int iCancel = 0;
           if (m_GFPReportProgressFunc != null)
           {
              iCancel = m_GFPReportProgressFunc(100, 33, 0, 1, "FileName.dat");
           }
        }

        private static Func<int, int, int, int, string, int> m_GFPReportProgressFunc = null;
        public void setCallbackFunc(Func<int, int, int, int, string, int> ProgressFunc)
        {
           m_GFPReportProgressFunc = ProgressFunc;
        }

    }
}


Net2Assembly - Program.cs - The .NET 2 Apllication
using System;
using Net4ToNet2Adapter;

namespace Net2Assembly
{
    class Program
    {
        static unsafe void Main(string[] args)
        {
            Console.WriteLine("CLR version from EXE: {0}", Environment.Version);
            
            Type myClassAdapterType = Type.GetTypeFromProgID("Net4ToNet2Adapter.MyClassAdapter");
            object myClassAdapterInstance = Activator.CreateInstance(myClassAdapterType);
            IMyClassAdapter myClassAdapter = (IMyClassAdapter)myClassAdapterInstance;

            try
            {
               ptrLocalProgressFunc = MyCallbackFunction;
               myClassAdapter.setCallbackFunc(ptrLocalProgressFunc);
               myClassAdapter.DoNet4Action();
            }
            catch (Exception ex)
            {
               Console.WriteLine(ex.ToString());
            }
        }

        public static int MyCallbackFunction(
           int iFileSize, int iNrOfBytesTransmitted,
           int iStop, int iActualActionID, string strFileName)
        {
           System.Console.WriteLine("<" + iFileSize.ToString() + "> <" +
                                          iNrOfBytesTransmitted.ToString() + "> <" +
                                          iStop.ToString() + "> <" +
                                          getActualAction(iActualActionID) + "> <" +
                                          strFileName + ">");
           return 0;
        }

        static private string getActualAction(int iActualActionID)
        {
           string strActualAction = "some action";
           // convert Action ID into informative string ...
           return strActualAction;
        }
        static private Func<int, int, int, int, string, int> ptrLocalProgressFunc;

    }
}
Posted

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900