1

Using outlook VBA - I would like to open an attachment in a particular instance of excel, and then copy the sheets from that attachment into an open workbook.

I've used a couple of code snippets from (Saving Outlook attachment with date in the filename and Check to see if Excel is open (from another Office 2010 App) to save an attachment from an email and then find the excel window I need to open it in - both work in isolated outlook test macros.

Trouble is, I can't seem to link the two parts together into working code, at the end of all of it I have:

Option Explicit
Private Declare Function newFindWindowEx 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 GetDesktopWindow Lib "user32" () As Long

 Private Declare Function AccessibleObjectFromWindow& Lib "oleacc" _
  (ByVal hwnd&, ByVal dwId&, riid As newGUID, xlWB As Object)

Private Const newOBJID_NATIVEOM = &HFFFFFFF0

Private Type newGUID
    lData1 As Long
    iData2 As Integer
    iData3 As Integer
    aBData4(0 To 7) As Byte
End Type


Sub AttachmentToExcel()

  Dim obj As Object
  Dim msg As Outlook.MailItem

  Dim objAtt As Object, iDispatch As newGUID
  Dim sPath As String, sFileName As String, sFile As String, filewithoutExt As String
  Dim attachFileName As String, DealID As String
  Dim srcWorkbook As Object

  sPath = "\\eu.insight.com\users\mklefass\Data\Desktop\"
  sFileName = "Test Workbook.xlsx": filewithoutExt = "Test Workbook.xlsx"
  sFile = sPath & sFileName


  Set obj = GetCurrentItem
  If TypeName(obj) = "MailItem" Then
      Set msg = obj
      DealID = FindDealID(msg.Subject)

      For Each objAtt In msg.Attachments
        If Right(objAtt.FileName, 4) = ".txt" Then
            attachFileName = "C:\Users\mklefass\Desktop\tmp\" & objAtt.FileName & ".tsv"
            objAtt.SaveAsFile attachFileName
            Set objAtt = Nothing
        End If
      Next

    ' Find window that has our main workbook open

      Dim dsktpHwnd As Long, hwnd As Long, mWnd As Long, cWnd As Long, wb As Object

      newSetIDispatch iDispatch

      dsktpHwnd = GetDesktopWindow

      hwnd = newFindWindowEx(dsktpHwnd, 0&, "XLMAIN", vbNullString)

      mWnd = newFindWindowEx(hwnd, 0&, "XLDESK", vbNullString)

      While mWnd <> 0 And cWnd = 0
        cWnd = newFindWindowEx(mWnd, 0&, "EXCEL7", filewithoutExt)
        hwnd = newFindWindowEx(dsktpHwnd, hwnd, "XLMAIN", vbNullString)
        mWnd = newFindWindowEx(hwnd, 0&, "XLDESK", vbNullString)
      Wend

    '~~> We got the handle of the Excel instance which has the file
      If cWnd > 0 Then
        '~~> Bind with the Instance
        Debug.Print AccessibleObjectFromWindow(cWnd, newOBJID_NATIVEOM, iDispatch, wb)
        '~~> Work with the file

        Set srcWorkbook = wb.accParent.Application.Workbooks.Open(attachFileName)
        'srcWorkbook.Worksheets(sheetNr).Copy after:=ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count)

        srcWorkbook.Close
        Set srcWorkbook = Nothing
      End If
   End If

End Sub
Private Sub newSetIDispatch(ByRef ID As newGUID)
 With ID
    .lData1 = &H20400
    .iData2 = &H0
    .iData3 = &H0
    .aBData4(0) = &HC0
    .aBData4(1) = &H0
    .aBData4(2) = &H0
    .aBData4(3) = &H0
    .aBData4(4) = &H0
    .aBData4(5) = &H0
    .aBData4(6) = &H0
    .aBData4(7) = &H46
 End With
End Sub

SetIDispatch, Findwindowex, accessibleobjectfromwindow are all defined in Check to see if Excel is open (from another Office 2010 App) and are the same in my code.

The last line fails, with runtime error 438: Object doesn't support this property or method. This suggests to me that I'm probably barking up the wrong tree - I'm afraid though that I've no idea which tree to aim for!

