質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

Q&A

解決済

2回答

5239閲覧

ExcelVBA ユーザーフォームを表示後に別ブックを表示し、表示したブックを全て閉じてもユーザーフォームは残るようにしたい

negi0206

総合スコア0

VBA

VBAはオブジェクト指向プログラミング言語のひとつで、マクロを作成によりExcelなどのOffice業務を自動化することができます。

0グッド

2クリップ

投稿2021/02/06 15:21

編集2021/02/09 07:32

開発環境

3台分ございます。
①期待通りの動作をするマシン その1
OS:Windows10 Home 64bit
Office(Excel):Home and Business 2019 64bit

②期待通りの動作をするマシン その2
OS:Windows10 Pro 64bit
Office(Excel):Home and Business 2016 64bit

③期待通りに動作しないマシン(改善したい対象)
OS:Windows10 Home 64bit
Office(Excel):Professional Plus 2016 32bit

前提・要件

お世話になります。初質問のため、至らぬ点がございましたら
申し訳ございませんがご教示ください。

ExcelVBAにて、とある機能のユーザーフォーム(例:test_main.xlsm)を開発しております。
ユーザーフォームはtest_main.xlsmを起動時に即表示するようになっております。
こちらのユーザーフォームは起動時の要件として、以下がございます。

要件
①test_main.xlsmのブックを表示できないようにすること(他ブックが開かれていない場合はエクセルのアプリケーション自体を表示しないこと)
②他ブックへの影響を与えないこと(例:application.visible=falseなどは他ブックも巻き込んでしまうためNG)
③test_main.xlsmの起動前後で開いているブックを全て閉じてもtest_main.xlsmのフォームが閉じられないこと(巻き込まれないこと)

ソースコード

上記の要件を満たすため、ソースコードを以下のように組みました。
「test.xlsm」と「test_main.xlsm」の2ファイルがございますが、「test.xlsm」から起動して「test_main.xlsm」を呼び出す構成です。

test.xlsm

'MicroSoft Excel Objects->ThisWorkbook Private Sub Workbook_Open() Const MAIN_MACRO_NAME As String = "test_main.xlsm" Dim exApp As Excel.Application 'ブックの非表示 Windows(ThisWorkbook.Name).Visible = False '別プロセスでフォーム用マクロを起動 Set exApp = CreateObject("Excel.Application") exApp.EnableEvents = True exApp.Workbooks.Open ThisWorkbook.Path & "\" & MAIN_MACRO_NAME exApp.Visible = False Set exApp = Nothing '開かれているブックがこの起動用マクロのみならExcelを終了する If Workbooks.Count = 1 Then Application.Quit End If 'この起動用マクロのブックを閉じる ThisWorkbook.Close End Sub

test_main.xlsm

'MicroSoft Excel Objects->ThisWorkbook Private Sub Workbook_Open() 'ブックの非表示 Windows(ThisWorkbook.Name).Visible = False 'ユーザーフォームの表示 UserForm1.Show vbModeless End Sub
'フォーム->UserForm1 Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) 'プロセスが残らないようにする用の処理 If CloseMode = 0 Then If Workbooks.Count = 1 Then Application.Quit End If ThisWorkbook.Close End If End Sub

発生している問題

上記のマクロを実行した結果、開発環境①②では要件を全て満たせておりました。
しかし、開発環境③で実行すると要件③を満たせていないことが分かりました。
開発環境③の場合、ユーザーフォーム表示後に別のブック(適当なxlsxファイルなど)を開き、そのブックを閉じた場合にユーザーフォームも一緒に閉じられてしまいます。(複数ブックを開いている場合は最後のブックを閉じた場合に)

※ユーザーフォーム起動前に適当なブックを開く→ユーザーフォーム起動→最初に開いたブックを閉じる の場合はユーザーフォームは閉じられないため問題なし。

###考えられる原因
上記の実行結果を見比べた際に、以下のことが分かりました。
開発環境①②で実行した場合は、ユーザーフォーム用マクロと後に開いたブックは別プロセスとなっている。(期待通り)
開発環境③で実行した場合は、ユーザーフォーム用マクロと後に開いたブックは同じプロセスになってしまっている。

