如何让 Chrome Native Messaging to Listen 应用程序?

How to get Chrome Native Messaging to Listen to application?

所以我有一个 Chrome 使用 C# 的本机消息传递的工作示例,它可以很好地发送到应用程序然后得到响应。不过,我真正需要做的是能够调用我的本机应用程序并将信息发送到 chrome 扩展(以加载网站 url)。

问题是,当我用参数调用我的 exe(控制台应用程序)时,chrome 没有在监听。当我先 chrome 监听时,它会启动我的应用程序,我不能再先向它发送命令,如果我再次启动它 chrome 没有连接到那个应用程序,所以什么也没有发生。

理想情况下,我想这样调用我的应用程序:

nativeapplication.exe viewAccount ABCDEFG

并让我的扩展正在监听 运行 它的 viewAccount 方法,参数为 ABCDEFG

我从 chrome -> application -> chrome 开始工作还不错,但我想去 other application (or command line with arguments) -> application -> chrome

唯一的方法是让我的应用程序充当 wcf 服务或类似服务,让另一个应用程序向它发送数据,然后将消息发送到 chrome?我想避免让我的应用程序在 "idle" 时坐下来从文件中读取或以其他方式使用资源。使用 WCF 的单实例是最佳选择还是我遗漏了一些简单的东西?

注意:使用 C# 以外的其他语言的示例都很好,只要它是调用扩展的本机应用程序,而不是相反。

您没有遗漏任何简单的东西:Native Messaging 通过启动新进程工作,它不能附加到现有进程。

但是,您可以启动一个实例并使用 connectNative 而不是 sendNativeMessage 来进行单次交换。然后,该实例将不得不监听一些外部事件的发生。

在很高的层次上,这确实可以achieved by a single-instance application。不过,我对 C# 没有更具体的建议。

好吧,我最终使用了单个实例应用程序,并使用了我在某个地方找到的 SingleInstance 代码(抱歉浏览了这么多网站寻找最简单的)

最终使用了这个主要的 class

/// <summary>
/// Holds a list of arguments given to an application at startup.
/// </summary>
public class ArgumentsReceivedEventArgs : EventArgs
{
    public string[] Args { get; set; }
}

public class SingleInstance : IDisposable
{
    private Mutex _mutex;
    private readonly bool _ownsMutex;
    private Guid _identifier;

    /// <summary>
    /// Enforces single instance for an application.
    /// </summary>
    /// <param name="identifier">An _identifier unique to this application.</param>
    public SingleInstance(Guid identifier)
    {
        this._identifier = identifier;
        _mutex = new Mutex(true, identifier.ToString(), out _ownsMutex);
    }

    /// <summary>
    /// Indicates whether this is the first instance of this application.
    /// </summary>
    public bool IsFirstInstance
    { get { return _ownsMutex; } }

    /// <summary>
    /// Passes the given arguments to the first running instance of the application.
    /// </summary>
    /// <param name="arguments">The arguments to pass.</param>
    /// <returns>Return true if the operation succeded, false otherwise.</returns>
    public bool PassArgumentsToFirstInstance(string[] arguments)
    {
        if (IsFirstInstance)
            throw new InvalidOperationException("This is the first instance.");

        try
        {
            using (var client = new NamedPipeClientStream(_identifier.ToString()))
            using (var writer = new StreamWriter(client))
            {
                client.Connect(200);

                foreach (var argument in arguments)
                    writer.WriteLine(argument);
            }
            return true;
        }
        catch (TimeoutException)
        { } //Couldn't connect to server
        catch (IOException)
        { } //Pipe was broken

        return false;
    }

    /// <summary>
    /// Listens for arguments being passed from successive instances of the applicaiton.
    /// </summary>
    public void ListenForArgumentsFromSuccessiveInstances()
    {
        if (!IsFirstInstance)
            throw new InvalidOperationException("This is not the first instance.");
        ThreadPool.QueueUserWorkItem(ListenForArguments);
    }

    /// <summary>
    /// Listens for arguments on a named pipe.
    /// </summary>
    /// <param name="state">State object required by WaitCallback delegate.</param>
    private void ListenForArguments(object state)
    {
        try
        {
            using (var server = new NamedPipeServerStream(_identifier.ToString()))
            using (var reader = new StreamReader(server))
            {
                server.WaitForConnection();

                var arguments = new List<string>();
                while (server.IsConnected)
                    arguments.Add(reader.ReadLine());

                ThreadPool.QueueUserWorkItem(CallOnArgumentsReceived, arguments.ToArray());
            }
        }
        catch (IOException)
        { } //Pipe was broken
        finally
        {
            ListenForArguments(null);
        }
    }

