• 締切済み

EXCEL:他のソフトを使っている間に自動保存する

EXCEL2003です。 ブックは開けっ放しにしておいて、他のアプリケーション(例えばブラウザなど)を使っている間に一定時間が経過すると(例えば3分)自動で保存させたいと思っています。 他のアプリケーションが何になるかは特定でないので、EXCELのマクロで自分自身がアクティブでなくなったことを拾って、そこから一定時間、自分がアクティブにならなかったら自動的に保存する、というVBAを作りたいと思っています。 アドバイスを頂きたいのはアプリケーションレベルで「自分自身がアクティブでなくなったこと」をどうやって拾うか、ということです。 Microsoftサポート(http://support.microsoft.com/kb/213566/ja)やMSDNを参照して、EXCELアプリケーションのイベントを拾おうとしてみたのですが、ブックの切り替えのイベントは拾えても、EXCEL自身がアクティブでなくなったことは拾えないようです。 何か良い方法はないでしょうか。(参考になるURLがあったら教えてください)

みんなの回答

  • kumatti1
  • ベストアンサー率60% (73/121)
回答No.6

HSP用のサイトで見て欲しかったのは、WM_ACTIVATEメッセージの解説のところだけですね。 手に負えない様でしたら、業者に発注するのも一考ではないでしょうか。

liamg
質問者

お礼

皆さんの回答を参考にして作ってみました。 本来はある程度時間が経過した後に保存されるようにしたかったのですが、手に負えなさそうそうなので、EXCELがアクティブでなくなったときに保存されるようにということで良しとしました。 以下のモジュールをブック内に作成して、ブックのOpenイベントででもDeactiveSaveStartをコールしてもらえれば、あとは放ったらかしでOKです。 EXCELがディアクティブになったとき(他のアプリがアクティブになったとき)に裏で保存されます。(ブックに変更が無いときはスルーします) Option Explicit Public n_hWnd As Long '新しいウィンドウのハンドル Public p_hWnd As Long '元のウィンドウのハンドル 'ウィンドウメッセージ定数 Public Const GWL_WNDPROC = -&H4 'ウインドウプロシージャのアドレスを変更する 'クラス名・キャプションからウィンドウのハンドルを取得する Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long 'ウィンドウに関する情報を取得する Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long) As Long '指定されたウィンドウの属性を変更する Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long '指定されたウィンドウプロシージャにメッセージ情報を渡す Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Public Function DeactiveSaveStart() As Integer 'サブクラス化を開始する Dim hWnd As Long Dim strWndCpt As String 'すでにフックされているときは終了する If p_hWnd <> 0 Then Exit Function End If 'ウィンドウハンドルを取得する strWndCpt = Application.Caption hWnd = FindWindow(vbNullString, strWndCpt) 'メッセージフックを開始する p_hWnd = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf DeactiveSaveProc) If p_hWnd = 0 Then DeactiveSaveStart = 0 'フック失敗 Else DeactiveSaveStart = 1 'フック成功 n_hWnd = hWnd End If End Function Public Sub DeactiveSaveStop() 'サブクラス化を終了する Dim lngRet As Long 'サブクラス化されていない場合 If p_hWnd = 0 Then '何もしない Exit Sub End If lngRet = SetWindowLong(n_hWnd, GWL_WNDPROC, p_hWnd) p_hWnd = 0 End Sub Public Function DeactiveSaveProc(ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long 'ウインドウプロシージャにメッセージを渡す前にそれを横取りして必要な処理を実行する Dim intFlagActive As Integer Const WM_ACTIVATE = &H6 Const WA_INACTIVE = 0 If Msg = WM_ACTIVATE Then intFlagActive = wParam And &HFFFF& If intFlagActive = WA_INACTIVE Then 'アクティブでなくなったら保存する If Not ThisWorkbook.Saved Then Application.StatusBar = "保存中..." ThisWorkbook.Save Application.StatusBar = "" End If End If End If 'デフォルトウィンドウプロシージャを呼び出す DeactiveSaveProc = CallWindowProc(p_hWnd, hWnd, Msg, wParam, lParam) End Function

  • kumatti1
  • ベストアンサー率60% (73/121)
回答No.5

リンク先では、サブクラスプロシージャ(コールバック)がマシン語(英数字の羅列部分)なので、分かりにくかったかもしれませんが、 ループは関係ありません。 いずれにしても、実行時にVBA単独では、開発時に多少のアセンブリ言語の知識が要求されるので、確かに現実的ではありませんね。

