- ベストアンサー
64ビットエクセルでのAPI宣言/PtrSafe
- 64ビットエクセルでのAPI宣言/PtrSafeに関する質問です。
- エクセルのInputboxで、入力された文字列を自動的にアスタリスクで隠す方法を探しています。
- 質問者は回答No1に掲載されているコードが助かっていたが、64ビットのエクセルでは動作しないことに気づきました。質問者はPtrSafeという言葉を調べ、API宣言を変更しましたが、エラーが発生しています。
- みんなの回答 (2)
- 専門家の回答
質問者が選んだベストアンサー
(1/2) こんにちは。 暫く回答お休み中で、質問を読むこともないこの頃なのですが、 たまたま馴染みのアバターをお見かけしましたので、このご質問だけレスしてみます。 Win32 API コールバック、と、64|32bit環境互換については 今の時点での情報が少な過ぎてこちらも確信を持てるものは書けません。 必要な手当てをして、動作確認はしましたが、もっとスマートなやり方があるかも知れません。 お求めに寄り添った直接的な回答として、2回の投稿で記述を掲げますが、 そもそもの方法として、これが唯一のものではないことを知っておいてください。 リンクを張られた質問スレ(元スレ)においては、 「恐らくAccess VBAついての質問であろう」とう前提で回答が付いています。 今回は、Excel カテゴリに書かれた質問ですから、 PasswordCharプロパティを * に設定したTextBoxを配置したユーザーフォーム (他にPromptを表示するLabel、OKボタンにあたるCommandButton)を用意しておいて、 ' ' 標準モジュール Public rtn Sub Report_Open() UserForm1.Show vbModal If rtn <> "password" Then MsgBox "社員コードが間違っています。" End If End Sub ' ' ユーザーフォームモジュール Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) rtn = TextBox1 End Sub Private Sub CommandButton1_Click() Unload Me End Sub のようなものを奨めるのがExcel VBA的には本筋だと考えています。 この場合はバージョン互換を意識する必要はありませんし、デザインが自在ですし、 何よりシンプルです。 本題に戻って、、、 AddressOf演算子に渡すFunctionの型は明示的である必要があります。 64bitでは Function NewProc(...) As LongPtr 32bitでは Function NewProc(...) As Long なので、各関数を丸ごと条件付きコンパイルの内側で書き分けてあげる必要があります。 もしも仮に32bit互換を捨てて、64bit環境に限った話としては、 64bit用のDeclare文のすべてと、 各Functionの戻り型、引数、変数について、 Long型の宣言をLongPtr型に(今回必要な記述に関してのみ) ご提示の記述から全置換すれば期待の動作にはなります。 64bit版については、明らかにLongLong型である場合でも、 LongPtr型で統一して解り易い(編集し易い)ように書いています。 以下、お求めの記述。 ' ' /// Option Explicit 'Constants to be used in our API functions Private Const EM_SETPASSWORDCHAR = &HCC Private Const WH_CBT = 5 Private Const HCBT_ACTIVATE = 5 Private Const HC_ACTION = 0 #If VBA7 And Win64 Then '〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓 ' ' 〓〓〓〓〓64ビット版、以下〓〓〓〓〓 Private Declare PtrSafe Function CallNextHookEx Lib "user32" ( _ ByVal hHook As LongPtr, ByVal ncode As LongPtr, _ ByVal wParam As LongPtr, lParam As Any _ ) As LongPtr Private Declare PtrSafe Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" ( _ ByVal lpModuleName As String _ ) As LongPtr Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _ ByVal idHook As LongPtr, ByVal lpfn As LongPtr, _ ByVal hmod As LongPtr, ByVal dwThreadId As LongPtr _ ) As LongPtr Private Declare PtrSafe Function UnhookWindowsHookEx Lib "user32" ( _ ByVal hHook As LongPtr _ ) As LongPtr Private Declare PtrSafe Function SendDlgItemMessage Lib "user32" Alias "SendDlgItemMessageA" ( _ ByVal hDlg As LongPtr, ByVal nIDDlgItem As LongPtr, _ ByVal wMsg As LongPtr, ByVal wParam As LongPtr, ByVal lParam As LongPtr _ ) As LongPtr Private Declare PtrSafe Function GetClassName Lib "user32" Alias "GetClassNameA" ( _ ByVal hwnd As LongPtr, ByVal lpClassName As String, ByVal nMaxCount As LongPtr _ ) As LongPtr Private Declare PtrSafe Function GetCurrentThreadId Lib "kernel32" () As LongPtr Private Const VER64BIT = True Private hHook As LongPtr Private Function NewProc(ByVal lngCode As LongPtr, ByVal wParam As LongPtr, ByVal lParam As LongPtr) As LongPtr Dim strClassName As String Dim RetVal As LongPtr Dim lngBuffer As LongPtr If lngCode < HC_ACTION Then NewProc = CallNextHookEx(hHook, lngCode, wParam, lParam) Exit Function End If strClassName = String$(256, " ") lngBuffer = 255 If lngCode = HCBT_ACTIVATE Then 'A window has been activated RetVal = GetClassName(wParam, strClassName, lngBuffer) If Left$(strClassName, CLng(RetVal)) = "#32770" Then 'Class name of the Inputbox 'This changes the edit control so that it display the password character *. 'You can change the Asc("*") as you please. SendDlgItemMessage wParam, &H1324, EM_SETPASSWORDCHAR, Asc("*"), &H0 End If End If 'This line will ensure that any other hooks that may be in place are 'called correctly. CallNextHookEx hHook, lngCode, wParam, lParam End Function
その他の回答 (1)
- cj_mover
- ベストアンサー率76% (292/381)
(2/2) Public Function InputBoxDK( _ Prompt As String, Optional Title, Optional Default, _ Optional XPos, Optional YPos, Optional HelpFile, Optional Context _ ) As String Dim lngModHwnd As LongPtr Dim lngThreadID As LongPtr lngThreadID = GetCurrentThreadId lngModHwnd = GetModuleHandle(vbNullString) hHook = SetWindowsHookEx(WH_CBT, AddressOf NewProc, lngModHwnd, lngThreadID) InputBoxDK = InputBox(Prompt, Title, Default, XPos, YPos, HelpFile, Context) UnhookWindowsHookEx hHook End Function ' ' 〓〓〓〓〓64ビット版、以上〓〓〓〓〓 #Else '〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓 ' ' 〓〓〓〓〓32ビット版、以下〓〓〓〓〓 Private Declare Function CallNextHookEx Lib "user32" ( _ ByVal hHook As Long, ByVal ncode As Long, _ ByVal wParam As Long, lParam As Any _ ) As Long Private Declare Function GetModuleHandle Lib "kernel32" Alias "GetModuleHandleA" ( _ ByVal lpModuleName As String _ ) As Long Private Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" ( _ ByVal idHook As Long, ByVal lpfn As Long, _ ByVal hmod As Long, ByVal dwThreadId As Long _ ) As Long Private Declare Function UnhookWindowsHookEx Lib "user32" ( _ ByVal hHook As Long _ ) As Long Private Declare Function SendDlgItemMessage Lib "user32" Alias "SendDlgItemMessageA" ( _ ByVal hDlg As Long, ByVal nIDDlgItem As Long, _ ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long _ ) As Long Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" ( _ ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long _ ) As Long Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long Private Const VER64BIT = False Private hHook As Long Private Function NewProc(ByVal lngCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long Dim strClassName As String Dim RetVal As Long Dim lngBuffer As Long If lngCode < HC_ACTION Then NewProc = CallNextHookEx(hHook, lngCode, wParam, lParam) Exit Function End If strClassName = String$(256, " ") lngBuffer = 255 If lngCode = HCBT_ACTIVATE Then 'A window has been activated RetVal = GetClassName(wParam, strClassName, lngBuffer) If Left$(strClassName, CLng(RetVal)) = "#32770" Then 'Class name of the Inputbox 'This changes the edit control so that it display the password character *. 'You can change the Asc("*") as you please. SendDlgItemMessage wParam, &H1324, EM_SETPASSWORDCHAR, Asc("*"), &H0 End If End If 'This line will ensure that any other hooks that may be in place are 'called correctly. CallNextHookEx hHook, lngCode, wParam, lParam End Function Public Function InputBoxDK( _ Prompt As String, Optional Title, Optional Default, _ Optional XPos, Optional YPos, Optional HelpFile, Optional Context _ ) As String Dim lngModHwnd As Long Dim lngThreadID As Long lngThreadID = GetCurrentThreadId lngModHwnd = GetModuleHandle(vbNullString) hHook = SetWindowsHookEx(WH_CBT, AddressOf NewProc, lngModHwnd, lngThreadID) InputBoxDK = InputBox(Prompt, Title, Default, XPos, YPos, HelpFile, Context) UnhookWindowsHookEx hHook End Function ' ' 〓〓〓〓〓32ビット版、以上〓〓〓〓〓 #End If '〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓〓 ' ' 〓〓〓〓〓 Sub Report_Open() If InputBoxDK("パスワードを入力して下さい") <> "password" Then MsgBox "社員コードが間違っています。" End If ' Debug.Print VER64BIT ' 確認用 64bit環境なら True End Sub ' ' 〓〓〓〓〓
お礼
ありがとうございました。
お礼
cj_mover さん、いつもありがとうございます。 アバタじゃなくてえくぼ、いやホクロなんですが・・・なんて冗談はおいときまして、さっそく64bitエクセルで試したところ当然ですがちゃんと作動してくれました。 助かりました。 本当はおっしゃるようにユーザーフォームを使用するべきなんですね。ただユーザーフォームってこれまでつかったことがないのでなんとなく尻込みしていました。 ユーザーフォームの方も試してみて、またわからないことがありましたら質問させていただきます。 ありがとうございました。