OUTLOOKのイベントをエクセルVBAのトリガーにする。

OUTLOOK研修所から以前に問い合わせについての解答をいただきましたが,私の環境で,なかなか実現でき
ませんでした。実現できなかった原因は,そもそも,設定仕方等が,全く違っていたのです。それに,肝
心のこと忘れていました。イベントを受け取るにには,プロシージャが終了していては,いけないのです。
肝心要のこのことも抜けていました。それで,copilotにしつこく聞いたり,google先生をたよったりして,
本日やっと実現できました。

まず,OUTLOOKのイベントをつかむには,

Public WithEvents mySentItems As Outlook.Items

を書く必要がありますが,このコードは,クラスモジュールに書かなければいけにようです。同時に,
OUTLOOK研究所でおしえてもらった,サブルーチン等も,クラスモジュールに置く必要があるようで,標
準モジュール等に置くと,エラーになります。また,クラスモジュールにおいたサブルーチンを呼ぶには
手続きがいるようで,これも抜けていたようです。下記がClassモジュールにおいたものの全文です。

Option Explicit

Public myOutlookApp As Outlook.Application
Public mySentFolder As Outlook.Folder
Public WithEvents mySentItems As Outlook.Items

Private Sub Class_Initialize()
    Set myOutlookApp = Nothing    'objoutlookの初期値これがないとGetObjectに失敗するとobjoutlookはemptyのまま
    
    On Error Resume Next
        Set myOutlookApp = GetObject(, "Outlook.Application")     '起動済みのOutlookをクラス名を指定して取得
    On Error GoTo 0

    If myOutlookApp Is Nothing Then                               'objoutlookがNothingなら
        Set myOutlookApp = CreateObject("Outlook.Application")    'Outlookを起動
    End If

    Dim oNS As Namespace
    Set oNS = myOutlookApp.GetNamespace("MAPI")
    Set mySentFolder = oNS.GetDefaultFolder(olFolderSentMail)
    Set mySentItems = mySentFolder.Items

End Sub
Public Sub AddTrackInfo(ByVal objMail As MailItem, iRow As Integer, iCol As Integer)
     Dim olkApp As Outlook.Application
     Dim fldSentMail As Folder
     Dim strTrackInfo As String
     Dim propTrack As UserProperty

     ' 送信済みアイテム フォルダーを取得
     Set fldSentMail = objMail.Application.Session.GetDefaultFolder(olFolderSentMail)

     ' mySentItems が設定されていなければ送信済みアイテム フォルダーの Items を設定
     If mySentItems Is Nothing Then
         Set mySentItems = fldSentMail.Items
     End If


     ' メールに送信後の保存先フォルダーを設定

     Set objMail.SaveSentMessageFolder = fldSentMail


     ' 送信状況を追跡するためのプロパティを設定
     Set propTrack = objMail.UserProperties.Add("TrackInfo", olText, True)

     ' 送信日時を保存するセルの行番号と列番号を設定
     propTrack.Value = iRow & "," & iCol
     
End Sub


' 送信済みアイテム フォルダーにアイテムが追加されたときに実行されるイベント
Public Sub mySentItems_ItemAdd(ByVal Item As Object)
     Dim objMail As MailItem
     Dim propTrack As UserProperty
     Set objMail = Item
     ' 送信状況を追跡するプロパティの確認
     Set propTrack = objMail.UserProperties.Find("TrackInfo")

     ' プロパティが存在したら
     If Not propTrack Is Nothing Then
         Dim arrRC As Variant
         ' 送信日時を保存するセルの行番号と列番号を取得
         arrRC = Split(propTrack.Value, ",")
         ' 取得した行、列のセルに送信日時を保存
         Sheet1.Cells(CInt(arrRC(0)), CInt(arrRC(1))).Value = objMail.SentOn
     End If
End Sub

これを実行するには,標準モジュール等から,このクラスモジュールのサブルーチンを下記のようにして
呼び出します。VBAを終了しないよううに,最後にユーザーフォームを表示してます。

