c# - 如何让 Chrome Native Messaging 收听应用程序?

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

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

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

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

nativeapplication.exe viewAccount ABCDEFG

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

我在 chrome -> application -> chrome 中运行良好,但我想转到 其他应用程序(或带参数的命令行)-> application -> chrome.

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

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

最佳答案

好吧,我最终使用了一个单实例应用程序,并使用了我在某个地方找到的 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();
    }
}

关于c# - 如何让 Chrome Native Messaging 收听应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34279201/

相关文章:

javascript - 如何检测调试扩展选项卡?

html - 我的 chrome 扩展中的弹出窗口很小,不显示数据

javascript - 如何将按键上的事件监听器添加到 Chrome 的地址栏 - Chrome 扩展

javascript - Google Chrome 中选项卡之间最快的通信是什么?

java - 为什么我需要在批处理文件中提及绝对地址?

c# - 从 Windows 8 上的 Windows 服务打印失败

c# - 连接过程后 Linq 更新不同的表

google-chrome-extension - 将 Google Chrome 扩展程序限制为选择平台

c# - 如何从 Asp Net Core WebApi 将日志存储到 Azure 表存储?

c# - Java Annotations 和 C# Attributes 有什么异同?