変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

[C#]warning CS8602: null 参照の可能性があるものの逆参照です。

よく判らない警告が出る。

JsonArray arr = jsonNode.AsArray();
string[] paramArray = new string[arr.Count];
ArrayList list = new ArrayList();
for (int i = 0; i < arr.Count; i++)
{
    list.Add(arr[i].ToString()); ※「warning CS8602: null 参照の可能性があるものの逆参照です。」が発生する。
}
return paramArray;

正解は、

JsonArray arr = jsonNode.AsArray();
string[] paramArray = new string[arr.Count];
ArrayList list = new ArrayList();
for (int i = 0; i < arr.Count; i++)
{
    var ji = arr[i];
    list.Add((ji != null)?ji.ToString():"");
}
return paramArray;

arr[i] は何の型かは不定だから、var宣言の変数に格納するしか手段はない。

そう明示的に書けば、CS8602も大人しくなる。

が・・・

普通の型の配列子にはしつこい。

関数(... string?[]? paramArray) {
・・・
object?[] param = new object?[] { null, null, null, null};
if (paramArray != null)
{
    param[0] = paramArray[0].Replace("%EXEPATH%", Resource.GetExePath());
}
t.InvokeMember(methodName,
    BindingFlags.InvokeMethod,
    null,
    obj,
    param);

は、

関数(... string?[]? paramArray) {
・・・
object?[] param = new object?[] { null, null, null, null};
if (paramArray != null)
{
    var p0 = paramArray[0];
    if (p0 != null)
    {
        p0 = p0.Replace("%EXEPATH%", Resource.GetExePath());
    }
    param[0] = p0;
}
t.InvokeMember(methodName,
    BindingFlags.InvokeMethod,
    null,
    obj,
    param);


[C#(v7) + Power Shell(v7)]ActiveXObjectのメンバーの情報を取得したい

64ビット環境では、

Activator.CreateInstance(t)

の結果が全てラッパーなクラス(System.__ComObject)になるので、progIdのインタフェースの内容を調べる方法が無い。(らしい

Power Shell でActiveXObjectの変数を作ると

> [System.Environment]::Is64BitProcess
True ※ 64ビット版で動作中(らしい
> $ac = New-Object -ComObject ADODB.Connection
> Get-Member -InputObject $ac > ADODB.Connection.txt

Get-Member でインタフェースの内容がテキストで出力される。

   TypeName: System.__ComObject#{00001550-0000-0010-8000-00aa006d2ea4}
Name              MemberType Definition                                        
----              ---------- ----------                                        
BeginTrans        Method     int BeginTrans ()                                 
Cancel            Method     void Cancel ()                                    
Close             Method     void Close ()                                     
CommitTrans       Method     void CommitTrans ()                               
Execute           Method     _Recordset Execute (string, Variant, int)         
Open              Method     void Open (string, string, string, int)           
OpenSchema        Method     _Recordset OpenSchema (SchemaEnum, Variant, Var...
RollbackTrans     Method     void RollbackTrans ()                             
Attributes        Property   int Attributes () {get} {set}                     
CommandTimeout    Property   int CommandTimeout () {get} {set}                 
ConnectionString  Property   string ConnectionString () {get} {set}            
ConnectionTimeout Property   int ConnectionTimeout () {get} {set}              
CursorLocation    Property   CursorLocationEnum CursorLocation () {get} {set}  
DefaultDatabase   Property   string DefaultDatabase () {get} {set}             
Errors            Property   Errors Errors () {get}                            
IsolationLevel    Property   IsolationLevelEnum IsolationLevel () {get} {set}  
Mode              Property   ConnectModeEnum Mode () {get} {set}               
Properties        Property   Properties Properties () {get}                    
Provider          Property   string Provider () {get} {set}                    
State             Property   int State () {get}                                
Version           Property   string Version () {get}                           

参考:https://docs.microsoft.com/ja-jp/powershell/scripting/samples/creating-.net-and-com-objects–new-object-?view=powershell-7.2&viewFallbackFrom=powershell-6

の「WScript.Shell によるデスクトップ ショートカットの作成」の項

この記事、作成したショートカットの内容を保存するにはSaveメソッドを使います。という内容。

統合開発環境(IDE)でソースを書けば、自動的にメソッドの候補がリストアップされるのが当たり前だけど、PowerShellの窓は窓だけそんなモノはない。

そこで窓の中でメソッドを調べる方法を優しく伝授したかったらしい。(良い人じゃないですか!

$lnk | Get-Member

さて、C#からPowerShellを呼び出すには、

普通にWindows Storeからインストすると、コマンドラインのみ許可なインストに。

普通にダウンロードしてPowerShellをインストール。

おや? InvalidProgramException ex

どうやら、32ビットのDLLらしい。

仕方なく、PowerShellVer7の64ビット版をインストール。

やっと動き出す。

using (var invoker = new RunspaceInvoke())
{
    string source = @"$ac = New-Object -ComObjec " + progID + @" ; Get-Member -InputObject $ac";
    var results = invoker.Invoke(source, new object[] { });
    foreach (var result in results)    {
        Console.Write(result);
    }
}

古いVer5ではInvokeしまくればよかったのに、

今のVer7では

// PowerShellオブジェクトを作成
PowerShell ps = PowerShell.Create();
// 実行するコマンドを追加
ps.AddCommand("New-Object")
    .AddParameter("-ComObject", progId);
// コマンドを呼び出す
var results1 = ps.Invoke();
//ps.AddCommand("Get-Member -InputObject $ac");
ps.AddStatement().AddCommand("Get-Member")
    .AddParameter("-InputObject", results1);
// コマンドを呼び出す
var results2 = ps.Invoke();

かと思ったけど、AddScriptを使えばPowerShellの窓で打った内容をそのまま使えるらしい。

PowerShell ps = PowerShell.Create();
// 実行するコマンドを追加
ps.AddScript("$ac = New-Object -ComObject ADODB.Connection");
ps.AddScript("Get-Member -InputObject $ac");
var results = ps.Invoke();

しかし、参照するDLLが多い。

吐き出される InvalidProgramException を読みつつ参照DLLを追加していくと

今回使用分だけなのに、このザマ。

しかも、ローカルにコピーを指定しても、PowerShellをアンスコすると動かなくなる。

あげくに、結果がSystem.__ComObjectの内容だった。

★ADODB.Connection
☆start
System.__ComObject
void Add(psobject item), void ICollection[psobject].Add(psobject item), int IList.Add(System.Object value)
void Clear(), void ICollection[psobject].Clear(), void IList.Clear()
bool Contains(psobject item), bool ICollection[psobject].Contains(psobject item), bool IList.Contains(System.Object value)
void CopyTo(psobject[] array, int index), void ICollection[psobject].CopyTo(psobject[] array, int arrayIndex), void ICollection.CopyTo(array array, int index)
bool Equals(System.Object obj)
System.Collections.Generic.IEnumerator[psobject] GetEnumerator(), System.Collections.Generic.IEnumerator[psobject] IEnumerable[psobject].GetEnumerator(), System.Collections.IEnumerator IEnumerable.GetEnumerator()
int GetHashCode()
type GetType()
int IndexOf(psobject item), int IList[psobject].IndexOf(psobject item), int IList.IndexOf(System.Object value)
void Insert(int index, psobject item), void IList[psobject].Insert(int index, psobject item), void IList.Insert(int index, System.Object value)
bool Remove(psobject item), bool ICollection[psobject].Remove(psobject item), void IList.Remove(System.Object value)
void RemoveAt(int index), void IList[psobject].RemoveAt(int index), void IList.RemoveAt(int index)
string ToString()
psobject Item(int index) {get;set;}
int Count {get;}
bool IsFixedSize {get;}
bool IsReadOnly {get;}
bool IsSynchronized {get;}
System.Object SyncRoot {get;}
☆end

もうダメか?と思ったけど、先のaddScriptを使う方はうまくいった。

一旦Get-Memberの結果をC#に引き渡した時にラップされすぎしたのかもしれない。

★ADODB.Connection
☆start
int BeginTrans ()
void Cancel ()
void Close ()
void CommitTrans ()
_Recordset Execute (string CommandText, Variant RecordsAffected, int Options)
void Open (string ConnectionString, string UserID, string Password, int Options)
_Recordset OpenSchema (SchemaEnum Schema, Variant Restrictions, Variant SchemaID)
void RollbackTrans ()
int Attributes () {get} {set}
int CommandTimeout () {get} {set}
string ConnectionString () {get} {set}
int ConnectionTimeout () {get} {set}
CursorLocationEnum CursorLocation () {get} {set}
string DefaultDatabase () {get} {set}
Errors Errors () {get}
IsolationLevelEnum IsolationLevel () {get} {set}
ConnectModeEnum Mode () {get} {set}
Properties Properties () {get}
string Provider () {get} {set}
int State () {get}
string Version () {get}
☆end

Errorsとかコレクションの場合はCountプロパティがあればほぼ間違いない。

> Get-Member -InputObject $ac.Errors
   TypeName: System.__ComObject#{00000501-0000-0010-8000-00aa006d2ea4}
Name    MemberType            Definition
----    ----------            ----------
Clear   Method                void Clear ()
Refresh Method                void Refresh ()
Item    ParameterizedProperty Error Item (Variant) {get}
Count   Property              int Count () {get}

> Get-Member -InputObject $ac.Properties
   TypeName: System.__ComObject#{00000504-0000-0010-8000-00aa006d2ea4}
Name    MemberType            Definition
----    ----------            ----------
Refresh Method                void Refresh ()
Item    ParameterizedProperty Property Item (Variant) {get}
Count   Property              int Count () {get}

という感じでネストすればよさそう。

意外だったのが、ADODB.Fieldがレジストリィ未登録らしく失敗するので、これは特定のコマンドを叩いて調べることにする。

もう少しテンプレの作り方を工夫した方がよさそうだ。

いや、そもそも、GetMemberBinderをどうにかできれば、PowerShellを呼ばなくてもいいんだけどね。

internal class PowerShellExec
{
    public static void MakeActiveXTemplateJDFile(string docRoot, string[] classNames)
    {
        //  ActiveXObjectのテンプレ作成
        foreach (string className in classNames)
        {
            MakeActiveXObjectJsFile(docRoot, className);
        }
    }
    //  ActiveXObjectのテンプレ作成
    public static void MakeActiveXObjectJsFile(string docRoot, string className)
    {
        MemInfo[]? result = PowerShellExecute(className);
        // 複数行の場合がある
        className = className.Split("\r\n")[0];
        if (result == null)
            return;
        foreach (MemInfo p0 in result)
        {
            //Console.WriteLine(p0.ToString());
        }
        MemInfo[] p = result.Where(x => x.memberType == PSMemberTypes.Property && x.child == null).ToArray();
        MemInfo[] c = result.Where(x => x.memberType == PSMemberTypes.Property && x.child != null).ToArray();
        MemInfo[] m = result.Where(x => x.memberType == PSMemberTypes.Method).ToArray();
        //
        string temp = @"
//  " + className + @" クラス
class " + className.Replace(".", "_") + @" extends ActiveXObject {
    constructor() {
        // とりあえず定番
        super();
        this.className = this.constructor.name.replace('_', '.');
        // プロパティ
        // 名前リストからプロパティを作成
        this.propertyNames = [" + string.Join(", ", p.Select(x => "'" + x.name + "'")) + @"];
        this.makeProperties(this.propertyNames);
        // プロパティ(コレクション)
        // 名前リストからダミーのメソッド(コレクション)を作成
        this.propertyCollections = {
" + string.Join(@",
",
    c.Select(x1 => {
        MemInfo[] cp = x1.child.Where(x2 => x2.memberType == PSMemberTypes.Property).ToArray();
        MemInfo[] cm = x1.child.Where(x2 => x2.memberType == PSMemberTypes.Method).ToArray();
        return "            '" + x1.name + @"': {
" + "                'properties': [" + string.Join(", ", cp.Select(x3 => "'" + x3.name + "'")) + @"],
                'methods': [" + string.Join(", ", cm.Select(x3 => "'" + x3.name + "'")) + @"],
                'events': []
            }";
    }))
+ @"
        };
        this.makePropertiesCollection(this.propertyCollections);
        // メソッド
        // 名前リストからダミーのメソッドを作成
        this.methodNames = [" + string.Join(", ", m.Select(x => "'" + x.name + "'")) + @"];
        this.makeMethods(this.methodNames);
        // イベント
        // 名前リストからダミーのメソッドを作成
        this.eventNames = [];
        this.makeEvents(this.eventNames);
    }
}
";
        //
        string path = docRoot + className.Replace(".","_") + ".js";
        if (!File.Exists(path))
        {
            File.WriteAllText(path, temp);
        }
    }
    // 
    public static MemInfo[]? PowerShellExecute(string progInfo)
    {
        string progId="";
        try
        {
            List<MemInfo> rc = new();
            // PowerShellオブジェクトを作成
            // Powershell.Create("get-process").Invoke(); はもうできない。
            using (PowerShell ps = PowerShell.Create())
            {
                // 実行するコマンドを追加
                // $ac = New-Object -ComObject ADODB.Connection
                // Get-Member -InputObject $ac
                string[] infoA = progInfo.Split("\r\n");
                progId = infoA[0];
                //
                string[] cmdLines = new string[]{
                    "$ac = New-Object -ComObject " + progId,
                    "Get-Member -InputObject $ac"
                };
                // 
                if (infoA.Length > 1)
                {
                    // 先頭はprogId
                    progId = infoA[0];
                    // 以降はPowerShellコマンド
                    cmdLines = infoA.Skip(1).ToArray();
                }
                //
                foreach (string cmdLine in cmdLines)
                {
                    ps.AddScript(cmdLine);
                }
                var results = ps.Invoke();
                //
                foreach (var result in results)
                {
                    rc.Add(new MemInfo(ps, "$ac.", result));
                }
                //Console.WriteLine("☆end");
            }
            return rc.ToArray();
        }
        catch (BadImageFormatException ex)
        {
            Console.WriteLine("★★★★★★ " + progId + ":" + ex.GetType().FullName + "\r\n" + "64ビット用のPowerShellのDLLを使用してください。\r\n" + ex.Message + "★★★★★★");
        }
        catch (InvalidProgramException ex)
        {
            Console.WriteLine("★★★★★★ " + progId + ":" + ex.GetType().FullName + "\r\n" + "\r\n" + ex.Message + "★★★★★★");
        }
        catch (Exception ex)
        {
            Console.WriteLine("★★★★★★ " + progId + ":" + ex.GetType().FullName + "\r\n" + ex.Message + "★★★★★★");
        }
        return null;
    }
}
//
public class MemInfo
{
    public string name;
    public string? type; 
    public string defition;
    public PSMemberTypes memberType;
    public List<MemInfo>? child = null;
    //
    public MemInfo(PowerShell ps, string preCmd, PSObject m)
    {
        dynamic a = m.ImmediateBaseObject;
        this.name = a.Name;
        //
        string[] definition = a.Definition.Split(" ");
        this.type = definition[0];
        //
        this.defition = a.Definition;
        this.memberType = a.MemberType;
        //
        if (a.MemberType == PSMemberTypes.Property)
        {
            // Countプロパティを持つプロパティはネスト
            ps.AddScript("Get-Member -InputObject " + preCmd + definition[1] + " -Name Count");
            var results = ps.Invoke();
            if (results.Count > 0)
            {
                List<MemInfo> rc = new();
                ps.AddScript("Get-Member -InputObject " + preCmd + definition[1]);
                results = ps.Invoke();
                foreach (var result in results)
                {
                    rc.Add(new MemInfo(ps, preCmd + definition[1] + ".", result));
                }
                this.child = rc;
            }
        }
    }
    //
    public override string ToString()
    {
        return this.defition;
    }
    //
    public static string Right(string str, int len)
    {
        if (len < 0)
        {
            throw new ArgumentException("引数'len'は0以上でなければなりません。");
        }
        if (str == null)
        {
            return "";
        }
        if (str.Length <= len)
        {
            return str;
        }
        return str.Substring(str.Length - len, len);
    }
}

これを

string[] classNames = new string[] {
    "ADODB.Connection",
    @"ADODB.Field  ※← レジストリィ未登録なので変数$acを使用する特定のPowerShellコマンドを指定して作成する。
$ac = New-Object -ComObject ADODB.Recordset
$ac.Fields.Append(""fld1"", 129, 20)
Get-Member -InputObject $ac.Fields(0)",
    "ADODB.Recordset"
};
//string[] classNames = new string[] { "ADODB.Connection" };
PowerShellExec.MakeActiveXTemplateJDFile(docRoot, classNames);

な感じっで呼ぶと

ADODB_Connection.js

ADODB_Field.js

ADODB_Recordset.js

の3ファイルができる。

※コマンドの変数を$acに固定しているのは、プロパティをネストしていく際に使用する変数が$acだから

// new ActiveXObject("xxx")用
class ActiveXObject {
    constructor(className, json)
    {
        this.domain       = "localhost"; ←適宜修正
        this.port         = "8000"; ←適宜修正
        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);
            }
            else
            // 無ければ, C#のサーバに問い合わせる。
            {
                // サーバー側に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();
                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());
        }
    }
    //
    /**
     * 名前リストからプロパティを作成
     * 
     * @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[]} 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 未定
            //
        }
    }
    /**
     * 名前リストからメソッドを作成
     * 
     * @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 {any} eventNames
     * @param {any} staticMakeUrlParameters
     */
    makeEvents(eventNames) {
        // イベントは、逐次作成
    }
    //
    staticMakeUrlParameters(args) {
        if (args.length == 0) {
            return "";
        }
        let rc = [];
        for (let [arg, index] of args) {
            rc.push(`param${index}:${arg}`);
        }
        return rc.join("&");
    }

    // SendMessageの戻り値の['activeXobject']からプロパティを設定
    // 更新するプロパティがあれば処理する
    SetProperties(jsonReturn) {
        // 何もなければ何もしない
        if (typeof (jsonReturn) === 'undefined') {
            return;
        }
        if (jsonReturn === null) {
            return;
        }
        //
        delete jsonReturn["__constructor_name__"];
        this.__setup__(jsonReturn);
    }
}


[三国英雄の夜明け]ノビシロが無い

微妙

英雄も戦力もアップしてるけど、サーバ内順位はジワジワと下がっている。

主に幕府のレベル上げで底上げ、稀に定計で底上げ

つまり、

絶賛、伸び悩み

珍宝の強化はミスりっぱなしの金食い虫。

魂玉の品質7化の動きが鈍く特に神兵とかは動きが見えない。

副将のスキル上げは順調に上がるけど1000万銀銭も消し飛ぶ。

ps.2022/9/4

幕府、戦記上げ中。

幕府の文書購入で黄金が吹っ飛び、幕府出陣中の英雄評価を上げるのに銀両が吹っ飛ぶので、

黄金や銀両の消費が少ない百貨市場他くらいしか手が回らない。

ps.2022/9/9

戦計。

緑系は概ねレベル7

これだけレベル6

青系がなかなかレベル5にならない。

半分程度
もう少し。
もう少し。
と、
ずーっと
思ってる。

ピンク系(9種類)はレベル4にすらならない。

最近見かけない

グレー系(12種類)は本来の色すら判らない

何色なのかな?

竜金探索で食料2000万取得したので、自動訓練放置したら、一晩で1000万も食いつぶしていた。

兵隊もいっぱい増えたので、国戦の弔慰戦功を利用してシーズンイベントを進める。

やっと14/22
残り8任務が鬼レベルなのかもしれない

ps.2022.9.13

反間計がレベル7になり、

長かった

やっと、戦力が1900万台になった。

戦力19,109,898



[JavaScript] new ActiveXObject (progId)っぽく

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;



[Hyper-V]The unsigned image’s hash is not allowed

Hyper-Vで第2世代を選択するとUEFIが使えるし、Hyper-VマネージャからのシャットダウンもOK

LinuxのVMを作成して起動すると

暫くまたされた後にUEFIで

The unsigned image’s hash is not allowed

と出て起動に失敗する。

要はセキュアブートの初期設定がWindow用だったダケだった。

セキュアブートを変更

とすると、無事起動できた。

VMを作る時に設定できれば困らないのに・・・



[CentOS8]AlmaLinuxへ移行

移行スクリプトがあったので

# curl -O https://raw.githubusercontent.com/AlmaLinux/almalinux-deploy/master/almalinux-deploy.sh
# bash ./almalinux-deploy.sh

ダウンロードするパッケージは1.5Gbytesくらいあるらしい。

しかし、インストール時にエラってしまったので、

epelとremiリポジトリィを無効化し、ついでに最新状態にしておいた。

# dnf config-manager --disable epel epel-modular remi-modular remi-safe
#dnf update

再度、移行スクリプトを実行するとうまくいったようだ。

# cat /etc/redhat-release
AlmaLinux release 8.6 (Sky Tiger)

このまま再起動すると、sshもscpもhttpdも繋がらない・・・と思ったけど、サービスの起動にとても時間がかかっていただけだった。

x-windowとか諸々入っている。xrdpサービスを起動すればリモートデスクトップでも繋がる。

けど、J1900,4GBだから遅いなぁ



[飛行機]浮力

飛行機の翼の上面の気圧が底面の気圧より低い状態では浮力が生じる

というのは正しいらしい。

但し、静止した大気の中を飛行機が進むので、翼の上面と下面の空気は同じ時間で翼端まで到達するハズだから、距離が長い上面は早く移動し上面の気圧が低くなるハズだ。

と云うのは妄想らしい。

理由は厚みの少ない布張りでも翼の形なら揚力が発生するからだそうだ。

ま、気ままな大気の動きを人間が理解できる言葉で説明するのは大変ってことなんだろう。

ザックリとした新説の発表は無く、もっと流体光学を知りたい人は動画を観てねと云う広告動画でした。

また、翼端が細く長く伸びているので、下から空気が回り込む渦が出来、この渦が翼から剥がれると、渦を打ち消すような反対方向の渦が翼の上に取り残されて浮力が生じるという動画もあった。

しかしボクは、

  1. 飛行機が直進することで飛行機の翼は大気を圧縮する
  2. 大気は上に行くほど気圧が下がるが大気を圧縮することで気圧差が大きくなる。
    • 上と下の気圧差が1:2だと仮定すると、2倍に圧縮すると2:4になり、
      • 気圧差は(2-1)=1から(4-2)=2になる。
  3. 大気差が大きくなると大気が拡散する速度の差も大きくなる。
  4. 大気の速度差が大きくなり
  5. 上面と下面の圧力差で機体が浮いてしまう

なんだろうと思っている。

水平に置いた円錐を横に真っ二つにした上半分と下半分。どちらに浮力が生じるだろうか?

正解は、両方とも浮力が生じるそうだ。

先の考えなら、下半分には負の浮力しか無さそうだが、下半分のでっぱりで空気を圧縮するから反動で浮力が生じるらしい。もしそうなら上半分の方こそ負の浮力で急降下しそうだが、これはこれで圧縮の差から浮力が生じてしまう。

そもそも論として、地表は球面なので地面をそれなりに早い速度で直進すれば1Gで地球に引っ張られてるにしても早く直進すればするほど重量は減少するだろうから、

つまるところ、重力のあるところで大気を圧縮すると、どんな形でも浮力が生じるというのが真の正解の様だ。但し、姿勢制御の難易度にばらつきがあるので、無難なのは鳥の羽っぽいのが一番らしい。

速度が速いとなかなか地面に落ちてこないのはそのせいかな?と思ったら、弾丸は1G加速で綺麗な弾道軌道を描くんじゃなかったか?回転してるし、空気抵抗もあるけれど、上下均等に空気を押し分けるので浮力がさっぱりついてこないのかもしれない。

しかし、そうなると、

安定して空気を不均衡に圧縮しつづけないと浮力が生じないのなら

航空機の燃費が悪いのは宿命なのかもしれない。

単純に空気を圧縮さえすればいいなら、かまぼこ型の翼だって結構良いハズだが実際にはそうではない。かまぼこ型の翼は不安定で姿勢制御が難しそう。

下面でも少し圧縮し、更に飛行機の重心と揚力の中心を少しずらし、最低でも3点で支える仕組みの飛行機の翼にすることで、姿勢制御を多少安定させて楽をしている様に思える。

例えば、ちょっと下向きの負の浮力を持たせることで、飛行機がちょっと上向きになっても下向きの負の浮力が増え姿勢を元に戻せるし、ちょっと下向きになっても下向きの負の浮力が無くなって姿勢を元に戻せる様な気がする。

そんな風に考えても

その通りに大気が動いてくれる訳ではない。

主観的にはそう観えても、そんな法則が存在する訳ではない。

仮に存在すると仮定しても、それは人間の主観的な判断の中だけに存在するのであり、

大気がその法則に則って行動している訳では無い。

人間が生み出した物理法則は人間の主観的な判断の結果であり、世界はそれとは無縁な存在なのだ。

そう、

大気は気まぐれ。

魔法使いの様に風を操れるほどに風のことを知ることは難しいようだ。

だから、

人は魔法使いには成れない。

どうしても、空の魔法使いになりたい人は

まずは先のリンク先の動画をじっくり見てみるのが早道かもしれない。



[Oracle19c]プラガブル・データベースを・・・

サーバ起動後は

まずは、リスナーを起動

$ lsnrctl start
・・・
STATUS of the LISTENER
------------------------
・・・
Listener Parameter File   /usr/oracle/database/network/admin/listener.ora
Listener Log File         /u01/app/oracle/diag/tnslsnr/******/listener/alert/log.xml
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=******)(PORT=1521)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1521)))
The listener supports no services
The command completed successfully