Public Function Mail_Sousin()
        ▼
		詳細設定略
        ▲ 
           
                Dim clsSample1 As Class1 'Class1型のオブジェクト変数を宣言 「Class1」はクラスモジュール名
                Set clsSample1 = New Class1
    
                Call clsSample1.AddTrackInfo(objItem, 4, 9) 'objItemは,送信するメール本体4,9はRange(4,9)で
                               送信日時を記録すすセル
                           
                If jidou_sousin_flag = False Then
                    objItem.Display  '送信画面を表示
                End If
                If jidou_sousin_flag = True Then
                    objItem.Send    '自動で送信
                End If

              UserForm1.Show

               Set olkApp = Nothing
               Set objItem = Nothing
               Set acctToSend = Nothing

End Function
クラスモジュールのサブルーチンを呼ぶには,インスタンス?を下記のように作成しないとだめみたいでした。

                Dim clsSample1 As Class1 'Class1型のオブジェクト変数を宣言 「Class1」はクラスモジュール名
                Set clsSample1 = New Class1
    
                Call clsSample1.AddTrackInfo(objItem, 4, 9)

なお,一番参考になったのは,外国の掲示板でした。

ブラウン シェーバー 350CC 電池交換 際ぞりの外し方  分解注意

電池交換は無事完了しましたが,どうやら,分解の過程で,際ぞりの部分を外すときに,際ぞりの部品を破損
してしまったようです。部品交換するべく,ヤフオクで,中古品を購入しましたが,これも,破損させてしま
いました。何回も組み立て分解を繰り返しやっとその仕組みがわかりました。頭悪いですね。
際ぞりの部品は,写真,赤→と黄色矢印が合わさる位置でないと外れないようになってます。それを邪魔して
いるのが,緑丸の突起です。この突起が緑四角の中を移動ししますが,突起が,外れる位置まで,移動するの
を邪魔しています。

ですから,外すには,


最上部まで,スライドさせ,


突起の部分に,突起を持ち上げるものを差し込んで,さらに,上にスライドさせます。



赤→の穴が見える位置が,外せる場所です。通常は,爪のため,この位置まで,ずれません。このようにする
と,破損させることなくはずせます。高い,勉強代になりました。また,安い物がでていたら,購入
して直そうを思います。

これからは,google先生 よりも AI か

今日初めて,マイクロソフトのPC付属の cipilot に質問してみました。
質問は,

VBAエクセルで,アクティブセルの背景の色を変えるコードは。条件付き書式では」

で,下記のような解答でした。

 Private Sub ApplyConditionalFormatting()
     Dim ws As Worksheet
     Dim rng As Range
     
     ' シートと範囲を設定
     Set ws = ThisWorkbook.Sheets("Sheet1") ' シート名を適切に変更してください
     Set rng = ws.Range("A1:A100") ' 範囲を適切に変更してください
     
     ' 既存の条件付き書式をクリア
     rng.FormatConditions.Delete
     
     ' 新しい条件付き書式を追加
     With rng.FormatConditions.Add(Type:=xlExpression, Formula1:="=ROW()=CELL(""row"")")
         .Interior.Color = RGB(255, 255, 0) ' 背景色を黄色に設定
     End With
 End Sub

google先生よりも,具体的で,この解答,ほぼあってます。

ブラウン シェーバー 350CC 電池交換

アリエクから,電池がとどいたので,シェーバーをくみたてました。
電池を入れたところで,充電して,モーターが動くことを確認しました。
際ぞりの部分を外すとき,一部分,破損させてしまったようで,ちょっとがたがたしてます。
これで,また,しばらくつかえます。

Messaging API でグローバルIPの通知 ラズベリーパイ

ハードルが高いと思っていたMessaging APIで,メッセージを送ることがやっとできました。といっても,諸
兄の丸パクリ寄せ集めです。ただ,その過程で,今までよりも少し,Linux 関連の知識が増えたように思い
ます。
今回使ったのは,シェルスクリプトと呼ばれる一種のプログラムで,MS-DOS時代のバッチファイルのような
感じのものです。前から,存在は分かっていたのですが,今回の作業過程で,理解がふかまりました。これも
貴重な情報を掲載してくれている様々な諸兄のおかげです。
シェルスクリプ私なりに作成しましたが,大きく分けて,お二人の方の合体です。一人はグローバルIPを取
得する部分,お一人はメーッセージを送る部分,をかいていたので,それを自分なりにアレンジして合体し
て動かしました。

