Disabling CTRL-X shortcut only for documents belonging to my addon

J

Josef Meile

Hi

On my previous post:
"Disabling CTRL-X shortcut in Visio 2003 Addon fails"

I could disable the CTRL-X shortcut by setting a keyboard hook with the
SetWindowsHookEx function of the Windows API. The only problem here is that
the shortcut gets disabled for the whole visio application and not for the
opened documents belonging to my C# addon. This is how I did it:

//KeyHookHandler is a method I defined on the same class
//VisioApp is a Visio.Application object
mKeyHookHandle = SetWindowsHookEx(WH_KEYBOARD,
new HOOKPROC(KeyHookHandler),
(IntPtr)0,
VisioApp.ProcessID);

theoretically you could use either the threadID or processID of the current
ActiveWindow instead of the processID from visio; however, I haven't
succeeded here. I tried to get them with the GetWindowThreadProcessId and
called the SetWindowsHookEx function with them, but I the hook isn't set.
This is what I have done so far:

First I defined GetWindowThreadProcessId like this:

[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out
IntPtr lpdwProcessId);

I have tried this in the handler of the visEvtCodeDocCreate event:

Visio.Window docWindow = GetDocumentWindow(VisioApp.ActiveDocument);
IntPtr processId = (IntPtr)0;
IntPtr threadId = GetWindowThreadProcessId((IntPtr)docWindow.WindowHandle32,
out processId);

This is my GetDocumentWindow method:
public static Visio.Window GetDocumentWindow(Visio.Document vsoDocument)
{
foreach (Visio.Window vsoWindow in vsoDocument.Application.Windows)
{
if (vsoWindow.Document.FullName == vsoDocument.FullName)
return vsoWindow;
}
return null;
}

Now this is what I have done to set the hook:

mKeyHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
new CSystemUtils.HOOKPROC(KeyHookHandler),processId,(IntPtr)0);

Or:
mKeyHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
new CSystemUtils.HOOKPROC(KeyHookHandler),(IntPtr)0,threadId);

However mKeyHookHandle is always zero (0) and the error code returned by
Marshal.GetLastWin32Error(); is always 87 = "Wrong parameter". What am I
doing wrong here?

Another approach of solving this problem, which didn't work as well, is by
making a desicion on the keyboard hook as follows:

//Please note that I have a class attribute called Applications, where
//I have stored each opened document, which was created with my
//addon
private int KeyHookHandler(int nCode, IntPtr wParam, IntPtr lParam)
{
if (Applications.Contains(VisioApp.ActiveDocument.ID)) //It fails here
{
KeyboardHookStruct keyboardHookStruct = (KeyboardHookStruct)
Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
KeyEventArgs keyArgs = new KeyEventArgs((Keys)keyboardHookStruct.vkCode);
if (keyArgs.KeyCode == Keys.X)
{
int ctrlState = GetKeyState((int)VK_CTRL);
if ((ctrlState != 0) && (ctrlState != 1))
{
//This means that the ctrl key is also pressed. So, we return 1 to
disable it
return 1;
}
}
}
//Otherwise, the default actions will be excecuted.
return CallNextHookEx(mKeyHookHandle,nCode,wParam,lParam);
}

However, as soon as I try to access VisioApp, I get:

"An outgoing call cannot be made since the application is dispatching an
input-synchronous call."

Which I have solved in the past by doing something like this:

private delegate int myDelegate(args1, ..., argn);
public int myFunction(args1, ..., argn)
{
if (this.InvokeRequired)
return (System.Int32)this.Invoke(new myDelegate(myFunction),
new object[] {args1, ..., argn});

//Here code whatever myFunction is supposed to do
}

However, on this case "this.InvokeRequired" is always false (I already
tested this). Is there anyway of asking Visio for the ID of the current
document from a different thread?

Best regards
Josef
 
A

Al Edlund

One technique is to create a persistent event in your document template that
identifies it for your addon. When the document opens you get a marker event
for your addon and you can decide then what you want to allow.
al


Josef Meile said:
Hi

On my previous post:
"Disabling CTRL-X shortcut in Visio 2003 Addon fails"

I could disable the CTRL-X shortcut by setting a keyboard hook with the
SetWindowsHookEx function of the Windows API. The only problem here is that
the shortcut gets disabled for the whole visio application and not for the
opened documents belonging to my C# addon. This is how I did it:

//KeyHookHandler is a method I defined on the same class
//VisioApp is a Visio.Application object
mKeyHookHandle = SetWindowsHookEx(WH_KEYBOARD,
new HOOKPROC(KeyHookHandler),
(IntPtr)0,
VisioApp.ProcessID);

theoretically you could use either the threadID or processID of the current
ActiveWindow instead of the processID from visio; however, I haven't
succeeded here. I tried to get them with the GetWindowThreadProcessId and
called the SetWindowsHookEx function with them, but I the hook isn't set.
This is what I have done so far:

First I defined GetWindowThreadProcessId like this:

[DllImport("user32.dll", SetLastError = true)]
internal static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out
IntPtr lpdwProcessId);

I have tried this in the handler of the visEvtCodeDocCreate event:

