【发布时间】:2016-05-01 17:54:15
【问题描述】:
如果我想捕获在通过 C#-Interop 自动化 Excel 时可能发生的任何错误,实现这一目标的最佳方法是什么?
我创建了一个线程来检索所有子窗口窗口句柄以检查是否弹出 VBA 错误,但我不敢相信这是王道。
其余部分已经通过管道传输到 C#,但我需要真正了解正在发生的一切。也许你有一些更好的想法,在此先感谢,任何帮助表示赞赏。
代码示例:
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, _
ByRef lpdwProcessId As Integer) As Integer
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function EnumChildWindows(ByVal hWndParent As System.IntPtr, ByVal lpEnumFunc As EnumWindowsProc, ByVal lParam As Integer) As Boolean
End Function
Private Delegate Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Boolean
<DllImport("user32.dll", EntryPoint:="GetWindowText")>
Public Shared Function GetWindowText(ByVal hwnd As Integer, ByVal lpString As System.Text.StringBuilder, ByVal cch As Integer) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function GetWindowTextLength(ByVal hwnd As IntPtr) As Integer
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsProc, ByVal lParam As IntPtr) As Boolean
End Function
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Shared Function SendMessage(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, <Out()> ByVal lParam As StringBuilder) As IntPtr
End Function
<DllImport("user32.dll", CharSet:=CharSet.Auto)> _
Private Shared Function GetClassName(ByVal hWnd As System.IntPtr, _
ByVal lpClassName As System.Text.StringBuilder, _
ByVal nMaxCount As Integer) As Integer
' Leave function empty
End Function
Public Function HandleAccess()
While (True)
Threading.Thread.Sleep(100)
Try
myApplication.hWndAccessApp()
GetWindowThreadProcessId(myApplication.hWndAccessApp(), my_proc_id)
If (my_proc_id <> 0) Then
my_hwnd = myApplication.hWndAccessApp
EnumWindows(New EnumWindowsProc(AddressOf isMacroError), 0)
End If
Catch ex As Exception
Debug.Print(ex.ToString())
If alreadyLaunched = True Then
Return False
End If
End Try
End While
Return True
End Function
Public Function isMacroError(ByVal hWnd As IntPtr, ByVal lParam As IntPtr)
Dim form_to_work_with As Access.Form
Dim check_for_proc_id As Integer
Dim anzahl_daten As Integer
GetWindowThreadProcessId(hWnd, check_for_proc_id)
If check_for_proc_id = my_proc_id Then
Dim capacity As Integer = GetWindowTextLength(hWnd)
If capacity > 0 Then
Dim sb As StringBuilder = New StringBuilder(capacity + 1)
GetWindowText(hWnd, sb, capacity + 1)
If (determineControlType(hWnd, 0).Equals("OFormPopupNC")) Then
For i As Integer = 0 To myApplication.Forms.Count - 1
If myApplication.Forms.Item(i).Name.Equals("Testform") Or myApplication.Forms.Item(i).Name.Equals("TestForm") Then
form_to_work_with = myApplication.Forms.Item(i)
Dim CalendarWeek As Access.ComboBox = DirectCast(form_to_work_with.Controls("auswahl"), Access.ComboBox)
Dim AnzahlDaten As HashSet(Of Access.TextBox) = New HashSet(Of Access.TextBox)
Dim AnzahlDaten_after As HashSet(Of Access.TextBox) = New HashSet(Of Access.TextBox)
For Each control In form_to_work_with.Controls
If control.ToString().Equals("Microsoft.Office.Interop.Access.TextBoxClass") Then
AnzahlDaten.Add(DirectCast(control, Access.TextBox))
AnzahlDaten_after.Add(DirectCast(control, Access.TextBox))
End If
Next
If AnzahlDaten.Count > 0 Then
For Each textbox As Access.TextBox In AnzahlDaten
If textbox.Name.StartsWith("Anzahl") Or textbox.Name.StartsWith("Daten") Then
Else
AnzahlDaten_after.Remove(textbox)
End If
Next
AnzahlDaten = AnzahlDaten_after
For Each textbox As Access.TextBox In AnzahlDaten
Integer.TryParse(textbox.Value.ToString, anzahl_daten)
If anzahl_daten = 0 Then
End If
Next
myApplication.DoCmd.RunMacro("TestForm.btn_x_Click")
End If
End If
Next
ElseIf (sb.ToString().Equals("Microsoft Visual Basic")) Then
EnumChildWindows(hWnd, New EnumWindowsProc(AddressOf checkForChilds), 0)
Debug.Print("Makro Fehler!")
End If
End If
End If
Return True
End Function
Public Function checkForChilds(ByVal hWnd As IntPtr, ByVal lParam As IntPtr)
Dim capacity As Integer = (SendMessage(hWnd, WM_GETTEXTLENGTH, IntPtr.Zero, New StringBuilder) + 1)
If capacity > 1 Then
Dim temp_string_builder As StringBuilder = New StringBuilder(capacity)
SendMessage(hWnd, WM_GETTEXT, capacity, temp_string_builder)
Debug.Print(temp_string_builder.ToString)
End If
Return True
End Function
Public Function determineControlType(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As String
Dim temp_string_builder As StringBuilder = New StringBuilder(256)
Dim nRet As Integer = GetClassName(hWnd, temp_string_builder, 256)
If (nRet <> 0) Then
Return temp_string_builder.ToString
Else
Return ""
End If
End Function
这是我当前状态的样子,我更进一步,因为项目中有另一个构造(这里是 VB.NET),但总而言之(试图)在单独的 Windows 中捕获 Exceptions-Thrown .
【问题讨论】:
-
请阅读如何提供minimal reproducible example
-
你好宏人,是的,我知道 StackOverflow 的规则说应该提供一个例子。如果你愿意,我可以将我写下的内容粘贴到代码中,但主要是我在寻找如何解决问题的想法,没有人应该写我必须写的东西,而只是为了掌握遇到的问题。跨度>
-
I'm looking for Ideas how to approach the problem对于这里的问题来说太宽泛了,这个想法是你有一个可以用代码示例复制的特定编程问题。这不是您所要求的“最佳实践”问题。 -
添加了在主线程中引入的示例,抱歉行为不当=
-
现在你有一个使用 VBA 的 C# 项目,但代码在 VB.NET 中???
标签: c# excel vba outlook office-interop