#!/bin/sh

#IPアドレスの取得
newip=`curl -s inet-ip.info`
for i in `seq 5`
do
	if [ "$newip" = '' ]; then
		newip=`curl -s inet-ip.info`
	fi
done
if [ "$newip" = '' ]; then
	exit 1
fi

#IPアドレスが変化したら,メッセージを送る。
oldip=`cat /home/mabo52/pgip`
if [ $oldip != $newip ]; then
  echo "$newip" > /home/mabo52/pgip
  curl -X POST \
  -H 'Content-Type:application/json' \
  -H 'Authorization: Bearer {#公式アカウントで取得したTOKEN}' \
  -d '{
    "to": "#公式アカウントで取得したID",
    "messages":[
        {
        "type": "text",
        "text": "IPアドレスが変わりました。IP='$newip'"
    }
    ]
}' https://api.line.me/v2/bot/message/push

fi

これを実行してみると見事メッセージが送れましたので,久しぶりに,「やったー」になりました。
このスクリプトでは,pgip というグローバルIPを保存するファイルをあらかじめ同じ階層に作成
しておく必要があります。元のスクリプトでは,この保存ようのファイルがあるかないかのチェック
をいれてありますが,私のでは省いてあります。このファイルに実行権を与えて,あとは,crontab
にスケジュールを記載して,定期的に実行すればOKになります。
グローバルIPの取得に関しては,

  https://blog.n-hassy.info/2022/03/line-notify-from-raspi/

を,メッセージの送信かんしては,

  https://news.mynavi.jp/techplus/article/linebot-2/

をほぼ,丸パクリさせてもらいました。公式アカウントの取得では,

  https://zenn.dev/kou_pg_0131/articles/line-push-text-message

参考にさせていただきました。
ただ公式アカウントの作成では,

   ログイン
    ↓
   プロバイダーを作成する

までは,うまくいったのですが,この段階では,ビジネスアカウントの設定が不完全だったので,次の段階の

  チャンネルの作成

に手間取ってしまいました。作成の手順が参考にしたHPとは若干違っていたのか,何とかできました。

以前は,LINE Notify のIDとトウクンで,LINE Notifyにメッセージを送る感じ
でしrたが,今回は,

   ビジネスアカウントを作成
      ↓
   作成したアカウントと友達になる。
      ↓
   作成の過程で取得したトウクンとIPを保存Dでビジネスアカウントにメッセージを送る

一手間かかったように思います。ただ,ビジネスアカウントの無料のコースでは,一月にメッs-ジ
を200通しか送れません。まあ,グローバルIPの通知に使うのには,十分でしょうね。

まだ,スケジュールは組んでないのですが,この後,スケジュールを組んで,動作確認して,運用ですね。

ラズベリーパイ(3B) MJPG-STREAMER再構築

何日前からか,居間のの監視カメラ代わりに使っていたラズベリーパイの調子がおかしくなりました。このラ
ズベリーパイの設定をしたのが2016年7月ですから,8年近く,休みなく動いていてくれました。何のメ
インテナンスもしなかったので,当然かなと思います。MJPG-STREAMERのソフトの動きがカクカクになった
り,そもそも,動かなくなったりで,何度も再起動を繰り返して,だましだましつかってましたが,2,3,
日前に起動しなくなりました。
という訳で,ラズベリーパイのOSからの再構築いたしました。丸二日ほどかかりました。再構築に時間がか
かたのは,基幹のOSがアップデートされていて,以前参考にしていた諸兄の方法では,エラーがでてしまっ
てうまくいかなかったのが原因です。幸い,新しいOSに対応したやり方を掲載してくれた諸兄がいましたの
で,そこを丸パクリで,なんとか構築いたしました。

ただ,諸兄のページでも,二カ所修正が必要で,ここで,ちょっと躓きました。

   〇DEV=”/dev/video1″→→→→DEV=”/dev/video0
   〇WIDTH=1920→→→WIDTH=320  HEIGHT=1080→→→HEIGHT=240

