その他

タスクスケジューラー、「最上位の特権で実行する」を選択せずExcelVBAのuser32の関数を利用する方法[No35]

投稿日:2020年7月16日 更新日:

スポンサーリンク

ある処理を自動化するExcel VBAを作成しました。そのプログラムはタスクスケジューラーの「全般」タブの「最上位の特権で実行する」をチェックしたタスクでは上手く実行されない作りになっています。

しばらく経過して、本稼働させようとタスクスケジューラーに登録すると問題が発生。タスクスケジューラーで「最上位の特権で実行する」を未選択で実行するとWindowsAPIのuser32.dllの関数のmouse_event(マウスのクリック)が実行されないことが発覚。これはどう対処したら良いか、悪戦苦闘し何とか解決できました。今回はその方法を紹介します。

動作環境

  • Windows PC、Windows 10 Pro
  • PCにはアカウントは1つのみあり。そのユーザーは管理者。
  • 今回、そのユーザーでログオンしそのユーザーでの処理。
  • PCに電源アダプタは接続している

経緯(詳細)

次の状態で、タスクスケジューラー(「最上位の特権で実行する」を未チェック)でマウスを目的の場所に移動させて、クリックさせたいと思っていました。

  • 自動化マクロExcelを起動しクリック処理を実行すると、マウスが目的の座標に移動しクリックできる。
  • 「外部ファイルから起動するためのVBSファイル」をダブルクリックすると、マウスが目的の座標に移動しクリックできる。
  • 「外部ファイルから起動するためのVBSファイル」をタスクスケジューラー(「最上位の特権で実行する」をチェックした状態)で実行すると、マウスが目的の座標に移動しクリックできる。
  • 「外部ファイルから起動するためのVBSファイル」をタスクスケジューラー(「最上位の特権で実行する」をチェックしていない状態)で実行すると、マウスが目的の座標に移動せずクリックもされない。

同事象が発生するプログラム

次のマクロをDドライブ直下にMouseClickTest.xlsmという名前で作成します。


Declare Sub mouse_event Lib "user32" ( _
    ByVal dwFlags As Long, _
    Optional ByVal dx As Long, _
    Optional ByVal dy As Long, _
    Optional ByVal dwDate As Long, _
    Optional ByVal dwExtraInfo As Long)
Declare Function SetCursorPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long

Public Const MOUSE_LEFTDOWN     As Integer = 2
Public Const MOUSE_LEFTUP       As Integer = 4
Public Const MOUSE_RIGHTDOWN    As Integer = 8
Public Const MOUSE_RIGHTUP      As Integer = 16

Public Sub mouseClickR(ByVal psX As Integer, ByVal psY As Integer)
    SetCursorPos psX, psY
    mouse_event MOUSE_RIGHTDOWN, 0, 0, 0, 0
    mouse_event MOUSE_RIGHTUP, 0, 0, 0, 0
    timeWait ("00:00:01")
End Sub

Public Sub mouseClickL(ByVal psX As Integer, ByVal psY As Integer)
    SetCursorPos psX, psY
    mouse_event MOUSE_LEFTDOWN, 0, 0, 0, 0
    mouse_event MOUSE_LEFTUP, 0, 0, 0, 0
    timeWait ("00:00:01")
End Sub

Public Sub test_mouseClickR()
    Call mouseClickR(700, 525)
    MsgBox "クリック処理実行しました。"
End Sub

Public Sub test_mouseClickL()
    Call mouseClickL(700, 525)
    MsgBox "クリック処理実行しました。"
End Sub

Public Sub timeWait(ByVal lengthOfTime As String)
    Application.Wait Now() + TimeValue(lengthOfTime)
End Sub

「test_mouseClickL」をこのエクセルから実行すると、マウスがモニターのX座標700(横から700px)、Y座標525(縦525px)に移動して右クリックし、「クリック処理実行しました。」のメッセージボックスが表示されます。

そして、同じくDドライブ直下に「MouseClickTest_Kicker.vbs」を作成しダブルクリックで実行すると、同様に上手く動作してくれます