Thanks in advance.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Martin KS
  • 481
  • 7
  • 26
  • a liitle more of your code would be helpful, as the Problem suggests that it has do to with your definitions. Also there are question like is everything in one Sub/function or more than one, which would cause that in the last line maybe he does not know anymore what "attachfilename" is. Also please post the exact error message&number – Max Jun 11 '14 at 10:19
  • Thanks Max, I've added some more code and the error message now. Hopefully that gives a little clarity. – Martin KS Jun 11 '14 at 10:46

1 Answers1

2

Two problems: AccessibleObjectFromWindow returns a Window object and the Open method is a member of Application.Workbooks; and the window title doesn't have the file extension.

So to solve the first issue:

Set srcWorkbook = wb.Application.Open(attachFileName)

needs to become:

Set srcWorkbook = wb.Parent.Application.Workbooks.Open(attachFileName)

And for the second in some installations of Excel:

cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", "Test Workbook.xlsx")

may need to become:

cWnd = FindWindowEx(mWnd, 0&, "EXCEL7", "Test Workbook")

Note for future readers: This appears to depend on Windows and Excel versions, and whether or not you enable the "Hide known file extensions" in the windows explorer options.

Finally it seems that the window names need to be pointers (in 64-bit Office only):

Dim dsktpHwnd As Long, hwnd As Long, mWnd As Long, cWnd As Long, wb As Object

needs to become:

Dim dsktpHwnd As LongPtr, hwnd As LongPtr, mWnd As LongPtr, cWnd As LongPtr, wb As Object
aucuparia
  • 2,021
  • 20
  • 27
  • Both of those give the same error - runtime error 438: Object doesn't support this property or method – Martin KS Jun 11 '14 at 11:31
  • What return value is `AccessibleObjectFromWindow` giving? Also can you add a `debug.print wb.name` after that call and see what `wb` actually refers to? – aucuparia Jun 11 '14 at 12:34
  • I'm not sure how to answer your first question, but wb.Name doesn't seem to exist - I added a watch and have the following: `- : wb : : Object/IAccessible : Module5.AttachmentToExcel` `: accChildCount : 7 : Long : Module5.AttachmentToExcel` `: accFocus : Empty : Variant/Empty : Module5.AttachmentToExcel` `+ : accParent : : Object/IAccessible : Module5.AttachmentToExcel` `: accSelection : Empty : Variant/Empty :` – Martin KS Jun 11 '14 at 13:31
  • 1
    Can you change `Call AccessibleObjectFromWindow(...` to `MsgBox AccessibleObjectFromWindow(...` or `Debug.Print AccessibleObjectFromWindow(...`? `AccessibleObjectFromWindow` should return 0 for success, or a COM error code [MSDN](http://msdn.microsoft.com/en-gb/library/windows/desktop/dd317978(v=vs.85).aspx) – aucuparia Jun 11 '14 at 15:06
  • It returns 0 - so that doesn't seem to be the problem. – Martin KS Jun 11 '14 at 16:06
  • 1
    I was assuming AccessibleObjectFromWindow returned a `Workbook` object, but it doesn't - it returns a `Window`, of which the workbook is the parent. Answer edited. – aucuparia Jun 11 '14 at 16:24
  • Thanks for your edits, I've had a play on a few machines, and as you suggest the window title is different depending on a few options that can be selected. I've edited your answer to reflect this. Unfortunately though, there is no `wb.Parent` - there's a `wb.accParent` but no `wb.accParent.Application` only `.accChildCount` `.accFocus` `accParent` and `accSelection` all of the `accParent` items up to 7 are of type `Object/IAccessible` – Martin KS Jun 12 '14 at 09:26
  • 1
    Strange. With the edits, changing `Long` to `LongPtr`, and declaring a few undeclared variables e.g. `IDespatch` this works fine on my machine (Office 2010 64-bit). (as far as I can tell you also have `wb` declared twice). Try `Option Explicit` and see if the type of some of those variables matters? – aucuparia Jun 12 '14 at 09:58
  • looks like those are errors when I tried to shorten my code for here. I'll put the whole lot in. looks like it might be a version number difference, I'm on 2013. – Martin KS Jun 12 '14 at 10:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/55494/discussion-between-aucuparia-and-martin-ks). – aucuparia Jun 12 '14 at 10:44
  • I've now migrated to 64 bit office, and broke it again! I've started a new question though to avoid confusion http://stackoverflow.com/questions/35291946/migrating-vba-from-32-to-64-bit-office-fails-on-accessibleobjectfromwindow-in – Martin KS Feb 09 '16 at 12:21