ここを変更して,うまく動くようになりました。また,ベーシック認証がなかったので,

   #ベーシック認証の設定
   ID=”******” #ベーシック認証用のID
   PW=”******” #ベーシック認証用のパスワード

を加筆して,下記のように設定を変更しました。

/mjpg_streamer -i “./input_uvc.so -d $DEV -r $RES -f $FPS” -o “./output_http.so -w ./www -p $PORT”

/mjpg_streamer -i “./input_uvc.so -d $DEV -r $RES -f $FPS” -o “./output_http.so -w ./www -p $PORT -c $ID:$PW

これで,写真のように,居間の様子が,見れるようになりました。



それと,今までは,外出先からも確認できるようにと,グローバルIPの変化を,LINE Notify
で,携帯に送ってましたが,LINE Notifyも3月でサービス終了の連絡がきましたので,変更せざ
るをえなくなりました。
代替え案とし,MessagingAPI が紹介されていましたが,自力で変更するのはちょっとハードル
が高いので,自宅サーバーでも使っていた,DDNS のサービスを使うことにしました。以前は,イエサー
バーを使っていましたが,今回は,無料で使える DDNS Now を使うことにしました。幸い更新の方法もい
くつかあり,crontab を使う方法がありましたので,下記のものをcrontab に登録しました。

0-59 * * * * wget -O DDNSNow_update.log “https://f5.si/update.php?domain=****&password=****”

ログを見てみますと,1分おきにグローバルアドレスをチェックしているようで,うまくいきましたが,
この crontab の設定にも時間がかかり,一度は,OS を入れ替えました。

紆余曲折しましたが,防犯カメラよろしく,外部からも居間の様子を確認できるようになりました。
ただ,安価に確認するのであれば,市販のものを使った方が,はるかにいいと思います。

VBA プロシージャ名の取得 ハッシュテーブルの利用

VBAで,プロシャージャ名を取得する方法をあれこれあさっていましたが,あるサイトに,VBSでプロ
シージャ名を取得するVBSがのっていました。VBSとVBAの区別がつかなかったのですが,別物という
ことに気づき,あるサイトでVBSをVBAに変換するサイトがありましたので,そこでVBAに変換してみ
ました。下記のVBSを変換すると,

Set HashTbl = CreateObject("System.Collections.Hashtable")
Set CPobj = WBobj.VBProject.VBComponents("Module1")
Set CMod = CPobj.CodeModule
For i = 1 To CMod.CountOfLines
    ProcName = CMod.ProcOfLine(i,0)  ' プロシージャ名を取得
    If ProcName <> "" And HashTbl.ContainsKey(ProcName) = False Then
        HashTbl.Add ProcName, i
        EXLapp.MacroOptions CPobj.Name & "." & ProcName,,,,True,""
    End If
Next

下記のように変換してくれました。

' This code is translated from VBScript to VBA.
Dim HashTable As Object
Dim ComponentObject As Object
Dim CodeModule As Object
Dim ProcedureName As String
Dim LineIndex As Long

Set HashTable = CreateObject("Scripting.Dictionary") ' Using Scripting.Dictionary for better compatibility in VBA
Set ComponentObject = WBobj.VBProject.VBComponents("Module1")
Set CodeModule = ComponentObject.CodeModule

For LineIndex = 1 To CodeModule.CountOfLines
    ProcedureName = CodeModule.ProcOfLine(LineIndex, 0)  ' Retrieve the procedure name
    If ProcedureName <> "" And Not HashTable.Exists(ProcedureName) Then
        HashTable.Add ProcedureName, LineIndex
        EXLapp.MacroOptions ComponentObject.Name & "." & ProcedureName, , , , True, ""
    End If
Next LineIndex

ただ,このままではだめで,一部修正が必要でした。まず,

 EXLapp.MacroOptions ComponentObject.Name &amp; "." &amp; ProcedureName, , , , True, ""

の部分は,多分,ショートカットの設定の部分?なので,削除しました。また,「WBobj」の設定がしてない
ことでエラーになるので,ここを,とりあえず「Thisworkbook」に,そして,表示の部分を加えて下記のよ
うにしました。

Dim HashTable As Object
Dim ComponentObject As Object
Dim CodeModule As Object
Dim ProcedureName As String
Dim i As Integer
Dim keys() As Variant
Dim values() As Variant

