获取运行中的Excel实例的VB.NET代码

14 浏览
0 Comments

获取运行中的Excel实例的VB.NET代码

我有以下代码,取自这个答案

Option Compare Binary

Option Explicit On

Option Infer On

Option Strict Off

Imports Microsoft.Office.Interop

Imports System.Collections.Generic

Imports System.Runtime.InteropServices

Friend Module Module1

Private Declare Function GetDesktopWindow Lib "user32" () As IntPtr

Private Declare Function EnumChildWindows Lib "user32.dll" (ByVal WindowHandle As IntPtr, ByVal Callback As EnumWindowsProc, ByVal lParam As IntPtr) As Boolean

Private Declare Function GetClassName Lib "user32.dll" Alias "GetClassNameA" (ByVal hWnd As IntPtr, ByVal lpClassName As String, ByVal nMaxCount As Integer) As Integer

Private Delegate Function EnumWindowsProc(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Boolean

Private Declare Function AccessibleObjectFromWindow Lib "oleacc" (ByVal Hwnd As IntPtr, ByVal dwId As Int32, ByRef riid As Guid, ByRef ppvObject As Object) As Int32

Private lstWorkBooks As New List(Of String)

Public Sub Main()

GetExcelOpenWorkBooks()

End Sub

Private Sub GetExcelOpenWorkBooks()

EnumChildWindows(GetDesktopWindow(), AddressOf GetExcelWindows, CType(0, IntPtr))

If lstWorkBooks.Count > 0 Then MsgBox(String.Join(Environment.NewLine, lstWorkBooks))

End Sub

Public Function GetExcelWindows(ByVal hwnd As IntPtr, ByVal lParam As Int32) As Boolean

Dim Ret As Integer = 0

Dim className As String = Space(255)

Ret = GetClassName(hwnd, className, 255)

className = className.Substring(0, Ret)

If className = "EXCEL7" Then

Dim ExcelApplication As Excel.Application

Dim ExcelObject As Object = Nothing

Dim IDispatch As Guid

AccessibleObjectFromWindow(hwnd, &HFFFFFFF0, IDispatch, ExcelObject)

If ExcelObject IsNot Nothing Then

ExcelApplication = ExcelObject.Application

If ExcelApplication IsNot Nothing Then

For Each wrk As Excel.Workbook In ExcelApplication.Workbooks

If Not lstWorkBooks.Contains(wrk.Name) Then

lstWorkBooks.Add(wrk.Name)

End If

Next

End If

End If

End If

Return True

End Function

End Module

这段代码用于获取所有打开/运行的Excel实例/应用程序的引用。

如果没有在网上查找,我永远不会猜到如何做到这一点,因为我对其中的大部分内容不理解,所以这可能不是最好的方式来做,而且容易出现错误和漏洞。我正在尝试打开option strict1, 2),所以我将ExcelApplication = ExcelObject.Application这一行改为ExcelApplication = CType(ExcelObject, Excel.Application).Application,但这样做会抛出以下异常:

System.InvalidCastException 无法将类型为'System.__ComObject'的COM对象转换为接口类型'Microsoft.Office.Interop.Excel.Application'。此操作失败,因为对IID为'{000208D5-0000-0000-C000-000000000046}'的COM组件的查询接口调用失败,原因是没有支持此接口。(异常来自HRESULT:0x80004002(E_NOINTERFACE))。

我在不同的网站上找到了多个类似的参考资料,但尝试和错误的方法还没有解决它。

我的问题是如何打开option strict,如果有人能帮我改进代码或修复/解释其他问题,那就更好了。

0
0 Comments

之前我曾将其他回答标记为被接受的答案,这个回答非常好,但有一个问题(*),它只能获取到活动的对象,也就是第一个Excel进程。

在大多数情况下这已经足够了,但在某些特定情况下会存在多个Excel实例的问题。据我所知,这只有两种可能,一种是在启动Excel时按住Alt键,这样会提示在新实例中启动Excel,或者通过某些程序的代码来实现。

