【问题标题】:Catching Errors while working with Excel in Interop在 Interop 中使用 Excel 时捕获错误
【发布时间】: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


【解决方案1】:

这次我走得更远了,特别是对于宏错误 UI-Automation 似乎是要走的路。我在 Access-Window 上放置了一个事件处理程序,它不断吐出作为其子级弹出的 Windows。现在发生了另一个问题,这仅在应用程序设置为可见时才有效,这不如在出现不需要的异常之前隐藏起来那么好。不明白为什么会这样,可能是MS自带的保护机制吧?

如果有人得到任何提示,我很兴奋,这是我当前的代码:适用于任何其他窗口或其他。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Automation;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using Automation = System.Windows.Automation;

namespace testNS
{
public class UIAutomation
{
    private AutomationElement _currentScope = null;

    public UIAutomation(int hwnd)
    {
        if (_currentScope == null)
            _currentScope = AutomationElement.FromHandle((IntPtr)hwnd);

        Automation.Automation.AddAutomationEventHandler(Automation.WindowPattern.WindowOpenedEvent,
        _currentScope, TreeScope.Subtree, this.handleWindowEvents);
    }

    public void handleWindowEvents(object src, Automation.AutomationEventArgs e)
    {
        try
        {
            AutomationElement aeSrc = src as AutomationElement;
            if (aeSrc.Current.Name.Contains("Visual Basic"))
            {
               Int32? parentWindowHandle = (_currentScope.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty) as Int32?);
                if (parentWindowHandle != null && parentWindowHandle > 0)
                {
                    IntPtr ptrHandle = (IntPtr) parentWindowHandle;
                    //NativeMethods.ShowWindow(ptrHandle, 1);
                }
                AutomationElementCollection childrenAutomationElementCollection = aeSrc.FindAll(TreeScope.Children, Condition.TrueCondition);
                foreach (AutomationElement aeChildren in childrenAutomationElementCollection)
                {
                    if (aeChildren.Current.Name.Length > 10)
                        Console.WriteLine(aeChildren.Current.Name);
                }
            }
        }
        catch (Exception ex)
        {

        }
    }

    public void unregister()
    {
        if (_currentScope != null)
        {
            Automation.Automation.RemoveAutomationEventHandler(Automation.WindowPattern.WindowOpenedEvent, _currentScope, this.handleWindowEvents);
        }
    }
}
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多