0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Chrome拡張機能を作ってみよう②

Last updated at Posted at 2021-01-22

前回はChrome拡張機能からファイルのダウンロードまでを投稿しました。
今回はその続きです。

Chrome拡張機能とローカルプログラムの連携

Chrome拡張機能とローカルプログラムとの連携について
今回はローカルプログラムをvisual studioで作ったC#のプログラムとします。

##下準備
まずは連携するための下準備をします。

最初に連携するプログラムの配置場所を決めます。
今回は
c:\extensionSample\program
の配下に置くことにします。

連携用JSONファイルを準備
場所に決まりはないですが連携用のjsonファイルをprogramフォルダの配下に配置する

program.josn
{
  "name": "put.message",
  "description": "特定のURLでメッセージを表示する。",
  "path": "C:\\extensionSample\\program\\program.exe",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://gfoihpmphbkdehkldlglbbcgkcpdfmam/"
  ]
}

nameは連携に必要です。今回は「put.message」としています。

pathにはプログラムのパスを指定します。

allowed_originsは拡張機能のIDを指定します。
chromeのアドレスバーに「chrome://extensions」と入力して
前回追加した拡張機能のIDを記載します。

レジストリに登録
次に、レジストリにjsonファイルを登録します。
コンピューター\HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts
にjsonファイルのnameに指定した「put.message」キーを作成する。
既定のデータにjsonファイルパスを指定する。
今回は「c:\extensionSample\program\program.josn」とします。
image.png

C#プログラム

では実際にC#のプログラムです。
(結構適当に書いています。ご容赦を。。。)

using Microsoft.JScript;
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;

namespace program
{
    /// <summary>
    /// ローカルプログラム
    /// </summary>
    class localprogram
    {
        private static bool NativeMessageFlg = true;
        private static readonly Encoding enc = Encoding.UTF8;

        /// <summary>
        /// メイン処理
        /// </summary>
        static void Main(string[] args)
        {
#if DEBUG
            // デバッグ時のみ、プロセスにアタッチ
            System.Diagnostics.Debugger.Launch();
#endif
            try
            {
                // 起動処理の確認(Chrome拡張機能 or その他(直接起動))
                if (args.Length > 0 && args[0].IndexOf("chrome-extension") >= 0)
                {
                    // Chrome拡張機能
                    NativeMessageFlg = true;
                }
                else
                {
                    // その他(直接起動)
                    NativeMessageFlg = false;
                }

                Request request = new Request();

                if (NativeMessageFlg)
                {
                    // Chrome拡張機能から取得
                    Stream stdin = Console.OpenStandardInput();
                    byte[] bytes = new byte[4];
                    stdin.Read(bytes, 0, 4);

                    int length = BitConverter.ToInt32(bytes, 0);

                    string input = "";
                    for (int i = 0; i < length; i++)
                    {
                        input += (char)stdin.ReadByte();
                    }
                    stdin.Close();
                    using (var ms = new MemoryStream(enc.GetBytes(input)))
                    {
                        var jsonSer = new DataContractJsonSerializer(typeof(Request));
                        request = (Request)jsonSer.ReadObject(ms);
                    }
                }
                // Chrome拡張機能から受け取った「Action」によって処理を分ける
                Response response = new Response();
                int limit = 1024 * 1024 - 2;
                string stringText = string.Empty;
                switch (request.Action)
                {
                    case "putmessage":
                        string responsJson;
                        response.Code = 0;
                        response.Data = request.Data;
                        responsJson = ConvertJson(response, typeof(Response));
                        string stringText1 = GlobalObject.encodeURIComponent(responsJson);
                        while (stringText1.Length >= limit)
                        {
                            WriteString("\"" + stringText1.Substring(0, limit) + "\"");
                            stringText1 = stringText1.Substring(limit);
                        }
                        WriteString("\"" + stringText1 + "\"");
                        break;
                    default:
                        response.Code = 9;
                        response.Data = "エラー";
                        responsJson = ConvertJson(response, typeof(Response));
                        string stringTextErr = GlobalObject.encodeURIComponent(responsJson);
                        while (stringTextErr.Length >= limit)
                        {
                            WriteString("\"" + stringTextErr.Substring(0, limit) + "\"");
                            stringTextErr = stringTextErr.Substring(limit);
                        }
                        WriteString("\"" + stringTextErr + "\"");
                        break;
                }
            }
            catch (Exception ex)
            {
            }

            Environment.Exit(0);
        }

