【问题标题】:Set value for cell in excel file get error when open multi excel files打开多个 excel 文件时,为 excel 文件中的单元格设置值会出错
【发布时间】:2017-04-03 17:48:06
【问题描述】:

我想在outlook中写一个宏来检查excel文件是否打开,如果这个文件没有打开,打开它并为cell(1,1)设置值。否则,如果它正在打开,只需为 cell(1,1) 设置值,无需再次打开它。我这样做了,它运行正常。

这是我的源代码

Sub test_3()
    Dim objExcel As Object
    Dim WB As Object
    Dim WS As Object
    If (IsWorkBookOpen("C:\Users\sang\Desktop\Book2.xlsm") = True) Then 'check whether is file opening? if yes
        Set objExcel = GetObject(, "Excel.Application")
        objExcel.Visible = True
        Set WB = objExcel.Workbooks("Book2.xlsm")
        WB.Activate
    Else 'file is not opening
        Set objExcel = CreateObject("Excel.Application")
        objExcel.Visible = True
        Set WB = objExcel.Workbooks.Open("C:\Users\sang\Desktop\Book2.xlsm") 'open file
        WB.Activate
    End If
    Set WS = WB.Worksheets("Sheet1")
    WS.Range("A1").Value = "haha" 'set value for cell
End Sub

Function IsWorkBookOpen(FileName As String)
    Dim ff As Long, ErrNo As Long
    On Error Resume Next
    ff = FreeFile()
    Open FileName For Input Lock Read As #ff
    Close ff
    ErrNo = Err
    On Error GoTo 0
    Select Case ErrNo
    Case 0:    IsWorkBookOpen = False
    Case 70:   IsWorkBookOpen = True
    Case Else: Error ErrNo
    End Select
End Function

但我的问题是当这个文件正在打开并且其他一些文件也正在打开时。它无法为单元格设置值并得到错误“下标超出范围”。当我调试时,错误位于“Set WB = objExcel.Workbooks("Book2.xlsm")”。你能告诉我它有什么问题,我该如何解决。当只有我的单个 excel 文件时,一切都运行良好,并且在用它打开的文件很少时出现问题

【问题讨论】:

  • 我确实像你说的但是当我运行时,我得到一个错误“在自动化操作期间找不到类名的文件名”,当我调试它时,它突出显示这一行“Set objExcel = GetObject( "C:\Users\sang\Desktop\Book2.xlsm", "Excel.Application")" 我像你说的那样添加了更多路径。请帮我看看它有什么问题
  • 即使您的帖子已得到答复,请在下面的答案中检查我的(长)代码,它也适用于您打开多个 Excel 实例的情况

标签: vba excel outlook-2010


【解决方案1】:

下面的代码也适用于多个打开的 Excel 实例。

为适应本文而修改的部分代码取自Ozgrid

下面的代码有点长,但除此之外它工作得很好(经过测试)

Option Explicit

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 IIDFromString Lib "ole32" _
(ByVal lpsz As Long, ByRef lpiid As GUID) As Long

Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal hWnd As Long, ByVal dwId As Long, ByRef riid As GUID, _
ByRef ppvObject As Object) As Long

Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Private Const RETURN_OK As Long = &H0
Private Const IID_IDispatch As String = "{00020400-0000-0000-C000-000000000046}"
Private Const OBJID_NATIVEOM As Long = &HFFFFFFF0

