如何使用 .net 8.0.2 处理 vb.net 中打开的 excel 文件。?

问题描述 投票:0回答:1

我正在使用 vb.net 和 .net 8.0.2。应用程序在 Windows 11 上运行。 我已经尝试了几个小时来指向用户已经打开的 Excel 文件。

我尝试过:

Dim app_excel As Microsoft.Office.Interop.Excel.Application

 app_excel = GetObject(, "Excel.Application")
 wbk  = app_excel.Workbooks("filename.xlsx")

这不起作用,oXL 正在创建一个新的 Excel 应用程序,而 wbk 为空。

我也尝试过:


  app_excel = CType(System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application"), Excel.Application)
  wbk  = app_excel.Workbooks("filename.xlsx")

但我收到 IDE 错误,指出 GetActiveObject 不是 Marshal 的成员。 显然 Marshal 已在最新版本的 .net GetActiveObject 中重建,不再存在。 我看到一些资源说他们必须完全重建 Marshal,所以我用 C# 构建了 Marshal2 类,如下所述: 未从 System.Runtime.InteropServices.Marshal C# 中找到 GetActiveObject 的定义

并在我的代码中使用它:

Dim ExcelInstance As Excel.Application = ProjectMarshal.Marshal2.GetActiveObject("Excel.Application")
wkb_configFile = ExcelInstance.Workbooks("configuration.xlsm")

它确实可以编译,但在执行时出现“无效索引”错误。 当我检查监视窗口时,尽管文件已打开,但 ExcelInstance 中似乎没有任何工作簿。

如果我将第三行替换为:

wkb_configFile = ExcelInstance.Workbooks(1)

我得到 wkb_configFile = "Book1" 这很可能是 Excel 启动时默认打开的工作簿。 ExcelInstance.Workbooks(2) 不存在(同时打开了 2 个文件)。

3 月 22 日编辑: 在社区成员,特别是 user246821 的帮助下,我得出的结论是,为项目定位 .net Framework (4.8) 而不是 .net 8.0 是更好的选择。它似乎确实适用于标准 GetObject(, "Excel.Application") 方法。 但是,现在我运行时,尽管打开了文件,但仍然找不到文件。我怀疑这可能是因为打开了其他 Excel 文件,并且每个 Excel 在代码中创建了一个 Excel 应用程序的单独实例。 我添加了对所有 Excel 实例的检查以找到正确的工作簿:



Dim ExcelInstances As Process() = Process.GetProcessesByName("EXCEL") 
Dim instance As Microsoft.Office.Interop.Excel.Application'EXCEL 
For Each instance In ExcelInstances
   'If app_excel.Workbooks(s_pathToFileToOpen) IsNot Nothing Then
   If instance.Workbooks("configuration.xlsx") IsNot Nothing Then
     fun_isFileOpened = True
     Exit Function
   End If
   Debug.WriteLine($"ExcelInstances: ProcessName: {instance.ProcessName} Id: {instance.Id}")
Next

感谢您的阅读!

.net excel vb.net visual-studio
1个回答
0
投票

以下演示如何使用 VB.NET (.NETFramework) 项目与打开的 Excel 工作簿进行交互。使用组件对象模型 (COM) 时,在使用完 COM 对象后释放它们非常重要。使用 Visual Studio 调试器并引发异常时,COM 对象可能不会被释放,并且 Microsoft Excel 进程仍然在后台运行 - 即使在关闭 Microsoft Excel 后也是如此。您可以通过打开任务管理器(即:按Ctrl-Alt-Del)并在后台进程下查找Microsoft Excel来验证这一点。在下面的代码中,检测到这种情况并终止孤立进程。

注意:虽然下面的代码似乎可以工作,但它的测试有限,这意味着应该对其进行测试以确保其行为符合预期。

尝试以下操作:

Public Sub UpdateExcel(value As String, fileMonikerName As String)

    Dim excelApp As Excel.Application = Nothing
    Dim desiredWb As Excel.Workbook = Nothing
    Dim ws As Excel.Worksheet = Nothing

    Try
        Dim ExcelProcesses() As Process = Process.GetProcessesByName("EXCEL")

        If ExcelProcesses.Count() = 0 Then
            Debug.WriteLine("Info: No Excel processes found.")
            Exit Sub
        End If

        For Each p As Process In ExcelProcesses
            Debug.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} - p.Id: {p.Id} p.MainWindowHandle: {p.MainWindowHandle.ToString("X8")}")

            If p.MainWindowHandle = IntPtr.Zero Then
                'an exception in this method may cause Excel to remain running in
                'the background even after both this program and Excel have been closed
                'if the Excel process is valid p.MainWindowHandle <> IntPtr.Zero
                'however, if the Excel process has been orphaned, then p.MainWindowHandle = IntPtr.Zero
                'and the 'Microsoft Excel' process can be observed in Task Manager under 'Background processes'
                'kill orphaned Excel process
                p.Kill()

                Debug.WriteLine("Info: An orphaned Excel process was terminated, but no active Excel instances were found. Open desired Excel workbook and try again.")
                Exit Sub
            End If
        Next

        'get existing Excel instance
        excelApp = DirectCast(Marshal.GetActiveObject("Excel.Application"), Excel.Application)

        'prevent prompting to save
        excelApp.DisplayAlerts = False

        'ensure Excel is visible
        excelApp.Visible = True

        'get desired workbook

        For Each workbook As Excel.Workbook In excelApp.Workbooks
            Debug.WriteLine($"               wb FullName: '{workbook.FullName}' Name: '{workbook.Name}'")

            If fileMonikerName = workbook.FullName Then
                'set value
                desiredWb = workbook

                For Each worksheet As Excel.Worksheet In desiredWb.Sheets
                    Debug.WriteLine($"               ws Name: '{worksheet.Name}'")

                    'ToDo: select desired worksheet
                    'set value
                    ws = worksheet

                    'activate worksheet
                    ws.Activate()
                Next
            End If
        Next

        If desiredWb Is Nothing Then
            Throw New Exception($"Error: Desired workbook not found ('{fileMonikerName}').")
        End If

        If ws Is Nothing Then
            Throw New Exception("Error: Worksheet not found.")
        End If

        'set value - for A1 also known as 1,1

        'format column to display date
        ws.Cells().Range("A1", "A4").EntireColumn.NumberFormat = "mm/dd/yyyy"

        'set value
        ws.Cells(1, 1) = $"{DateTime.Now.ToOADate()}"
        ws.Cells(1, 2) = value

        'save
        desiredWb.SaveAs(fileMonikerName, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, Excel.XlSaveAsAccessMode.xlNoChange, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value)

    Finally

        If ws IsNot Nothing Then
            Marshal.ReleaseComObject(ws)
            ws = Nothing
        End If

        If desiredWb IsNot Nothing Then
            Marshal.ReleaseComObject(desiredWb)
            desiredWb = Nothing
        End If

        If excelApp IsNot Nothing Then
            Marshal.ReleaseComObject(excelApp)
            excelApp = Nothing

            GC.Collect()
        End If
    End Try
End Sub

用法

Using ofd As OpenFileDialog = New OpenFileDialog()
    ofd.Filter = "Excel File (*.xlsx)|*.xlsx"

    If ofd.ShowDialog() = DialogResult.OK Then
        UpdateExcel($"Test {DateTime.Now.ToString("HH:mm:ss")}", ofd.FileName)
    End If
End Using

资源:

© www.soinside.com 2019 - 2024. All rights reserved.