次にCDBを起動

$ sqplus /NOLOG
SQL> startup;
*******

プラガブル・データベースの状態をチェック

SQL> show pdbs;
    CON_ID CON_NAME			  OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
	 2 PDB$SEED			  READ ONLY  NO
	 3 ******  			  READ WRITE NO

とりあえず接続先をチェック。※多分 CDB$ROOTのハズ

SQL> show CON_NAME;

プラガブル・データベースを起動してみよう

SQL> alter pluggable database ****** open;
Pluggable database altered.

SQL> show pdbs;
    CON_ID CON_NAME			  OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
	 2 PDB$SEED			  READ ONLY  NO
	 3 ******  			  READ WRITE NO

CDB作成時に作られたPDBは管理者名が不明で表領域もあるのかどうかも判らなかったので、

dbcaコマンドで別途にPDB(PDB1,PDB2)を追加してみた。

PDBの管理者名はpdbadminとか何となく判るものを作っておく。

表領域の作成にチェックを入れれば作ってくれるので便利。※手順は省略。

ここで、lsnrctl status を何度か叩くと、最後に各PDB名っぽいSERVICE_NAMEの情報が拾えるので

※今回はCDBのサービス名のドメイン名のマシン名の部分がPDB名に差し替えられていた。

そこからサーバのtnsnames.oraにPDB用の設定を手で追加。

