【问题标题】:How to get Chrome Native Messaging to Listen to application?如何让 Chrome Native Messaging 监听应用程序?
【发布时间】:2016-03-20 15:38:53
【问题描述】:

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

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

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

nativeapplication.exe viewAccount ABCDEFG

并让我正在监听的扩展程序使用 ABCDEFG 的参数运行其 viewAccount 方法。

我在chrome -> application -> chrome 工作得很好,但我想去other application (or command line with arguments) -> application -> chrome

是否只有这样才能使我的应用程序充当 wcf 服务或类似服务,并让另一个应用程序向其发送数据,然后将消息发送到 chrome?我想避免让我的应用程序坐下来从文件中读取或以其他方式在“空闲”时使用资源。使用 WCF 的单实例是最佳选择还是我缺少一些简单的东西?

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

【问题讨论】:

    标签: c# google-chrome-extension chrome-native-messaging


    【解决方案1】:

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

    最终使用了这个主类

    /// <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# 应用程序主要来自 (C# native host with Chrome Native Messaging):

    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();
        }
    }
    

    【讨论】:

      【解决方案2】:

      您不会错过任何简单的东西:本机消息传递通过启动一个新进程来工作,它不能附加到现有进程。

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

      在非常高的级别上,这确实可以是achieved by a single-instance application。不过,我对 C# 没有更具体的建议。

      【讨论】:

      • 好的,谢谢,我昨晚确实以单实例应用程序的形式完成了它,并在下面发布了我的代码。只是想确保我没有做完全错误的事情或错过了什么:)
      猜你喜欢
      • 1970-01-01
      • 2022-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-29
      • 2014-03-23
      • 1970-01-01
      • 2013-01-26
      相关资源
      最近更新 更多