Remoting in .NET with Unity Standalone

Hello folks,

I have been trying to implement a simple remoting project with Unity and encountered a bug on the exported Standalone build only, whereas the program runs fine in the Unity Editor.

The architecture of the project is :

  • An interface DLL exposes methods to a client program (Unity).

  • A server programs is running and has a class that implements the interface’s methods. (C# console application)

  • The client calls methods on the server via the interface but the processing is therefore done by the server (this is crucial) which can then return values to the client.

1) The first step is to build the interface DLL (Visual Studio is used to to that). With UnityVS, I made sure the target framework was “Unity 3.5 .net framework Full Base Class Libraries”.

using System;

namespace RemotingInterfaces
{
    public interface IRemoteOperation
    {
        string TestRemoting(string txt);
    }
}

2) Second step is to build and run the server program in a separate Visual Studio project. Make sure the exported DLL (RemotingInterface.dll) from step 1 is copied in this project’s Assemblies folder and included in the project. Again, use “Unity 3.5 .net framework Full Base Class Libraries” as target framework.

2.a) Here is the class that implements the interface :

using System;

namespace RemotingServer
{
    public class RemoteOperation : MarshalByRefObject, RemotingInterfaces.IRemoteOperation
    {
        public override object InitializeLifetimeService()
        {
            return null;
        }

        public string TestRemoting(string txt)
        {
            Console.WriteLine("TestRemoting: " + txt + " - "+System.DateTime.Now.ToLongTimeString());

            return txt + " Done by Remoting Server!";
        }
    }
}

2.b) Here is the server main loop code :

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using RemotingInterfaces;

namespace RemotingServer
{
    class RemotingMain
    {
        [STAThread]
        static void Main(string[] args)
        {
            try
            {
                // Create a new listening channel on port 1079
                TcpServerChannel channel = new TcpServerChannel(1079);

                // Register channel
                ChannelServices.RegisterChannel(channel, false);
                // Start listening by exposing the singleton object
                RemotingConfiguration.RegisterWellKnownServiceType(
                    typeof(RemoteOperation),
                    "RemoteOperation",
                    WellKnownObjectMode.Singleton);


                Console.WriteLine("Server ("+channel+") successfully started.\n Press Enter to unregister the channel.\n");
                Console.ReadLine();

                // This unregisters the channel, but leaves the application
                // domain running.
                ChannelServices.UnregisterChannel(channel);
                Console.WriteLine("Unregistered the channel. Client now receives a WebException. press Enter to exit.");
                Console.ReadLine();
            }
            catch
            {
                Console.WriteLine("Error while starting server");
                Console.ReadLine();
            }
        }
    }
}

When the server has been built, run its console app. The console should display the following :

3) The third step is to build a simple Unity script to test against the server. The exported DLL (RemotingInterfaces.dll) must be copied in the Assets/Plugins folder of the Unity project.
I also included Microsoft’s “System.Runtime.Remoting.dll” (version .NET 2.0x, usually found under C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework\v3.5\Profile\Unity Full v3.5\System.Runtime.Remoting.dll) into that folder, in order to access the System.Runtime.Remoting.Channels.Tcp libraries.

Basically, the script connects to the server via a Tcp Channel and accesses the exposed object via the URL of its class.

using UnityEngine;
using System.Collections;
using System;

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

public class RemotingClient : MonoBehaviour
{
    private RemotingInterfaces.IRemoteOperation remoteOperation;
    string text;

    void Awake()
    {
        if (Application.isEditor)
            gameObject.name += " Unity Editor";
        else
            gameObject.name += " Unity Standalone";
    }

    void Start()
    {
        // 1) configure server connexion
        try
        {
            TcpClientChannel channel = new TcpClientChannel();

            remoteOperation = (RemotingInterfaces.IRemoteOperation)Activator.GetObject(
                typeof(RemotingInterfaces.IRemoteOperation),
                "tcp://localhost:1079/RemoteOperation");

            Debug.Log("Connection Server OK");
            text += "Connection Server OK\n";
        }
        catch
        {
            Debug.LogError("Error Connection Server");
            text += "Error Connection Server\n";
        }

        // 2) check and test the remote op object
        try
        {
            Debug.Log("remoteOperation type = " + remoteOperation);
            text += "remoteOperation type = " + remoteOperation + "\n";
            if (remoteOperation != null)
            {
                string res = remoteOperation.TestRemoting(gameObject.name);
                Debug.Log(res);
                text += res + "\n";
            }
        }
        catch(Exception e)
        {
            Console.WriteLine(e.Message + "\n" + e.StackTrace + "\n" + e.Source);
            Debug.LogError("Remote Operation object Error ! ");
            text += "Remote Operation object Error ! \n";
        }
    }
 
    void OnGUI()
    {
        GUILayout.Label(text);
    }
}

Attach this script to any game object in an empty unity scene as usual and press Play in the editor.
The game window and console should display :

The server also prints good log :

Now that everything seems fine, I export the Unity game to a Standalone by making sure in the Player Settings that the “API Compatibility Level” is set to “.NET 2.0”.
When launching the standalone, an error is thrown, and the output log reads :

Meanwhile the server program did not print or receive anything from the Unity standalone game.

After spending quite a time searching the web for a solution, I often saw that adjusting the .NET target framework version solved this error. But I tried all possible combinations of .NET target framework between the DLL, the server program and the client program, this did not affect the output. Though normal target framework should be “Unity 3.5 .net framework Full Base Class Libraries” (brought into Visual Studio by UnityVS).

I have a feeling that the problem comes from a difference between Unity Editor and Standalone Build I am not aware of.
Do someone have a hint about this ?

  • Sorry for the long post ! :hushed:

EDIT : Added an attachment with the Visual Studio project folders for both the interface dll and the server, as well as the Unity project assets (and DLL to be copied).
Extra link to the UnityVS plugin used is here : http://unityvs.com/

2212101–147112–Remoting net with Unity.zip (93.7 KB)

No time for testing right now but it must have to do with the mono / .net framework which is referenced.
So your Api Compatibility Level is “.Net 2.0” and not “.Net 2.0 Subset”? and the stripping level is disabled?

Maybe you also want to try x86 vs. 64 bit dlls…

Yes Api is not defined on “subset” ; and yes the stripping is disabled but this parameter is only in mobile build settings (I am building a simple standalone).
And changing platform settings to x86 or x64 on DLLs or Unity build unfortunately did not affect the output.

Bump, the problem still lives, as additional info I’ll let know that I tried to execute the project on both Unity 5 and 4.x ;
Results are the same : program works in the Editor but breaks once exported as Windows Standalone.

I also submitted a bug report a week ago (Case 716450), but still got no feedback.
Thanks in advance.

1 Like