Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

'Assembly with type "....ActionService" is not loaded' #7

Closed
miki-bgd opened this issue Feb 12, 2022 · 8 comments
Closed

'Assembly with type "....ActionService" is not loaded' #7

miki-bgd opened this issue Feb 12, 2022 · 8 comments

Comments

@miki-bgd
Copy link

Hello, i have tried to create sample solution, but it crashes on this line...
var service = await factory.CreateInstanceAsync<ActionService, IActionService>();

image

internal class Client
{
	private static bool running;

	static void Main(string[] args)
	{
		Run();
		running = true;
		int counter = 0;
		while (running)
		{
			Thread.Sleep(1000);
			Console.WriteLine("Client ping " + counter++);
		}
	}

	static async void Run()
	{
		await using var factory = new PipeProxyFactory();

		await factory.InitializeAsync(Constants.PipeName);

		var service = await factory.CreateInstanceAsync<ActionService, IActionService>();

		while (running)
		{
			await Task.Delay(1000);
			service.MessageReceived += (_, e) => Console.WriteLine($"Client: message received on server" + e);

			service.SendMessage("Hello from Client");

			string processName = service.GetProcessId();

			Console.WriteLine("Client: Server process name: " + processName);
		}
	}
}
using System;
using H.ProxyFactory;
using RemoteNamedPipe.Shared;

namespace RemoteNamedPipe.Server 
{
	internal class Server
	{
		private static bool running;
		private static PipeProxyServer server;
		static void Main(string[] args)
		{
			Run();
			running = true;
			int counter = 0;
			while (running)
			{
				Thread.Sleep(1000);
				Console.WriteLine("Server ping " + counter++);
			}
		}

		static async void Run()
		{
			server = new PipeProxyServer();

			await server.InitializeAsync(Constants.PipeName);
		}
	}
}
public interface IActionService
{
	event EventHandler<string> MessageReceived;

	void SendMessage(string message);

	void CloseProcess();

	string GetProcessId();
}
public class ActionService : IActionService
{
	public event EventHandler<string> MessageReceived;

	public void CloseProcess()
	{

	}

	public string GetProcessId()
	{
		return Process.GetCurrentProcess().ProcessName;
	}

	public void SendMessage(string message)
	{
		Console.WriteLine("Server: Message received " + message);
		MessageReceived?.Invoke(this, message);
	}
}

And here is Project structure. Client and Server reference Shared project, and both are started as independent processes.
image

Am i missing something?
Thanks

@miki-bgd miki-bgd changed the title 'Assembly with type "RemoteNamedPipe.Shared.ActionService" is not loaded' 'Assembly with type "....ActionService" is not loaded' Feb 12, 2022
@miki-bgd
Copy link
Author

PS. Starting Client and Server inside single process is working.
Could it be a problem with some permissions or something...?

@HavenDV
Copy link
Owner

HavenDV commented Feb 13, 2022

Hello.

You need to load assembly:

await factory.LoadAssemblyAsync(typeof(ActionService).Assembly.Location).ConfigureAwait(false);

There is example app:
https://github.com/HavenDV/H.ProxyFactory/blob/master/src/apps/H.ProxyFactory.Apps.Wpf/ClientWindow.xaml.cs#L55

But I do not recommend using this library, I see the development of IPC in explicit code generation instead of IL code generation like here.

@miki-bgd
Copy link
Author

Thank you for response.

I ended up using your wrapper for Named pipes (PipeServer and PipeClient) 👍

@HavenDV
Copy link
Owner

HavenDV commented Feb 15, 2022

Yes, this is the best option for now.

@HavenDV
Copy link
Owner

HavenDV commented Mar 16, 2022

I'm actively developing a SourceGenerator that generates client/server code based on a C# interface. I would be glad if you take a look at it (if you are still interested) and provide any feedback - https://github.com/HavenDV/H.Ipc

@miki-bgd
Copy link
Author

I have built solution around your Pipes wrapper, and everything seems to be working good. It is kind of private project, paused for now, and not sure what will happen...

However, since things are good in this project, it is unlikely that i will change communication method in the future.
Anyway, for any future projects i will keep your library in mind.

Best regards

@HavenDV
Copy link
Owner

HavenDV commented Mar 18, 2022

I want to clarify that this is just a SourceGenerator that removes the boilerplate code. You will still continue to use H.Pipes. Here is an example of what is generated based on this code:

namespace H.Ipc.Apps.Wpf;

public interface IActionService
{
    void ShowTrayIcon();
    void HideTrayIcon();
    void SendText(string text);
}

[H.IpcGenerators.IpcClient]
public partial class ActionServiceClient : IActionService
{
}

[H.IpcGenerators.IpcServer]
public partial class ActionService : IActionService
{
    public void ShowTrayIcon()
    {
    }

    public void HideTrayIcon()
    {
    }

    public void SendText(string text)
    {
    }
}

will produce:

//HintName: ActionServiceClient.generated.cs

#nullable enable

namespace H.Ipc.Apps.Wpf
{
    public partial class ActionServiceClient
    {
        #region Properties

        private global::H.Pipes.IPipeConnection<string>? Connection { get; set; }