※サーバで使わないハズだけど、サーバに元ネタとして置いておかないと新しいクライアントを作る時に悩むハズ。

PDB1 =
  (DESCRIPTION =
     (ADDRESS = (PROTOCOL = TCP)(HOST = ******)(PORT = 1521))
     (CONNECT_DATA =
       (SERVER = DEDICATED)
       (SERVICE_NAME = pdb1.******)
     )
  )

PDB2 =
  (DESCRIPTION =
     (ADDRESS = (PROTOCOL = TCP)(HOST = ******)(PORT = 1521))
     (CONNECT_DATA =
       (SERVER = DEDICATED)
       (SERVICE_NAME = pdb2.******)
     )
  )

本命のクライアントのtnsnames.oraを追記

PDB1 =
(DESCRIPTION =
   (ADDRESS = (PROTOCOL = TCP) (HOST = ******)(PORT = 1521))
   (CONNECT_DATA =
     (SERVER = DEDICATED)
     (SERVICE_NAME = pdb1.******)
   )
)

PDB2 =
(DESCRIPTION =
   (ADDRESS = (PROTOCOL = TCP) (HOST = ******)(PORT = 1521))
   (CONNECT_DATA =
     (SERVER = DEDICATED)
     (SERVICE_NAME = pdb2.******)
   )
)

