VSTO / Word 2003 events

We are working on a smart client / workflow solution with VSTO and Word 2003. In our xml schema we have elements which are occurring subsequently. For example like this:

<heading> Some heading</heading>

<text block>first block </text block>

<text block>second block </text block>

<text block>third block </text block>

<text block>fourth block </text block>

We cannot tell beforehand how many text blocks user would have. It would be handy if you could do so, that then user strikes enter key, the new text block and its placeholder would appear.

Problem: Word 2003 + Enter
Description: After InsertParagraph (=keypress Enter) add Tags outside Paragraph

How to: capture KeyDown event word 2003 or
capture InsertParagraph event

If you could show some light that would be great. Thanks,

~Brett

[2709 byte] By [BRJ2k5] at [2008-2-7]
# 1
Hi,

I have been looking into this question during the last week and may have found a workaround for your scenario. There is no way from withing Word to monitor the key press events. So I had to come up with another way. I have created some code that starts a timer and monitors the number of paragraphs in the document. When a new one is added it selects the previous paragraph.

Paul Stubbs
Program Manager
Microsoft
http://blogs.msdn.com/pstubbs/

Here is the code.

Public Class ThisDocument

Dim pcount As Integer

Dim WithEvents timer As New System.Windows.Forms.Timer()

Private Sub ThisDocument_Open() Handles Me.Open

'count the number of paragraphs

pcount = Me.Paragraphs.Count

'set the timer interval

timer.Interval = 750

'start the timer

timer.Start()

End Sub

Private Sub ThisDocument_Startup(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Startup

End Sub

Private Sub ThisDocument_Shutdown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shutdown

'stop the timer

timer.Stop()

End Sub

Private Sub timer_Tick(ByVal sender As Object, ByVal e As System.EventArgs) Handles timer.Tick

If (Me.Paragraphs.Count > pcount) Then

'put the added paragraph in a bookmark

Me.Paragraphs.Item(Me.Paragraphs.Count - 1).Range.Select()

'Now that you have the range of the previous paragraph you

'can wrap it in a bookmark or XMLNode.

'set the new count

pcount = Me.Paragraphs.Count

'TODO: manage the deletion of paragraphs

End If

End Sub

End Class

PaulStubbs at 2007-9-8 > top of Msdn Tech,Visual Studio Tools for Office,Visual Studio Tools for Office...
# 2
Why wouldn't you add some UI (like button - "Click me to add another paragraph") to the programmable task pane (aka ActionsPane) to let the user drive the creation of the paragraphs rather than determining this automatically based on the enter key? The enter keys may appear in places you would not expect.
MishaShneerson at 2007-9-8 > top of Msdn Tech,Visual Studio Tools for Office,Visual Studio Tools for Office...
# 3
This could be done , but the question was how to automatically make this happen. The sample code I provided should be modified to make it more robust for an actual application, for example only check for new paragraphs if they are added to a particluar node.

Paul

PaulStubbs at 2007-9-8 > top of Msdn Tech,Visual Studio Tools for Office,Visual Studio Tools for Office...
# 4
Hi Brett

Here is some code that hooks a keyboard for Outlook and will work the same in word, you have to be careful to make sure callnexthook through,

Appologies for layout, basically the Top part is the definitions, You can call HookKeyboard and Unhook as required, I would advise saving your work regularly whilst playing with this as the Keyboard Control is literal and can stop you being able to do anything in word :)

Good Luck

Mike Walker
Visual Developer VSTO

Private Declare Function GetFocus Lib "user32" Alias "GetFocus" () As Integer

Private Declare Function UnhookWindowsHookEx Lib "user32" _

(ByVal hHook As Integer) As Integer

Private Declare Function SetWindowsHookEx Lib "user32" _

Alias "SetWindowsHookExA" (ByVal idHook As Integer, _

ByVal lpfn As KeyboardHookDelegate, ByVal hmod As Integer, _

ByVal dwThreadId As Integer) As Integer

Private Declare Function GetAsyncKeyState Lib "user32" _

(ByVal vKey As Integer) As Integer

Private Declare Function CallNextHookEx Lib "user32" _

(ByVal hHook As Integer, _

ByVal nCode As Integer, _

ByVal wParam As Integer, _

ByVal lParam As KBDLLHOOKSTRUCT) As Integer

Private Structure KBDLLHOOKSTRUCT