Option Explicit

'-----
Dim FilePath
FilePath = "D:\MouseClickTest.xlsm"

Dim MacroName
MacroName = "test_mouseClickR"
'-----

Dim ObjExcelApp
Set ObjExcelApp = WScript.CreateObject("Excel.Application")
ObjExcelApp.Visible = True

ObjExcelApp.Workbooks.Open FilePath
ObjExcelApp.Application.Run MacroName

Set ObjExcelApp = Nothing

しかし、作成した「MouseClickTest_Kicker.vbs」をタスクスケジューラーで「最上位の特権で実行する」を選ばずタスクから実行するとマウス移動やクリックは行われず、メッセージボックス「クリック処理実行しました。」が表示されます。

そして、「最上位の特権で実行する」を選択してタスクから実行すると正常に動作してくれます。

私のように特別の事情がなければ、「最上位の特権で実行する」で実行すれば事は済んでしまいます。

スポンサーリンク

わかっていたこと、わかったこと

憶測を含みますが、わかったことは次のことです。

  • VBAでマウス操作をする「mouse_event」はuser32の関数。
  • user32は「C:\Windows\System32」にある「user32.dll」のこと。
  • このuser32.dllを右クリックすると「削除」「名前の変更」には管理者権限(ユーザーアカウント制御(UAC)関連)が必要。このことから察するに、タスクスケジューラーから実行する際は、強い権限でVBAを起動することでuser32の関数を利用できる。
  • user32.dllのファイルを別のDドライブ直下にコピーしVBAプログラム内で参照設定先の変更を色々な方法(LoadLibraryの利用など)で試したがどれも上手く行かず、「System32」フォルダごとD直下にコピーし、そこのuser32を利用しようとしたが、これも上手く行かなかった。
  • タスクスケジューラーの「セキュリティ オプション」の「タスクの実行時に使うユーザーアカウント」の「ユーザーまたはグループの変更」ボタンから選択できる全てのオブジェクトで試したが、正常に動作するには「最上位の特権で実行する」ボタンを選択する必要があった。
  • タスクスケジューラーでExcelを実行するには「C:\Windows\System32\config\systemprofile」「C:\Windows\SysWOW64\config\systemprofile」のそれぞれに「Desktop」フォルダを作成しておく。正確にはパソコンの構成によって片方だけで良いがどちらにも作成すれば良いと思います。
  • 管理者権限に影響を与えそうな特別な設定はしていない。
  • マウス操作の処理なので、「ユーザーがログオンしているときのみ実行する(R)」を選択しておく必要がある。「ユーザーがログオンしているかどうかにかかわらず実行する」を選択してはいけない。

行いたいことの再確認

画像1:マウスがここにある状態でVBAプログラムを実行させて、

画像1

画像2:マウスポインタを画面の中心付近に移動させ右クリックさせ、

画像2

画像3:処理したことがわかるように、ポップアップで「クリック処理実行しました。」を表示させる。

画像3

この一連の処理をタスクスケジューラーのタスク(「最上位の特権で実行する」は未選択にして最上位の権限に昇格させない状態)で実行させたい。

スポンサーリンク

解決方法

何度も諦めかけ、「最上位の特権で実行する」を選択して実行させ、「他の特別の事情を抱えているプログラム」を修正することにしました。そのような試行錯誤の末、私なりの答えがこちらになります。

今回のカギ、ポイント

  1. 「VBAファイルを実行させる外部ファイル」を実行させるバッチファイルを作成する。
  2. そのファイルでVBAファイルを全面表示にさせアクティブにする。

がカギでありポイントでした。

プログラム

次のファイルを用意し説明します。

・ファイル1:MouseClickTest.xlsm(先程紹介したファイルです。場所は「D:\MouseClickTest.xlsm」)
 ※VBAのマウス関連の処理が書かれたメインのファイルです。このファイルを外部ファイルで起動し、ファイル内の「test_mouseClickR」マクロを実行させたい。