どっちもほぼ同じで、HOSTのみ、各環境で判るドメイン名とかIPアドレスとかにしておかないといけない。※IPアドレスにすれば同一になるはず。

dbcaコマンドでは各PDBの管理者にDBA権限が付与されていないので、

$ sqlplus / as sysdba
SQL> alter session set container=${各PDB名}; ※だったかな?
SQL> GRANT DBA TO ${各PDBの管理者名};

で、それぞれのPDBの管理者にDBA権限を付加した後に

それぞれのPDBの管理者でPDBに入り

$ sqlplus ${管理者名}/${パスワード}@${PDB名}
SQL> CREATE TABLE TBL1 か TBL2
 (
 empno VARCHAR2(10) NOT NULL,
 empname VARCHAR2(50),
 gender_f NUMBER(1,0)
 ) 
;  2    3    4    5    6    7  
Table created.

な感じで、PDB1にTBL1、PDB2にTBL2を作成し、

A5から、

CDB:(接続文字列:orcl、 ユーザ:system)、

PDB1:(接続文字列:pdb1、ユーザ: pdbadmin)

PDB2:(接続文字列:pdb1、ユーザ: pdbadmin)

でORACLE(OCL経由)接続を作った。

※ORACLE直接接続は

各赤枠はクライアントのtnsnames.oraの通り

