可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need help in
- figuring out how to iterate through currently open Excel add-in files (.xla) that have not been registered in Excel using the
Tools > Add-ins
menu path.
- more specifically, I am interested in any workbook that doesn't appear in the Add-In dialog, but has
ThisWorkbook.IsAddin = True
.
Demonstrating the issue:
Trying to loop through workbooks as follows doesn't get workbooks with .AddIn = True
:
Dim book As Excel.Workbook
For Each book In Application.Workbooks
Debug.Print book.Name
Next book
Looping through add-ins doesn't get add-ins that are not registered:
Dim addin As Excel.AddIn
For Each addin In Application.AddIns
Debug.Print addin.Name
Next addin
Looping through the VBProjects collection works, but only if user has specifically trusted access to the Visual Basic Project in the Macro Security settings - which is rarely:
Dim vbproj As Object
For Each vbproj In Application.VBE.VBProjects
Debug.Print vbproj.Filename
Next vbproj
However, if the name of the workbook is known, the workbook can be referenced directly regardless of whether it is an add-in or not:
Dim book As Excel.Workbook
Set book = Application.Workbooks("add-in.xla")
But how the heck to get reference to this workbook if the name is not known, and user's macro security settings cannot be relied on?
回答1:
As of Office 2010, there is a new collection .AddIns2 which is the same as .AddIns but also includes the unregistered .XLA plug-ins.
Dim a As AddIn
Dim w As Workbook
On Error Resume Next
With Application
For Each a In .AddIns2
If LCase(Right(a.name, 4)) = ".xla" Then
Set w = Nothing
Set w = .Workbooks(a.name)
If w Is Nothing Then
Set w = .Workbooks.Open(a.FullName)
End If
End If
Next
End With
回答2:
I have had issues with addins that are installed (and in the VBE) not being available via user's Addin
on Exel 2013 (in a work environment).
Tinkering with the solution from Chris C gave a good workaround.
Dim a As AddIn
Dim wb As Workbook
On Error Resume Next
With Application
.DisplayAlerts = False
For Each a In .AddIns2
Debug.Print a.Name, a.Installed
If LCase(Right$(a.Name, 4)) = ".xla" Or LCase(Right$(a.Name, 5)) Like ".xla*" Then
Set wb = Nothing
Set wb = .Workbooks(a.Name)
wb.Close False
Set wb = .Workbooks.Open(a.FullName)
End If
Next
.DisplayAlerts = True
End With
回答3:
I'm still on the lookout for a sane solution for this problem, but for the time being it seems that reading the window texts of all workbook windows gives a collection of all open workbooks, add-in or not:
Private Declare Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hWnd1 As Long, ByVal hWnd2 As Long, ByVal lpsz1 As String, ByVal lpsz2 As String) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hWnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Public Function GetAllOpenWorkbooks() As Collection
'Retrieves a collection of all open workbooks and add-ins.
Const EXCEL_APPLICATION_WINDOW As String = "XLDESK"
Const EXCEL_WORKBOOK_WINDOW As String = "EXCEL7"
Dim hWnd As Long
Dim hWndExcel As Long
Dim contentLength As Long
Dim buffer As String
Dim bookName As String
Dim books As Collection
Set books = New Collection
'Find the main Excel window
hWndExcel = FindWindowEx(Application.hWnd, 0&, EXCEL_APPLICATION_WINDOW, vbNullString)
Do
'Find next window
hWnd = FindWindowEx(hWndExcel, hWnd, vbNullString, vbNullString)
If hWnd Then
'Create a string buffer for 100 chars
buffer = String$(100, Chr$(0))
'Get the window class name
contentLength = GetClassName(hWnd, buffer, 100)
'If the window found is a workbook window
If Left$(buffer, contentLength) = EXCEL_WORKBOOK_WINDOW Then
'Recreate the buffer
buffer = String$(100, Chr$(0))
'Get the window text
contentLength = GetWindowText(hWnd, buffer, 100)
'If the window text was returned, get the workbook and add it to the collection
If contentLength Then
bookName = Left$(buffer, contentLength)
books.Add Excel.Application.Workbooks(bookName), bookName
End If
End If
End If
Loop While hWnd
'Return the collection
Set GetAllOpenWorkbooks = books
End Function
回答4:
What about this:
Public Sub ListAddins()
Dim ai As AddIn
For Each ai In Application.AddIns
If Not ai.Installed Then
Debug.Print ai.Application, ai.Parent, ai.Name, ai.FullName
End If
Next
End Sub
Any use?
回答5:
Use =DOCUMENTS, an Excel4 macro function.
Dim Docs As Variant
Docs = Application.Evaluate("documents(2)")
Here's the documentation for it (available here):
DOCUMENTS
Returns, as a horizontal array in text form, the names of the specified open workbooks in alphabetic order. Use DOCUMENTS to retrieve the names of open workbooks to use in other functions that manipulate open workbooks.
Syntax
DOCUMENTS(type_num, match_text)
Type_num is a number specifying whether to include add-in workbooks in the array of workbooks, according to the following table.
Type_num Returns
1 or omitted Names of all open workbooks except add-in workbooks
2 Names of add-in workbooks only
3 Names of all open workbooks
Match_text specifies the workbooks whose names you want returned and can include wildcard characters. If match_text is omitted, DOCUMENTS returns the names of all open workbooks.
回答6:
Is iterating through the registry a possibility? I know that that doesn't give you a snapshot of what your instance of Excel is using, but what a new instance would use - but depending on what you need it for, it might be good enough.
The relevant keys are:
'Active add-ins are in values called OPEN*
HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\Options
'Inactive add-ins are in values of their full path
HKEY_CURRENT_USER\Software\Microsoft\Office\12.0\Excel\Add-in Manager