・ファイル2:MouseClickTest_Kicker.vbs(先程とは#####の所が異なります。場所は「D:\MouseClickTest_Kicker.vbs」)
 ※このファイルを「最上位の特権で実行する」をすることなく、このファイルをキックしてファイル1を実行させたいが上手く行かない。


Option Explicit

'-----
Dim FilePath
FilePath = "D:\MouseClickTest.xlsm"

Dim MacroName
MacroName = "test_mouseClickR"
'-----

Dim ObjExcelApp
Set ObjExcelApp = WScript.CreateObject("Excel.Application")
ObjExcelApp.Visible = True
'#####
'WScript.CreateObject("WScript.Shell").AppActivate ObjExcelApp.Caption
Dim ObjWSH
Set ObjWSH = WScript.CreateObject("WScript.Shell")
ObjWSH.AppActivate ObjExcelApp.Caption
'#####
ObjExcelApp.Workbooks.Open FilePath
ObjExcelApp.Application.Run MacroName

Set ObjExcelApp = Nothing
'#####
Set ObjWSH = Nothing
'#####

・ファイル3:MouseClickTest_Kicker_Kicker_VerBat.bat(場所は「D:\MouseClickTest_Kicker_Kicker_VerBat.bat」)
 ※ファイル2を「最上位の特権で実行する」を選択せず、ファイル2をキックするとファイル1を起動でき、マウスの処理をしてくれる。(バッチファイルのバージョン)


@echo off
start "" "D:\MouseClickTest_Kicker.vbs"

・ファイル4:MouseClickTest_Kicker_Kicker_VerVbs.vbs(場所は「D:\MouseClickTest_Kicker_Kicker_VerVbs.vbs」)
 ※ファイル3のVBSファイル版。


Set ObjWSH = CreateObject("Wscript.Shell")
ObjWSH.run "cmd /c D:\MouseClickTest_Kicker.vbs", vbhide

スポンサーリンク

タスクスケジューラーの設定

画像4:次の3つタスクを作成します。

画像4

画像5:各タスクの「トリガー」はテスト用なので未選択で作成します。

画像5

画像6:各タスクの「条件」は初期値のまま変更しないでおきます。

スポンサーリンク

画像7:同じく「設定」も初期値のままにしておきます。

画像7

画像8:次のように選択します。「タスクの実行時に使うユーザーアカウント」はログオンしているユーザー(管理者)にします、私は初期値でした。

画像8

画像9:次のようにします。

画像9

画像10:次のように選択します。「タスクの実行時に使うユーザーアカウント」は同じく、ログオンしているユーザー(管理者)にします。

画像10

画像11:次のようにします。

画像11

画像12:次のように選択します。「タスクの実行時に使うユーザーアカウント」は同じくログオンしているユーザー(管理者)です。

画像12

スポンサーリンク

画像13:次のように選択します。

画像13

備考

・ファイル2(MouseClickTest_Kicker.vbs)の次の箇所がないと上手く動作しない。この処理はエクセルをアクティブにして前面に表示する処理。


'WScript.CreateObject("WScript.Shell").AppActivate ObjExcelApp.Caption
Dim ObjWSH
Set ObjWSH = WScript.CreateObject("WScript.Shell")
ObjWSH.AppActivate ObjExcelApp.Caption

・また、ファイル2のように、Visible処理とWorkbooks.Openの間に、最前面に表示する処理を書かないと上手く行かない。マクロを実行した後に、最前面に表示するを入れるといけない。

・VBA側で前面表示する処理を行っても上手く行かなかった。

・以下のように前面表示を関数化しておけば「BrigToFront Obj01」のように使える。


Sub BringToFront(ByVal Obj)
   On Error Resume Next
   WScript.CreateObject("WScript.Shell").AppActivate Obj.Caption
   On Error Goto 0
   Set obj = Nothing
End Sub

参考

Shell 関数 (Visual Basic for Applications)

最後までお付き合いいただきありがとうございます!

この情報が誰かの役にたてれば幸いです。

スポンサーリンク

タグ

-その他

© 2021 BookALittle