Visio.Window docWindow = GetDocumentWindow(VisioApp.ActiveDocument);
IntPtr processId = (IntPtr)0;
IntPtr threadId = GetWindowThreadProcessId((IntPtr)docWindow.WindowHandle32,
out processId);

This is my GetDocumentWindow method:
public static Visio.Window GetDocumentWindow(Visio.Document vsoDocument)
{
foreach (Visio.Window vsoWindow in vsoDocument.Application.Windows)
{
if (vsoWindow.Document.FullName == vsoDocument.FullName)
return vsoWindow;
}
return null;
}

Now this is what I have done to set the hook:

mKeyHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
new CSystemUtils.HOOKPROC(KeyHookHandler),processId,(IntPtr)0);

Or:
mKeyHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL,
new CSystemUtils.HOOKPROC(KeyHookHandler),(IntPtr)0,threadId);

However mKeyHookHandle is always zero (0) and the error code returned by
Marshal.GetLastWin32Error(); is always 87 = "Wrong parameter". What am I
doing wrong here?

Another approach of solving this problem, which didn't work as well, is by
making a desicion on the keyboard hook as follows:

//Please note that I have a class attribute called Applications, where
//I have stored each opened document, which was created with my
//addon
private int KeyHookHandler(int nCode, IntPtr wParam, IntPtr lParam)
{
if (Applications.Contains(VisioApp.ActiveDocument.ID)) //It fails here
{
KeyboardHookStruct keyboardHookStruct = (KeyboardHookStruct)
Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
KeyEventArgs keyArgs = new KeyEventArgs((Keys)keyboardHookStruct.vkCode);
if (keyArgs.KeyCode == Keys.X)
{
int ctrlState = GetKeyState((int)VK_CTRL);
if ((ctrlState != 0) && (ctrlState != 1))
{
//This means that the ctrl key is also pressed. So, we return 1 to
disable it
return 1;
}
}
}
//Otherwise, the default actions will be excecuted.
return CallNextHookEx(mKeyHookHandle,nCode,wParam,lParam);
}

However, as soon as I try to access VisioApp, I get:

"An outgoing call cannot be made since the application is dispatching an
input-synchronous call."

Which I have solved in the past by doing something like this:

private delegate int myDelegate(args1, ..., argn);
public int myFunction(args1, ..., argn)
{
if (this.InvokeRequired)
return (System.Int32)this.Invoke(new myDelegate(myFunction),
new object[] {args1, ..., argn});

//Here code whatever myFunction is supposed to do
}

However, on this case "this.InvokeRequired" is always false (I already
tested this). Is there anyway of asking Visio for the ID of the current
document from a different thread?

Best regards
Josef
 
J

Josef Meile

One technique is to create a persistent event in your document template that
identifies it for your addon. When the document opens you get a marker event
for your addon and you can decide then what you want to allow.
I already have a persistent event and I can see the documents belonging to
my addon, which indeed I stored into a Hashtable. However, when the keyboard
hook is triggered, I can't access the ActiveDocument property of the
Application object because of "input-synchronous calls".

The other solution is to set a keyboard hook for each visio document
belonging to my addon; however, here I need to determine either the threadId
or processId and set the hook, but this fails as well.
 
J

Josef Meile

Hi Al
One technique is to create a persistent event in your document template that
identifies it for your addon. When the document opens you get a marker event
for your addon and you can decide then what you want to allow.
Nevermind. I found the problem in the previous thread I posted:

* Disabling CTRL-X shortcut in Visio 2003 Addon fails
http://tinyurl.com/24e29m

There I took this code and converted it to C#:

* Visio2000: How to Bind a Custom Accelerator Key for a Custom Menu to a
Specific Drawing Window
http://tinyurl.com/ywgp92

I tried first to use an Enumerator or a foreach. I must confess that the
"accelItemsObj.Item(i)" from VBA confused me a little bit. I thaught in .Net
the AccelItems class should have a method called "get_Item" as usual in most
of the .Net wrappers; however, it doesn't exist. So, I only found
"GetEnumerator", which I tried to use first in a while convined with the
"MoveNext()" method, then I used a foreach, but for some strange reason, both
approaches raised this exception:

System.InvalidCastException: QI for IEnumVARIANT failed on the unmanaged
server.

Then after having tried lots of more complicated things (ie: setting a
keyboard hook through the Windows API), today I finally tried this:

Visio.AccelItem vsoAccelItem;
for (int i = 0; i < vsoAccelItems.Count; i++)
{
vsoAccelItem = vsoAccelItems;
if (vsoAccelItem.CmdNum == (short)Visio.VisUICmds.visCmdUFEditCut)
{
vsoAccelItem.Delete();
break;
}
}

//This is necessary in order to prevent changing a shape's
//text while pressing CTRL-X
vsoAccelItem = vsoAccelItems.Add();
vsoAccelItem.AddOnArgs = "null";
vsoAccelItem.AddOnName = "null";
vsoAccelItem.Alt = 0;
vsoAccelItem.Control = -1;
vsoAccelItem.Key = 88;
vsoAccelItem.Shift = 0;

Yes, I tried with the obvious array notation "[]", which somehow I skiped
last week. Now it works.

Best regards
Josef
 
Top