🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
VBA

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

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

3回答

10557閲覧

Excel VBAでフォームの最大化/最小化ボタンを設定するコードが上手く動かない

d1234

総合スコア5

VBA

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

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

0グッド

0クリップ

投稿2019/11/25 10:44

Excel VBAのユーザフォームに最大化/最小化するボタン設定及び、
閉じるボタンの無効化をしたく、
ネットで調べたところWindows APIを使用するコードの例が記載されていたので、
それを参考にコードを記述しました。
すると、あるフォームでは最大化/最小化ボタンが追加され、閉じるボタンも無効化され、意図した挙動になったのですが、
同じエクセルブックの別のフォームでは最大化/最小化ボタンは追加されず、閉じるボタンのみ無効化され、意図した挙動になりませんでした。特にエラーメッセージも出ておりません。
原因がわからず困っております。
最大化/最小化を設定できないフォームの条件があるのでしょうか。
わかる方おりましたら教えていただけいただけますと幸いです。
Excel2016 32bit、windows10のPCを使用しています。

◆標準モジュールに記述したコード
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long) As Long
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal hWnd As Long, ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Declare Function DrawMenuBar Lib "user32" _
(ByVal hWnd As Long) As Long
Declare Function GetSystemMenu Lib "user32.dll" _
(ByVal hWnd As Long, ByVal bRevert As Long) As Long
Declare Function DeleteMenu Lib "user32" _
(ByVal hMenu As Long, ByVal nPosition As Long, _
ByVal wFlags As Long) As Long

Public Const GWL_STYLE = -16
Public Const SC_CLOSE = &HF060
Public Const MF_BYCOMMAND = &H0&
Public Const WS_THICKFRAME = &H40000
Public Const WS_MINIMIZEBOX = &H20000
Public Const WS_MAXIMIZEBOX = &H10000

◆各ユーザーフォームのInitializeに記述したコード(各フォームには同じコードを記述)
Dim wRet As Long
Dim hWnd As Long
Dim wStyle As Long
Dim hMenu As Long
Dim rClose As Long

hWnd = FindWindow("ThunderDFrame", Me.Caption)
wStyle = GetWindowLong(hWnd, GWL_STYLE)
wStyle = (wStyle Or WS_THICKFRAME Or _
WS_MAXIMIZEBOX Or WS_MINIMIZEBOX)
wRet = SetWindowLong(hWnd, GWL_STYLE, wStyle)
hMenu = GetSystemMenu(hWnd, 0&)
rClose = DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND)
wRet = DrawMenuBar(hWnd)

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

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

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

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

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

guest

回答3

0

ベストアンサー

ユーザーフォームのキャプションを変更すると、設定がいくつかリセットされるので、そのせいかもしれません。

それ以外で、いくつか気になった点を。

1.Windows API の記述が古い。

Excel2016 32bit なので動作はするはずですが、VBA7より前のバージョン用の古い記述です(現行は7.1)。
MS Officeのバージョンアップ時に動かなくなる可能性があるため、記述を更新した方がベターでしょう。

新しい記述は、以下からダウンロードできるファイルが参考になります。

Download Office 2010 Help Files: Win32API_PtrSafe with 64-bit Support from Official Microsoft Download Center

2.UserForm_Initialize に処理を記述している

UserForm_Initializeの段階では内部的にはフォームが生成されているものの、画面に表示されていないため、若干不安感があります。

UserForm_Activateにすると表示されたときになるので、こちらの方がいいかもしれません。

3.Windows API の返り値をチェックしていない

Windows APIははエラーを返り値で返すことが多いため、正常に動いているかは、返り値を確認しないといけません。


参考にWindows APIなどを書き直した物になります。

vba