Sub ComplexTest()

    Dim hWndXL As Long
    Dim oXLApp As Object
    Dim oWB As Object         
    Dim objExcel As Object
    Dim WB As Object
    Dim WS As Object
    Dim FullFileName    As String
    Dim CleanFileName   As String

    FullFileName = "C:\Users\sang\Desktop\Book2.xlsm"
    CleanFileName = Right(FullFileName, Len(FullFileName) - InStrRev(FullFileName, "\"))

    ' check if the Excel's file name is already open
    If IsWorkBookOpen(FullFileName) Then                                        
         ' first Excel Window
        hWndXL = FindWindowEx(0&, 0&, "XLMAIN", vbNullString)             
         ' got one Excel instance open ?
        Do While hWndXL > 0

             ' Get a reference to current excel instance
            If GetReferenceToXLApp(hWndXL, oXLApp) Then                     
                 ' loop through workbooks
                For Each oWB In oXLApp.Workbooks
                    If oWB.Name = CleanFileName Then
                        Set WB = oWB
                    End If
                Next
            End If

             ' Find the next Excel Window
            hWndXL = FindWindowEx(0, hWndXL, "XLMAIN", vbNullString)
        Loop
    Else
        Set objExcel = CreateObject("Excel.Application")
        objExcel.Visible = True
        Set WB = objExcel.Workbooks.Open(FullFileName) 'open file
    End If

    Set WS = WB.Worksheets("Sheet1")
    WS.Range("A1").Value = "haha" 'set value for cell

End Sub

 ' This section of code was taken from Ozgrid
 ' link: http://www.ozgrid.com/forum/showthread.php?t=182853
 '
 ' The Function Returns a reference to a specific instance of Excel.
 ' The Instance is defined by the Handle (hWndXL) passed by the calling procedure

Function GetReferenceToXLApp(hWndXL As Long, oXLApp As Object) As Boolean

    Dim hWinDesk As Long
    Dim hWin7 As Long
    Dim obj As Object
    Dim iID As GUID

     ' Rather than explaining, go read
     ' http://msdn.microsoft.com/en-us/library/windows/desktop/ms687262(v=vs.85).aspx
    Call IIDFromString(StrPtr(IID_IDispatch), iID)

     ' We have the XL App (Class name XLMAIN)
     ' This window has a child called 'XLDESK' (which I presume to mean 'XL desktop')
     ' XLDesk is the container for all XL child windows....
    hWinDesk = FindWindowEx(hWndXL, 0&, "XLDESK", vbNullString)

     ' EXCEL7 is the class name for a Workbook window (and probably others, as well)
     ' This is used to check there is actually a workbook open in this instance.
    hWin7 = FindWindowEx(hWinDesk, 0&, "EXCEL7", vbNullString)

     ' Deep API... read up on it if interested.
     ' http://msdn.microsoft.com/en-us/library/windows/desktop/dd317978(v=vs.85).aspx
    If AccessibleObjectFromWindow(hWin7, OBJID_NATIVEOM, iID, obj) = RETURN_OK Then
        Set oXLApp = obj.Application
        GetReferenceToXLApp = True
    End If

End Function

Function IsWorkBookOpen(FileName As String)

    Dim ff As Long, ErrNo As Long

    On Error Resume Next
    ff = FreeFile()
    Open FileName For Input Lock Read As #ff
    Close ff
    ErrNo = Err
    On Error GoTo 0

    Select Case ErrNo
        Case 0:    IsWorkBookOpen = False
        Case 70:   IsWorkBookOpen = True
        Case Else: Error ErrNo
    End Select

End Function

【讨论】:

  • @ThomasInzina 你有没有测试一下?
  • 是的,我做到了。出于某种原因,On Error Resume Next 不会逃脱由错误的getObject 调用引发的Error 9 ActiveX component can't create object。我确信这是我计算机上的错误配置。除此之外,小故障,您的代码工作得非常好!荣誉。
  • @ThomasInzina 感谢您的测试,如果您能修复它,请告诉我(在完全适应其他项目之前我要小心)
  • 您的代码运行良好。是我的机器。解决方法实际上是因为我无法逃脱IsWorkBookOpen 抛出的错误。在我重写之后,一切都像梦一样。你可以考虑用我的IsWorkBookOpen 替换你的IsWorkBookOpen。通过测试 Excel 用于工作簿的临时文件是否存在,我能够将 IsWorkBookOpen 减少到 4 行代码,并且不会抛出任何错误。
  • 我创建了一个类来包装你的代码:ExcelLocator。我还通过将一些代码提取到单独的方法中来添加一些功能。 getOpenApplications 返回所有打开的 Excel 应用程序的集合,getShortName 从文件路径中提取文件名。
【解决方案2】:

如果有多个 Excel.Application 实例正在运行,您会遇到问题,但否则会起作用。

Sub TestWrite()
    Const FULLNAME As String = "C:\Users\sang\Desktop\Book2.xlsm"

    Dim objExcel As Object, WB As Object, WS As Object
    Set objExcel = getExcelAppication
    objExcel.Visible = True
    Set WB = getWorkbook(objExcel, FULLNAME)

    If WB Is Nothing Then
        MsgBox "File not found: " & FULLNAME, vbInformation, ":("
    Else
        Set WS = WB.Worksheets("Sheet1")
        WS.Range("A1").Value = "haha"
    End If

End Sub

Function getExcelAppication() As Object
    Dim objExcel As Object
    If GetObject("winmgmts:").ExecQuery("select * from win32_process where name='Excel.exe'").Count > 0 Then
        Set objExcel = GetObject(, "Excel.Application")
    Else
        Set objExcel = CreateObject("Excel.Application")
    End If
    Set getExcelAppication = objExcel
End Function

Function getWorkbook(objExcel As Object, FULLNAME As String) As Object
    Dim ShortName As String
    Dim WB As Object, WS As Object
    ShortName = Right(FULLNAME, Len(FULLNAME) - InStrRev(FULLNAME, "\"))

    For Each WB In objExcel.Workbooks
        If WB.Name = ShortName Then
            Set getWorkbook = WB
            Exit Function
        End If
    Next

    Set getWorkbook = objExcel.Workbooks.Open(FULLNAME)

End Function

【讨论】:

  • 当我运行宏时出现错误,我猜 Workbooks.Open(ShortName) 应该是完整路径,而不仅仅是文件名,所以,我将其更改为 Set getWorkbook = objExcel.Workbooks.Open(全名)。它运行良好,但似乎是你重新打开了这个文件,因为当这个文件与其他几个 excel 文件一起打开时,当我运行宏时它重新打开这个文件并说“Book2.xlsm 已经打开,重新打开会导致任何您所做的更改将被丢弃。是否要重新打开 Book2.slsm"。我不想重新打开并收到此消息。如何在没有此消息的情况下运行宏
  • 您好 Thomas Inzina,对不起,我做到了。只需在“Set getWorkbook = objExcel.Workbooks.Open(ShortName)”行中将 ShortName 更改为 FULLNAME,它运行得非常好。非常感谢您尝试提供帮助
  • @Bruce 别抱歉!我想了一会儿,我快疯了。我几乎有同名的工作簿。当我在测试时,我可以发誓有一个数字列表,然后它们就消失了。结果是使用ShortName 导致我的文档中的工作簿打开......大声笑。
  • 是的,使用短名称打开它,它应该改为“全名”,我看到你编辑了你的问题,它看起来很棒,再次感谢你的帮助:D
  • 感谢您接受我的回答。这是一个有趣的问题:)
【解决方案3】:

如果打开了多个 Excel 实例,则无法保证

Set objExcel = GetObject(, "Excel.Application") 

将获取在其中打开文件的实例。

试试吧

Set objExcel = GetObject("C:\Users\sang\Desktop\Book2.xlsm", "Excel.Application")

或者只是

Set objExcel = GetObject("C:\Users\sang\Desktop\Book2.xlsm")

【讨论】:

  • 我确实像你说的但是当我运行时,我得到一个错误“在自动化操作期间找不到类名的文件名”,当我调试它时,它突出显示这一行“Set objExcel = GetObject( "C:\Users\sang\Desktop\Book2.xlsm", "Excel.Application")" 我像你说的那样添加了更多路径。请帮我看看它有什么问题
  • 应该可以 - 文件肯定在 Excel 中打开了吗?
  • 是的,我的 excel 文件肯定正在打开。此外,如果这个excel只打开一个(没有其他excel文件),它仍然会出现这样的错误
  • 你好蒂姆威廉姆斯,它仍然得到错误但我用 Thomas Inzina 的来源做了它,无论如何,非常感谢你尝试帮助:)
猜你喜欢
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多