    /// <summary>
    /// Calls the OnArgumentsReceived method casting the state Object to String[].
    /// </summary>
    /// <param name="state">The arguments to pass.</param>
    private void CallOnArgumentsReceived(object state)
    {
        OnArgumentsReceived((string[])state);
    }
    /// <summary>
    /// Event raised when arguments are received from successive instances.
    /// </summary>
    public event EventHandler<ArgumentsReceivedEventArgs> ArgumentsReceived;
    /// <summary>
    /// Fires the ArgumentsReceived event.
    /// </summary>
    /// <param name="arguments">The arguments to pass with the ArgumentsReceivedEventArgs.</param>
    private void OnArgumentsReceived(string[] arguments)
    {
        if (ArgumentsReceived != null)
            ArgumentsReceived(this, new ArgumentsReceivedEventArgs() { Args = arguments });
    }

    #region IDisposable
    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (_mutex != null && _ownsMutex)
            {
                _mutex.ReleaseMutex();
                _mutex = null;
            }
            disposed = true;
        }
    }

    ~SingleInstance()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    #endregion
}

我的 C# 应用程序主要来自 ():

class Program
{
    const string MutexId = "ENTER YOUR GUID HERE, OR READ FROM APP";

    public static void Main(string[] args)
    {
        using (var instance = new SingleInstance(new Guid(MutexId)))
        {
            if (instance.IsFirstInstance)
            {
                instance.ArgumentsReceived += Instance_ArgumentsReceived;
                instance.ListenForArgumentsFromSuccessiveInstances();

                DoMain(args);
            }
            else
            {
                instance.PassArgumentsToFirstInstance(args);
            }
        }
    }

    private static void Instance_ArgumentsReceived(object sender, ArgumentsReceivedEventArgs e)
    {
        TryProcessAccount(e.Args);
    }

    // This is the main part of the program I use, so I can call my exe with program.exe 123 42424 to have it open that specific account in chrome. Replace with whatever code you want to happen when you have multiple instances.

    private static void TryProcessAccount(string[] args)
    {
        if (args == null || args.Length < 2 || args[0] == null || args[1] == null || args[0].Length != 3) { return; }
        var openAccountString = GetOpenAccountString(args[0], args[1]);
        Write(openAccountString);
    }

    private static void DoMain(string[] args)
    {
        TryProcessAccount(args);

        JObject data = Read();
        while ((data = Read()) != null)
        {
            if (data != null)
            {
                var processed = ProcessMessage(data);
                Write(processed);
                if (processed == "exit")
                {
                    return;
                }
            }
        }

    }

    public static string GetOpenAccountString(string id, string secondary)
    {
        return JsonConvert.SerializeObject(new
        {
            action = "open",
            id = id,
            secondary = secondary
        });
    }

    public static string ProcessMessage(JObject data)
    {
        var message = data["message"].Value<string>();
        switch (message)
        {
            case "test":
                return "testing!";
            case "exit":
                return "exit";
            case "open":
                return GetOpenAccountString("123", "423232");
            default:
                return message;
        }
    }

    public static JObject Read()
    {
        var stdin = Console.OpenStandardInput();
        var length = 0;

        var lengthBytes = new byte[4];
        stdin.Read(lengthBytes, 0, 4);
        length = BitConverter.ToInt32(lengthBytes, 0);

        var buffer = new char[length];
        using (var reader = new StreamReader(stdin))
        {
            while (reader.Peek() >= 0)
            {
                reader.Read(buffer, 0, buffer.Length);
            }
        }

        return (JObject)JsonConvert.DeserializeObject<JObject>(new string(buffer))["data"];
    }

    public static void Write(JToken data)
    {
        var json = new JObject
        {
            ["data"] = data
        };

        var bytes = System.Text.Encoding.UTF8.GetBytes(json.ToString(Formatting.None));

        var stdout = Console.OpenStandardOutput();
        stdout.WriteByte((byte)((bytes.Length >> 0) & 0xFF));
        stdout.WriteByte((byte)((bytes.Length >> 8) & 0xFF));
        stdout.WriteByte((byte)((bytes.Length >> 16) & 0xFF));
        stdout.WriteByte((byte)((bytes.Length >> 24) & 0xFF));
        stdout.Write(bytes, 0, bytes.Length);
        stdout.Flush();
    }
}