liamg
質問者

お礼

ありがとうございます。 教えて頂いたURLでは「3.メインループ中で以下の操作を行なう」「メッセージが取得されているかどうかを調べる」とあるのですが、ループは関係ないのですか? ん~、どう見ても"goto *mainloop"でループしてウィンドウがアクティブになったというメッセージをひろったらループを抜けているように見えるのですが・・・

  • kumatti1
  • ベストアンサー率60% (73/121)
回答No.4

非アクティブになったタイミングを知りたいという事なのでしょうけど、 本題の場合は、エクセル本体をサブクラス化して、WM_ACTIVATEメッセージを処理する事で分かります。 ↓ HSP言語用ですが、説明が分かりやすいので。 http://chokuto.ifdef.jp/urawaza/hsgetmsg3.html ただ、VBAではサブクラス化は負荷が結構、大きいです。 なので、特殊なテクニックを用いてその負荷を軽減するのですが、 http://web.archive.org/web/20040111002552/http:/www2.moug.net/app/bbs/message.php?cat=exvba&id=20031023-000053 ご覧いただくと分かりますが難易度は高いと思います。

liamg
質問者

お礼

あきらめてしまっていたので見るのが遅くなりお礼が遅れました。すみません。 そうですね、たしかに難易度が高く正直何をやっているのかよくわかりませんでした。フックをスタートする、と言っても、今回の場合はディアクティブ時のケースなので、ループで待機しておくわけにもいかないため提供はできそうにないように思いました。 (アクティブ時のケースであればディアクティブ中はループで待機していてもOKだと思いますが、ディアクティブのケースはアクティブ中は他の操作が行われているのが自然なのでループ待機でディアクティブになったことを取得するのは現実的でないです)

liamg
質問者

補足

お礼コメント、"提供"ではなく"適用"の誤りでした。すみません。

  • aoyama984
  • ベストアンサー率45% (253/561)
回答No.3
liamg
質問者

お礼

おそらくウィンドウのハンドルを拾って何とかしてはどうかという意味に思えますが、繰り返しますが、質問は、EXCEL自身がアクティブでなくなったことをどうやって拾うか、ということです。ウィンドウのハンドルを拾っておいて、ループ待機でディアクティブになったのを検出するにしても、そのループをどうやってKickする(開始する)かという課題が残ります。やはり「ディアクティブになった」というイベントを捉えるしかないと思うのですが・・・その方法が・・・

  • aoyama984
  • ベストアンサー率45% (253/561)
回答No.2

http://www.eurus.dti.ne.jp/yoneyama/Excel/vba/vba_event.html#selectionchange どんな使い方をするブック,シートなのかによりますが セルの移動があるなら 位置が変わったら時間を記録して 監視用のループをまわして毎秒毎分などの間隔で時間を見て処理するとか 標準機能の自動保存ではなぜだめなのかの説明が無いので 基本VBAでは無理 標準機能が最適 という回答が 他の方も書いていますが 私も同じ考え方です どうしてもやる場合は かなりムリヤリかなりの負荷になるのでは それを超えてやりたい理由や状況を詳しく説明してもらえたらありがたいのですが ブックの共有のような問題なのでしょうか

liamg
質問者

お礼

やっぱりここで質問するのはやめておいた方がよかったかもしれません。余計な手間ばかりかかります。自動保存では何故ダメなのか説明する必要はありません。どんな使い方をするブックまたはシートなのかを説明する必要もありません。 回答をお願いしているポイントは明確であって、質問には"VBAを作りたいと思っています。"と記載済みであって、"アドバイスを頂きたいのはアプリケーションレベルで「自分自身がアクティブでなくなったこと」をどうやって拾うか、ということです。"と記載してあるからです。 本当に、回答になっていない回答はご遠慮いただくわけにはいかないのでしょうか。どうかよろしくお願いします。

liamg
質問者

補足

回答をお持ちでない方の回答はご遠慮ください。 どうか、どうか、よろしくお願いしますm(__)m

  • Epsilon03
  • ベストアンサー率24% (868/3495)
回答No.1

私はExcel2002を使っていますが、自動保存は以下の方法で設定しています。 ツール → オプション → 自動保存 で、自動保存を行うにチェックを入れ、何処へ保存するのかを指定しています。

liamg
質問者

お礼

すみません、「VBAを作りたいと思っています。」と明確に記載してあるはずです。関連の無い回答はご遠慮いただけませんでしょうか。よろしくお願いします。