1'標準モジュール 2 3'https://msdn.microsoft.com/ja-jp/windows/ms633499(v=vs.80) 4Public Declare PtrSafe Function _ 5 FindWindow Lib "user32" Alias "FindWindowA" ( _ 6 ByVal lpClassName As String, ByVal lpWindowName As String _ 7 ) As LongPtr 8'https://msdn.microsoft.com/ja-jp/windows/ms633585(v=vs.80) 9Public Declare PtrSafe Function _ 10 GetWindowLongPtr Lib "user32" Alias "GetWindowLongA" ( _ 11 ByVal hWnd As LongPtr, ByVal nIndex As Long _ 12 ) As LongPtr 13'https://msdn.microsoft.com/ja-jp/windows/ms644898%28v=vs.80%29?f=255&MSPPError=-2147217396 14Public Declare PtrSafe _ 15 Function SetWindowLongPtr Lib "user32" Alias "SetWindowLongA" ( _ 16 ByVal hWnd As LongPtr, ByVal nIndex As Long, ByVal dwNewLong As LongPtr _ 17 ) As LongPtr 18'https://docs.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-drawmenubar 19Public Declare PtrSafe Function _ 20 DrawMenuBar Lib "user32" ( _ 21 ByVal hWnd As LongPtr _ 22 ) As API_BOOL 23'https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getsystemmenu 24Public Declare PtrSafe Function _ 25 GetSystemMenu Lib "user32" ( _ 26 ByVal hWnd As LongPtr, ByVal bRevert As Long _ 27 ) As LongPtr 28'https://docs.microsoft.com/ja-jp/windows/win32/api/winuser/nf-winuser-deletemenu 29Public Declare PtrSafe Function _ 30 DeleteMenu Lib "user32" ( _ 31 ByVal hMenu As LongPtr, ByVal nPosition As Long, ByVal wFlags As Long _ 32 ) As API_BOOL 33 34Public Const GWL_STYLE = -16 35Public Const SC_CLOSE = &HF060 36Public Const MF_BYCOMMAND = &H0& 37Public Const WS_THICKFRAME = &H40000 38Public Const WS_MINIMIZEBOX = &H20000 39Public Const WS_MAXIMIZEBOX = &H10000 40 41Public Enum API_BOOL 42 apiFalse = 0 43 apiTrue = 1 44End Enum

vba

1'フォーム 2Private Sub UserForm_Activate() 3 'このフォームのハンドルを取得。 4 Dim hWnd As LongPtr 5 hWnd = FindWindow("ThunderDFrame", Me.Caption) 6 If hWnd = 0 Then 7 '取得できていない 8 Stop 9 End If 10 11 Dim wStyle As LongPtr 12 wStyle = GetWindowLongPtr(hWnd, GWL_STYLE) 13 '最大化・最小化ボタンフラグを立てる 14 wStyle = (wStyle Or WS_THICKFRAME Or WS_MAXIMIZEBOX Or WS_MINIMIZEBOX) 15 Call SetWindowLongPtr(hWnd, GWL_STYLE, wStyle) 16 17 Dim hMenu As LongPtr 18 hMenu = GetSystemMenu(hWnd, 0&) 19 Dim rClose As API_BOOL 20 rClose = DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND) 21 If rClose <> apiTrue Then 22 '何か失敗している 23 Stop 24 End If 25 26 Dim wRet As API_BOOL 27 wRet = DrawMenuBar(hWnd) 28 If wRet <> apiTrue Then 29 '何か失敗している 30 Stop 31 End If 32 33End Sub

191128 20:22 追記

vba

1Public Sub ChangeUserFormStyle(inFormCaption As String) 2 Const ClassNameOfVBAUserFrom = "ThunderDFrame" 3 'このフォームのハンドルを取得。 4 Dim hWnd As LongPtr 5 hWnd = FindWindow(ClassNameOfVBAUserFrom, inFormCaption) 6 If hWnd = 0 Then 7 '取得できていない 8 Stop 9 End If 10 11 Dim wStyle As LongPtr 12 wStyle = GetWindowLongPtr(hWnd, GWL_STYLE) 13 'リサイズ可能・最大化・最小化ボタンフラグを立てる 14 wStyle = (wStyle Or WS_THICKFRAME Or WS_MAXIMIZEBOX Or WS_MINIMIZEBOX) 15 Call SetWindowLongPtr(hWnd, GWL_STYLE, wStyle) 16 17 Dim hMenu As LongPtr 18 hMenu = GetSystemMenu(hWnd, 0&) 19 Dim rClose As API_BOOL 20 rClose = DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND) 21 If rClose <> apiTrue Then 22 '何か失敗している 23 Stop 24 End If 25 26 Dim wRet As API_BOOL 27 wRet = DrawMenuBar(hWnd) 28 If wRet <> apiTrue Then 29 '何か失敗している 30 Stop 31 End If 32 33End Sub

投稿2019/11/27 23:15

編集2019/11/28 11:23
imihito

総合スコア2166

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

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

d1234

2019/11/28 08:26

