プロが教える店舗&オフィスのセキュリティ対策術

excel VBAで、
ユーザからの操作を待ち受け、操作に応じて外部アプリケーションを
起動し、外部アプリケーションウインドウのスクリーンショットを撮り、
保存するアプリを開発しています。
セルに保存したデータをパラメータとして外部アプリケーションに渡すために、
excel上での開発をしています。

当方、プログラミング歴は浅く、Cのポインタ、Javaのオブジェクト指向で頓挫し、
必要に迫られ、VBAとSwiftでコードを書きはじめた初心者です。
参考URL、同じような問題を解決した時の方法、前提知識の誤解の指摘等、皆様のお知恵を拝借いただけますと、幸いです。よろしくお願い致します。


<困ったこと>
 ・外部アプリケーションを起動し、スクリーンショット作成時に用いるwindowsAPIを
  呼び出した時、オブジェクトが破棄されるため、処理が進まない
 ・オブジェクトの破棄は、UserFormを起動したタイミングでも起こっている模様

<知りたいこと>
 ・VBAコード全体でオブジェクト変数を保持するエレガントな方法を知りたい
 ・そもそも、VBAで外部アプリケーションとの連携をする際、
  どのようなプログラム構造であるのが望ましいのか
  (イベント駆動型のプログラムを書くときの作法 等)


<アプリケーションのイメージ>

UserFofmで作成したメニュー画面を表示
 ※マクロが入ったexcelファイルの起動時もしくは、別の起動用マクロからUserFormを表示。

ユーザ動作を待ち、UserFormに設置したコマンドボタンを押下した際、下記の処理を行う
 コマンドボタン1(btnLaunch)押下時
  別のアプリケーション(例えばIE)が起動する

 コマンドボタン2(btnTakePicture)押下時
  コマンドボタン1で起動したウインドウのスクリーンショットを撮る
※keybd_event(windowsAPI)を使用

 コマンドボタン3(btnTerminate)押下時
  VBAのコード全体が終了する
  ※コマンドボタン1で生成したアプリケーションのプロセスも正しく終了する


<アプリケーション全体の構造>
 ・メインのコードは標準モジュールmainに配置。
 ・mainモジュールから呼び出す関数は役割ごとに異なる標準モジュールに配置
 ・コマンドボタン押下時に起動するアプリケーションのオブジェクト変数は、
  mainモジュールのグローバル変数として定義

<コード>
※細かい部分は省略しています

●mainモジュール
————————————————————————————
Option Explicit
'WindowsAPIの設定
'キーボード関係
Private Declare Sub keybd_event Lib "user32" _
(ByVal bVk As Byte, ByVal bScan As Byte, _
ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

Private Declare Function GetKeyboardState Lib "user32" _
(pbKeyState As Byte) As Long

Const VK_NUMLOCK = &H90 '「NumLock」キー
Const KEYEVENTF_EXTENDEDKEY = &H1 'キーを押す
Const KEYEVENTF_KEYUP = &H2 'キーを放す

'Sleep関数
#If VBA7 Then
Private Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal ms As LongPtr)
#Else
Private Declare Sub Sleep Lib "kernel32" (ByVal ms As Long)
#End If

'ウインドウ制御関係
'cf. http://www.vba-ie.net/ie/foreground.html
Declare Function IsIconic Lib "user32.dll" (ByVal hWnd As Long) As Long
Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hWnd As Long) As Long
Declare Function ShowWindowAsync Lib "user32.dll" ( _
ByVal hWnd As Long, _
ByVal nCmdShow As Long _
) As Long

Const SW_RESTORE As Long = 9

'for event loop
Declare Function GetInputState Lib "user32.dll" () As Long


Public objIe As Object
Public menuKeyFlg As String

Public Sub init()
MainCtrl.Show
End Sub

Public Sub terminate()
Unload MainCtrl
 ‘その他、オブジェクトの破棄処理等

End Sub

Public Sub launch()
Set objIe = new_ie(***URL***)

ie_submit objIe

'無限ループにしておく
Do While True
If GetInputState() Then DoEvents
Sleep 500
Loop

End Sub

Public Sub takePicture(ByRef objIe As Object)

'ウィンドウが最小化されているかのチェック
If IsIconic(objIe.hWnd) Then
ShowWindowAsync objIe.hWnd, SW_RESTORE
End If

'IEを最前面に表示
SetForegroundWindow (objIe.hWnd)

'・ウインドウ全体のスナップショットを作成
 ‘ウインドウの表示イメージをクリップボードに保存
Call keystroke
Call saveClipBoard

End Sub

Private Sub keystroke()
' alt+PrtScreenを模擬し、クリップボードに保存
keybd_event VK_LMENU, 0&, fKEYDOWN, 0& 'Altキーを押す
keybd_event vbKeySnapshot, 0&, fKEYDOWN, 0&
keybd_event vbKeySnapshot, 0&, fKEYUP, 0&
keybd_event VK_LMENU, 0&, fKEYUP, 0& 'Altキーを離す
End Sub

Private Sub saveClipBoard()
 クリップボードの中身を画像ファイルとして書き出す
 **内容省略**
End Sub


●UserFormモジュール
————————————————————————————
Private Sub btnLaunch_Click()
call launch
End Sub

Private Sub btnTakePicture_Click()
call takePicture
End Sub

Private Sub btnTerminate_Click()
call terminate
End Sub

Private Sub UserForm_Initialize()
call init
End Sub

A 回答 (1件)

コードの詳細をよく読んでいないのですが……



> Public Sub takePicture(ByRef objIe As Object)

と引数objIeを必要とする定義にしているのに

> Private Sub btnTakePicture_Click()
>   call takePicture
> End Sub

呼ぶ側で引数を与えていないのは変ですよね?


そもそもobjIeは、広域変数として宣言しているのでtakePictureの定義では引数は不要ではないでしょうか?
    • good
    • 0
この回答へのお礼

siffon9さん ご回答ありがとうございます。

仰るとおりですね。投稿した際に使ったコードが古いものでした。
Public Sub takePicture で定義しています。

お礼日時:2016/05/03 11:33

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!