(Officeの64bitと32bitによる挙動の違い…?)

実現したいこと

開発環境③の場合でも要件③を満たすため、ユーザーフォームを起動後に別ブックを開いて閉じてもユーザーフォームはプロセスに残るようにしたいです。

よろしくお願いいたします。

試したこと

①test_main.xlsmの変数exAppの型をObject型に変更し、事前バインディングから実行時バインディングになるようにしてみた。
⇒変化なし。
②test.xlsm起動時にtest_main.xlsm用のExcelObjectを生成する前にあらかじめダミーのマクロでExcelObjectを生成する。
ダミーの内容はWorkbook_Openイベント内で「Windows(ThisWorkbook.Name).Visible = False」のみ。
⇒test_main.xlsmとは別プロセスになり、後から開いたブックもダミーのプロセスに吸収されることを確認した。(先に開いたExcelプロセスが優先される?)
しかし、後から開いたブックを全て閉じればダミーのプロセスが消滅し、次回からブックを開いた際はtest_main.xlsmのプロセスが吸収してしまう。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

radames1000

2021/02/09 00:03

下記で試しましたが③と同じ動作をします。 OS:Windows10 Pro 64bit Office(Excel):Professional Plus 2016 32bit ですので仰る通りOSのbitは関係なくOfficeのbit違いによるものと考えられますが、 解決策は思い浮かんでないので共有だけですいません。
negi0206

2021/02/09 04:45 編集

radames1000様 動作確認いただきありがとうございます。 情報共有だけでも大変助かります。
guest

回答2

0

自己解決

一旦、以下のように対応いたしました。
大きな点はBeforeCloseイベントの追加とフォームを閉じる際のフラグを設定したことです。
(Application.quit的な動きだとBeforeCloseイベントには入らないと思い込んでた)

BeforeCloseイベントでCancelをそのままTrueにしてしまうと最後のブックも閉じられなくなるため、このような形になりました。
(当ブックのみ起動している場合も含む)

難点な部分は、BeforeCloseイベントを循環参照のように動かすため、保守性が少々辛いところです。

プロセスの状態は質問投稿時点と同様のままです。
開発環境①②③で共通対応可能の認識です。

test.xlsm
修正なし

test_main.xlsm

'MicroSoft Excel Objects->ThisWorkbook Public formCloseFlg As Boolean Private Sub Workbook_Open() formCloseFlg = False 'ブックの非表示 Windows(ThisWorkbook.Name).Visible = False 'ユーザーフォームの表示 UserForm1.Show vbModeless End Sub Private Sub Workbook_BeforeClose(Cancel As Boolean) Dim wb As Excel.Workbook Dim exApp As Excel.Application Dim wbIdx As Excel.Workbook 'フォームの右上×以外でBeforeCloseイベントに入った場合 If formCloseFlg = False Then 'このフォームと同じExcelインスタンス(プロセス)を取得 Set wb = GetObject(ThisWorkbook.FullName) Set exApp = wb.Application '同プロセス内で他のブックで閉じる操作等をした場合 If exApp.Workbooks.Count > 1 Then For Each wbIdx In exApp.Workbooks If wbIdx.Name <> ThisWorkbook.Name Then formCloseFlg = True Workbooks(wbIdx.Name).Close Application.Visible = False formCloseFlg = False 'クローズを中止 Cancel = True Exit For End If Next End If End If End Sub
'フォーム->UserForm1 Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) 'プロセスが残らないようにする用の処理 If CloseMode = 0 Then formCloseFlg = True If Workbooks.Count = 1 Then Application.Quit End If ThisWorkbook.Close End If End Sub

投稿2021/02/09 12:55

編集2021/02/09 14:08
negi0206

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ダミーのプロセスの終了前イベントでさらに別プロセスを生成するとか?(収集がつかなくなりそう・・・)

投稿2021/02/09 08:50

jinoji

総合スコア4585

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

negi0206

2021/02/09 13:04

ご回答いただきありがとうございます。 ダミー側でクローズ前に新たなプロセスを作るのも今思えば可能だったかもしれません。 (新たに開くブックがどのプロセスに吸収されるか問題はありますが) 今後の参考にさせていただきます。 また機会がありましたらお力添えいただきたく思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問