At the end, I went through another route, I use a number of functions to hook on the keys of a normal form. That way I'm able to present forms from my addin, yet managing things like tabs etc...
This is the code I associated to my commandexecute
public static void BoxCommandControlButtonExecute()
{
var foilBoxForm = new FoilBoxForm();
new ModelessHelper(foilBoxForm);
foilBoxForm.ShowInTaskbar = false;
foilBoxForm.Show(new WindowWrapper((IntPtr) AddInGlobal.InventorApp.MainFrameHWND));
}
}
This is the code for modelesshelper
namespace FoilAddin
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
internal class ModelessHelper
{
/// <summary>
/// Other constants, uncategorized
/// </summary>
public const int GwlStyle = -16;
public const int WsDisabled = 0x08000000;
public const int UisSet = 1;
public const int UisClear = 2;
public const int UisInitialize = 3;
public const int UisfHidefocus = 0x1;
public const int UisfHideaccel = 0x2;
/// <summary>
/// Constructor
/// </summary>
/// <param name="control">
/// Control to hook
/// </param>
public ModelessHelper(Form control)
{
this._mControl = control;
this._mControl.HandleCreated += this.ControlHandleCreated;
this._mControl.HandleDestroyed += this.ControlHandleDestroyed;
this._mLocalWindowsHook.HookInvoked += this.LocalWindowsHookHookInvoked;
}
public static void TabToNextControl(Form form, bool forward)
{
Control curr = GetActiveControl(form);
List<Control> flatcontrols = new List<Control>();
CollectControls(form, flatcontrols);
int controlIndex = flatcontrols.IndexOf(curr);
if (controlIndex < 0)
{
return; // shouldn't happen
}
if (flatcontrols.Count > 0)
{
if (forward)
{
controlIndex++;
if (controlIndex >= flatcontrols.Count)
{
controlIndex = 0;
}
}
else
{
controlIndex--;
if (controlIndex < 0)
{
controlIndex = flatcontrols.Count - 1;
}
}
form.ActiveControl = flatcontrols[controlIndex];
// Display keyboard focus indicator. There's no such indicator in modeless window.
// Reference: .NET source, WinForms/Managed/System/WinForms/Control.cs, ProcessUICues()
WindowsInterop.SendMessage(
form.Handle,
WindowMessage.WmChangeuistate,
// or WindowMessage.WM_UPDATEUISTATE ?
(IntPtr) (UisClear | (UisfHidefocus << 16)),
IntPtr.Zero);
}
}
private static void CollectControls(Control c, List<Control> flatcontrols, int depth = 0)
{
if (c.Controls.Count == 0)
{
return;
}
// Copy controls to array and perform stable sort using TabIndex as a key
var f1 = new Control[c.Controls.Count];
c.Controls.CopyTo(f1, 0);
IEnumerable<Control> f = f1.OrderBy(n => n.TabIndex);
foreach (var control in f)
{
var isContainer = control is ContainerControl;
if (!control.Enabled || !control.Visible)
{
continue;
}
if (control.TabStop && control.CanSelect
&& (!isContainer || (isContainer && !HasFocusableChild(control))))
{
flatcontrols.Add(control);
}
CollectControls(control, flatcontrols, depth + 1);
}
}
private static Control GetActiveControl(Control control)
{
while (control != null)
{
if (control is Form)
{
control = (control as Form).ActiveControl;
}
else if ((control is ContainerControl) && HasFocusableChild(control))
{
control = (control as ContainerControl).ActiveControl;
}
else
{
return control;
}
}
return null;
}
private static bool HasFocusableChild(Control control)
{
foreach (Control c in control.Controls)
{
if (c is ContainerControl || c is GroupBox)
// GroupBox is not ContainerControl, so precess it separately
{
if (HasFocusableChild(c))
{
return true;
}
}
else
{
if (c.CanSelect && c.TabStop)
{
return true;
}
}
}
return false;
}
/// <summary>
/// LocalWindowsHook.HookInvoked event callback, called by LocalWindowsHook when a message is received.
/// </summary>
/// <param name="sender">
/// </param>
/// <param name="e">
/// </param>
private void LocalWindowsHookHookInvoked(object sender, HookEventArgs e)
{
if (e.HookCode >= 0 && new IntPtr(WindowMessage.PmRemove) == e.WParam)
{
var type = typeof(WindowsInterop.MSG);
// Copy the message to a local int variable
var msg = (uint) Marshal.ReadInt32(e.LParam, Marshal.OffsetOf(type, "Msg").ToInt32());
var wParam = (uint) Marshal.ReadInt32(e.LParam, Marshal.OffsetOf(type, "wParam").ToInt32());
var lParam = (uint) Marshal.ReadInt32(e.LParam, Marshal.OffsetOf(type, "lParam").ToInt32());
var hwnd = Marshal.ReadIntPtr(e.LParam, Marshal.OffsetOf(type, "hwnd").ToInt32());
// Don't translate non-input events. grab messages that are for this window.
if (msg >= WindowMessage.WmKeyfirst && msg <= WindowMessage.WmKeylast
&& WindowsInterop.GetForegroundWindow() == this._mControl.Handle)
{
if (WindowsInterop.IsDialogMessage(this._mControl.Handle, e.LParam) != 0)
{
// The value returned from this hook 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. If we'll disable these
// lines, system will start to produce "bell" sounds when hitting <Tab> key.
Marshal.WriteInt32(e.LParam, Marshal.OffsetOf(type, "Msg").ToInt32(), WindowMessage.WmNull);
Marshal.WriteInt32(e.LParam, Marshal.OffsetOf(type, "wParam").ToInt32(), 0);
Marshal.WriteInt32(e.LParam, Marshal.OffsetOf(type, "lParam").ToInt32(), 0);
}
}
else if (msg == WindowMessage.WmTimer)
{
// WM_TIMER is always run by Autodesk Inventor, so we can rely on it. Check if we
// have some modal window running (by calling IsWindowEnabled() for application
// window) and disable/enable form's window.
var isMainWindowDisabled =
WindowsInterop.IsWindowEnabled((IntPtr) AddInGlobal.InventorApp.MainFrameHWND) == 0;
if (isMainWindowDisabled != this._mRunningModal)
{
this._mRunningModal = isMainWindowDisabled;
this.SetNativeEnabled(!this._mRunningModal);
}
}
}
}
private void SelectNextControl()
{
TabToNextControl(this._mControl, WindowsInterop.GetKeyState(Keys.ShiftKey) >= 0);
}
private void SetNativeEnabled(bool enabled)
{
WindowsInterop.SetWindowLong(
this._mControl.Handle,
GwlStyle,
WindowsInterop.GetWindowLong(this._mControl.Handle, GwlStyle) & ~WsDisabled
| (enabled ? 0 : WsDisabled));
}
private class Keys
{
public const int ShiftKey = 0x10;
}
/// <summary>
/// Virtual key codes
/// </summary>
private class VirtualKeys
{
public const int VkAbntC1 = 0xC1;
public const int VkAbntC2 = 0xC2;
public const int VkAdd = 0x6B;
public const int VkAttn = 0xF6;
public const int VkBack = 0x08;
public const int VkCancel = 0x03;
public const int VkClear = 0x0C;
public const int VkCrsel = 0xF7;
public const int VkDecimal = 0x6E;
public const int VkDivide = 0x6F;
public const int VkEreof = 0xF9;
public const int VkEscape = 0x1B;
public const int VkExecute = 0x2B;
public const int VkExsel = 0xF8;
public const int VkIcoClear = 0xE6;
public const int VkIcoHelp = 0xE3;
public const int VkKey0 = 0x30;
public const int VkKey1 = 0x31;
public const int VkKey2 = 0x32;
public const int VkKey3 = 0x33;
public const int VkKey4 = 0x34;
public const int VkKey5 = 0x35;
public const int VkKey6 = 0x36;
public const int VkKey7 = 0x37;
public const int VkKey8 = 0x38;
public const int VkKey9 = 0x39;
public const int VkKeyA = 0x41;
public const int VkKeyB = 0x42;
public const int VkKeyC = 0x43;
public const int VkKeyD = 0x44;
public const int VkKeyE = 0x45;
public const int VkKeyF = 0x46;
public const int VkKeyG = 0x47;
public const int VkKeyH = 0x48;
public const int VkKeyI = 0x49;
public const int VkKeyJ = 0x4A;
public const int VkKeyK = 0x4B;
public const int VkKeyL = 0x4C;
public const int VkKeyM = 0x4D;
public const int VkKeyN = 0x4E;
public const int VkKeyO = 0x4F;
public const int VkKeyP = 0x50;
public const int VkKeyQ = 0x51;
public const int VkKeyR = 0x52;
public const int VkKeyS = 0x53;
public const int VkKeyT = 0x54;
public const int VkKeyU = 0x55;
public const int VkKeyV = 0x56;
public const int VkKeyW = 0x57;
public const int VkKeyX = 0x58;
public const int VkKeyY = 0x59;
public const int VkKeyZ = 0x5A;
public const int VkMultiply = 0x6A;
public const int VkNoname = 0xFC;
public const int VkNumpad0 = 0x60;
public const int VkNumpad1 = 0x61;
public const int VkNumpad2 = 0x62;
public const int VkNumpad3 = 0x63;
public const int VkNumpad4 = 0x64;
public const int VkNumpad5 = 0x65;
public const int VkNumpad6 = 0x66;
public const int VkNumpad7 = 0x67;
public const int VkNumpad8 = 0x68;
public const int VkNumpad9 = 0x69;
public const int VkOem1 = 0xBA;
public const int VkOem102 = 0xE2;
public const int VkOem2 = 0xBF;
public const int VkOem3 = 0xC0;
public const int VkOem4 = 0xDB;
public const int VkOem5 = 0xDC;
public const int VkOem6 = 0xDD;
public const int VkOem7 = 0xDE;
public const int VkOem8 = 0xDF;
public const int VkOemAttn = 0xF0;
public const int VkOemAuto = 0xF3;
public const int VkOemAx = 0xE1;
public const int VkOemBacktab = 0xF5;
public const int VkOemClear = 0xFE;
public const int VkOemComma = 0xBC;
public const int VkOemCopy = 0xF2;
public const int VkOemCusel = 0xEF;
public const int VkOemEnlw = 0xF4;
public const int VkOemFinish = 0xF1;
public const int VkOemFjLoya = 0x95;
public const int VkOemFjMasshou = 0x93;
public const int VkOemFjRoya = 0x96;
public const int VkOemFjTouroku = 0x94;
public const int VkOemJump = 0xEA;
public const int VkOemMinus = 0xBD;
public const int VkOemPa1 = 0xEB;
public const int VkOemPa2 = 0xEC;
public const int VkOemPa3 = 0xED;
public const int VkOemPeriod = 0xBE;
public const int VkOemPlus = 0xBB;
public const int VkOemReset = 0xE9;
public const int VkOemWsctrl = 0xEE;
public const int VkPa1 = 0xFD;
public const int VkPacket = 0xE7;
public const int VkPlay = 0xFA;
public const int VkProcesskey = 0xE5;
public const int VkReturn = 0x0D;
public const int VkSelect = 0x29;
public const int VkSeparator = 0x6C;
public const int VkSpace = 0x20;
public const int VkSubtract = 0x6D;
public const int VkTab = 0x09;
public const int VkZoom = 0xFB;
public const int VkNone = 0xFF;
public const int VkAccept = 0x1E;
public const int VkApps = 0x5D;
public const int VkBrowserBack = 0xA6;
public const int VkBrowserFavorites = 0xAB;
public const int VkBrowserForward = 0xA7;
public const int VkBrowserHome = 0xAC;
public const int VkBrowserRefresh = 0xA8;
public const int VkBrowserSearch = 0xAA;
public const int VkBrowserStop = 0xA9;
public const int VkCapital = 0x14;
public const int VkConvert = 0x1C;
public const int VkDelete = 0x2E;
public const int VkDown = 0x28;
public const int VkEnd = 0x23;
public const int VkF1 = 0x70;
public const int VkF10 = 0x79;
public const int VkF11 = 0x7A;
public const int VkF12 = 0x7B;
public const int VkF13 = 0x7C;
public const int VkF14 = 0x7D;
public const int VkF15 = 0x7E;
public const int VkF16 = 0x7F;
public const int VkF17 = 0x80;
public const int VkF18 = 0x81;
public const int VkF19 = 0x82;
public const int VkF2 = 0x71;
public const int VkF20 = 0x83;
public const int VkF21 = 0x84;
public const int VkF22 = 0x85;
public const int VkF23 = 0x86;
public const int VkF24 = 0x87;
public const int VkF3 = 0x72;
public const int VkF4 = 0x73;
public const int VkF5 = 0x74;
public const int VkF6 = 0x75;
public const int VkF7 = 0x76;
public const int VkF8 = 0x77;
public const int VkF9 = 0x78;
public const int VkFinal = 0x18;
public const int VkHelp = 0x2F;
public const int VkHome = 0x24;
public const int VkIco00 = 0xE4;
public const int VkInsert = 0x2D;
public const int VkJunja = 0x17;
public const int VkKana = 0x15;
public const int VkKanji = 0x19;
public const int VkLaunchApp1 = 0xB6;
public const int VkLaunchApp2 = 0xB7;
public const int VkLaunchMail = 0xB4;
public const int VkLaunchMediaSelect = 0xB5;
public const int VkLbutton = 0x01;
public const int VkLcontrol = 0xA2;
public const int VkLeft = 0x25;
public const int VkLmenu = 0xA4;
public const int VkLshift = 0xA0;
public const int VkLwin = 0x5B;
public const int VkMbutton = 0x04;
public const int VkMediaNextTrack = 0xB0;
public const int VkMediaPlayPause = 0xB3;
public const int VkMediaPrevTrack = 0xB1;
public const int VkMediaStop = 0xB2;
public const int VkModechange = 0x1F;
public const int VkNext = 0x22;
public const int VkNonconvert = 0x1D;
public const int VkNumlock = 0x90;
public const int VkOemFjJisho = 0x92;
public const int VkPause = 0x13;
public const int VkPrint = 0x2A;
public const int VkPrior = 0x21;
public const int VkRbutton = 0x02;
public const int VkRcontrol = 0xA3;
public const int VkRight = 0x27;
public const int VkRmenu = 0xA5;
public const int VkRshift = 0xA1;
public const int VkRwin = 0x5C;
public const int VkScroll = 0x91;
public const int VkSleep = 0x5F;
public const int VkSnapshot = 0x2C;
public const int VkUp = 0x26;
public const int VkVolumeDown = 0xAE;
public const int VkVolumeMute = 0xAD;
public const int VkVolumeUp = 0xAF;
public const int VkXbutton1 = 0x05;
public const int VkXbutton2 = 0x06;
}
/// <summary>
/// Windows message Id constants
/// </summary>
private class WindowMessage
{
public const int WmNull = 0;
public const int WmKeyfirst = 0x0100;
public const int WmKeydown = 0x0100;
public const int WmKeylast = 0x0109;
public const int WmTimer = 0x0113;
public const int WmChangeuistate = 0x0127;
public const int WmUpdateuistate = 0x0128;
public const int PmRemove = 0x0001;
}
/// <summary>
/// Windows imports
/// </summary>
private static class WindowsInterop
{
[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
public static extern short GetKeyState(int keyCode);
[DllImport("user32.dll")]
public static extern int GetWindowLong(IntPtr hWnd, int nIndex);
/// <summary>
/// See: http://support.microsoft.com/kb/71450
/// </summary>
/// <param name="hDlg">
/// </param>
/// <param name="lpMsg">
/// </param>
/// <returns>
/// </returns>
[DllImport("user32.dll")]
public static extern int IsDialogMessage(IntPtr hDlg, IntPtr lpMsg);
[DllImport("user32.dll")]
public static extern int IsWindowEnabled(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
/// <summary>
/// Windows MSG structure definition
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
private IntPtr hwnd;
private uint Msg;
private IntPtr wParam;
private IntPtr lParam;
private int time;
private int x; // decomposed POINT pt
private int y;
}
}
#region Private members
private LocalWindowsHook _mLocalWindowsHook = new LocalWindowsHook(HookType.WhGetmessage);
private Form _mControl;
private bool _mRunningModal;
#endregion Private members
#region Control event callbacks
/// <summary>
/// Install the hook when the control window handle is created
/// </summary>
/// <param name="sender">
/// </param>
/// <param name="e">
/// </param>
private void ControlHandleCreated(object sender, EventArgs e)
{
this._mLocalWindowsHook.Install();
}
/// <summary>
/// Uninstall the hook when the control window is destroyed
/// </summary>
/// <param name="sender">
/// </param>
/// <param name="e">
/// </param>
private void ControlHandleDestroyed(object sender, EventArgs e)
{
this._mLocalWindowsHook.Uninstall();
}
#endregion Control event callbacks
}
}
this is windowwrapper
namespace FoilAddin
{
using System;
using System.Windows.Forms;
public class WindowWrapper : IWin32Window
{
public WindowWrapper(IntPtr handle)
{
this.Handle = handle;
}
public IntPtr Handle { get; private set; }
}
}
This is localwindowshook
namespace FoilAddin
{
using System;
using System.Runtime.InteropServices;
public class LocalWindowsHook
{
public delegate void HookEventHandler(object sender, HookEventArgs e);
public delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
private HookProc _mFilterFunc = null;
private IntPtr _mHhook = IntPtr.Zero;
private HookType _mHookType;
public LocalWindowsHook(HookType hook)
{
this._mHookType = hook;
this._mFilterFunc = new HookProc(this.CoreHookProc);
}
public LocalWindowsHook(HookType hook, HookProc func)
{
this._mHookType = hook;
this._mFilterFunc = func;
}
public event HookEventHandler HookInvoked;
public void Install()
{
this._mHhook = SetWindowsHookEx(
this._mHookType,
this._mFilterFunc,
IntPtr.Zero,
(int) AppDomain.GetCurrentThreadId());
}
public void Uninstall()
{
UnhookWindowsHookEx(this._mHhook);
}
[DllImport("user32.dll")]
private static extern int CallNextHookEx(IntPtr hhook, int code, IntPtr wParam, IntPtr lParam);
private int CoreHookProc(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0)
{
return CallNextHookEx(this._mHhook, code, wParam, lParam);
}
var e = new HookEventArgs {HookCode = code, WParam = wParam, LParam = lParam};
this.OnHookInvoked(e);
return CallNextHookEx(this._mHhook, code, wParam, lParam);
}
private void OnHookInvoked(HookEventArgs e)
{
if (this.HookInvoked != null)
{
this.HookInvoked(this, e);
}
}
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(HookType code, HookProc func, IntPtr hInstance, int threadId);
[DllImport("user32.dll")]
private static extern int UnhookWindowsHookEx(IntPtr hhook);
}
}
this is hooktype
namespace FoilAddin
{
public enum HookType : int
{
WhJournalrecord = 0,
WhJournalplayback = 1,
WhKeyboard = 2,
WhGetmessage = 3,
WhCallwndproc = 4,
WhCbt = 5,
WhSysmsgfilter = 6,
WhMouse = 7,
WhHardware = 8,
WhDebug = 9,
WhShell = 10,
WhForegroundidle = 11,
WhCallwndprocret = 12,
WhKeyboardLl = 13,
WhMouseLl = 14
}
}
and this is hookeventparms
namespace FoilAddin
{
using System;
public class HookEventArgs : EventArgs
{
public int HookCode;
public IntPtr LParam;
public IntPtr WParam;
}
}
The rest is typical code for an addin...
Hope it helps.
Thanks!