それぞれで、

select * from TBL1;
select * from TBL2;

してみたら、

CDBは共に無し

PDB1はTBL1のみ

PDB2はTBL2のみ

と予想通りの結果になった。

改めて、sqlplusでpdb1に接続してみよう

SQL> alter session set container=PDB1;
SQL> show pdbs;

    CON_ID CON_NAME			  OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
	 4 PDB1 			  READ WRITE NO
※もう自分しかみえなくなっている
※CDBに戻ってPDBをこの状態で、自動起動にする
SQL> alter session set container=CDB$ROOT;
SQL> alter pluggable database all save state;

プラガブル・データベースを終了してみよう

SQL> alter pluggable database ****** close;
Pluggable database altered.

SQL> show pdbs;
    CON_ID CON_NAME			  OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
	 2 PDB$SEED			  READ ONLY  NO
	 3 ****** 			  MOUNTED

CDBも終了、SQLPLUSも終了する

SQL> shutdown;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> quit

これで、startupすれば、今まで通りに使えるはず。

※多分

と、ここまで書いたものの・・・

ネットをググると、多種多様な結果がヒットするので、

  • ひたすらSQLPLUSでコマンドを打ち続ける人
  • ツールを使いつつSQLPLUSでコマンドを打ち続ける人
  • ツールのみの人

それぞれで、

各々データベースの構成も違うし、

設定ファイルの更新の有無も変わるし、

手順やコマンドも変わるし、

ここの内容は極一部の人にしか参考にならない様な気がしてきた。

うーむ、恐るべしは・・・・・・。



[Oracle19c]どうやって起動 するん?

まず、SQLPLUSからCONNECTしようとしたら

ORA-12162: TNS:net service name is incorrectly specified

原因は、ORACLE_SIDを環境変数に登録していなかったせいらしい。

~/.bash_profileに追加して

$ source ~/.bash_prodile
$ sqlplus /NOLOG
SQL> connect AS SYSDBA
Enter user-name: sys
Enter password: ****************
Connected to an idle instance.
SQL> startup
ORACLE instance started.

Total System Global Area 2432695832 bytes
Fixed Size		    9137688 bytes
Variable Size		  570425344 bytes
Database Buffers	 1845493760 bytes
Redo Buffers		    7639040 bytes
Database mounted.
Database opened.
SQL> 

終わる時は、shutdown、quit。

さて、A5から直接接続できるか試してみたけど

ORA-12514: TNS:listener does not currently know of service requestion in connect description

から先に進めなかったので、

順当にOracleClientをインストし、

ORACLEデータベースをインストしたLINUXにあるtnsnames.oraから下記をコピペして、

ORCL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = ***)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = ****)
    )
  )
HOST = ***はPCからデータベースをインストしたサーバの名前かIPアドレス
SERVICE_NAME=****はそのまま使う。

PCの適当なフォルダにtnsnames.oraファイルを保存し、

保存先フォルダ名はユーザ環境変数「TNS_ADMIN」に保持。※システム環境変数の方がいいはず

ODBCデータソースアドミニストレイーターで、データソースを新規追加する。

ORACLEクライアントを選択して

TNSサービス名にSID(ここではtnsnames.oraでORCLだから)を入れ、「接続テスト」
ユーザー名とパスワードを入れて
で、良かったらしい。

さて、A5では

な感じで「テスト接続」は、

だったので、【OK】して保存。

こんな調子で大丈夫なのか?と自分でもツッコミをいれたくなったけど

管理ツールタグを見れば

なので大丈夫らしい。

あとはPDBにテーブルを作って、アクセスできればOKかな?




top