I have a Datepicker written in vb.net that looks like this.
I had to do it this way because of some Security Settings we have. Because of that, the Build in Datepicker from Excel generate sometimes an Error.
The Date Picker works fine if there Is Only One Excel Application Open. The Problem is when there are Multiple Excel Applications Open.
The code only takes the first Excel Application, but I want to get the Excel Workbook that's called "Test".
I Think the problem is this statement:
objExcel = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application")
For a better understanding the Code from Excel and the DatePicker Application is below:
That's how I open the vb.net Application in Excel:
Set wsh = VBA.CreateObject("WScript.Shell")
arg = ActiveWorkbook.Path & ";" & ActiveWorkbook.Name & ";" & ActiveSheet.Name & ";" & Target.Address
' Wait for the shelled application to finish:
errorCode = wsh.Run(strPathDatePicker & " " & arg, windowStyle, waitOnReturn)
The code for the DatePciker is:
Public Sub frmDatePicker_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim Par() As String
Dim strTemp As String
Try
Dim arg = Environment.GetCommandLineArgs
strTemp = arg(1)
Par = Split(strTemp, ";")
'Split arg, to get the Information from the Excel Workbook
strWbPath = Par(0)
strWbName = Par(1)
strWsName = Par(2)
strAdresse = Par(3)
Catch ex As Exception
Me.Close()
End Try
End Sub
If you now click on a Date the vb.net Application will run this code for inserting the selected date into Excel:
Private Sub MonthCalendar1_DateSelected(sender As Object, e As DateRangeEventArgs) Handles MonthCalendar1.DateSelected
Dim objExcel As Excel.Application
Dim wb As Excel.Workbook
Dim ws As Excel.Worksheet
Try
'Get the Excel Object
objExcel = System.Runtime.InteropServices.Marshal.GetActiveObject("Excel.Application")
For Each wb In objExcel.Workbooks
If wb.Name = strWbName Then
ws = wb.Sheets(strWsName)
ws.Range(strAdresse.ToString).Value = e.Start()
Me.Close()
End If
Next
Catch ex As Exception
MessageBox.Show(ex.Message)
Me.Close()
End Try
End Sub
The usual ways would probably be to pass the process Id, or Application.Hwnd, or even the risky ActiveWindow.Caption, but I think just the full external address is enough to find the Excel instance. For example in VB.Net (not tested):
Dim o As Object = GetObject("Book 1.xls")
Dim wb As Excel.Workbook = TryCast(o, Excel.Workbook)
Note that ; is valid character in file, workbook, worksheet, and named range names so I would recommend looking for a different separator. For example non-printable characters like Chr(0) might work, or just use the full external address Target.Address(,,,1).
Environment.GetCommandLineArgs(1) will cause problems if the argument contains spaces and is not surrounded by ": https://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs
Note: This is not the solution but I am posting it here to avoid long code in the comments (to improve readability). I will leave this answer here since the OP comment to this answer provides more information about his problem.
Maybe you should change:
arg = ActiveWorkbook.Path & ";" & ActiveWorkbook.Name & ";" & ActiveSheet.Name & ";" & Target.Address
to
arg = ThisWorkbook.Path & ";" & ThisWorkbook.Name & ";" & ActiveSheet.Name & ";" & Target.Address
At least you are referencing the workbook that's currently running the code and since you may have several workbooks open that could change the ActiveWorkbook as you are navigating between them.
HTH ;)
Related
I am trying to copy a module from one excel workbook to another using VBA.
My Code:
'Copy Macros
Dim comp As Object
Set comp = ThisWorkbook.VBProject.VBComponents("Module2")
Set Target = Workbooks("Food Specials Rolling Depot Memo 46 - 01.xlsm").VBProject.VBComponents.Add(1)
For some reason, this copies the module, but does not copy the VBA code inside, why?
Please can someone show me where i am going wrong?
Thanks
Sub CopyModule below, receives 3 parameters:
1.Source Workbook (as Workbook).
2.Module Name to Copy (as String).
3.Target Workbook (as Workbook).
CopyModule Code
Public Sub CopyModule(SourceWB As Workbook, strModuleName As String, TargetWB As Workbook)
' Description: copies a module from one workbook to another
' example: CopyModule Workbooks(ThisWorkbook), "Module2",
' Workbooks("Food Specials Rolling Depot Memo 46 - 01.xlsm")
' Notes: If Module to be copied already exists, it is removed first,
' and afterwards copied
Dim strFolder As String
Dim strTempFile As String
Dim FName As String
If Trim(strModuleName) = vbNullString Then
Exit Sub
End If
If TargetWB Is Nothing Then
MsgBox "Error: Target Workbook " & TargetWB.Name & " doesn't exist (or closed)", vbCritical
Exit Sub
End If
strFolder = SourceWB.Path
If Len(strFolder) = 0 Then strFolder = CurDir
' create temp file and copy "Module2" into it
strFolder = strFolder & "\"
strTempFile = strFolder & "~tmpexport.bas"
On Error Resume Next
FName = Environ("Temp") & "\" & strModuleName & ".bas"
If Dir(FName, vbNormal + vbHidden + vbSystem) <> vbNullString Then
Err.Clear
Kill FName
If Err.Number <> 0 Then
MsgBox "Error copying module " & strModuleName & " from Workbook " & SourceWB.Name & " to Workbook " & TargetWB.Name, vbInformation
Exit Sub
End If
End If
' remove "Module2" if already exits in destination workbook
With TargetWB.VBProject.VBComponents
.Remove .Item(strModuleName)
End With
' copy "Module2" from temp file to destination workbook
SourceWB.VBProject.VBComponents(strModuleName).Export strTempFile
TargetWB.VBProject.VBComponents.Import strTempFile
Kill strTempFile
On Error GoTo 0
End Sub
Main Sub Code (for running this code with the Post's data):
Option Explicit
Public Sub Main()
Dim WB1 As Workbook
Dim WB2 As Workbook
Set WB1 = ThisWorkbook
Set WB2 = Workbooks("Food Specials Rolling Depot Memo 46 - 01.xlsm")
Call CopyModule(WB1, "Module2", WB2)
End Sub
Actually, you don't need to save anything to a temporary file at all. You can use the .AddFromString method of the destination module to add the string value of the source. Try the following code:
Sub CopyModule()
Dim SourceVBProject As VBIDE.VBProject, DestinationVBProject As VBIDE.VBProject
Set SourceVBProject = ThisWorkbook.VBProject
Dim NewWb As Workbook
Set NewWb = Workbooks.Add ' Or whatever workbook object you have for the destination
Set DestinationVBProject = NewWb.VBProject
'
Dim SourceModule As VBIDE.CodeModule, DestinationModule As VBIDE.CodeModule
Set SourceModule = SourceVBProject.VBComponents("Module1").CodeModule ' Change "Module1" to the relevsant source module
' Add a new module to the destination project
Set DestinationModule = DestinationVBProject.VBComponents.Add(vbext_ct_StdModule).CodeModule
'
With SourceModule
DestinationModule.AddFromString .Lines(1, .CountOfLines)
End With
End Sub
Should be self-explanatory! The .AddFomString method simply takes a string variable. So in order to get that, we use the .Lines property of the source module. The first argument (1) is the start line, and the second argument is the end line number. In this case, we want all the lines, so we use the .CountOfLines property.
Fantastic Code by Chris Melville, Thanks a ton, just a few small addition which i did & added few comments.
Just make sure, following things are done before running this macro.
VB Editor > Tools > References > (Check) Microsoft Visual Basic for Applications Extensibility 5.3
File -> Options -> Trust Center -> Trust Center Setttings -> Macro Settings -> Trust Access to the VBA Project object model.
Once you do above thing, copy & paste below code in Source File
Sub CopyMacrosToExistingWorkbook()
'Copy this VBA Code in SourceMacroModule, & run this macro in Destination workbook by pressing Alt+F8, the whole module gets copied to destination File.
Dim SourceVBProject As VBIDE.VBProject, DestinationVBProject As VBIDE.VBProject
Set SourceVBProject = ThisWorkbook.VBProject
Dim NewWb As Workbook
Set NewWb = ActiveWorkbook ' Or whatever workbook object you have for the destination
Set DestinationVBProject = NewWb.VBProject
'
Dim SourceModule As VBIDE.CodeModule, DestinationModule As VBIDE.CodeModule
Set SourceModule = SourceVBProject.VBComponents("Module1").CodeModule ' Change "Module1" to the relevsant source module
' Add a new module to the destination project
Set DestinationModule = DestinationVBProject.VBComponents.Add(vbext_ct_StdModule).CodeModule
'
With SourceModule
DestinationModule.AddFromString .Lines(1, .CountOfLines)
End With
End Sub
Now run the "CopyMacrosToExistingWorkbook" macro in destination file, you will see the source file macro copied to destination file.
I had a lot of trouble getting the previous answers to work, so I thought I'd post my solution. This function is used to programmatically copy modules from a source workbook to a newly created workbook that was also created programmatically with a call to worksheet.copy. What doesn't happen when a worksheet is copied to a new workbook is the transfer of the macros that the worksheet depends upon. This procedure iterates through all modules in the source workbook and copies them into the new one. What's more is that it actually worked for me in Excel 2016.
Sub CopyModules(wbSource As Workbook, wbTarget As Workbook)
Dim vbcompSource As VBComponent, vbcompTarget As VBComponent
Dim sText As String, nType As Long
For Each vbcompSource In wbSource.VBProject.VBComponents
nType = vbcompSource.Type
If nType < 100 Then '100=vbext_ct_Document -- the only module type we would not want to copy
Set vbcompTarget = wbTarget.VBProject.VBComponents.Add(nType)
sText = vbcompSource.CodeModule.Lines(1, vbcompSource.CodeModule.CountOfLines)
vbcompTarget.CodeModule.AddFromString (sText)
vbcompTarget.Name = vbcompSource.Name
End If
Next vbcompSource
End Sub
The function should hopefully be as simple as possible and fairly self-explanatory.
You can try
Open both workbooks
Open VBA Dev window
Dropdown Modules and drag and drop a copy from one module are to another
This is to make sure Module Names are not duplicated.
Also if you have modules that contain same named function / Subs then there will be a clash.
I do this and then run debug. Seems to work fine.
P.S. I copy many in to my PERSONAL.xlsb
I'm starting a new project and having trouble right at the start =[. So often I need to pull out specific data from a very large excel sheet and create a new excel sheet for just that data. At the moment I am currently trying to create a new workbook and save it to a file path. I am getting the error on the SaveAs execution line. Any idea why this might be happening? The error is:
"Method 'Save As' of object' _Workbook' failed.
Dim Path As String
Dim dat As String
Dim Client As String
Path = "C:\Back\Test\"
ThisWorkbook.Sheets("Control Panel").Activate
dat = Range("F42")
Client = Range("F43")
Workbooks.Add
ActiveWorkbook.SaveAs Filename:=Path & Date & "-" & Client & ".xls", FileFormat:=xlNormal
newWBName = ActiveWorkbook.Name
I will propose my access to your need.
Here is sub which should do what you need. So first i recomend to Dim all of your variables and do not use activate. Instead use sheet variable and also acces single values via cells not via range.
But your main issue maybe is that you try to use reserved word Date. Let me know if something isnt clear to you.
Sub save()
Dim filePath As String
Dim dateFromSheet As String
Dim clientName As String
Dim controlPanelSheet As Worksheet
Dim newWorkbookName As String
Set controlPanelSheet = Sheets("Control Panel")
filePath = "c:\Users\sukl\Documents\"
With controlPanelSheet
dateFromSheet = .Cells(42, "F").Value
clientName = .Cells(43, "F").Value
End With
ThisWorkbook.SaveAs Filename:=filePath & dateFromSheet & "-" & clientName & ".xls", FileFormat:=xlNormal
newWorkbookName = ThisWorkbook.Name
End Sub
I'm having an issue where, when looking at my VBA code, I'll press F8 to "Step Into" the next line. After about 8 steps, when I "Step Into" the line: Set wb = Workbooks.Open(fileopen), it takes off and runs the entire program. What am I doing wrong?
Sub NSC_test()
'
' NSC_test Macro
'
' Keyboard Shortcut: Ctrl+u
'
Const filepath As String = "H:\DEPT\Supply Management\Shared\No Standard Cost Reports\No Standard Cost Reports FYE 15\"
Dim filename As String
Dim fileopen As String
Dim lastrow As Integer
Dim tempcell As Range
Dim ws As Worksheet
If ActiveWorkbook.Name = "NSC Template.xlsm" Then
'DATE OF PREVIOUS WEEK'S REPORT
filename = Format(DateAdd("d", -1, Now()), "mm-dd-yy") 'SET THIS BACK TO -7
filename = "NSC " & filename & ".xlsm"
fileopen = filepath & filename
'MsgBox fileopen 'TO CHECK IF THE FILEPATH IS CORRECT
Dim wb As Workbook
Set wb = Workbooks.Open(fileopen)
If wb Is Nothing Then MsgBox "File does not exist": Exit Sub
blah
blah
Thanks very much.
The code line,
filename = "NSC " & filename & ".xlsm"
... indicates to me that you are opening an macro-enabled workbook. This workbook will have event macros and/or sub procedures that are initiated on opening. This releases the debug session and the remaining code runs its course.
I am looking for a way to return the value from an arbitrary workbook (the workbook will also not be open at the time of running the UDF), defined based on calculations in the UDF.
Pseudo code:
Start by calling =someFunc(currentCell) in any cell
Function someFunc(adr As Range)
region_eval = "C" & Range(adr).Row ' where column C contains string entries, all of which have a corresponding sub-dir (see fileReference).
networkLocation = ActiveWorkbook.Path
networkPath = networkLocation & "\Locations\"
fileReference = networkPath & region_eval & "\ProductList.xlsx"
Workbook.Open fileReference readonly
Perform index/match call against some sheet in this workbook
someFunc = returned value
Close workbook and end function
This is the desired behavior.
The logic to return the desired values is OK, I have tried it in a simpler formula, and in a UDF that relies on the file being opened manually:
INDEX(locationlist_$A$5000, MATCH(masterlist_A1, locationlist_$B$5000))
I have, after hours of hair-pulling, discovered that this functionality is not directly available in a UDF designed to work on workbooks that aren't opened manually, and that this is intended from Microsoft's side. But I have also discovered that there is a possible workaround!
Ref:
1. https://stackoverflow.com/a/27844592/4604845
2. http://numbermonger.com/2012/02/11/excel-pull-function-creating-dynamic-links-to-closed-workbooks/
These solutions require hardcoded file paths, which defeats the purpose for my intended usage.
Is there anyone who has insight about how to achieve what is achieved in any of the two above links, but with an arbitrary filepath (as in, contained in a cell neighbouring the cell where the UDF is being called from)?
Note: I tried doing the heavy lifting in a sub, and just call the sub as the first line in the UDF, set the result as a global var, and set the UDF return value to the same var after the sub finished, but either I crashed and burned pretty heavily or Excel saw through my trick and denied it.
EDIT:
Here's the sub/func combo.
Option Explicit
Public networkLocation As String, networkPath As String, fileReference As String, c_formula As String
Public sheet_src As Worksheet, sheet As Worksheet, wb_src As Workbook, wb As Workbook
Public region_eval As String, sheetName_src As String, sheetName As String, regionPath As String, fileName As String
Sub findProductStatus(adr As Range)
networkLocation = ActiveWorkbook.Path
networkPath = networkLocation & "\Locations\"
sheetName_src = "Sheet1"
sheetName = "Sheet1"
Set wb_src = ThisWorkbook
Set sheet_src = wb_src.Sheets(sheetName_src)
region_eval = Range("I" & adr.Row)
regionPath = networkPath & region_eval
'fileReference = regionPath & "\ProductList.xlsx"
fileName = "ProductList.xlsx"
ChDir regionPath
Workbooks.Open fileName:=fileName, ReadOnly:=True
'Set wb = Workbooks.Open(fileName:=ThisWorkbook.Path & "\Locations\Test\ProductList.xlsx", ReadOnly:=True)
Set wb = Workbooks("ProductList.xlsx")
Set sheet = wb.Sheets(sheetName)
c_formula = Application.WorksheetFunction.Index(sheet.Range("$K$2:$K$5000"), Application.WorksheetFunction.Match(sheet_src.Range("A" & adr.Row), sheet.Range("$A$2:$A$5000"), 0))
End Sub
Function getProductStatus(adr As Range) As String
Call findCourseStatus(adr)
getCourseStatus = c_formula
wb.Close
End Function
I haven't tested the sub/func combo against an open file, but when all of the code was inside the Function and the file in question was opened manually, it worked flawlessly. Stepping through the code and using Debug.Print, I see that even though "Workbooks.Open ..." goes through without any discernible error, the workbook doesn't actually get opened, and thus, when we try to use the workbook object to set the sheet, the function/sub terminates.
This can be achieved with a combination of a UDF() and an Event macro.
To retrieve data from a closed workbook, we need four things:
the path
the filename
the sheetname
the cell address
The only thing the UDF will do is to display these items in a very specific format:
Public Function someFunc() As String
Dim wbPath As String, wbName As String
Dim wsName As String, CellRef As String
Dim Ret As String
wbPath = "C:\TestFolder\"
wbName = "ABC.xls"
wsName = "xxx"
CellRef = "B9"
someFunc = "'" & wbPath & "[" & wbName & "]" & _
wsName & "'!" & Range(CellRef).Address(True, True, -4150)
End Function
Take note of the position of the single quotes.
We then use a Calculate event macro to detect the UDF's execution and retrieve the data:
Private Sub Worksheet_Calculate()
Dim r1 As Range, r2 As Range
Set r1 = Range("C3")
Set r2 = Range("C4")
r2.Value = ExecuteExcel4Macro(r1.Value)
End Sub
The Calculate macro needs to know where the UDF is returning the string (C3) and it also needs to know where to put the retrieved data (C4).
I want to collect data from different files and insert it into a workbook doing something like this.
Do While THAT_DIFFERENT_FILE_SOMEWHERE_ON_MY_HDD.Cells(Rand, 1).Value <> "" And Rand < 65536
then 'I will search if the last row in my main worksheet is in this file...
End Loop
If the last row from my main worksheet is in the file, I'll quit the While Loop. If not, I'll copy everything. I'm having trouble finding the right algorithm for this.
My problem is that I don't know how to access different workbooks.
The best (and easiest) way to copy data from a workbook to another is to use the object model of Excel.
Option Explicit
Sub test()
Dim wb As Workbook, wb2 As Workbook
Dim ws As Worksheet
Dim vFile As Variant
'Set source workbook
Set wb = ActiveWorkbook
'Open the target workbook
vFile = Application.GetOpenFilename("Excel-files,*.xls", _
1, "Select One File To Open", , False)
'if the user didn't select a file, exit sub
If TypeName(vFile) = "Boolean" Then Exit Sub
Workbooks.Open vFile
'Set targetworkbook
Set wb2 = ActiveWorkbook
'For instance, copy data from a range in the first workbook to another range in the other workbook
wb2.Worksheets("Sheet2").Range("C3:D4").Value = wb.Worksheets("Sheet1").Range("A1:B2").Value
End Sub
You might like the function GetInfoFromClosedFile()
Edit: Since the above link does not seem to work anymore, I am adding alternate link 1 and alternate link 2 + code:
Private Function GetInfoFromClosedFile(ByVal wbPath As String, _
wbName As String, wsName As String, cellRef As String) As Variant
Dim arg As String
GetInfoFromClosedFile = ""
If Right(wbPath, 1) <> "" Then wbPath = wbPath & ""
If Dir(wbPath & "" & wbName) = "" Then Exit Function
arg = "'" & wbPath & "[" & wbName & "]" & _
wsName & "'!" & Range(cellRef).Address(True, True, xlR1C1)
On Error Resume Next
GetInfoFromClosedFile = ExecuteExcel4Macro(arg)
End Function
Are you looking for the syntax to open them:
Dim wkbk As Workbook
Set wkbk = Workbooks.Open("C:\MyDirectory\mysheet.xlsx")
Then, you can use wkbk.Sheets(1).Range("3:3") (or whatever you need)
There's very little reason not to open multiple workbooks in Excel. Key lines of code are:
Application.EnableEvents = False
Application.ScreenUpdating = False
...then you won't see anything whilst the code runs, and no code will run that is associated with the opening of the second workbook. Then there are...
Application.DisplayAlerts = False
Application.Calculation = xlManual
...so as to stop you getting pop-up messages associated with the content of the second file, and to avoid any slow re-calculations. Ensure you set back to True/xlAutomatic at end of your programming
If opening the second workbook is not going to cause performance issues, you may as well do it. In fact, having the second workbook open will make it very beneficial when attempting to debug your code if some of the secondary files do not conform to the expected format
Here is some expert guidance on using multiple Excel files that gives an overview of the different methods available for referencing data
An extension question would be how to cycle through multiple files contained in the same folder. You can use the Windows folder picker using:
With Application.FileDialog(msoFileDialogFolderPicker)
.Show
If .Selected.Items.Count = 1 the InputFolder = .SelectedItems(1)
End With
FName = VBA.Dir(InputFolder)
Do While FName <> ""
'''Do function here
FName = VBA.Dir()
Loop
Hopefully some of the above will be of use
I had the same question but applying the provided solutions changed the file to write in. Once I selected the new excel file, I was also writing in that file and not in my original file. My solution for this issue is below:
Sub GetData()
Dim excelapp As Application
Dim source As Workbook
Dim srcSH1 As Worksheet
Dim sh As Worksheet
Dim path As String
Dim nmr As Long
Dim i As Long
nmr = 20
Set excelapp = New Application
With Application.FileDialog(msoFileDialogOpen)
.AllowMultiSelect = False
.Filters.Add "Excel Files", "*.xlsx; *.xlsm; *.xls; *.xlsb", 1
.Show
path = .SelectedItems.Item(1)
End With
Set source = excelapp.Workbooks.Open(path)
Set srcSH1 = source.Worksheets("Sheet1")
Set sh = Sheets("Sheet1")
For i = 1 To nmr
sh.Cells(i, "A").Value = srcSH1.Cells(i, "A").Value
Next i
End Sub
With excelapp a new application will be called. The with block sets the path for the external file. Finally, I set the external Workbook with source and srcSH1 as a Worksheet within the external sheet.