According to this article, you cannot move or copy worksheet from one instance of Excel to another. Unfortunately, it's the only way I have to make my program function properly.
I have 2 instances of Excel: one is run by our ancient ERP system and another through OLE call. The macros running in the second should copy first worksheet from opened workbook (ThisWorkbook) into workbook opened in the first instance (Wb). I'm using ForEachLoop's solution to get Wb:
Public Function GetExcelObjectFromHwnd(ByVal hWnd As Long) As Boolean
...
If AccessibleObjectFromWindow(hWnd, OBJID_NATIVEOM, iid, obj) = 0 Then 'S_OK
Dim objApp As Excel.Application
Set objApp = obj.Application
Dim Wb As Workbook
For Each Wb In objApp.Workbooks
ProcessWorkbook Wb
Next Wb
fOk = True
End If
...
End Function
Sub ProcessWorkbook(Wb as Worksheet)
...
'This produces error because ThisWorkBook and Wb are opened in different instances of Excel:
ThisWorkbook.Sheets(1).Copy , Wb.Sheets(1)
'What I developed so far
Wb.Sheets.Add , Wb.Sheets(1)
'this doesn't work too:
ThisWorkbook.Sheets(1).UsedRange.Copy Wb.Sheets(2).Range("A1")
'and this works but doesn't copy formatting:
With ThisWorkbook.Sheets(1).UsedRange
Wb.Sheets(2).Range("A1").Resize(.Rows.Count, .Columns.Count) = .Value
End With
' later I perform some operations with cells
...
End Sub
As you can guess, I first tried to use Worksheet.Copy method then Range.Copy method and they both don't work. And the direct range assignment copies only values and I need also formatting to be copied.
So, apparently, solution which will copy formatting is appropriate, but I'd prefer direct copying if there is any way to do it. Also, please don't suggest to use clipboard, as it is always bad idea.
I suggest you SaveAs your workbook from Excel instance A to a temp file, and then open this temp file in Excel instance B in order to copy the sheet you need.
Related
Say I have two workbooks open with two tabs both named "Tab1". If in VBA, I want to refer to something on a tab, one would say "Tab1!" followed by what one is referring to, e.g. "Tab1!R1C1", etc.
However, how does the code know what Tab1 I am referring to in this case?
If it's worth mentioning, the code I am running is inside one of the workbooks. So will it always default back to that workbooks tab 1? If not, what does it do, and if yes, how would I make it refer to the tab 1 of the other workbook?
For context, I need to do this in the case of calling the method "ChangePivotCache" and give it a SourceData.
Without the code it's difficult to say definitively, but lets assume that you're using a range object. When you put "Tab1!R1C1" into that range object it uses the workbook of it's parent to make the determination. Most (if not all) methods that take a range argument like that will require that some parent of theirs is a workbook or worksheet.
In general if you're working with multiple workbooks best practice is to assign an object to them as you open them. That object can then be used to anchor any range calls you make. Either by calling directly from it or making children from it.
For example:
set workbook_code_is_in = Thisworkbook
set another_workbook = Application.Workbooks.Open(...)
set active_workbook = Activeworkbook
The first line will set workbook_code_is_in to the workbook that the code is located in.
The second line will set another_workbook to whatever you open.
The third line will set active_workbook to whatever workbook currently has focus. This is dangerous and prone to errors. It's very possible for a user to click a different workbook while a script is running and make the Activeworkbook an unintended one.
Referring to a Second Workbook
If you only have two workbooks open, then you refer to the workbook containing this code with ThisWorkbook and you can refer to the other workbook by using a For Each...Next loop to compare the workbooks' names.
Option Explicit
Sub ReferToSecondWorkbook()
Dim wb1 As Workbook: Set wb1 = ThisWorkbook ' workbook containing this code
If Workbooks.Count <> 2 Then
MsgBox "You need to have only two workbooks open.", vbCritical
Exit Sub
End If
Dim wb2 As Workbook
For Each wb2 In Workbooks
If StrComp(wb2.Name, wb1.Name, vbTextCompare) <> 0 Then ' different
Exit For
' Else ' it is 'wb1'
End If
Next wb2
Dim ws1 As Worksheet: Set ws1 = wb1.Worksheets("Tab1")
Dim ws2 As Worksheet: Set ws2 = wb2.Worksheets("Tab1")
Debug.Print "First: ", ws1.Name, wb1.Name
Debug.Print "Second: ", ws2.Name, wb2.Name
End Sub
So I am trying to write a Macro for Excel, that adds 2 worksheets from an excel file to a new one.
Therefore, I try this:
Sub addfile()
Dim sheet1 As Worksheet
Dim sheet2 As Worksheet
Set sheet1 = Sheets.Add(Type:="C:\Users\Helge\AppData\Roaming\Microsoft\Templates\page1.xltx")
Set sheet2 = Sheets.Add(Type:="C:\Users\Helge\AppData\Roaming\Microsoft\Templates\page2.xltx")
End Sub
When I test it, it imports the first page, but the 2nd page gives me a Runtime error 1004.
Why does this happen?
And is there another way to get 2 sheets from one excel file to another via vba?
Much to my surprise this version of your code actually worked for me.
Sub addfile()
Dim Sheet1 As Worksheet
Dim Sheet2 As Worksheet
Set Sheet1 = Sheets.Add(Type:=Environ("Userprofile") & "\OneDrive\Desktop\Template1.xltx")
Set Sheet2 = Sheets.Add(Type:=Environ("Userprofile") & "\OneDrive\Desktop\Book2.xlsx")
Debug.Print Sheet1.Name, Sheet2.Name
End Sub
The reason for my surprise is that Sheet1 and Sheet2 are the default CodeName for the first and second worksheets in any workbook. Therefore there is a conflict of naming between the Sheet1 in the workbook and the Sheet1 you declare which should come to the surface not later than Debug.Print Sheet1.Name. In fact, it may have. I didn't check which name was printed. But the code didn't crash. Since it crashes on your computer, perhaps you have an older version of Excel. Try to stay clear of variable names that Excel also uses. Or there is something wrong with the path & file name, which is hard to tell in that syntax and therefore kept me fooled for quite some time too.
In fact, I discovered the above only after finding out that my Desktop was on OneDrive and not before I had written the function below which is designed to avoid the use of Sheets.Add. It also has some extras such as being able to specify the sheet to take from the template (you could have one template with 2 or more sheets). You can specify an index number or a sheet name. And the function will give a name to the copy, too, if you specify one.
Private Function AddWorksheet(ByVal Template As String, _
TabId As Variant, _
Optional ByVal TabName As String) As Worksheet
Dim Wb As Workbook
Dim Path As String
Dim FileName As String
Set Wb = ThisWorkbook ' change to suit
' make sure the path ends on "\"
Path = "C:\Users\Helge\AppData\Roaming\Microsoft\Templates\"
With Workbooks.Open(Path & Template)
.Sheets(TabId).Copy After:=Wb.Sheets(Wb.Sheets.Count)
.Close
End With
Set AddWorksheet = ActiveSheet
If Len(TabName) Then ActiveSheet.Name = TabName
End Function
You can call the function from a sub routine like this:-
Sub AddWorksheets()
Dim Tab1 As Worksheet
Dim Tab2 As Worksheet
Application.ScreenUpdating = False
Set Tab1 = AddWorksheet("Page1.xltx", 1, "New Tab")
Set Tab2 = AddWorksheet("Page2.xltx", "Sheet1", "Another new Tab")
Application.ScreenUpdating = True
End Sub
Please observe the difference between the two function calls.
I'm trying to create a macro in Excel that allow me to copy and paste data from one workbook to another workbook. The workbook names are always going to be different, so I used GetOpenFilename() method and stored it in a variable, so I can specifically choose the workbook I want. However, I am getting a runtime 9 error.
Here's the code so far. (macro1 is just another macro I created that crunches numbers)
Sub Everything()
Dim f1 As String
Dim f2 As String
Dim wb1 As Workbook
Dim wb2 As Workbook
f1 = Application.GetOpenFilename(FileFilter:="Excel Files,*xl*;*xm*;")
Set wb1 = Workbooks.Open(f1)
f2 = Application.GetOpenFilename(FileFilter:="Excel Files,*xl*;*xm*;")
Set wb2 = Workbooks.Open(f2)
Call Macro1
Workbooks(wb2.Name).Worksheets("Sheet1").Range("D4:D25").Copy _
Workbooks(wb1.Name).Worksheets("Sheet1").Range("E11:E32")
End Sub
I get the run time error on the very last two lines of code.
Please help, thank you!
I can create a code add-in by setting a workbook's .IsAddin property to True, and saving it in *xlam format.
I can then add this as a reference to any other workbook as such:
Sub Test()
Dim wb As Workbook
Set wb = Workbooks.Add
wb.VBProject.References.AddFromFile "PathToAddIn.xlam"
End Sub
The add-in's code procedures will then be available to call from within the main wb workbook.
However, what if when running a procedure in the add-in, I want to get the workbook object of the workbook which references the add-in?
For example, something like this:
Sub ThisProcedureIsWithinTheAddIn()
Debug.Print ThisWorkbook.Path ' Returns the path of this add-in (xlam) file
Dim ReferringWorkbook As Workbook
Set ReferringWorkbook = ThisWorkbook... ' How does one reference the caller/referrer?
End Sub
For the purpose of this question, assume that the code execution is in a completely separate thread, and the wb object (as originally created) no longer exists as a variable. In fact, in my application the wb has been created in an entirely separate New Excel.Application.
I realise I could go through each workbook in the application with For Each wb In Application.Workbooks, and find some relevant matching criterion. But that's a messy solution. Is there something more elegant?
I'd like to create a macro that will:
Open a browser window to select a saved workbook (let's call it WB1)
In the same macro assign WB1 some form of reference that will allow it to be referenced by other macros
I can achieve step 1 via the following code:
Sub Add_New_Survey()
Dim pathString As String
Dim resultWorkbook As Workbook
Dim found As Boolean
pathString = Application.GetOpenFilename(fileFilter:="All Files (* . xl*) , *.xl* ")
' check if it's already opened
For Each wb In Workbooks
If InStr(pathString, wb.Name) > 0 Then
Set resultWorkbook = wb
found = True
Exit For
End If
Next wb
If Not found Then
Set resultWorkbook = Workbooks.Open(pathString)
End If
End Sub
This will open the workbook. I then need to perform a number of data preparation activities on WB1 which I would like to automate. Is there a way to reference WB1 as I open it from the browser so the following macros know to look on WB1 specifically?
Many thanks
Welcome to SO. Your object resultWorkbook is linked to Workbooks.Open(pathString), so as long as you dont unlink it with Set resultWorkbook = Nothing, you can reference that workbook always on any sub, (but declare the variable as Public first in the module, outside of all subs).
To declare a Variable as Public, please read:
How do I declare a global variable in VBA?