        #endregion

        #region Events


        public event global::System.EventHandler<global::System.Exception>? ExceptionOccurred;

        private void OnExceptionOccurred(global::System.Exception exception)
        {
            ExceptionOccurred?.Invoke(this, exception);
        }


        #endregion

        public void Initialize(global::H.Pipes.IPipeConnection<string> connection)
        {
            Connection = connection ?? throw new global::System.ArgumentNullException(nameof(connection));
        }

        public async void ShowTrayIcon()
        {
            try
            {
                await WriteAsync(new ShowTrayIconMethod()).ConfigureAwait(false);
            }
            catch (global::System.Exception exception)
            {
                OnExceptionOccurred(exception);
            }
        }

        public async void HideTrayIcon()
        {
            try
            {
                await WriteAsync(new HideTrayIconMethod()).ConfigureAwait(false);
            }
            catch (global::System.Exception exception)
            {
                OnExceptionOccurred(exception);
            }
        }

        public async void SendText(string text)
        {
            try
            {
                await WriteAsync(new SendTextMethod(text)).ConfigureAwait(false);
            }
            catch (global::System.Exception exception)
            {
                OnExceptionOccurred(exception);
            }
        }

        private async global::System.Threading.Tasks.Task WriteAsync<T>(
            T method,
            global::System.Threading.CancellationToken cancellationToken = default)
            where T : global::H.IpcGenerators.RpcRequest
        {
            if (Connection == null)
            {
                throw new global::System.InvalidOperationException("You need to call Initialize() first.");
            }

            var json = global::System.Text.Json.JsonSerializer.Serialize(method);

            await Connection.WriteAsync(json, cancellationToken).ConfigureAwait(false);
        }
    }
}
//HintName: ActionService.generated.cs

#nullable enable

namespace H.Ipc.Apps.Wpf
{
    public partial class ActionService
    {
        #region Events


        public event global::System.EventHandler<global::System.Exception>? ExceptionOccurred;

        private void OnExceptionOccurred(global::System.Exception exception)
        {
            ExceptionOccurred?.Invoke(this, exception);
        }


        #endregion

        public void Initialize(global::H.Pipes.IPipeConnection<string> connection)
        {
            connection = connection ?? throw new global::System.ArgumentNullException(nameof(connection));
            connection.MessageReceived += (_, args) =>
            {
                try
                {
                    var json = args.Message ?? throw new global::System.InvalidOperationException("Message is null.");
                    var request = Deserialize<global::H.IpcGenerators.RpcRequest>(json);

                    if (request.Type == global::H.IpcGenerators.RpcRequestType.RunMethod)
                    {
                        var method = Deserialize<global::H.IpcGenerators.RunMethodRequest>(json);
                        switch (method.Name)
                        {
                            case nameof(ShowTrayIcon):
                                {
                                    var arguments = Deserialize<ShowTrayIconMethod>(json);
                                    ShowTrayIcon();
                                    break;
                                }

                            case nameof(HideTrayIcon):
                                {
                                    var arguments = Deserialize<HideTrayIconMethod>(json);
                                    HideTrayIcon();
                                    break;
                                }

                            case nameof(SendText):
                                {
                                    var arguments = Deserialize<SendTextMethod>(json);
                                    SendText(arguments.Text);
                                    break;
                                }
                        }
                    }
                }
                catch (global::System.Exception exception)
                {
                    OnExceptionOccurred(exception);
                }
            };
        }

        private static T Deserialize<T>(string json)
        {
            return
                global::System.Text.Json.JsonSerializer.Deserialize<T>(json) ??
                throw new global::System.ArgumentException($@"Returned null when trying to deserialize to {typeof(T)}.
    json:
    {json}");
        }
    }
}
//HintName: IActionService_Requests.generated.cs

#nullable enable

namespace H.Ipc.Apps.Wpf
{
    public class ShowTrayIconMethod : global::H.IpcGenerators.RunMethodRequest
    {


        public ShowTrayIconMethod()
        {
            Name = "ShowTrayIcon";

        }
    }

    public class HideTrayIconMethod : global::H.IpcGenerators.RunMethodRequest
    {


        public HideTrayIconMethod()
        {
            Name = "HideTrayIcon";

        }
    }

    public class SendTextMethod : global::H.IpcGenerators.RunMethodRequest
    {
        public string Text { get; set; }

        public SendTextMethod(string text)
        {
            Name = "SendText";
            Text = text ?? throw new global::System.ArgumentNullException(nameof(text));
        }
    }

}

@miki-bgd
Copy link
Author

miki-bgd commented Mar 18, 2022

Aha, i get it now. This looks really promising, will definitely give it a try.

PS. Here is my use-case, maybe it will benefit you.
I have standalone program (with complete UI; lets call it UIClient) which can execute and process some commands. And i created VS extension, to integrate that functionality into VS (essentially, VS just shows some data and can invoke specific commands - with lighbulb, but everything is executed in UIClient).
UIClient can run as standalone, or can be started from VS (in which case pipeName is provided as argument). Only 1 UIClient can be related to single VS - this allows running multiple UIClient instances on same machine, either as standalone or connected to VS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants