変奏現実

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

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

パソコン

[Excel VBA]WORD文書の赤色属性を含む文字の頁番号を列挙する

先の[Excel VBA]WORD文書の赤字の頁番号を列挙するでは、赤字以外の属性(抹消線など)があるテキストをヒットしなかったので、改訂版です。

今回はWORDの「高度な検索」の「書式」で「赤字」だけ設定する方法を使います。

と云っても、赤文字頁番号検索()少し変えたダケです。(笑

Public Sub 赤文字頁番号検索()
On Error GoTo err1
    '初期化
    Dim wordApp As Word.Application
    Set wordApp = CreateObject("Word.Application")
    Dim 赤字の開始行 As Long
    赤字の開始行 = ActiveSheet.Range("タイトル行").row + 1
    Dim 赤字の列 As Long
    赤字の列 = ActiveSheet.Range("タイトル行").Find("赤字").Column
    Dim 頁番号の列 As Long
    On Error Resume Next
    Err.Clear
    頁番号の列 = ActiveSheet.Range("タイトル行").Find("赤字頁番号").Column
    If Err.Number <> 0 Then
        頁番号の列 = ActiveSheet.Range("タイトル行").Find("頁番号").Column
    End If
    '検索対象のWORD文書を開く
    Dim wordFname As String
    wordFname = 検索対象のWORD文書名を調べる
    Dim wordDoc As Word.document
    Set wordDoc = 検索対象のWORD文書を開く(wordApp, wordFname)
    If TypeName(wordDoc) <> "Nothing" Then
        Dim row As Long
        row = 赤字の開始行
        wordDoc.ActiveWindow.Selection.Start = 0
        wordDoc.ActiveWindow.Selection.End = 0
        Do While (高度な赤字検索(wordDoc))
            Dim 赤字 As String
            赤字 = wordDoc.ActiveWindow.Selection.text
            赤字 = Replace(赤字, vbCr, "")
            赤字 = Replace(赤字, vbLf, "")
            赤字 = Trim(赤字)
            If 赤字 <> "" Then
                wordDoc.ActiveWindow.Selection.Copy
                ActiveSheet.Cells(row, 赤字の列).Select
                ActiveSheet.Paste
                Dim pasteRows As Integer
                pasteRows = Selection.Count
                Dim st, ed
                st = wordDoc.ActiveWindow.Selection.Start
                ed = wordDoc.ActiveWindow.Selection.End
                Dim 開始頁番号 As Integer
                wordDoc.ActiveWindow.Selection.Start = st
                wordDoc.ActiveWindow.Selection.End = st
                開始頁番号 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
                Dim 終了頁番号 As Integer
                wordDoc.ActiveWindow.Selection.Start = ed
                wordDoc.ActiveWindow.Selection.End = ed
                終了頁番号 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
                '右セルに頁番号を書き込む
                Dim 頁番号説明文 As String
                If 開始頁番号 <> 終了頁番号 Then
                    ActiveSheet.Cells(row, 頁番号の列).Value = 開始頁番号 & "~" & 終了頁番号 & "頁"
                Else
                    ActiveSheet.Cells(row, 頁番号の列).Value = 開始頁番号 & "頁"
                End If
                row = row + pasteRows
            End If
        Loop
    End If
exit1:
    Call MSWORDをそのまま閉じる(wordApp)
    Exit Sub
err1:
    MsgBox Err.Description
    GoTo exit1
End Sub

後は、高度な赤字検索()を追加するだけです。

中身はWORDで「高度な検索」の「書式」で「赤字」だけ設定した時のマクロ記録内容を少し調整したものです。

Private Function 高度な赤字検索(wordDoc As Word.document) As Boolean

    With wordDoc.ActiveWindow.Selection.Find
        '書式関連を初期化
        .ClearFormatting
        .Font.Color = wdColorRed
        '文字関連を初期化
        .text = ""
        .Replacement.text = ""
        .Forward = True
        .Wrap = wdFindStop '文書の終わりで中断
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchByte = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = False
        .MatchFuzzy = True
        '検索
        .Execute
        '結果を取得
        高度な赤字検索 = .Found
    End With

End Function

検索対象のWORD文書名を調べる検索対象のWORD文書を開くMSWORDをそのまま閉じるは前の記事のままです。

Font.ColorとFont.ColorIndexのいづれを使うのか良いのかは悩ましいです。

後、デバッグ中は起動したMS-WORDをタスクマネージャから終了していましたが、上記のwordAppを外部変数にして、マクロ画面から終了させた方が便利そう。

Dim wordApp As Word.Application
・・・
Set wordApp = CreateObject("Word.Application")
・・・
public sub マクロ実行中に起動したMSWORDを強制終了
    Call MSWORDをそのまま閉じる(wordApp)
End Sub 


[Excel VBA]WORD文書の赤字の頁番号を列挙する「赤字以外の属性不可」

WORD文書の中の赤字を見つけるマクロを見つけた。

添削するには便利、しかしそんな便利なマクロを作ってる人は稀なので大抵は

P10、12を修正

の様に列挙したリストも渡さないといけないので、そんなマクロを作ってみた。

WORD文書には

ヘッダかフッタに頁番号のフィールドを割り当てておく。

マクロを配置するEXCELのワークシートには

な感じで対象WORD文書名、赤字、頁番号と書いたセルにタイトル行と名付け、対象WORD文書名の下に赤字を探すWORD文書のファイル名を入れておく。

赤文字頁番号検索マクロを実行すると

図の様に見つけた赤字とその頁番号がワークシートに書き込まれる。

そんなマクロのメイン処理はこんな感じ。

Public Sub 赤文字頁番号検索()
On Error GoTo err1

    '初期化
    Dim wordApp As Word.Application
    Set wordApp = CreateObject("Word.Application")
    '
    Dim 赤字の開始行 As Long
    赤字の開始行 = ActiveSheet.Range("タイトル行").row + 1

    Dim 赤字の列 As Long
    赤字の列 = ActiveSheet.Range("タイトル行").Find("赤字").Column
    
    Dim 頁番号の列 As Long
    頁番号の列 = ActiveSheet.Range("タイトル行").Find("頁番号").Column

    '検索対象のWORD文書を開く
    Dim wordFname As String
    wordFname = 検索対象のWORD文書名を調べる
    Dim wordDoc As Word.document
    Set wordDoc = 検索対象のWORD文書を開く(wordApp, wordFname)
    If TypeName(wordDoc) <> "Nothing" Then
        '赤文字を1文字選択する
        Call 赤文字を1文字選択する(wordDoc)
        '選択中の文字と同じ属性の文字を全て選択状態にする
        Call 選択中の文字と同じ属性の文字列を全部クリップボードにコピる(wordDoc)
        'ワークシートに貼り付ける
        ActiveSheet.Cells(赤字の開始行, 赤字の列).Select
        ActiveSheet.Paste
        'ワークシートの選択状態のセルの文字を検索する
        Dim row As Long
        For row = 赤字の開始行 To 赤字の開始行 - 1 + Selection.Count
            Dim text As String
            text = ActiveSheet.Cells(row, 赤字の列).Value
            Dim 頁番号 As Integer
            頁番号 = 赤字テキスト検索(wordDoc, text)
            If 頁番号 > 0 Then
                '右セルに頁番号を書き込む
                ActiveSheet.Cells(row, 頁番号の列).Value = 頁番号 & "頁"
            Else
                ActiveSheet.Cells(row, 頁番号の列).Value = "見つかりません"
            End If
            'ワークシートの選択状態の最後のセルまで繰り返す
        Next row
    End If
exit1:
    Call MSWORDをそのまま閉じる(wordApp)
    Exit Sub

err1:
    MsgBox Err.Description
    GoTo exit1

End Sub

察しが付いたと思うがとても長いし、WORD「選択」で複数個の選択状態になった場合、VBAでその結果にダイレクトにアクセスする方法がみあたらなかったので、一旦クリップボードに入れてワークシートに書き出し、1つづつ頁番号を調べているので、処理が終わるまでじっと待たなければいけない。

以下、サブルーチン群。

セル名を使っているけど、単なる趣味でしかない。

Private Function 検索対象のWORD文書名を調べる() As String
    
    Dim ファイル名列 As Integer
    ファイル名列 = ActiveSheet.Range("タイトル行").Find("対象WORD文書名").Column
    Dim wordFname As String
    wordFname = ActiveSheet.Cells(ActiveSheet.Range("タイトル行").row + 1, ファイル名列)
    'ドライブレターやパスが無い場合
    If InStr(wordFname, "\") = 0 Then
        wordFname = ActiveWorkbook.Path & "\" & wordFname
    End If
    検索対象のWORD文書名を調べる = wordFname

End Function

普通にWORD文書を開くとマクロのデバッグでSTOPしたりすると時々EXCELの裏に「壊れたWORD文書です。xxxxx」と表示されたりするので、読み取り専用で開くのが吉。

Private Function 検索対象のWORD文書を開く(wordApp As Word.Application, docPath As String) As Word.document
On Error GoTo err1

    '読み取り専用
    Set 検索対象のWORD文書を開く = wordApp.Documents.Open(Filename:=docPath, ReadOnly:=True)
    Exit Function

err1:
    MsgBox Err.Description
    Set 検索対象のWORD文書を開く = Nothing

End Function

WORDの「高度な検索」で「書式」から赤字を指定した場合のマクロ

Private Sub 赤文字を1文字選択する(wordDoc As Word.document)

    '赤文字を検索
    wordDoc.ActiveWindow.Selection.Find.ClearFormatting
    wordDoc.ActiveWindow.Selection.Find.Font.Color = wdColorRed
    
    With wordDoc.ActiveWindow.Selection.Find
        .text = ""
        .Replacement.text = ""
        .Forward = True
        .Wrap = wdFindContinue
        .Format = True
        .MatchCase = False
        .MatchWholeWord = False
        .MatchByte = False
        .MatchAllWordForms = False
        .MatchSoundsLike = False
        .MatchWildcards = False
        .MatchFuzzy = True
    End With
    
    wordDoc.ActiveWindow.Application.Selection.Find.Execute
    
    '1文字だけ選択する
    wordDoc.ActiveWindow.Selection.End = wordDoc.ActiveWindow.Selection.Start + 1

End Sub

WORDの「類似した書式の文字列を選択」のマクロ

Private Sub 選択中の文字と同じ属性の文字列を全部クリップボードにコピる(wordDoc As Word.document)

    '類似した書式の文字列を選択するWordマクロ三種     https://www.ka-net.org/blog/?p=5486
    wordDoc.Application.Run "SelectSimilarFormatting"
    'Application.CommandBars.FindControl(ID:=5946).Execute
    'Application.CommandBars.ExecuteMso "SelectTextWithSimilarFormatting"
    
    'クリップボードにコピる
    wordDoc.ActiveWindow.Selection.Copy

End Sub

赤字な特定の文字列を検索するマクロ

Private Function 赤字テキスト検索(wordDoc As Word.document, text As String) As Integer

    赤字テキスト検索 = 0
    With wordDoc.Content.Find
        '赤文字を指定
        .ClearFormatting
        .Font.Color = wdColorRed
        'テキストを指定
        .text = text
        '順方向を指定
        .Forward = True
        'いざ検索
        .Execute
        '結果は?
        If .Found = True Then
            wordDoc.ActiveWindow.Selection.Start = .Parent.Start
            wordDoc.ActiveWindow.Selection.End = .Parent.End
            '頁番号を返す
            赤字テキスト検索 = wordDoc.ActiveWindow.Selection.Information(wdActiveEndAdjustedPageNumber)
            Exit Function
        End If
    End With

End Function

意外と面倒なWORDの終了

ループしなくても

wordApp.Documents.Close SaveChanges:=wdDoNotSaveChanges

で済むらしいけど、念のためループしている。

Private Sub MSWORDをそのまま閉じる(ByRef wordApp As Word.Application)

    Dim idx As Integer
    For idx = 1 To wordApp.Documents.Count
        wordApp.Documents(idx).Close SaveChanges:=wdDoNotSaveChanges
    Next
    wordApp.Quit
    Set wordApp = Nothing

End Sub



【Excel】マクロや計算式を除外して保存する方法

資料を作る時はVLOOKUPとか、いっぱい数式が入ってる。

しかし、誰かに渡す時は安心して扱えるように全ての数式をデータに変換しておきたい時がある。

※何気にキーを押すと数式が#ERRORを吐くことがあるから

具体的には、

  1. 全シート(但し、非表示シートを除く)を選択状態
  2. 全セルを選択
  3. ワークシートをコピー
  4. 値(あるいは計算結果)を使用するモードで、ワークシートにペースト
  5. 普通のExcelファイルに保存

するダケ。

'全シート選択
Dim worksheet As Worksheet
Dim wss() As String
Dim cnt As Integer
cnt = 0
For Each worksheet In Worksheets
  If worksheet.Visible = xlSheetVisible Then
    cnt = cnt + 1
    ReDim Preserve wss(cnt -1)
    wss(cnt - 1) = woeksheet.Name
  Else
    '確認メッセージを非表示
    Application.DisplayAlerts = False
    'ワークシートを削除
    worksheet.Delete
    '確認メッセージを表示
    Application.DisplayAlerts = True
  End If
Next
Sheets(wss).Select

'コピー
Cells.Select
Selection.Copy

’データ貼り付け
Selection.PasteSpecial Paste:= xlPasteValues, _
  Operation:= xlNone, _
  SkipBlanks:= False, _
  Transpose:= False

'セル選択の解除
Application.CutCopyMode = False

全シート選択時は非表示シートが含まれているとエラるので注意。

非表示シートは不要なら削除して手渡しした方が良いかもしれないけど

確認メッセージが出るのがうざい。

そうなるとマクロも消して保存したくなる。

名前を変えて保存で、ファイルの種類を「Excel ブック (*.xlsx)」に切り替えて保存すればいいが

マクロ記録したコードでは

読めないExcelファイルを保存しまいがち

実際エラったり散々なようだ。

  • MSのサイトではエラったら・・・・・・・・
  • FileFormatパラメータは 1を使う
  • FileNameパラメータは 拡張子を省く

ことをお勧めしていた。

確かにエラーは発生しないものの。

使い物にならないEXCELファイルになってしまう

※開くと「アカン!読めんw」と泣きが入る

※開くと「一部読めんw」と呟いて、書式が壊れCSVファイルを読んだ様な画面になる

色々試した結果

こうなった。

Dim bookName as String
bookName = ActiveWorkbook.Name
bookName = Replace(ActiveWorkbook.Name,".xlsm","xlsx")
Dim bookNamePath
bookNamePath = Split(ActiveWorkbook.FullName,"\")
ReDim Preserve bookNamePath(UBound(bookNamePath)-1)
bookNamePath = Join(bookNamePath,"\")
ChDir bookNamePath
'確認メッセージを非表示
Application.DisplayAlerts = False
'普通のExcelファイルに保存
ActiveWorkbook.SaveAs Filename:= bookName, _
  FileFormat:= xlOpenXMLWorkbook, _
  Password:= "", _
  WriteResPassword:= "", _
  ReadOnlyRecommended:= False, _
  CreateBackup:= False
'確認メッセージを表示
Application.DisplayAlerts = True
'処理完了メッセージ
MsgBox bookNamePath & "\" & bookName & vbCrLf & "へ名前を変えて保存"

SaveAsのパラメータは

  • FileFormatパラメータは xlOpenXMLWorkbook を使う
  • FileNameパラメータは 拡張子をxlsx に変える

これで、確認メッセージは出てくるもののちゃんと保存できたし、マクロも消えていた。

しかし、このコードがいつのまにか使い物にならなくなっている可能性がきわめて高いのが

MSクオリティ。

PS.2022/6/10

てか、サブPCの古いExcelで動作をチェックしてたので、色々支障がありました。

今のExcelなら【Microsoft365用】Excelのマクロや計算式を除外して保存する方法を参考にしてください。

後、ReadOnlyRecommendedの綴りがまちがっていたので訂正。多分、何度も貼り換えてるうちに変になったんだろうなぁ・・・



【Win11】NASのiSCSIグループに割り当てたドライブが表示されない

PCを起動すると、NASのiSCSIのグループに割り当てたドライブが表示されない時がままある。

iSCSIイニシエータを見ると

再接続中。つまり繋がっていない。

ここで早まって【切断】し、IPアドレスを入力して【ENTER】キーを押すと、

「クイック接続」になってしまい

PCを再起動すると、今は「再接続中」になる。

この「システムの再起動後ターゲットは利用できなくなります。」の意味は、

・【切断】に失敗する

・再「クイック接続」できない ※できる場合もあるかもしれない。

くらいに「ターゲットは(一切)利用できなくなります。」と云う意味だ。

仕方が無いので、

「探索」「お気に入り」のタグからNASのIPアドレスを覚えている箇所を削除し、

再起動時にNASのことをすっかり忘れてもらった隙に

IPアドレスを入力し今度は【ENTER】キーには一切触れず【接続】ボタンを押さなければいけない。

※うっかり【ENTER】キーを押してしまった場合は、「探索」「お気に入り」タグから削除しなおして再起動。

無事「ターゲットの接続」画面が表示されれば【OK】を押すと、

PC起動時にNASのiSCSIグループにに再ログインしてくれるようだ。

しかし、これで暫く繋がっていたハズなんだが・・・

根本的な対策にはならないんだよね。



【javascript】indexedDB

まだ、イマイチ感がある。

注意点

1.PC上のHTMLの場合、データベースのスコープ(有効範囲)はPC単位。

つまりPC上のHTMLで共有してしまう

2.データベースをオープンしたら、必ず自分でクローズすること

クローズし忘れると、次のオープン時にロックしやすい

大抵はF5のオートコミットで済むが、ブラウザを閉じないとダメな場合もある

successイベントは1回のみなので、try catchの後のfinallyでクロースすればいい

const request = window.indexedDB.open("TestDatabase");
request.addEventListener('success', (event) => {
  const database = event.target.result;
  try {
    ・・・データ処理・・・
  } catch (ex) {
    console.log(`${ex}`);
  } finally {
    database.close();
  }
});

しかし、Promiseを使って処理の同期を取りたい場合は・・・

const promise = new Promise( (resolve, reject) => {
  const request = window.indexedDB.open("TestDatabase");
  request.addEventListener('success', (event) => {
    const database = event.target.result;
    try {
      ・・・データ処理・・・
   resolve(`xxxx(...): success`);
    } catch (ex) {
      console.log(`${ex}`);
   reject(`xxxx(...): catch(${ex})`);
    } finally {
      database.close();
    }
  });
  *** エラー処理とか ***
});
return promise;

とすると、resolveの後にfinally句のクローズ処理が通るかどうか?

あまり自信が無い。

と云うのも

function test() {
  return  true;
  alert('OK');
}

のalert(‘OK’);がいつのまにか処理されなくなってたからだ。

upgradeneeded発生時は、後にsuccessイベントが続くので、successイベントに任せればいい

errorイベントも忘れずにクローズ。

request.addEventListener('error', (event) => {
  const error = event.target.error;
  database.close();
  reject(`xxxx(...): error\n${error}`);
});

3.オブジェクトストアの生成・削除はデータベースのバージョンアップ時のみ

4.インデックスの生成・削除もデータベースのバージョンアップ時のみ

5.だが、トランザクションはデータベースのバージョンアップ時は不可

6.トランザクションも自分でcommitabortすること

commitabortを忘れるとタイムアウトするまで次の処理がロックする

const trans = database.transaction(オブジェクトストア・リスト);
try {
  const objectStore = trans.objectStore(オブジェクトストア);
  ***データ処理***
  trans.commit();
} catch (ex) {
  trans.abort();
  throw ex;
}

だからと云ってcursorのsuccessイベントは何回も降ってくることが多いので

毎回commitすると、2周目でつまずく

const trans = database.transaction(オブジェクトストア・リスト);
try {
  const objectStore = trans.objectStore(オブジェクトストア);
  const cursor objectStore.createCursor().onsuccess(event => {
    const cursor = event.target.result;
    ***データ処理***
    cursor.continue();
    trans.commit(); //毎回コミットは・・・
  });
} catch (ex) {
  trans.abort();
  throw ex;
}

7.インデックスの生成・削除できるタイミングはオブジェクトストアの生成直後だけ

const request = indeedDB.openDatabase(データベース名)
request.addEventListener(`onupgradeneed', (event) => {
  const database = event.target.result;
  const objectStore = database.createObjectStore(オブジェクトストア名);
  objectStore.createIndex(インデックス名);
  objectStore.deleteIndex(インデックス名);
});

後でインデックスを調整したい場合は、オブジェクトストアのexports機能を自作して、イジって、importsするしかない。

8.カーソルの生成はデータベースのバージョンアップ時は不可だと思う

9.カーソルのsuccessイベントはカーソルの移動回数分繰り返すので、Promise使った方が良さそう

最終のsuccessイベントは、event.target.result === undefined なので、うっかりcursor.valueすると痛い。resolveだけ処理すればいい。

const trans = database.transaction(オブジェクトストア名);
const promise = new Promise( (resolve, reject) => {
  try {
    const objectStore = trans.objectStore(オブジェクトストア名);
    const cursor = objectStore.openCursor();
    cursor.onsuccess = (event) => {
      var cursor = event.target.result;
      if (cursor) {
        console.log(`${cursor.key} is ${cursor.value}.`);
        cursor.continue();
      } else {
        console.log("end");
        resolve(true);
      }
    };
    cursor.onerror = (event) => {
      const error = event.target.error;
      reject(error);
    };
  } catch (ex) {
    reject(ex);
  }
});
return promise;

な感じだった。

データベースの構成をイジる処理をコードしてみると、

何かと面倒くさかったので、

全データベースまたはオブジェクトストア単位でのexportsimports機能を作って、

データを編集するコード(createDatabasedeleteDatabaseとか)を

使いまわしをした方が良さそう。

どうせやることは、

class IndexedDbOp {
  constractor(json) {
    this.json;
  };
  const createDatabase = (databaseName) => {
    this.json.databases[this.json.databases.length] = {'database':{
    'name': databaseName,
    'version': 1
    }}
    return this;
  }
  const deleteDatabase = (databaseName) => {
    this.json.databases = this.json.databases.filter( database => database.name !== databaseName );
    return this;
  }
  ・・・以下同文・・・
}

なのにブラウザにはindexedDBのexportsimports機能が付いていない不思議。

※上記のソースコードは雰囲気です。



【JavaScript】正規表現の\sではまる

空白でテキストを区切る場合

text.split(” “)で十分だけど

タブや改行でも区切りたかったので

text.split(/\s/g)にすると

ECMAScript 準拠なら、\s: 空白文字 ([ \t\f\r\n\v])だからいいかな?と思った。

同様に

text.match(/:[^\+…\’\s]+/g)で、SQLのパラメータっぽい事を処理しようと

「select * from table1 where aaa =’:コード[20]’」は予定通り「:コード[20]」が取れた。

「select * from table1 where aaa =’:コード{20}’」は予定通り「:コード{20}」が取れた。

しかし、

「select * from table1 where aaa =’:コード(20)’」は予定外の「:コード」が取れた。

これでは困るので\sのかわりに\u0020を使って

text.match(/:[^\+…\’\u0020]+/g)とすると

「select * from table1 where aaa =’:コード(20)’」は予定通り「:コード(20)」が取れた。

ところが、サンプルを作ってみると異常は起きない。

特定の環境か使い方でしか起きない様だ。

そうだったら恐ろしいけど、よくよく見ればコードミスかな?



[MS-Access]ODBC接続先変更

接続先データベースのIPアドレスが変わってしまった場合、WindowsのODBCマネージャでIPアドレスを変更すると概ねOKだったが、MS-Accessで外部データベースをODBC接続でリンクした場合だけ、MS-Access内部でIPアドレスをコピっているらしく変更前のIPアドレスに繋ごうとする。

リンクテーブルマネジャを使用してもIPアドレスを書き換えれない様なので、リンクテーブルを2-3個作り直すハメになった。

しかし、VBAを使えばリンクテーブルが1000個あっても泣かずに済みそうだ。

Public Sub ODBC接続先更新(strConnect As String)
  For Each tableDef In CurrentDb.TableDefs
    If tableDef.Attributes = dbAttachedODBC Then   'リンクテーブルならば、
      tableDef.Connect = connectString       'ODBCの接続テキストを書換える
      tableDef.RefreshLink                         '書き換えた内容で動く様にする
    End If
  Next tdf
End Sub

短く読みやすい記事に載せるコードとしては最良だ。

しかし、今時のMS-Office365の中のAccess2007の中で保持しているODBC接続テキストが

ODBC;DATABASE=xxxxxx;DSN=xxxxxx;OPTION=0;PORT=xxxx;SERVER=192.168.1.xxx;CHARSET=xxxxx;

だったりするので、Connectプロパティを直に書換えるのはバージョンの依存度がかなり高そう。

※CHARSETを正しく指定しないと環境文字(㈱等)が?になったりするので地味に痛い。

実際にはこんな感じで使ってみた。

Public Sub ODBC接続先個別更新(SERVER_IP_ADR As String, UID As String,  PWD As String)
  For Each tableDef In CurrentDb.TableDefs
    If tableDef.Attributes = dbAttachedODBC Then   'リンクテーブルならば、dbAttachedODBC=536870912
        con = tbldef.Connect
        If con <> "" Then   '  一応、conが""の場合、をsplit(con,";") すると ubound() = -1になってしまうため除外する
            Dim a() As String
            a = Split(con, ";")
            Select Case a(0)
            Case "":    ' MDB
            Case "ODBC":    ' ODBC
                Dim fff As Boolean
                fff = False
                Dim b() As String
                Dim i As Integer
                Dim fUID As Boolean
                fUID = True
                Dim fPWD As Boolean
                fPWD = True
                For i = 1 To UBound(a)
                    If a(i) <> "" Then
                        b = Split(a(i), "=")
                        Select Case b(0)
                            Case "DATABASE":  '特に何もしない
                            Case "UID":
                                fUID = False
                                If b(1) <> UID Then
                                    Debug.Print "SERVER='" & b(1) & "' を 'SERVER=" & SERVER_IP_ADR & "'に更新します。"
                                    fUID = True
                                    b(1) = UID
                                    a(i) = Join(b, "=")
                                End If
                            Case "PWD":
                                fPWD = False
                                If b(1) <> PWD Then
                                    Debug.Print "SERVER='" & b(1) & "' を 'SERVER=" & SERVER_IP_ADR & "'に更新します。"
                                    fPWD = True
                                    b(1) = PWD
                                    a(i) = Join(b, "=")
                                End If
                            Case "DSN":     '特に何もしない
                            Case "OPTION":  '特に何もしない
                            Case "PORT":    '特に何もしない
                            Case "SERVER":
                                If b(1) <> SERVER_IP_ADR Then
                                    Debug.Print "SERVER='" & b(1) & "' を 'SERVER=" & SERVER_IP_ADR & "'に更新します。"
                                    fff = True
                                    b(1) = SERVER_IP_ADR
                                    a(i) = Join(b, "=")
                                End If
                            Case "CHARSET":  '特に何もしない
                            Case Else:       '特に何もしない
                        End Select
                    End If
                Next
                ' UIDが未設定の場合、追記する
                If fUID Then
                    ReDim Preserve a(UBound(a) + 1)
                    a(UBound(a)) = "UID=" & UID
                    fff = True
                End If
                ' PWDが未設定の場合、追記する
                If fPWD Then
                    ReDim Preserve a(UBound(a) + 1)
                    a(UBound(a)) = "PWD=" & PWD
                    fff = True
                End If
                ' 何か変更されていた場合は、接続テキストを再構成する
                If fff Then
                    con = Join(a, ";")
                    tbldef.Connect = con
                    tbldef.RefreshLink
                    Debug.Print "設定を更新しました。" & vbCrLf & "'" & con & "'"
                End If
            End Select
        End If
    End If
  Next tdf
End Sub

どうやらリンクテーブルには

  • TableDefAttributeEnum.dbAttachedODBC
    MS SQLServer など、ODBC を使うリンクテーブル
  • TableDefAttributeEnum.dbAttachedTable
    MS Access などの非ODBCのリンクテーブル

の2種類があるらしいけど、ググってもどんな動作環境なのかは判らなかったので、

  • Enumクラスを指定しないと遺憾な場合
  • Enumクラスがそもそも無い場合

がありそうで、この辺は実際に動かして調整するしか無さそうだ。

それとPORTなんかが違う場合もありそうだ。

※デフォは学習用、xxxxはデバッグ用、yyyyはテスト用、zzzzは実機とかね。

他のデータベースの場合は、コードページの設定やオプション等も違うだろうし、データベース・ディクショナリ名とか実行時ロール名とか独特な何かを指定しないとダメな場合もあるだろう。

そんな風にDATABASEが違うだけでテキストが全く異なる場合には最初のコードの方が良さそうだから、

MS-ACCESSのVBAのBASファイルには両方を置いておいた方が安心かな。



[Windows11]ブラックアウト

例のゲーム画面が真っ暗になる中国ブラゲーのPC版を遊んでいるうちに

ふと気が付くと、デスクトップのショートカットが全て消えていた。

観えるのは初期設定のままの壁紙だけ。

画面の下ではアプリ、通知、時計その他一切のアイコンが空っぽになったタスクバーが点滅していた。

サインアウト。

ログイン。

壁紙そして空っぽのタクスバーが点滅。

何も変化なし。

ALT+CTRL+DELキー操作でタスクモニタを起動すると

Expolore.exeが出たり消えたりしてうっとおしい。

スクロールしていくとその下に大量の通信(VHOST)の形跡が続く。

グラフに切り替え、動きを眺めると通信だけがいつまでも繰り返されている。

ログイン直後にタスクバーから通知か何かを表示するために通信している最中にExplore.exeがクラッシュ&リブートしている様な感じ。

どうやら、またログイン直後のトラブルにしハマった様だ。

シャットダウン。

ログインすると画面は隅から隅まで真っ暗になった。

使えるのはマウスとALT+CTRL+DELのキー操作だけ。

再起動前は壁紙が映っていたがVRAMに残っていた残骸だったのかもしれない。

空っぽのタスクバーすらもVRAMに残っていた残骸だったのかもしれない。

とりあえずタスクバーであるexplore.exeが何か表示しようとしたらクラッシュ。

タクスマネージャーが何も考えず消えたexplore.exeを起動しなおしている。

そういうことだ。

もう一度再起動しログインしても画面は真っ暗。

(と、ここまで書いたらWindowsUpdateが勝手に再起動した。

こうならないようになっているハズなんだが・・・

まだ不調なのかな?)

USBの回復ドライブを挿して起動するもSSDから起動してしまう。

再起動。

DELキーを押し続けUEFIに切り替える。

ブート画面からUSBメモリを指定して起動。

ログイン。

すると、普段どおりに戻った。

WindowsUpdateを行うと

  • (KB5010414)
  • (KB5010474)と.Net4.8

の更新が入った。

KB5010474は、4.8 で導入された IRawElementProviderSimple オブジェクトのリーク.NET Frameworkします。だそうだ。

注記には

NET ライブラリ– TLS 1.3 のネゴシエート時にクライアント証明書が使用されている場合に Ssl ネゴシエーションが無期限にハングする可能性がある問題に対応します。 変更の再ネゴシエーション (PostHandshakeAuthentiction) が失敗する前に、SslStream または HttpWebRequest でタイムアウトが観察されます。

と書いてあった。

まさにそんな感じになっていた。

多分、治ったんだろう。

とは云え気になるので回復ドライブを更新しようとしたら16GB以上を要求された。

(前もそうだったのかな?)

で、作成中にさっきの強制再起動。

もう一回作り直し。

16GB以上推奨だからUSB2タイプのメモリではコピー時間が長すぎる。

USB2の転送レートは480Mbpsだから、理論値でも

16[GB] * (480[Mbps]/1000[G/M]/10(bit/byte))≒333秒≒6分

ファイルがいっぱいあれば、ファイルを管理する情報の書き換えもいっぱい発生するので1時間ぐらいかかりそうだ。

本当に1時間ほどかかってしまった後・・・

8GBしか使ってないw

山ほどファイルがあったのかな?

ファイルの総数:
             226 個のファイル       7,845,198,033 バイト
             271 個のディレクトリ  25,963,888,640 バイトの空き領域

ファイル数もそんなに多くない。

USBメモリが熱ダレ(過熱防止)してたのか?

イミフに長~く待たされてる間に

F8でセーフティモードで起動する設定は無効になってた様なので有効にしておいた。

> bcdedit  /set {default} bootmenupolicy legacy

戻す時は最後のlegacyをstandardに差し替える。

例のゲームのタチの悪さが増した様な気がするなぁ。



[Windows11] ペイントの場所

WinSCPで画像をイジろうとしたらmspaint.exeが行方不明になっていた。

Windows10までは、

C:\Windows\System32\mspaint.exe

だったハズだけど、Windows11になったら、見当たらない。

以前はアイコンを右クリック&プロパティでインストール先が判ったけど、

Windows11では徹底的にその辺は隠蔽する方針らしく無理なので、コマンドラインからwhereコマンドでファイル検索してみる。

C:\>where /R C:\ mspaint.exe
C:\Users\${ユーザ名}\AppData\Local\Microsoft\WindowsApps\mspaint.exe
C:\Users\${ユーザ名}\AppData\Local\Microsoft\WindowsApps\Microsoft.Paint_*************\mspaint.exe
C:\Windows\servicing\LCU\Package_for_RollupFix~****************~*****~~*****.***.*.*\amd64_microsoft-windows-mspaint_****************_**.*.*****.**_none_****************\*\mspaint.exe

といくつか候補が出てきたけど、最初のものが短くてよさそう。他は乱数っぽい文字列が沢山付いてて流用が難しそう。

C:\Users\${ユーザ名}\AppData\Localは、SETコマンドで見つけた環境変数LOCALAPPDATAで代替すれば

%LOCALAPPDATA%\Microsoft\WindowsApps\mspaint.exe

になる。

コマンドラインからmspaint.exeダケでも実行できるから、

%LOCALAPPDATA%\Microsoft\WindowsApps

がパスに追加されてるのかな?

C:\WINDOWS\system32
C:\WINDOWS
C:\WINDOWS\System32\Wbem
C:\WINDOWS\System32\WindowsPowerShell\v1.0\
C:\WINDOWS\System32\OpenSSH\
C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR
C:\Program Files\Git\cmd
C:\Program Files\dotnet\
C:\PROGRA~1\JPKI
C:\Program Files (x86)\dotnet\
C:\WINDOWS\system32
C:\WINDOWS
C:\WINDOWS\System32\Wbem
C:\WINDOWS\System32\WindowsPowerShell\v1.0\
C:\WINDOWS\System32\OpenSSH\
C:\Program Files\nodejs\
C:\Users\${ユーザ名}\AppData\Local\Microsoft\WindowsApps
C:\Users\${ユーザ名}\AppData\Local\Programs\Microsoft VS Code\bin
C:\Users\${ユーザ名}\AppData\Roaming\npm
C:\Users\${ユーザ名}\.dotnet\tools

色々インストしたから色々とパスに追加され過ぎな気がする。

WinSCPの環境設定のエディタに追加してあったMspaintの外部エディタの設定を

"%LOCALAPPDATA%\Microsoft\WindowsApps\mspaint.exe" !.!

に変えてみると、

また画像を直接編集できるようになった。

しかし大型アップデートが来たら、また編集できなくなるかもしれない。

あれ?WordPressで文字単位で色が変えられない!

と思ったら「A テキスト色」が「A ハイライト」に変わったダケだった。

背景色もつけられるから便利だけど、文字の下に色指定のパレットが出てくるのでパレットが切れ気味。



[三国英雄の夜明け]いまいち

12 million power

戦力は順調に増強中。

しかし周囲も順調に増強中につき、1月はずーっと勢力内29位、ワールド内85位のままピクリとも動かず。

但し、シーズンイベントの職業限定の限定挑戦の為に装備や副将を調整している時は戦力が200万くらい下がってしまうこともままある。主に文民が一強状態のせい。貂蝉を200万まで上げるのに手間取り、馬超、趙雲の強化が出遅れ、小喬、甄姫以下はほとんど放置気味だったので今になって強化中。

大した戦力でもないのに慢性的な食料不足で9英雄の同時運用は難しい。部隊数が必要な時は副将を外して兵力を減らしていかないと続かない。

今は4兵舎に兵士48万づつを待機させられるが兵士1人に付き食料8.4が必要なので、最大兵士192万を作成するには食料1,612万8千が必要で甕陽戦の後にはそれがほぼ空っぽになってしまうのが食糧難の主な原因。

空っぽの兵舎の様子

対戦時にパワーアップする幕府要員の強化はもっと遅れてて、特に逸材、弓兵が66%と低い、弓兵は70%に上げたけど、逸材はそのまま。

資材と黄金を放り込む兵舎の技術は歩兵技術だけランク80に他もボチモチ上げたい。

砂盤演技は30章の序盤で 馬超、趙雲 が戦力200万未満なせいか負け続けてて、英雄の装甲の強化が止まってる。

魂玉は全6品質、八宝獣霊は全紫色を目標に強化中だけど日々の黄金探索と朝廷の密使クエストを潰していくしかないから地味。戦力が弱いから密使で「プレイヤー部隊の撃破」が出ると影響が大きすぎ。今日も2つ目のお題が4/7と未了になりそう。

・・・

パラメータ・・・

多すぎ。

ps.2022/2/2

貿易がうまくいかず、特産品の集まりが一番悪く、

李代桃僵 あと一つ
火事場泥棒はさっぱり進まない

そんな訳で、趙雲に定計を付くのはまだまだ先。

戦力1250万

戦力増強はあまり進んでいない。

ps.2022/03/13

戦力1410万
とてもゆっくりな進捗の定計

やっていること。

  • 領地の官邸の幕府のレベル上げ(今レベル35前後、対戦時の強化率は82~86%)
    • 珍宝商店で文書を買うのがメインなので黄金の消耗が激しい
      • 採掘の黄金狙いだけでは全く足りないので、建設で賄賂ボタンを押下する様になった。
  • 幕府出陣キャラのSクラス維持
    • 基礎パラメータの補助スキルアップで評価が上がりやすいがカードは出にくい
  • 兵舎の技術ランクアップ(今ランク74~88)
    • 資源(木材、金属)の消耗より失敗時の黄金の消耗が激しい
  • ミニゲーム(竜金探索)で得る魂玉の品質アップ(今の最高品質は7)
    • 竜金探索の採掘レベルがMax40になったのでペースは一定
  • 獣霊のレベルアップ
    • LV20までは獣霊元気、それ以上は獣霊元神を消費してレベルアップ
    • アイテムは朝廷の密旨のお題次第(ランダム)
  • 基礎パラメータ(武力、知力、魅力、統率)アップ
    • 補助スキル(勇武、才学、修養、兵法)のアップ
      • 訪問や狩場を補助スキルが出るキャラ(甘寧、筍或、法正、呂蒙)に集中
        • 毎日の任務で拝帳(訪問時に消耗)や角笛(狩場で消耗)を得る
          • ランダムなのでコンスタントに得るのは難しい
  • 定計のスキルの入手と★アップ
    • 貿易でアイテム交換して入手
      • 相手が欲しがるアイテムは大抵領地外の港の名物
    • 4★までアップすると、キャラに定計スキルを付加できる
      • 4★にアップ直前になると大枚の黄金を要求されることが多い

そんな感じで毎日、黄金千枚と銀両600万を使い込んでいる。

ps.2022.4.9

戦力増強は停滞気味。

戦力1500万

引き続き「砂盤演義 第30章 檮杌と遭遇」のため、2番手3番手を優先。

ps.2022/6/19

昨日、FORTEを買ったらCAMELのクーポンが出たけど品切れだったので今日引き換えに行ってFORTEを買い忘れたスラです。

このゲーム内でもクーポンが出ます。

クーポン(タダ券)続出中

チャットの見た感じでは無料でクーポン7枚をゲットしたかの様に観えますが、

クーポンを引き当てるまで黄金500枚(5回分)を消費してるので実質59%、6割引きでした。

なので、見た目ほど美味しい訳ではありません。

今の勢力の進捗は・・・

主に、

  • 八門獣霊狙いで、密使シーズンイベント
  • 魂玉狙いで、竜金探索
  • 幕府文書狙いで、珍宝商店

がメインとなっています。

マップでの無謀な振る舞いは主に密使の悪だくみかシーズンイベントの限定挑戦のポイント稼ぎの勢。

密使とシーズンイベントの攻略は、兵士の消耗が著しく食糧難が続き黄金の備蓄も厳しいですが、戦力強化のメインなのでサボれません。年度報酬で戦力消耗も倍率に加算されるので建設も頑張り年度報酬の倍率を8~9倍にしたい。しかし、8倍で4000枚越える時もあれば9倍で3000枚を下回る時もあるのが悩ましい。

PC対戦で影響が大きい幕府レベルを上げる為に必要なアイテムはレベルに応じて増える。しかも幕府に所属する英雄の戦力もレベル成りに引き上げないと幕府の応援効果が上昇しない。その一方で英雄の戦力(スキル)レベルを上げる度に消費するカード数や銀銭も増えるから、金欠まっしぐらです。

なのに、

自戦力全体で100万UPする頃には、課金勢は単騎で100万UPしてたみたいな感じで戦力差が拡大している様にしか思えない状況w




top