Labelsoft
asked on
Hide Open/Save FileDialog toolbarbuttons
I am trying to modify the Save/Load FileDialog to fit my own needs. To this end I have been trying to work with memory hooks. So far I have been able to install a keyboard hook which prevents certain key combinations from being executed.
My next step is to try and handle the toolbarbuttons on the FileDialog. I want to be able to make them visible if I desire so. To achieve this goal I have also tried to get a hook to the toolbar, but somehow I just don't seem to get it to work.
The HideToolbarButtons Sub contains the code that should be hiding the correct toolbarbutton, but when I run the project it is still visible in the dialog. I am not sure what is going wrong here.
My next step is to try and handle the toolbarbuttons on the FileDialog. I want to be able to make them visible if I desire so. To achieve this goal I have also tried to get a hook to the toolbar, but somehow I just don't seem to get it to work.
The HideToolbarButtons Sub contains the code that should be hiding the correct toolbarbutton, but when I run the project it is still visible in the dialog. I am not sure what is going wrong here.
Imports System.Text
Imports System.Text.Encoding
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Data
Imports System.Runtime.InteropServices
Public Class LCITDialog
Inherits Component
#Region "Fields"
Protected Encodings As System.Text.Encoding() = New System.Text.Encoding() {ASCII, UTF8, Unicode, UTF7, UTF32}
Private _Filter As String = ""
Private _DefaultExt As String = ""
Private _FileName As String = ""
Private _ActiveScreen As Screen
Private _ReadOnlyDialog As Boolean
Public KeyboardHandle As Integer
Public ToolbarHandle As Integer
#End Region
#Region "Constants"
Private Const OFN_ENABLEHOOK As Integer = 32
Private Const OFN_EXPLORER As Integer = 524288
Private Const OFN_FILEMUSTEXIST As Integer = 4096
Private Const OFN_HIDEREADONLY As Integer = 4
Private Const OFN_CREATEPROMPT As Integer = 8192
Private Const OFN_NOTESTFILECREATE As Integer = 65536
Private Const OFN_OVERWRITEPROMPT As Integer = 2
Private Const OFN_PATHMUSTEXIST As Integer = 2048
Private Const SWP_NOSIZE As Integer = 1
Private Const SWP_NOMOVE As Integer = 2
Private Const SWP_NOZORDER As Integer = 4
Private Const WM_INITDIALOG As Integer = 272
Private Const WM_DESTROY As Integer = 2
Private Const WM_USER As Integer = &H400
Private Const WM_SETFONT As Integer = 48
Private Const WM_GETFONT As Integer = 49
Private Const CBS_DROPDOWNLIST As Integer = 3
Private Const CBS_HASSTRINGS As Integer = 512
Private Const CB_ADDSTRING As Integer = 323
Private Const CB_SETCURSEL As Integer = 334
Private Const CB_GETCURSEL As Integer = 327
Private Const WS_VISIBLE As UInteger = 268435456
Private Const WS_CHILD As UInteger = 1073741824
Private Const WS_TABSTOP As UInteger = 65536
Private Const CDN_FILEOK As Integer = -606
Private Const CDN_FIRST As Long = -601
Private Const CDN_INITDONE As Long = (CDN_FIRST - &H0&)
Private Const WM_NOTIFY As Integer = 78
Private Const WH_KEYBOARD_LL As Integer = 13&
' Virtual Keys
Public Const VK_TAB = &H9
Public Const VK_CONTROL = &H11
Public Const VK_ESCAPE = &H1B
Public Const VK_DELETE = &H2E
' Low-Level Keyboard Constants
Private Const HC_ACTION As Integer = 0
Private Const LLKHF_EXTENDED As Integer = &H1
Private Const LLKHF_INJECTED As Integer = &H10
Private Const LLKHF_ALTDOWN As Integer = &H20
Private Const LLKHF_UP As Integer = &H80
'Toolbarbutton keys
Private Const TB_BTN_BACK As Integer = 40971
Private Const TB_BTN_UPONELEVEL As Integer = 40961
Private Const TB_BTN_NEWFOLDER As Integer = 40962
Private Const TB_BTN_VIEWMENU As Integer = 40970
Private Const TB_SETBUTTONINFO As Integer = (WM_USER + 66)
Private Const TBIF_IMAGE As Integer = &H1
Private Const TBIF_TEXT As Integer = &H2
Private Const TBIF_STATE As Integer = &H4
Private Const TBIF_STYLE As Integer = &H8
Private Const TBIF_LPARAM As Integer = &H10
Private Const TBIF_COMMAND As Integer = &H20
Private Const TBIF_SIZE As Integer = &H40
Private Const TBSTATE_CHECKED As Integer = &H1
Private Const TBSTATE_PRESSED As Integer = &H2
Private Const TBSTATE_ENABLED As Integer = &H4
Private Const TBSTATE_HIDDEN As Integer = &H8
Private Const TBSTATE_INDETERMINATE As Integer = &H10
Private Const TBSTATE_WRAP As Integer = &H20
Private Const TBSTATE_ELLIPSES As Integer = &H40
Private Const TBSTATE_MARKED As Integer = &H80
#End Region
#Region "Properties"
Public Property DefaultExt() As String
Get
Return _DefaultExt
End Get
Set(ByVal value As String)
_DefaultExt = value
End Set
End Property
Public Property Filter() As String
Get
Return _Filter
End Get
Set(ByVal value As String)
_Filter = value
End Set
End Property
Public Property FileName() As String
Get
Return _FileName
End Get
Set(ByVal value As String)
_FileName = value
End Set
End Property
Public Property ReadOnlyDialog() As Boolean
Get
Return _ReadOnlyDialog
End Get
Set(ByVal value As Boolean)
_ReadOnlyDialog = value
End Set
End Property
#End Region
#Region "Methods"
<DllImport("Comdlg32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetSaveFileName(ByRef lpofn As OPENFILENAME) As Boolean
End Function
<DllImport("Comdlg32.dll")> _
Private Shared Function CommDlgExtendedError() As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function SetWindowPos(ByVal hWnd As Integer, ByVal hWndInsertAfter As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As UInteger) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function GetWindowRect(ByVal hWnd As Integer, ByRef lpRect As RECT) As Boolean
End Function
<DllImport("user32.dll")> _
Private Shared Function GetParent(ByVal hWnd As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function SetWindowText(ByVal hWnd As Integer, ByVal lpString As String) As Boolean
End Function
<DllImport("user32.dll")> _
Private Overloads Shared Function SendMessage(ByVal hWnd As Integer, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Overloads Shared Function SendMessage(ByVal hWnd As Integer, ByVal Msg As Integer, ByVal wParam As Integer, ByVal lParam As String) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function DestroyWindow(ByVal hwnd As Integer) As Boolean
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetDlgItem(ByVal hDlg As Integer, ByVal nIDDlgItem As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function CreateWindowEx(ByVal dwExStyle As Integer, ByVal lpClassName As String, ByVal lpWindowName As String, ByVal dwStyle As UInteger, ByVal x As Integer, ByVal y As Integer, ByVal nWidth As Integer, ByVal nHeight As Integer, ByVal hWndParent As Integer, ByVal hMenu As Integer, ByVal hInstance As Integer, ByVal lpParam As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function ScreenToClient(ByVal hWnd As Integer, ByRef lpPoint As POINT) As Boolean
End Function
<DllImport("user32.dll", EntryPoint:="SetWindowsHookExA")> _
Private Shared Function SetWindowsHookEx(ByVal idHook As Integer, ByVal lpfn As KeyboardHookDelegate, ByVal hmod As Integer, ByVal dwThreadId As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function GetAsyncKeyState(ByVal vKey As Integer) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function CallNextHookEx(ByVal hHook As Integer, ByVal nCode As Integer, ByVal wParam As Integer, ByVal lParam As KBDLLHOOKSTRUCT) As Integer
End Function
<DllImport("user32.dll")> _
Private Shared Function UnhookWindowsHookEx(ByVal hHook As Integer) As Integer
End Function
<DllImport("kernel32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
Private Shared Function GetModuleHandle(ByVal lpModuleHandle As String) As Integer
End Function
<DllImport("user32.dll", EntryPoint:="FindWindowExA", CallingConvention:=CallingConvention.StdCall, CharSet:=CharSet.Ansi)> _
Private Shared Function FindWindowEx(ByVal hwndParent As UInteger, ByVal hwndChildAfter As UInteger, ByVal lpszClass As String, ByVal lpszWindow As String) As UInteger
End Function
Private Function HookProc(ByVal hdlg As Integer, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Select Case (msg)
Case WM_INITDIALOG
'we need to centre the dialog
Dim sr As Rectangle = _ActiveScreen.Bounds
Dim cr As RECT = New RECT
Dim parent As Integer = GetParent(hdlg)
GetWindowRect(parent, cr)
Dim x As Integer = CInt((sr.Right + (sr.Left - (cr.Right - cr.Left))) / 2)
Dim y As Integer = CInt((sr.Bottom + (sr.Top - (cr.Bottom - cr.Top))) / 2)
SetWindowPos(parent, 0, x, y, (cr.Right - cr.Left), ((cr.Bottom - cr.Top) + 32), SWP_NOZORDER)
ToolbarHandle = FindWindowEx(parent, 0, "ToolbarWindow32", 0)
Case WM_DESTROY
UnhookKeyboard()
Case WM_NOTIFY
'We need to intercept the CDN_INITDONE message
Dim nmhdr As NMHDR = CType(Marshal.PtrToStructure(New IntPtr(lParam), GetType(NMHDR)), NMHDR)
Select Case nmhdr.Code
Case CDN_INITDONE
If Me.ReadOnlyDialog Then
'If we have a readonly dialog then we need to install a keyboardhook to prevent certain keys from firing
HookKeyboard()
HideToolbarButtons()
End If
Case Else
End Select
End Select
Return 0
End Function
Public Function ShowDialog() As DialogResult
'set up the struct and populate it
Dim ofn As OPENFILENAME = New OPENFILENAME
ofn.lStructSize = Marshal.SizeOf(ofn)
ofn.lpstrFilter = (_Filter.Replace("|", Microsoft.VisualBasic.Chr(0)) + Microsoft.VisualBasic.Chr(0))
ofn.lpstrFile = (_FileName + New String(Microsoft.VisualBasic.Chr(32), 512))
ofn.nMaxFile = ofn.lpstrFile.Length
ofn.lpstrFileTitle = (System.IO.Path.GetFileName(_FileName) + New String(Microsoft.VisualBasic.Chr(32), 512))
ofn.nMaxFileTitle = ofn.lpstrFileTitle.Length
ofn.lpstrTitle = "Save test"
ofn.lpstrDefExt = _DefaultExt
'position the dialog above the active window
ofn.hwndOwner = Form.ActiveForm.Handle
'We need to find out the active screen so the dialog box is centred on the correct display
_ActiveScreen = Screen.FromControl(Form.ActiveForm)
'set up some sensible flags
ofn.Flags = (OFN_EXPLORER _
Or (OFN_PATHMUSTEXIST _
Or (OFN_NOTESTFILECREATE _
Or (OFN_ENABLEHOOK _
Or (OFN_HIDEREADONLY Or OFN_OVERWRITEPROMPT)))))
'This is where the hook is set.
ofn.lpfnHook = New OFNHookProcDelegate(AddressOf HookProc)
'if we're running on Windows 98/ME then the struct is smaller
If (System.Environment.OSVersion.Platform <> PlatformID.Win32NT) Then
ofn.lStructSize = (ofn.lStructSize - 12)
End If
'show the dialog
If Not GetSaveFileName(ofn) Then
Dim ret As Integer = CommDlgExtendedError()
If (ret <> 0) Then
Throw New ApplicationException(("Couldn't show file open dialog - " + ret.ToString))
End If
Return DialogResult.Cancel
End If
'Birb-start
Dim oldFilename As String = _FileName
_FileName = ofn.lpstrFile
Dim cancelCheck As New CancelEventArgs()
RaiseEvent FileOK(Me, cancelCheck)
If cancelCheck.Cancel Then
_FileName = oldFilename 'restore filename since dialog was canceled
Return DialogResult.Cancel
Else
Return DialogResult.OK
End If
'Birb-end
End Function
Public Delegate Function KeyboardHookDelegate(ByVal Code As Integer, ByVal wParam As Integer, ByRef lParam As KBDLLHOOKSTRUCT) As Integer
<MarshalAs(UnmanagedType.FunctionPtr)> _
Private callback As KeyboardHookDelegate
Public Sub HookKeyboard()
callback = New KeyboardHookDelegate(AddressOf KeyboardCallback)
Using curProcess As Process = Process.GetCurrentProcess
Using curModule As ProcessModule = curProcess.MainModule
KeyboardHandle = SetWindowsHookEx( _
WH_KEYBOARD_LL, callback, _
GetModuleHandle(curModule.ModuleName), 0)
End Using
End Using
End Sub
Public Function KeyboardCallback(ByVal Code As Integer, _
ByVal wParam As Integer, _
ByRef lParam As KBDLLHOOKSTRUCT) As Integer
If (Code = HC_ACTION) Then
If (IsHooked(lParam)) Then
Return 1
End If
End If
Return CallNextHookEx(KeyboardHandle, Code, wParam, lParam)
End Function
' This function blocks as many key combinations as we'd like
Public Function IsHooked( _
ByRef Hookstruct As KBDLLHOOKSTRUCT) As Boolean
If Hookstruct.vkCode = VK_DELETE Then
Return True
End If
Return False
End Function
Public Sub UnhookKeyboard()
If (KeyboardHandle <> 0) Then
Call UnhookWindowsHookEx(KeyboardHandle)
End If
End Sub
Private Sub HideToolbarButtons()
Dim tbInfo As New TBBUTTONINFO
tbInfo.dwMask = TBIF_STATE
tbInfo.fsState = TBSTATE_HIDDEN Or TBSTATE_INDETERMINATE
tbInfo.cbSize = Marshal.SizeOf(tbInfo)
'Initialize unmanaged memory to hold the structure
Dim point As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(tbInfo))
Marshal.StructureToPtr(tbInfo, point, True)
SendMessage(ToolbarHandle, TB_SETBUTTONINFO, TB_BTN_NEWFOLDER, CInt(point))
End Sub
Public Delegate Function OFNHookProcDelegate(ByVal hdlg As Integer, ByVal msg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
#End Region
#Region "Structures"
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Private Structure OPENFILENAME
Public lStructSize As Integer
Public hwndOwner As IntPtr
Public hInstance As Integer
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrFilter As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrCustomFilter As String
Public nMaxCustFilter As Integer
Public nFilterIndex As Integer
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrFile As String
Public nMaxFile As Integer
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrFileTitle As String
Public nMaxFileTitle As Integer
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrInitialDir As String
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrTitle As String
Public Flags As Integer
Public nFileOffset As Short
Public nFileExtension As Short
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpstrDefExt As String
Public lCustData As Integer
Public lpfnHook As OFNHookProcDelegate
<MarshalAs(UnmanagedType.LPTStr)> _
Public lpTemplateName As String
'only if on nt 5.0 or higher
Public pvReserved As Integer
Public dwReserved As Integer
Public FlagsEx As Integer
End Structure
Private Structure RECT
Public Left As Integer
Public Top As Integer
Public Right As Integer
Public Bottom As Integer
End Structure
Private Structure POINT
Public X As Integer
Public Y As Integer
End Structure
Private Structure NMHDR
Public HwndFrom As Integer
Public IdFrom As Integer
Public Code As Integer
End Structure
Public Structure KBDLLHOOKSTRUCT
Public vkCode As Integer
Public scanCode As Integer
Public flags As Integer
Public time As Integer
Public dwExtraInfo As Integer
End Structure
Public Structure TBBUTTONINFO
Public cbSize As Long
Public dwMask As Long
Public idCommand As Long
Public iImage As Long
Public fsState As Byte
Public fsStyle As Byte
Public cx As Integer
Public lParam As Long
'Public pszText As String
Public cchText As Long
End Structure
#End Region
#Region "Events"
Public Event FileOK(ByVal sender As System.Object, ByVal e As System.ComponentModel.CancelEventArgs) 'Birb
#End Region
End Class
ASKER
That article points to the custom places bar, which is on the left side of the dialog. My problem concerns the toolbar on top with the buttons 'Go To Last Folder Visited', 'Up One Level', 'New Folder' and 'View Menu' buttons on it.
ASKER
I think that the FindWindowEx call is returning 0 for some reason that I am not able to determine yet.
Yes it looks like it IS returning 0. In fact it looks like there are no children of your parent window when that function is triggered. Even moving the code to the CDN_INITDONE section doesn't work.
ASKER
I fixed the FindWindowEx call, but that did not help anything.
ASKER
I changed ToolbarHandle = FindWindowEx(parent, 0, "ToolbarWindow32", 0) to ToolbarHandle = FindWindowEx(parent, 0, "ToolbarWindow32", "") and that got me a return value. But somehow it seems it isn't a correct one as the buttons remain unaltered.
On TB_GETBUTTONINFO or TB_SETBUTTONINFO you're getting an error code 1421 which is "Control ID not found" so it looks like it's not finding TB_BTN_NEWFOLDER.
ASKER
I am pretty sure that the value of TB_BTN_NEWFOLDER is the correct one. That would mean that the pointer to the control somehow isn't correct.
I think the pointer is correct...when I run a TB_BUTTONCOUNT on it, it returns 4 as it should.
I think the thing to do is try to do a TB_GETBUTTON to see what the IDs in it actually are. You need to define a structure TBBUTTON for that to work and I can't find a VB.Net declaration of it.
I think the thing to do is try to do a TB_GETBUTTON to see what the IDs in it actually are. You need to define a structure TBBUTTON for that to work and I can't find a VB.Net declaration of it.
ASKER
I have the following sturcture declared in my program:
Public Structure TBBUTTONINFO
Public cbSize As Long
Public dwMask As Long
Public idCommand As Long
Public iImage As Long
Public fsState As Byte
Public fsStyle As Byte
Public cx As Integer
Public lParam As Long
'Public pszText As String
Public cchText As Long
End Structure
Which is used in:
Private Sub HideToolbarButtons()
Dim tbInfo As New TBBUTTONINFO
tbInfo.dwMask = TBIF_STATE
tbInfo.fsState = TBSTATE_HIDDEN Or TBSTATE_INDETERMINATE
tbInfo.cbSize = Marshal.SizeOf(tbInfo)
'Initialize unmanaged memory to hold the structure
Dim point As IntPtr = Marshal.AllocHGlobal(Marsh al.SizeOf( tbInfo))
Marshal.StructureToPtr(tbI nfo, point, True)
SendMessage(ToolbarHandle, TB_SETBUTTONINFO, TB_BTN_NEWFOLDER, CInt(point))
End Sub
When the SendMessage is executed all parameters have a value, which makes me think that it should work, but it doesn't.
Public Structure TBBUTTONINFO
Public cbSize As Long
Public dwMask As Long
Public idCommand As Long
Public iImage As Long
Public fsState As Byte
Public fsStyle As Byte
Public cx As Integer
Public lParam As Long
'Public pszText As String
Public cchText As Long
End Structure
Which is used in:
Private Sub HideToolbarButtons()
Dim tbInfo As New TBBUTTONINFO
tbInfo.dwMask = TBIF_STATE
tbInfo.fsState = TBSTATE_HIDDEN Or TBSTATE_INDETERMINATE
tbInfo.cbSize = Marshal.SizeOf(tbInfo)
'Initialize unmanaged memory to hold the structure
Dim point As IntPtr = Marshal.AllocHGlobal(Marsh
Marshal.StructureToPtr(tbI
SendMessage(ToolbarHandle,
End Sub
When the SendMessage is executed all parameters have a value, which makes me think that it should work, but it doesn't.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
By the way, I think your TBBUTTONINFO structure is a vb definition, not VB.Net. The "Long" values should be "Integer". But that's not what's causing the problem.
ASKER
I think the problem is with:
Dim point As IntPtr = Marshal.AllocHGlobal(Marsh al.SizeOf( tbInfo))
Marshal.StructureToPtr(tbI nfo, point, True)
SendMessage(ToolbarHandle, TB_SETBUTTONINFO, TB_BTN_NEWFOLDER, CInt(point))
Perhaps the CInt(point)? But I am not sure about that.
Just deleting the button works fine, yet I am still puzzeled why the hiding doesn't work.
Dim point As IntPtr = Marshal.AllocHGlobal(Marsh
Marshal.StructureToPtr(tbI
SendMessage(ToolbarHandle,
Perhaps the CInt(point)? But I am not sure about that.
Just deleting the button works fine, yet I am still puzzeled why the hiding doesn't work.
http://msdn.microsoft.com/en-us/magazine/cc300434.aspx