Set HashTable = CreateObject("Scripting.Dictionary") ' Using Scripting.Dictionary for better compatibility
Set ComponentObject = ThisWorkbook.VBProject.VBComponents("Module1")
Set CodeModule = ComponentObject.CodeModule

For i = 1 To CodeModule.CountOfLines
    ProcedureName = CodeModule.ProcOfLine(i, 0)  ' プロシージャ名を取得
    If ProcedureName <> "" And Not HashTable.Exists(ProcedureName) Then
        HashTable.Add ProcedureName, i '要素にプロシャージャの開始行番号を追加
    End If
Next i

keys = HashTable.keys
values = HashTable.Items

For i = 0 To UBound(keys)
    Debug.Print keys(i) & " " & values(i)
Next i

これで,自身のModule1に記載された,プロシャージャとその開始行番号が表示されるようになりました。

今回,参考にしたHPにハッシュテーブルを使った例がでていましたので,ハッシュテーブルを使ってみまし
た。ハッシュテーブル(連想配列)は,以前から知っていましたが,使ったことはありませんでした。通常の
配列と違って,設定した key によって,それに応じた item にアクセスできるところが便利な所
のようです。

VBA 他シートへ関数の書き込み

開いているBook 間では,VBA の プロシャージャの書き込みもできるようです。

下記のプロシャージャでは,新しく開いたエクセルのファイルに,macro.txt に書かれた関数を書き込みます。

Private Sub CommandButton3_Click()
    'OPENしたBOOKのSHEET1に ”macro.txt” のプロしィージャを書き込む
    Dim wb As Workbook
    
    fTyp = "Excelファイル(*.xlsm),*.xlsm,Excelファイル(*.xlsx),*.xlsx,Excelファイル(*.xls),*.xls"
    prompt = "Excelファイルを選択してください"
    fPath = Application.GetOpenFilename(fTyp, , prompt)
    
    Set wb = Workbooks.Open(fPath, UpdateLinks:=Ture, ReadOnly:=Ture)
    
    With wb.VBProject.VBComponents("Sheet1").CodeModule
        .AddFromFile "C:\Users\mabo-2021\Desktop\****\*****\*****\*****\macro.txt"
    End With
    
End Sub

macro.txtという下記の内容のファイルを同じフォルダーにおいてきます。

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 Application.ScreenUpdating = True
End Sub

VBA 他シートへの条件付き書式の書き込み

他シートに記載してあるデータを転記する必要がありました。特定の列を転記するので,転記す
る列を強調するにはどうしようかと考えていました。
メインとなっているBookから,転記するシートを開く,転記する列を選ぶ,転記する,との一連
の動作は,一通りできるようになってますが,よりわかりやすくするのに,転記する列を強調し
たくなり,その方法をいろいろかんがえていました。転記するBookに条件付き書式を手動で設定
すれば,事足りるのですが,手動では,あまりにも芸がないので,VBAから,できないか,思案し
ていました。
VBAからVBAに書き込むようかなとあれこれあさっていましたが,条件付き書式は,ブックを開いて
さえいれば,メインのBookから,他のBookに条件付き書式の書き込みができるようなので,実際に
実行してみました。
メインのVBAに

 '読み込んだファイル名でブックを開く
    Set wb = Workbooks.Open(fPath, UpdateLinks:=Ture, ReadOnly:=Ture)
    Set Taisyou_Sheet = wb.Worksheets("受付名簿")
    
    '対象シートに条件付き書式を設定
    Taisyou_Sheet.Cells.FormatConditions.Delete
    Range(Taisyou_Sheet.Cells(4, 2), Taisyou_Sheet.Cells(101, 14)).FormatConditions.Add(Type:=xlExpression, Formula1:="=CELL(""COL"")=COLUMN()").Interior.Color = rgbPowderBlue

のように書き込むことで,条件付き書式の設定ができたようです。
ただ,これだけでは,だめで,

Private Sub Worksheet_SelectionChange(ByVal Target As Range)
 Application.ScreenUpdating = True
End Sub

を該当シートに書き込む必要があるので,やはり,VBAからVBAを操作する必要がありますね。