The funny thing is that if I put the same control in a Feature Manager
Tree tab, it works fine and the shortcut keys don't trigger solidworks
keyboard shortcuts and the letters pressed are entered into the
textbox.
If anyone knows a way to disable shortcut keys at least while a text
box has the focus, please let me know.
I have heard that it is possible to make macros (Add-ins?) that will
appear in the Feature Tree just like native SW commands do, but I have
not tried anything like that yet.
Ken
Jonathan Anderson
I have found that VB6 add-ins and my old VC++ 6 add-in work fine
without any special message handlers, so its just .NET.
--Mark
I have a variable and a function in my addin class as follows:
static LRESULT CALLBACK MessageHook(int code, WPARAM wParam, LPARAM
lParam);
static HHOOK m_hook;
In the ConnectToSW function I have a line:
m_hook = SetWindowsHookEx(WH_GETMESSAGE, MessageHook, NULL,
GetCurrentThreadId());
That puts my function in the message hook list.
In the DisconnectFromSW function I have:
if (m_hook != NULL)
UnhookWindowsHookEx(m_hook);
to clean up when it's done.
Now, the MessageHook function:
LRESULT CALLBACK CPWAddin::MessageHook(int code, WPARAM wParam, LPARAM
lParam)
{
LPMSG lpMsg = (LPMSG) lParam;
if (code >= 0 && wParam == PM_REMOVE)
{
// Don't translate non-input events.
if ( (lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST)
)
{
// This loop is just going through a list of modeless dialog boxes
that
// may be open, so I need to check the messages against all the
dialogs
std::vector<CInsertPartDlg*>::iterator it =
TheApplication->m_pPartDlgs.begin();
while (it != TheApplication->m_pPartDlgs.end())
{
// Here's the important part, it checks to make sure the dialog
// is actually formed and open, and then checks to see if the
// message is for that dialog, and if so tells Windows to ignore
// the message.
if (IsWindow((*it)->m_hWnd) && (*it)->IsDialogMessage(lpMsg))
{
// The value returned from this hookproc is ignored,
// and it cannot be used to tell Windows the message has been
handled.
// To avoid further processing, convert the message to WM_NULL
// before returning.
lpMsg->message = WM_NULL;
lpMsg->lParam = 0;
lpMsg->wParam = 0;
break;
}
++it;
}
}
}
// call the next hook in line in Windows
return CallNextHookEx(m_hook, code, wParam, lParam);
}
std::vector<CInsertPartDlg*>::iterator it =
TheApplication->m_pPartDlgs.begin();
...I think that CInsertPartDlg is the class in your program that
creates the modeless dialogs in an array of m_pPartDlgs forms in your
application object named TheApplication, is this correct? I only have
one modeless dialog, so I would not have to iterate through my dialogs
and would just check if the message was from my dialog right?
Then, in the same function you define lpMsg and set it equal to lParam,
then if the message is from one of your dialogs, you set lpMsg to Null,
but don't do anything with it. I must be missing something or my C++
memory is failing me.
Thanks for your help so far.
--Mark
The lpMsg and lParam stuff I copied off of a website. With this
particular type of message hook (WH_GETMESSAGE - specified in the
SetWindowsHookEx call), the wParam is unused and the lParam is a
pointer to a MSG structure (docs available on MSDN). The lpMsg =
lParam line is assigning a pointer to the MSG structure that is the
same as the lParam pointer. So when lpMsg->message is changed, it is
changing the value pointed to by lParam, which is then passed on to the
CallNextHookEx function.
Hopefully that helps instead of confusing you more. My main language
is C++, and I'm not terribly familiar with windows system calls in VB,
so I don't really know how it handles the pointers and such.
I have the following in the add-in class...
#Region "Functions and Constants for Modeless Form Message Hook"
Public Const PM_NOREMOVE = &H0
Public Const PM_REMOVE = &H1
Public Const WM_NULL = &H0
Public Const WM_KEYFIRST = &H100
Public Const WM_KEYLAST = &H108
Public Const WH_KEYBOARD = &H2
Public Const WH_GETMESSAGE = &H3
Public Declare Function GetCurrentThreadId Lib "kernel32" () As
Integer
Public Declare Function UnhookWindowsHookEx Lib "user32" _
(ByVal hHook As Integer) As Integer
Public Declare Function IsWindow Lib "user32" _
(ByVal hWnd As Integer) As Boolean
Public Declare Function IsDialogMessage Lib "user32" _
(ByVal hWnd As Integer, _
ByRef lpMsg As System.Windows.Forms.Message) As Boolean
Public Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Integer, _
ByVal nCode As Integer, _
ByVal wParam As Integer, _
ByRef lParam As System.Windows.Forms.Message) As Integer
Public Delegate Function MessageHookDelegate(ByVal code As Integer, _
ByVal wParam As Integer, _
ByRef lParam As System.Windows.Forms.Message) As Integer
<MarshalAs(UnmanagedType.FunctionPtr)> _
Private MessageHookCallback As MessageHookDelegate
Public Declare Function SetWindowsHookEx Lib "user32" _
Alias "SetWindowsHookExA" _
(ByVal idHook As Integer, _
ByVal lpfn As MessageHookDelegate, _
ByVal hmod As Integer, _
ByVal dwThreadId As Integer) As Integer
Public m_hook As Integer
#End Region
In the ConnectToSW function I have...
'Create MessageHook Delegate Callback
MessageHookCallback = New MessageHookDelegate( _
AddressOf MessageHook)
'Put MessageHook function in the Message Hook list
m_hook = SetWindowsHookEx(WH_GETMESSAGE, _
MessageHookCallback, 0, GetCurrentThreadId())
In the DisconnectFromSW function I have...
'Pull our function out of the Message Hook list
If (m_hook <> vbNull) Then UnhookWindowsHookEx(m_hook)
Then our MessageHook Function...
Public Function MessageHook(ByVal code As Integer, _
ByVal wParam As Integer, _
ByRef lParam As System.Windows.Forms.Message) As Integer
If code >= 0 And wParam = PM_REMOVE Then
'Don't translate non-input events
If (lParam.Msg >= WM_KEYFIRST) And (lParam.Msg <= WM_KEYLAST)
Then
'Make sure MainDialog object exists before we attempt to find
its handle
If Not MainDialog Is Nothing Then
'Determine whether the specified window handle identifies an
existing window
'And determine whether message is intended for the specified
dialog box
If IsWindow(MainDialog.Handle.ToInt32) _
And IsDialogMessage(MainDialog.Handle.ToInt32, lParam) Then
lParam.Msg = WM_NULL
lParam.WParam = IntPtr.Zero
lParam.WParam = IntPtr.Zero
End If
End If
End If
End If
Return CallNextHookEx(m_hook, code, wParam, lParam)
End Function
So far it seems to be working great, but if somebody spots something
wrong with it, please post something here. Also, I may move the Hook
and Unkook to just before and after my form is Shown and Closed to
avoid any unnecessary processing.
Thanks for all your help Johnathan.
--Mark
Jonathan Anderson