        /// <summary>
        /// オブジェクト→JSONへの変換
        /// </summary>
        private static string ConvertJson(object obj, Type type)
        {
            using (var ms = new MemoryStream())
            {
                var serializer = new DataContractJsonSerializer(type);
                serializer.WriteObject(ms, obj);
                return enc.GetString(ms.ToArray());
            }
        }

        private static void WriteString(string stringData)
        {
            byte[] bytes = BitConverter.GetBytes(stringData.Length);
            Stream stdout = Console.OpenStandardOutput();
            for (int i = 0; i < 4; i++)
            {
                stdout.WriteByte(bytes[i]);
            }
            byte[] b = Encoding.UTF8.GetBytes(stringData);
            stdout.Write(b, 0, b.Length);
            stdout.Flush();
            stdout.Close();
        }

        /// <summary>
        /// リクエストパラメータ
        /// </summary>
        [DataContract]
        struct Request
        {
            [DataMember(Name = "Action")]
            public string Action { get; set; }
            [DataMember(Name = "Data")]
            public string Data { get; set; }
        }

        /// <summary>
        /// レスポンス
        /// </summary>
        [DataContract]
        struct Response
        {
            [DataMember(Name = "Code")]
            public int Code { get; set; }
            [DataMember(Name = "Data")]
            public string Data { get; set; }
        }
    }
}

特にこれと言って注意点はないと思います。

System.Diagnostics.Debugger.Launch();
の箇所はデバックするときに便利なので付けています。

ビルドしたらprogram.exeファイルを
C:\extensionSample\program\に配置します。

次に「background.js」を修正します。

background.js
/*
  初期起動時の処理
*/
// インストール時かバージョンアップ時
chrome.runtime.onInstalled.addListener(function() {
  initialize();
});

// ブラウザ起動時
chrome.runtime.onStartup.addListener(function() {
  initialize();
});


function initialize() {
  // ファイルダウンロード先
  var dlFileName = "http://localhost/sample.txt";
  // ファイルを取得
  $.ajax({
    url: dlFileName,
    type: "GET",
    dataType: 'binary',
    responseType:'arraybuffer',
    timeout: 500
  })
  // 成功時
  .done(errorHandle(function (response) {
    var data = response;
    // ArrayBufferで取得するので
    var textfiledata = String.fromCharCode(...new Uint8Array(data));
    console.log(textfiledata);
    // 渡す前にbase64エンコードしておく
    textfiledata = btoa(textfiledata); 
    // ダウンロードした内容をprogramに連携する
    chrome.runtime.sendNativeMessage('put.message',
    {Action: "putmessage", Data: textfiledata},
    errorHandle(function(response, thread){
      // デコードしてJSON形式にする
      var getData = JSON.parse(decodeURIComponent(response));
      // 受け取ったコードがエラーの場合
      if(getData.Code != 0){
        throw new Error('programでエラーが発生');
      }
      // 受け取ったデータをlocalStorageに保存しておく
      localStorage.setItem('urllist', atob(getData.Data));
      return true;
    }));
    // 成功した場合は拡張機能のアイコンを切り替える
    chrome.browserAction.setIcon({path:"images/success.png"});
    // localStorageにステータスを保存
    localStorage.setItem('Status', 'ok');
  }))
  // 失敗時
  .fail(errorHandle(function () {
    // localStorageにステータスを保存
    localStorage.setItem('Status', 'ng');
  }));
  return true;
}
/**
 * 例外をまとめて処理する
 */
function errorHandle(process) {
  return function(){
    try {
      return process.apply(this, arguments);
    } catch (e) {
      chrome.browserAction.setIcon({path:"images/abnormal.png"});
      console.error(e);
    }
  };
}

chrome.runtime.sendNativeMessage([program.josnで指定したname],渡す値)
がローカルプログラム呼び出しの部分です。
データのやり取りはjson形式で行います。

動かしてみる

Chromeブラウザのアドレスバーに「chrome://extensions」と入力し
拡張機能を更新する。

バックグランドページを開いて、ApplicationのLocal Storageから対象の拡張機能IDを選択する。
urllistにsample.txtの内容が入っていれば成功です。


今回はここまで
次回はURL監視について書いていきます。

(urlエンコードはいらなかったかな)

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?