Public vkCode As Integer

Public scanCode As Integer

Public flags As Integer

Public time As Integer

Public dwExtraInfo As Integer

End Structure

' 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

' Virtual Keys

Private Const VK_TAB = &H9

Private Const VK_CONTROL = &H11

Private Const VK_ESCAPE = &H1B

Private Const VK_DELETE = &H2E

Private Const VK_ALT = &H12&

Private Const WH_KEYBOARD_LL As Integer = 13&

Private KeyboardHandle As Integer

Private Delegate Function KeyboardHookDelegate( _

ByVal Code As Integer, _

ByVal wParam As Integer, ByRef lParam As KBDLLHOOKSTRUCT) _

As Integer

<System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.FunctionPtr)> _

Private callback As KeyboardHookDelegate

' Implement this function to block as many

' key combinations as you'd like

Private Function IsHooked( _

ByRef Hookstruct As KBDLLHOOKSTRUCT) As Boolean

Debug.WriteLine("Hookstruct.vkCode: " & Hookstruct.vkCode & " - Time: " & Hookstruct.time.ToString() & " - Flags: " & Hookstruct.flags.ToString() & " - Extra:" & Hookstruct.dwExtraInfo.ToString())


If (Hookstruct.vkCode = System.Windows.Forms.Keys.Enter And CBool(GetAsyncKeyState(VK_CONTROL))) Then

'CTRL + ENTER Pressed

End If

If (Hookstruct.vkCode = System.Windows.Forms.Keys.D And CBool(GetAsyncKeyState(VK_CONTROL))) Then

GoTo Delete

End If

If (Hookstruct.vkCode = System.Windows.Forms.Keys.S And CBool(GetAsyncKeyState(VK_CONTROL))) Then

' CTRL S Pressed
End If

If (Hookstruct.vkCode = System.Windows.Forms.Keys.O And CBool(GetAsyncKeyState(VK_CONTROL))) Then

Dim cn As String = FWBS.Common.Functions.GetWindowClassName(GetFocus())

Debug.WriteLine(cn)

If cn = "SUPERGRID" Then

If (Session.OMS.IsLoggedIn) Then

_oms.RunCommand(_oms, "SYSTEM;OPEN")

Return True

End If

End If

End If

If Hookstruct.vkCode = VK_DELETE Then

If CBool(GetAsyncKeyState(VK_CONTROL)) = False And CBool(GetAsyncKeyState(VK_ALT)) = False Then

End If

End If

Return False

End Function

Private Sub HookedState(ByVal Text As String)

Debug.WriteLine(Text)

End Sub

Private Function KeyboardCallback(ByVal Code As Integer, _

ByVal wParam As Integer, _

ByRef lParam As KBDLLHOOKSTRUCT) As Integer

If (Code = HC_ACTION) Then

Debug.WriteLine("Calling IsHooked")

If (IsHooked(lParam)) Then

Return 1

End If

End If

Return CallNextHookEx(KeyboardHandle, _

Code, wParam, lParam)

End Function

Public Sub HookKeyboard()

If (Hooked() = False) Then

callback = New KeyboardHookDelegate(AddressOf KeyboardCallback)

Dim m As System.Reflection.Module = System.Reflection.Assembly.GetExecutingAssembly.GetModules()(0)

KeyboardHandle = SetWindowsHookEx( _

WH_KEYBOARD_LL, callback, _

System.Runtime.InteropServices.Marshal.GetHINSTANCE(m).ToInt32, 0)

'System.Diagnostics.Process.GetCurrentProcess().MainModule.BaseAddress.ToInt32(), 0)

End If

Call CheckHooked()

End Sub

Private Sub CheckHooked()

If (Hooked()) Then

Debug.WriteLine("Keyboard Hooked")

Else

Debug.WriteLine("Keyboard hook failed: " & Err.LastDllError)

End If

End Sub

Private Function Hooked()

Hooked = KeyboardHandle <> 0

End Function

Private Sub UnhookKeyboard()

If (Hooked()) Then

Call UnhookWindowsHookEx(KeyboardHandle)

KeyboardHandle = 0

Debug.WriteLine("Keyboard Unhooked")

End If

End Sub

MikeWalker at 2007-9-8 > top of Msdn Tech,Visual Studio Tools for Office,Visual Studio Tools for Office...