EdgeのIEモードもいつまであるのか判らないので・・・
C#のEXEでRESTサービスサーバを作り、ブラウサからデータベースにSQLでアクセスできる様な仕組みをポチポチと作ってみる。勿論、ポート開放なんてしない。
しかし思っていた以上に面倒なことが判明した。
public static object? CreateObject(string progId)
{
Type? t = Type.GetTypeFromProgID(progId);
return t == null ? null : Activator.CreateInstance(t);
}
object? activeXObject = CreateObject("ADODB.Connection");
それっぽいオブジェクトは出来るが、activeXObject変数のクラス名が”System.__ComObject”で、希望するメソッドやプロパティの情報は取得できず、ウォッチビューで中身を見ると[動的ビュー]の中にそれっぽいプロパティが見えるがアクセスする方法は判らなかった。
但し、その変数をそのActiveXObjectのクラスに置き換えると、Accessファイルも開けてしまうのでとりあえず、キャストしてコードを書けば良いらしい。
object? activeXObject
ADODB.Connection? cn = activeXObject;
cn.Open("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\"C:C:\\Users\・・・\Database1.accd\"");
事前にメソッドの名前さえ判っているなら、動的にメソッドを呼ぶ方法が使える。(カモ
参照:https://dobon.net/vb/dotnet/programing/typeinvokemember.html
string progId="ADODB.Connection";
activeXObject = ActiveX.CreateObject(progId);
// TEST
if (activeXObject != null)
{
Type t = activeXObject.GetType();
t.InvokeMember("Open",
BindingFlags.InvokeMethod,
null,
activeXObject,
new object[] { "Provider=Microsoft.ACE.OLEDB・・・\Database1.accdb\"" });
}
実行すると例外発生
Exception ex:
{"Exception has been thrown by the target of an invocation."}
ん?クラス名がダメなのかな?正しいTypeを取得する方法は予想の斜め上にあった。
参照:https://docs.microsoft.com/ja-jp/dotnet/api/system.type.gettype?view=net-6.0#system-type-gettype(system-string)
string progId="ADODB.Connection";
activeXObject = ActiveX.CreateObject(progId);
// TEST
if (activeXObject != null)
{
Type t = Type.GetType(progId);
t.InvokeMember("Open",
BindingFlags.InvokeMethod,
null,
activeXObject,
new object[] { "Provider=Microsoft.ACE.OLEDB・・・\Database1.accdb\"" });
}
これで、メソッドを呼び出すことができたので、キャストだらけのコードも改良の余地がありそうだ。
何でも(メソッド)勘でも(プロパティ)非同期通信で処理すると重そうので、newの後に一式プロパティを送信し、メソッドの実行後に置き換わりそうなプロパティを戻り値を[activeXObject]と[returnValue]に纏めて送信することにする。メソッドを実行するオブジェクトは、ハッシュ管理し、ブラウザからはハッシュを指定して実行する様にした。
※というか
IEの場合、
var table_name = schema.Fields("TABLE_NAME").Value
IE以外の場合、※(…)を[…]に書き換えるしかない様だ。
var table_name = schema.Fields["TABLE_NAME"].Value
な呼び出しをする場合が多い、ここにawait 入れると解読不能になるとしか思えなかった。(ので
※ハッシュ値は諸事象から $”{クラス名}_{ハッシュ値:x}” にした。
javascriptのnewはasyncが指定できないので、newの直後にJavaScript側でプロパティやメソッドの入り口を用意しておく。メソッド実行時にまだハッシュを受信していない場合は、ハッシュ受信時にonobjectIDを処理し、再度メソッド実行を送信するようにする。
※ブラウザ側で不要になったと判断したActiveXObjectオブジェクトは、ActiveXObject.term()を呼び出して、DELETEメソッドでオブジェクトの消去をするように。(できたらいいなぁ
if (this.objectID == null) {
this.onobjectID = async function () {
let rc = await this[methodName].apply(this, 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);
obj.objectID = json["objectID"];
if (typeof (obj.onobjectID) !== 'undefined')
{
obj.onobjectID();
}
JavaScript側でActiveXオブジェクトのインスタンスを配列で管理すると使用済みでも残ってしまうので、
インスタンスの管理はC#側のみで行う。
非同期通信の処理待ちをするため await を差し込まないといけないのは面倒だが仕方が無い。
await指定で呼び出す関数の方はasync指定して最後にresolveを渡す様にする。
何気に途中でreturn ; してるケースも同様。
※resolve(true)で処理が途切れるかと思い、手抜きしたら、ダブルresolve(true)してしまい動作が変になったので、直後にreturn を入れておく。
async function xxxxxx () {
・・・・・最後に
Promise.resolve(true);
return;
}
C#側から返す値はJSON形式にしたけど、javascript側からオブジェクトを渡すパラメータはtoStringで済ませているのでちゃんとしないとまずいなぁ。(そのうち何とかしよう
とか、簡単にしようとするとハマるパターンだ。
ちゃんと同期が取れていると
所感)
ブラウザとVisualStudioの両方でブレークポイントを指定して順に動かしていくと、ブラウザとVisualStudioが交互にポップアップして切り替わるのが面白かった。一度お試しあれ。