IEのJavaScriptで
var cn = new ActiveXObject("ADODB.Connect");
みたいなことが出来たので、
Chromeでもできないのか考えてみた。
ActiveXObjectクラスのnew演算子でパラメータをplogIdとするOCXっぽいオブジェクトが戻って
くれれば嬉しいので、ストレートに
returnを使ってみた。
class ActiveXObject {
constructor(className, json)
{
this.domain = "xxx.xxx.xxx"; // この辺はC#側の設定を転記
this.port = "xxxx"; // この辺はC#側の設定を転記
this.className = className;
this.objectID = null;
// 派生クラスの場合は再帰してしまうので、処理を省略
if(typeof(className) !== 'undefined')
{
// classNameから当該クラスのJavaScript側クラスのオブジェクトを作成
let cf = new Function('className', `return new ${className.replace('.', '_')}()`);
let obj = undefined;
try {
obj = cf(className);
} catch (ex) {
throw new Error(`★new ActiveXObject("${className}")未定義クラス`);
}
// 初期設定値(json)があれば、設定する
if (typeof (json) !== 'undefined') {
obj['objectID'] = json['objectID'];
obj.__setup__(json);
}
// 無ければ, C#のサーバに問い合わせる。
else
{
// サーバー側にnewしたことを通知
obj.init();
// 放置してもサーバ側のオブジェクトは解放されないので、オブジェクトが不要になったら、適宜termメソッドを呼び出す事
// this.term();
}
return obj; // new の戻り値
}
}
// 設定する
__setup__(json) {
// プロパティにコピー
for (let key of this.propertyNames) {
let jj = json[key]
if (typeof(jj) !== "undefined") {
this[`_${key}`] = json[key];
}
}
// プロパティ(コレクション)にコピー
for (let key in this.propertyCollections) {
let jj = json[key]
if (typeof(jj) !== "undefined") {
if (typeof (this[`_${key}`]) === "undefined" || this[`_${key}`] === null) {
this[`_${key}`] = {};
}
for (let idx in jj) {
let k = jj[idx].Name;
this[`_${key}`][`${k}`] = jj[idx];
}
// this[key]['Item'] = this[key]
}
}
}
// サーバー側にnewしたことを通知
async init() {
let json = await ActiveXObject.staticSendMessage('POST', this.domain, this.port, `ActiveXObject/${this.className}`);
ActiveXObject.staticInit(this, json);
}
// オブジェクトの解放
async term() {
return await ActiveXObject.staticSendMessage('DELETE', this.domain, this.port, `ActiveXObject/${this.objectID}`);
}
// サーバー側にnewしたことを通知
static staticInit(obj, json)
{
// 必須
if (typeof (json["objectID"]) === 'Nothing')
{
alert("objectIDが取得できません。");
}
obj.objectID = json["objectID"];
if (typeof (obj.onobjectID) !== 'undefined')
{
obj.onobjectID();
}
obj.SetProperties(json['activeXobject']);
//
}
// 送信
static staticSendMessage(method, domain, port, page, data)
{
let promise = new Promise( (resolve, reject) => {
try {
let parserFunction = this.parserFunction;
let xhr = new XMLHttpRequest();
// httpsにした方がいいのかもしれない
let url = encodeURI(`http://${domain}:${port}/${page}`);
xhr.open(method, url, true);
xhr.responseType = 'text';
xhr.send(this.staticEncodeHTMLForm(data));
xhr.onload = function(e) {
if (xhr.readyState == 4) {
if (xhr.status == 200 ) {
try {
let json = xhr.response;
json = JSON.parse(json, parserFunction);
resolve(json);
return;
} catch (ex) {
console.log(`'${ex.toString()}'\n url:http://${domain}:${port}/${url}\n json:'${xhr.response}'`);
}
}
}
};
}
catch (ex) {
reject(ex);
return;
}
});
return promise;
}
// データをBODYの形式に変換
static staticEncodeHTMLForm(data) {
if (typeof(data) === 'undefined' || data === null)
{
return null;
}
let a = [];
for (let aa of data) {
a.push(aa);
}
return "params=" + JSON.stringify(a);
}
//
static staticMakeUrlParameters(arg) {
let rc = [];
for (let idx = 1; idx <= arg.length; idx++) {
rc.add(`p${idx}=JSON.stringify(${arg[idx - 1]})`);
}
return rc.join('&');
}
//
static staticParserFunction(k,v)
{
try
{
if (typeof(v) === "string" && ((v.indexOf("function") === 0) || (v.indexOf("async") === 0)))
{
return eval(`(${v})`);
}
else
{
return v;
}
} catch (ex) {
console.log(ex.toString());
}
}
}
newで使用する各ActiveXObjectのテンプレ・クラスは・・・
// ADODB_Connection クラス
class ADODB_Connection extends ActiveXObject {
constructor() {
// とりあえず定番
super();
this.className = this.constructor.name.replace('_', '.');
// プロパティ
// 名前リストからプロパティを作成
this.propertyNames = ['Attributes', 'CommandTimeout', 'ConnectionString', 'ConnectionTimeout', 'CursorLocation', 'DefaultDatabase', 'IsolationLevel', 'Mode', 'Provider', 'State', 'Version'];
this.makeProperties(this.propertyNames);
// プロパティ(コレクション)
// 名前リストからダミーのメソッド(コレクション)を作成
this.propertyCollections = {
// https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/errors-collection-properties-methods-and-events
'Errors': {
'properties': ['Count', 'Item'],
'methods': ['Clear', 'Refresh'],
'events': []
},
// https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/properties-collection-properties-methods-and-events
'Properties': {
'properties': ['Count', 'Item'],
'methods': ['Refresh'],
'events': []
}
};
this.makePropertiesCollection(this.propertyCollections);
// メソッド
// 名前リストからダミーのメソッドを作成
this.methodNames = ['BeginTrans', 'CommitTrans', 'RollbackTrans', 'Cancel', 'Close', 'Execute', 'Open', 'OpenSchema'];
this.makeMethods(this.methodNames);
// イベント
// 名前リストからダミーのメソッドを作成
this.eventNames = ['BeginTransComplete', 'CommitTransComplete', 'RollbackTransComplete', 'ConnectComplete', 'Disconnect', 'ExecuteComplete', 'InfoMessage', 'WillConnect', 'WillExecute'];
this.makeEvents(this.eventNames);
}
}
“ADODB.Connection”というクラス名に使えなかいので、”ADODB_Connection”にしている。
makeXXXXは使いまわすのでActiveXObjectクラスで実装
//
/**
* 名前リストからプロパティを作成
*
* @param {String[]} propertyNames 名前リスト
*/
makeProperties(propertyNames) {
for (let propName of propertyNames) {
Object.defineProperty(this, propName, {
get() {
return this[`_${propName}`];
},
set(value) {
this[`_${propName}`] = value;
}
});
}
}
/**
* 名前リストからメソッドを作成
*
* @param {String[]} methodNames 名前リスト
*/
makeMethods(methodNames) {
for (let methodName of methodNames) {
this[methodName] = function () {
let args = arguments;
if (this.objectID == null) {
return new Promise(async (resolve, reject) => {
this.onobjectID = async function () {
let rc = await this[methodName].apply(this, args);
resolve(rc);
return;
}
});
} else {
let path = `ActiveXObject/${this.objectID}/${methodName}`;
return new Promise(async (resolve, reject) => {
let json = await ActiveXObject.staticSendMessage('PUT', this.domain, this.port, path, args);
// エラーがあれば返す。
if (typeof (json['error']) !== 'undefined') {
reject(json['error']);
return;
}
//
this.SetProperties(json['activeXObject']);
//
let rc = json['returnValue'];
if (rc != null && rc["__constructor_name__"]) {
let className = rc["__constructor_name__"];
delete rc["__constructor_name__"];
let obj = new ActiveXObject(className, rc);
resolve(obj);
return;
}
resolve(rc);
return;
});
}
};
}
}
/**
* 名前リストからプロパティ(コレクション)を作成
*
* @param {String[]} propertyCollectionNames 名前リスト
*/
makePropertiesCollection(propertyCollections) {
for (let collectionName in propertyCollections) {
if (typeof (this[collectionName]) === "undefined") {
this[collectionName] = {};
}
let co = this[collectionName];
// コレクション本体
Object.defineProperty(this, collectionName, {
get() {
return this[`_${collectionName}`];
},
set(value) {
this[`_${collectionName}`] = value;
}
});
// コレクションの設定内容
let collection = propertyCollections[collectionName];
// コレクションのプロパティ
// property
for (let propertyName of collection.properties) {
Object.defineProperty(co, propertyName, {
get() {
return co[`_${propertyName}`];
},
set(value) {
co[`_${propertyName}`] = value;
}
});
}
// コレクションのメソッド
for (let methodName of collection.methods) {
if (typeof (co[methodName]) === "undefined")
{
co[methodName] = function () {
let args = arguments;
if (this.objectID == null) {
this.onobjectID = async function () {
let rc = await co[methodName].apply(co, args);
resolve(rc);
return;
}
} else {
let path = `ActiveXObject/${this.objectID}/${collectionName}/${methodName}`;
return new Promise(async (resolve, reject) => {
let json = await ActiveXObject.staticSendMessage('PUT', this.domain, this.port, path, args);
// エラーがあれば返す。
if (typeof (json['error']) !== 'undefined') {
reject(json['error']);
return;
}
//
this.SetProperties(json['activeXObject']);
//
let rc = json['returnValue'];
if (rc != null && rc["__constructor_name__"]) {
let className = rc["__constructor_name__"];
delete rc["__constructor_name__"];
let obj = new ActiveXObject(className, rc);
resolve(obj);
return;
}
resolve(rc);
return;
});
}
};
}
}
// event 未定
//
}
}
コレクションはプロパティとメソッドのソースをマージした感じで雑になっている。
これで準備が整ったところで、
new ActiveXObject("ADODB.Connection");
すれば、いいはず。
これって、new 演算子をオーバーライドした事になるのかな?(知らんけど
真っ先にプロパティ・チェックするケースは見当たらなかったので、
最初にメソッド呼び出し時に、C#側からobjectIDが送られるまで待って処理する様にしている。
プロパティ参照時もobjectIDが送られるまで待って処理を入れた方がいいけど、
var table_name = schema.Fields["TABLE_NAME"].Value;
の様に呼び出されると、await が入れにくい。
うまくawaitが入っても、絶望的に読みにくくなりそう。
var table_name = await (await (await schema.Fields)["TABLE_NAME"]).Value;