1 year ago

#317473

test-img

Jeff H

How do I instantiate the COM interop Interface from a .NET Framework COM server DLL?

We have two versions of a client-server software application product. One version is what is now considered "legacy", as it is implemented in WinForms .NET Framework, while the other is now implemented in WPF .NET 5.

Since 3rd party applications won't always know whether the "legacy" or "new" client-server application is running remotely, I have developed a Client Loader COM Interop class in the .NET 5 client-server client assembly that a 3rd party client application can use to get the interface of the client-server client via COM Interop.

In addition, I need to support client-server client applications that are implemented n both .NET Framework and .NET Core.

After a LOT of searching and a bit of help from another StackOverflow user, I was able to successfully implement the .NET 5 COM Interop server. The information for that task can be found here: How to create .NET 5 COM Interop server.

But although I've been successful at getting this to work for a .NET 5 COM Interop server and accessing the COM class interface from a .NET Framework assembly, I for the life of me cannot get it to work for the .NET Framework COM Interop assembly.

I have implemented the test solutions that can be found here: COM Interop Test with the following folders:

  1. TestDotNet5ComServer - the .NET 5 COM Interop server solution
  2. TestDotNetFrameworkComServer - the .NET Framework COM Interop solution
  3. ClientLoaderTestApp - a .NET Framework client loader solution
  4. Com Interop Servers - Both versions of the COM Interop Server assemblies

Here is the code for the Client Loader class:

public class WinCalRemoteClientLoader
{
    #region Properties
    private List<Guid> ClassIdList = new List<Guid>();

    #region WinCal Remoting Client COM Server CLSIDs
    /// <summary>
    /// .NET Framework COM Server CLSID
    /// </summary>
    private Guid LegacyWinCalClientClsId = new Guid("B4C1FE68-5D20-4557-9637-AF81F7F9831E");

    /// <summary>
    /// .NET 5 COM Server CLSID
    /// </summary>
    private Guid WinCal5ClientClsId = new Guid("BB2B4532-4EE4-4536-8EC5-17DF5377A13D");
    #endregion WinCal Remoting Client COM Server CLSIDs

    /// <summary>
    /// The singleton instantiated Remoting Client interface
    /// </summary>
    public object RemotingClassObject
    {
        get { return _RemotingClassObject; }

        private set { _RemotingClassObject = value; }
    }
    private object _RemotingClassObject = null;

    /// <summary>
    /// The remoting client interface
    /// </summary>
    public IWinCalClient RemotingInterface
    {
        get { return _RemotingInterface; }

        private set { _RemotingInterface = value; }
    }
    private IWinCalClient _RemotingInterface = null;

    /// <summary>
    /// Whether or not the client should launch an event window
    /// </summary>
    public bool UseClientEventWindow
    {
        get { return _UseClientEventWindow; }

        set
        {
            _UseClientEventWindow = value;
        }
    }
    private bool _UseClientEventWindow = false;

    /// <summary>
    /// The server IP name
    /// </summary>
    public string WinCalServerName
    {
        get { return _WinCalServerName; }

        set
        {
            _WinCalServerName = value;
        }
    }
    private string _WinCalServerName = null;

    /// <summary>
    /// The WinCal server port number
    /// </summary>
    public int WinCalServerPort
    {
        get { return _WinCalServerPort; }

        set
        {
            _WinCalServerPort = value;
        }
    }
    private int _WinCalServerPort = 0;
    #endregion Properties

    #region Public Methods
    /// <summary>
    /// The Constructor
    /// </summary>
    public WinCalRemoteClientLoader()
    {
        ClassIdList.Add(LegacyWinCalClientClsId);
        ClassIdList.Add(WinCal5ClientClsId);
    }

    /// <summary>
    /// Load a compatible WinCal remoting client
    /// </summary>
    /// <param name="useClientEventWindow">Whether or not the client should launch an event window</param>
    /// <param name="serverName">The server IP name</param>
    /// <param name="serverPort">The WinCal server port number</param>
    /// <param name="connectToServer">If true, this method will return an interface that is already connected to the server</param>
    /// <returns>Either null or a connected IWinCalClient instance</returns>
    public IWinCalClient LoadCompatibleRemotingClient(bool useClientEventWindow, string serverName, int serverPort, bool connectToServer = true)
    {
        UseClientEventWindow = useClientEventWindow;
        WinCalServerName = serverName;
        WinCalServerPort = serverPort;

        RemotingInterface = TryLoadComServer(connectToServer);

        return RemotingInterface;
    }

    /// <summary>
    /// Release the COM object
    /// 
    /// If the WinCal Remoting client is still connected, the connection will be closed
    /// </summary>
    public void UnloadClient()
    {
        // Clean up the COM server, if it was created
        if (RemotingClassObject != null)
        {
            if (RemotingInterface != null && RemotingInterface.WinCalServerFound)
            {
                RemotingInterface.WinCalCloseConnection();
            }

            if (Marshal.IsComObject(RemotingClassObject))
            {
                Marshal.FinalReleaseComObject(RemotingClassObject);
            }

            RemotingClassObject = null;
        }

        // Perform garbage collection
        GC.Collect(); // collects all unused memory
        GC.WaitForPendingFinalizers(); // wait until GC has finished its work
        GC.Collect();
    }
    #endregion Public Methods

    #region Private Methods
    /// <summary>
    /// Attempt the COM Server option
    /// </summary>
    /// <param name="connectToServer"></param>
    /// <returns></returns>
    private IWinCalClient TryLoadComServer(bool connectToServer)
    {
        if (RemotingClassObject != null)
        {
            UnloadClient();
        }

        foreach (Guid clsId in ClassIdList)
        {
            try
            {
                RemotingClassObject = Activator.CreateInstance(Type.GetTypeFromCLSID(clsId, true));
            }
            catch (Exception e)
            {
                continue;
            }

            if (RemotingClassObject != null)
            {
                RemotingInterface = (IWinCalClient)RemotingClassObject;

                if (RemotingInterface == null)
                {
                    UnloadClient();
                    continue;
                }

                if (CanClientConnect(_RemotingInterface, connectToServer))
                {
                    break;
                }
            }

            if (Marshal.IsComObject(RemotingClassObject))
            {
                Marshal.FinalReleaseComObject(RemotingClassObject);
            }

            RemotingClassObject = null;
        }

        return RemotingInterface;
    }

    /// <summary>
    /// Determine whether the client and server versions are compatible
    /// </summary>
    /// <param name="client"></param>
    /// <param name="connectToServer">If true, the interface will not close the server connection after connecting to verify compatibility</param>
    /// <returns></returns>
    private bool CanClientConnect(IWinCalClient client, bool connectToServer)
    {
        bool serverConnected = client.WinCalOpenServer(WinCalServerName, WinCalServerPort) && client.WinCalServerFound;

        // If it isn't compatible and the client connected to the server, close the connection
        if (serverConnected && !connectToServer)
        {
            client.WinCalCloseConnection();
        }

        return serverConnected;
    }
    #endregion Private Methods
}

The line, "RemotingInterface = (IWinCalClient)RemotingClassObject;" fails for the .NET Framework case.

c#

asp.net-core

.net-5

com-interop

.net-framework-version

0 Answers

Your Answer

Accepted video resources