回答、およびコード記述ありがとうございます。 実は前任担当者がやめてしまいドキュメントがない上に複雑なVBAが組まれており、 それを直さなければいけない状況のため苦戦しています。 >ユーザーフォームのキャプションを変更すると、設定がいくつかリセットされるので、 >そのせいかもしれません。 上手く最大化最小化ボタンが設定できないユーザーフォームは、 標準モジュールの中でユーザーフォームのキャプションを変更しておりました。 キャプション変更の部分をコメントアウトすると意図した挙動になるのですが、 別のところでユーザーフォームのキャプションを使用している部分があり、 キャプション変更の部分は残さざるを得ない状況です。 実行されるVBAを補足すると、 ①ユーザーフォーム1(名称仮)のコマンドボタンを押す ②標準モジュールのサブプロシージャ―が呼び出される  (このサブプロシージャ―でユーザ―フォーム2のキャプション名を変更している   ※キャプション名変更は一部で、その他いろいろ実行している) ③ユーザーフォーム1のサブプロシージャ―が呼び出される  (このサブプロシージャ―でユーザーフォーム2とユーザ―フォーム3を起動している) ※ユーザフォーム2はキャプション名を変更している、  ユーザーフォーム3はキャプション名を変更していない  上手く最大化最小化ボタンが設定できないのはユーザーフォーム2です。 いただいたコードを記述して実行しますと、 ユーザーフォーム2が表示された際は最大化最小化ボタンは見えない状態で、 ユーザーフォーム2をクリックすると最大化最小化ボタンが現れますが、 ユーザーフォーム2を掴んで移動させようとすると以下のポイントで止まります。 If rClose <> apiTrue Then '何か失敗している Stop → ここで止まる End If このような挙動になるのですが、 どのようにしたらよいか教えていただけないでしょうか。
imihito

2019/11/28 11:31

キャプションを変更が必須であれば、キャプション変更の度にボタン設定をすればよいでしょう。 現状だと取り回しにくいので、追記した`ChangeUserFormStyle`のように別の処理に切り分けてしまうとやりやすいと思います(フォーム毎の処理で違うのはフォームのキャプションだけなので)。 また、閉じるボタンはキャプションを変更しても無効化されたままなので、`DeleteMenu`が失敗してしまうのだと思います。 その箇所は無視(Stopを消す)してしまってもいいかもしれません。 閉じるボタンであれば`UserForm_QueryClose`イベントでも、(実質的に)無効化できるので、無視が嫌であればそちらの手もありでしょう。
d1234

2019/11/29 08:53

ありがとうございます。 追記いただいた`ChangeUserFormStyle`を標準モジュールに追加し、 Caption変更の後に`ChangeUserFormStyle`を呼び出すことで上手くいきました。 Delete Menuが失敗する箇所は無視するか、QueryCloseで閉じるボタンを実質的に無効化するかどうかは検討します。
guest

0

今晩は。

間違っていたら ごめんなさい。

ユーザーフォームの設定は
大体の事は [表示] →[プロパティーウインドウ]で、
左下に出てくるウインドウで設定できますよ。

プロシージャー は あんまり書かなくても OK だと思います。

UserForm1.Show vbModeless  ← ユーザーフォームの表示

Unload UserForm1  ← ユーザーフォームを閉じる

必須なのは。それくらいです。

キャンセルボタンも

Call CButton_Enabled(False) もしくは

Cancel buttan.( )←ココに ↓ コレラを

True か False   (有効 ? 無効 ?)

Hide か Hiden   (表示 ? 非表示 ?)
を設定していく。

後は、色々貼り付けて 移動させたり、サイズ変えたり・・・。

いいフォーム作って下さい。

投稿2019/11/27 16:26

unagiinu

総合スコア42

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

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

d1234

2019/11/29 09:01

回答ありがとうございます。 imihito様への回答でも書かせていただいた通り、前任者の複雑なvbaを修正しなければいけない状況でした。 おっしゃる通り、私も極力シンプルなものを作りたいと思っています。
guest

0

モジュールの先頭に Option Explicit は書いてありますか?
もし、書いてないのなら、どこかでタイプミスしてる可能性が。

投稿2019/11/27 11:25

iruyas

総合スコア1067

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

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

d1234

2019/11/29 09:08

回答ありがとうございます。 imihito様への回答でも書かせていただいた通り、前任者の複雑なvbaを修正しなければいけない状況でした。 Option Explicitは記載してません、おそらく最初はシンプルなものを作っていたがだんだんと要望に応えるうちに複雑になっていったのだと推測します。使ってないモジュールやらフォームやらたくさんあって解読するだけで大変です。今は納期があるので最低限だけ修正して、今後作り替えたいと思っています。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問