前回はChrome拡張機能からファイルのダウンロードまでを投稿しました。
今回はその続きです。
Chrome拡張機能とローカルプログラムの連携
Chrome拡張機能とローカルプログラムとの連携について
今回はローカルプログラムをvisual studioで作ったC#のプログラムとします。
##下準備
まずは連携するための下準備をします。
最初に連携するプログラムの配置場所を決めます。
今回は
c:\extensionSample\program
の配下に置くことにします。
連携用JSONファイルを準備
場所に決まりはないですが連携用のjsonファイルをprogramフォルダの配下に配置する
{
"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」とします。
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」を修正します。
/*
初期起動時の処理
*/
// インストール時かバージョンアップ時
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エンコードはいらなかったかな)