另一方面,问题中的代码确实可以解决获取所有运行中Excel实例的问题。我遇到的唯一问题是将它从后期绑定(Option Strict Off)转换为早期绑定(Option Strict On),这导致了一个错误,我一直找不到答案,直到现在。

在另一个以C#为基础的问题的回答的帮助下,我发现我需要将函数AccessibleObjectFromWindow的参数ppvObject从:

<MarshalAs(UnmanagedType.IUnknown)> ByRef ppvObject As Object

替换为:

ByRef ppvObject As Excel.Window

并且将声明中变量ExcelObject的类型从Object更改为Excel.Window(在代码中将其重命名为ExcelWindow也是良好的实践)。

完整文章如下:

之前我曾将其他回答标记为被接受的答案,这个回答非常好,但有一个问题,它只能获取到活动的对象,也就是第一个Excel进程。

在大多数情况下这已经足够了,但在某些特定情况下会存在多个Excel实例的问题。据我所知,这只有两种可能,一种是在启动Excel时按住Alt键,这样会提示在新实例中启动Excel,或者通过某些程序的代码来实现。

另一方面,问题中的代码确实可以解决获取所有运行中Excel实例的问题。我遇到的唯一问题是将它从后期绑定(Option Strict Off)转换为早期绑定(Option Strict On),这导致了一个错误,我一直找不到答案,直到现在。

在另一个以C#为基础的问题的回答的帮助下,我发现我需要将函数AccessibleObjectFromWindow的参数ppvObject从:

ByRef ppvObject As Object

替换为:

ByRef ppvObject As Excel.Window

并且将声明中变量ExcelObject的类型从Object更改为Excel.Window(在代码中将其重命名为ExcelWindow也是良好的实践)。

0
0 Comments

问题的原因是:需要获取正在运行的Excel实例并访问已打开的工作簿。解决方法是通过VB.NET编程来实现。下面是解决方法的具体步骤:

1. 首先,创建一个名为"FindOpenedWorkBooks"的公共函数,它返回一个类型为List(Of Workbook)的对象。

2. 在函数内部,创建一个名为"OpenedWorkBooks"的本地List(Of Workbook)对象,用来存储已打开的工作簿。

3. 使用Process.GetProcessesByName("EXCEL")方法获取所有正在运行的Excel进程对象,并将其存储在名为"ExcelInstances"的数组中。

4. 如果ExcelInstances数组的长度为0,则表示没有正在运行的Excel实例,函数返回空值。

5. 使用Marshal.GetActiveObject("Excel.Application")方法获取当前活动的Excel实例对象,并将其转换为Excel.Application类型的对象,并将其存储在名为"ExcelInstance"的变量中。

6. 如果ExcelInstance对象为空,则表示没有活动的Excel实例,函数返回空值。

7. 创建一个名为"worksheets"的Sheets对象,并将其初始化为Nothing。

8. 使用For Each循环遍历ExcelInstance.Workbooks集合中的每个工作簿对象,将其添加到OpenedWorkBooks列表中,并将其工作表集合赋值给worksheets变量。

9. 在循环中,使用For Each循环遍历worksheets集合中的每个工作表对象,输出工作表的名称,并释放该对象。

10. 在外层循环结束后,释放worksheets对象,并将其赋值为Nothing。

11. 释放ExcelInstance对象,并调用Marshal.CleanupUnusedObjectsInCurrentContext()方法进行资源清理。

12. 将OpenedWorkBooks列表作为函数的返回值。

13. 在其他代码中调用FindOpenedWorkBooks()函数,获取已打开的工作簿列表,并进行相应的操作。

14. 在操作完成后,释放所有的Interop对象,包括释放工作表对象、工作簿对象以及Excel实例对象。

15. 最后,调用ExcelApplication.Quit()方法通知操作结束,并使用Marshal.FinalReleaseComObject(ExcelApplication)方法进行最终释放。

16. 使用Marshal.CleanupUnusedObjectsInCurrentContext()方法进行清理,确保所有资源得到释放。

以上就是使用VB.NET获取正在运行的Excel实例并访问已打开的工作簿的解决方法。根据具体需求,可能需要对代码进行适当的修改和调整。

0