Uipath error: "macro may not be available in this workbook"? - excel

I am trying to retrieve cell addresses from an Excel sheet with UIPath invoke VBA activity.
I am new to VBA coding. I have written a function to retrieve cell address array and I have written a sub procedure to call that function.
I am calling the sub in UIPath activity named 'invoke VBA'. When I run this it says
Invoke VBA : Cannot run the macro 'Main'. The macro may not be
available in this workbook or all macros may be disabled.
This is the screen shot where I have enabled macros in Excel:
Sub Main(Amount As Integer)
Call findcellFunction(Amount)
End Sub
Function findcellFunction(Amount As Integer)As Collection
On Error Resume Next
Dim rngX As Range
Dim WS As Worksheet
Dim datax As Range
Dim cellAddress As Variant
Dim index As Integer
Dim iTotal As Integer
Dim CellArray
iTotal = 0
Set CellArray = New Collection
'Iterate until all cell values are found
For index=1 To Amount
Set rngX = Worksheets("rptBOMColorPrint").Range("A1:EZ50").Find("Colour Name", lookat:=xlPart)
If Not rngX Is Nothing Then
MsgBox "Found at " & rngX.Address
CellArray.Add rngX.Address
End If
Cells(rngX.Row,rngX.Column).Delete
iTotel =iTotal + index
Next index
'shows list that has been populated with cell addresses
For Each cellAddress In CellArray
MsgBox "list populated " & cellAddress
Range(cellAddress).Value = "Colour Name"
Next
CellArray = findcellFunction(Amount)
End Function
This is how I call the VBA from the UIPath activity.

When you include a .xlsx excel file instead of .xls file it works. It is because Uipath invoke VBA activity does not support .xls files as it is older version.

Related

VBA Code runs properly but Run-time error '1004' still pops up

I'm learning VBA and I'm trying to create a workbook wherein in one sheet (sheet2) it would do the calculation then once the calculation is finished the items in sheet2, I would be able to press a commandbutton with the macro of copying the cells in the other sheet (sheet1). I am successful so far in copying over the data however every time the commandbutton is pressed, the error message
'Run-time error'1004': Application-defined or object-defined error'
would pop up. When the debug option is selected it points to line 4 & 5. I searched all over the internet regarding this issue and I haven't stumbled upon any situation like this. I've followed this https://www.youtube.com/watch?v=Z62yORhPr3Q and it's 5th method I'm running with. The code that I have is:
Private Sub CommandButton1_Click()
Dim Part As Range
For Each Part In Range(Range("Q4"), Range("Q4").End(xlDown))
Sheets("VStock").Range(Part.Value).Value = _
Sheets("Calc").Range(Part.Offset(0, 1).Value).Value
Next Part
End Sub
Any help would be appreciated
Thanks!
Suggestion not to loop the entire column and set the Ranges before the main task of "copy/paste".
In your case the ranges are set up incorrectly
Sheets("VStock").Range(Part.Value).Value = _
Sheets("Calc").Range(Part.Offset(0, 1).Value).Value
.Range should Look into the location of cells, example: .Range("Q4:Q144").Value
in your case, Range ends up with .Range(*theValue*).Value
Correct code Example:
Private Sub CommandButton1_Click()
Dim ws1 As Worksheet: Set ws1 = Sheets("Calc")
Dim ws2 As Worksheet: Set ws2 = Sheets("VStock")
Dim SourceRange, dbRange As Range
Dim lRow as Long
lRow = ws1.Range("Q" & ws1.Rows.Count).End(xlUp).Row
If lRow <= 4 Then
MsgBox ("No data to copy")
Exit Sub
End If
Set SourceRange = ws1.Range("Q4:Q" & lRow) ' Calc
Set dbRange = ws2.Range("Q4:Q" & lRow) ' VStock
dbRange.Value = SourceRange.Offset(0, 1).Value
End Sub

Get worksheet as collection excel vba

I'm building a basic Form Macro in excel. The user is passing the workbook object name i.e. the full file path and the worksheet name via my form. The macro needs to then open the activate the workbook, locate and activate the worksheet and then copy all the data on this sheet into a collection. The collection will be passed via ByRef or ByVal to another sub. I'm not efficient with Microsoft Excel VBA re its libraries, I work in a vb.net environment, the below code is what I would run in vb.net to get my worksheets data as a collection. You'll note in the code below there's three functions being used, GetWorksheet(), GetClipboardText() & ParseDelimSeparatedVariables(). Hoping someone may have a suggestion on how to recreate this is Excel VBA.
Assembly References: System.Data.dll, System.Xml.dll, System.Drawing.dll, System.Windows.Forms.dll
Namespace Imports: System, System.Drawing, System.Collections.Generic, System.IO, Microsoft.VisualBasic, System.Windows.Forms, System.Data, System.Diagnostics, System.Text, System.Threading, System.Runtime.InteropServices
Dim ws as Object = GetWorksheet(handle, workbookname, worksheetname, False)
' Do we have a sheet?
sheetexists = ws IsNot Nothing
' No sheet? No entry.
If Not sheetexists Then Return
ws.Activate()
ws.UsedRange.Select()
ws.UsedRange.Copy()
Dim data As String = GetClipboardText()
worksheetCollection = ParseDelimSeparatedVariables(data, vbTab, Nothing, True)
Below is the VBA code I've now got in place, currently getting a runtime error 9 subscript out of range error.
Sub getWSCol()
Dim wb As String
Dim wsName As String
Dim Arr() As Variant
Dim v As Variant
Dim colMailMerge As New Collection
wb = txtbxworkbook
wsName = txtbxworksheet
Workbooks(wb).Worksheets(wsName).Activate
Arr = Worksheets(wsName).UsedRange.Value
For Each v In Arr
col.Add v
Next v
End Sub
Image below of stepping through code
VBA lacks a lot of functions that make your life easy, so need to roll them all yourself.
Wouldn't a collection just result in a single dimension list of all the cell text? An array is probably better use case.
Dim Arr() as Variant
Arr = ws.UsedRange.value
I think if want a collection you'd have to loop through an array, can do it easily by then just
Dim Col as new Collection
Dim v as Variant
for each v in Arr
col.add v
next v
Getting worksheet, need to just reference by name in the Application.Workbooks(name).Worksheets(name) objects. It'll error if not there so need to what it on a function and error resume next past the errors, then can test if nothing outside of function.
For the sheet...
Dim Wk as workbook, Ws as worksheet
On error resume next
set wk = application.worksbooks(Wkbkname)
set ws = wk.worksheets(wsname)
on error goto 0
if ws is nothing then msgbox "Doesn't exist"
Arr = ws.UsedRange.Value

Why do I get object required error for the below VBA code?

I am new to VBA, I need help applying the below VBA code to two specific work sheets by Region and by Model. The code simply finds the last column which has the name total for the year and copies the previous months values into a new column. But I get an error object required at ws.
Sub Insert_New_Col()
Dim Found As Range, BeforeR As Long
Dim xSheets As Variant
Dim ws As Worksheet
Dim i As Long
xSheets = Array("By Region", "By Model") '
For i = LBound(xSheets) To UBound(xSheets)
Set ws = xSheets(i)
Set Found = ws.Rows(3).Find(What:="Total for the Year", Lookat:=xlWhole)
BeforeR = R.Column - 1
If Found Is Nothing Then
MsgBox ("The word 'Totals' was not found in Row 5 on Sheet: " & ws.Name)
Else
Columns(BeforeR).Copy
ws.Columns(R.Column).Insert Shift:=xlRight
End If
Next i
End Sub
You want an actual member of the Worksheets collection:
Set ws = ThisWorkbook.Worksheets(xSheets(i))
BeforeR = R.Column - 1
What is R? Add Option Explicit to the top of the module and declare all variables.

Save an Excel cell to a file

I need to save the contents of a cell to an xml file named by another cell.
I need to perform this check for every row in the sheet.
Sub FormatRange()
Dim rng As Range
Dim row As Range
Dim cell As Range
Set rng = Range("A1:AG686")
For Each row In rng.Rows
Next row
End Sub
For each row I need to save cell AG to xml file named after cell C in the same row.
I am guessing I can use StreamWriter to write the file. I think the real problem is referencing the cells I need.
Sub FormatRange()
Dim rng As Range
Dim row As Range
Dim cell As Range
Set rng = Range("A2:AG686")
' Declare a FileSystemObject.
Dim fso As FileSystemObject
' Create a FileSystemObject.
Set fso = New FileSystemObject
' Declare a TextStream.
Dim stream As TextStream
' Create a TextStream.
For Each row In rng.Rows
Dim path As String
path = "C:\vba\" & row.Cells(1, 4) & ".xml"
' MsgBox (path + row.Cells(1, 33))
Set stream = fso.CreateTextFile(path, True)
stream.Write row.Cells(1, 33)
Next row
stream.Close
End Sub
Did it! Clustered my way through. Thanks for the suggestions.
Do you have to use VBA? I'm guessing if you know what a StreamWriter is you use C# or another .NET language. If so have a look at EPPlus, you can easily loop through your Excel sheet and use the .NET Framework.
http://epplus.codeplex.com/
Here is my flexible solution with right-click action and file choosing dialog:
Public Sub CellSaver()
Dim cell
For Each cell In Application.Selection.Cells
With Application.FileDialog(msoFileDialogSaveAs)
.Title = "Please select the file to save cell contents"
If .Show Then
With CreateObject("Scripting.FileSystemObject").CreateTextFile(.SelectedItems(1), True)
.Write cell
.Close
End With
End If
End With
Next cell
End Sub
Private Sub Workbook_Open()
With Application.CommandBars("Cell").Controls.Add(Temporary:=True)
.Caption = "Save to file"
.Style = msoButtonCaption
.OnAction = "'CellSaver'"
End With
End Sub
Credits go to:
#JMG's answer here
https://www.extendoffice.com/documents/excel/4375-excel-add-button-macro-to-right-click-menu.html
Can I simultaneously declare and assign a variable in VBA?
Excel VBA Cannot call function with onaction

How do I return the location of the marching ants in Excel? [duplicate]

This question already has answers here:
Can I Get the Source Range Of Excel Clipboard Data?
(3 answers)
Closed 2 years ago.
I know about Application.CutCopyMode, but that only returns the state of the CutCopyMode (False, xlCopy, or xlCut).
How do I return the address of the currently copied range in Excel using VBA? I don't need the currently selected range (which is Application.Selection.Address). I need the address of the range of cells with the moving border (marching ants) around it.
In other words, if you select a range of cells, hit CTRL+C, and then move the selection to another cell, I need the address of the cells that were selected when the user hit CTRL+C.
Thanks!
As far as I know you can't do that with vba. You can however code your own copy sub and store the source in a global variable.
Something like this:
Option Explicit
Dim myClipboard As Range
Public Sub toClipboard(Optional source As Range = Nothing)
If source Is Nothing Then Set source = Selection
source.Copy
Set myClipboard = source
End Sub
10 years later you still can't refer directly to a copied Range
(shown by the "marching ants border" aka "dancing border", "moving border").
But you can get its address by copying the cells as link to a temporary worksheet. There you can collect the desired range's address.
Private Sub ThereAreTheMarchingAnts()
Dim rngCopied As Range ' the copied range with the marching ants border
Dim rngSelected As Range ' the selected range
Dim tmpWorksheet As Worksheet ' a temporary worksheet
Dim c As Range ' a cell for looping
' Exit, if nothing was copied (no marching ants border):
If Not (Application.CutCopyMode = xlCopy Or Application.CutCopyMode = xlCut) Then Exit Sub
' Exit, if no range is selected (just for demonstration)
If Not TypeName(Selection) = "Range" Then Exit Sub
' remember selected Range:
Set rngSelected = Selection
' add a temporary sheet and paste copied cells as link:
Set tmpWorksheet = ActiveWorkbook.Sheets.Add
tmpWorksheet.Paste link:=True
' go through all pasted cells and get the linked range from their formula:
For Each c In tmpWorksheet.UsedRange
If rngCopied Is Nothing Then
Set rngCopied = Range(Mid(c.Formula, 2))
Else
Set rngCopied = Union(rngCopied, Range(Mid(c.Formula, 2)))
End If
Next c
' delete the temporary worksheet without asking:
Application.DisplayAlerts = False
tmpWorksheet.Delete
Application.DisplayAlerts = True
' show the addresses:
MsgBox "Copied Range: " & rngCopied.Address(0, 0, xlA1, True) & vbLf & _
"Selected Range: " & rngSelected.Address(0, 0, xlA1, True)
End Sub
The code also works with multiranges and also if the copied range and the selected range are on different sheets.
When you copy a Range, the address is copied to the Clipboard along with other formats. You can check that with Clipboard Viewer application.
So if you need the copied Range, get it from Clipboard. It will be something like> $A2:$B5 or similar
The only way i can think of doing this is tracking the last range selected with a global variable and then waiting until you think a copy action is done. Unfortunately neither is easy.
The following is a quick attempt that has two problems;
If you copy the same data twice it
isn't updated
If a copy or paste is
fired from another app, the results
may vary.
This is one of those last hope tricks when tracking events that don't really exist. Hope this helps.
''# Add a reference to : FM20.dll or Microsoft Forms 2.0
''# Some more details at http://www.cpearson.com/excel/Clipboard.aspx
Option Explicit
Dim pSelSheet As String
Dim pSelRange As String
Dim gCopySheet As String
Dim gCopyRange As String
Dim gCount As Long
Dim prevCBText As String
Dim DataObj As New MSForms.DataObject
Private Sub Workbook_SheetSelectionChange(ByVal Sh As Object, _
ByVal Target As Excel.Range)
CopyTest
pSelSheet = Sh.Name
pSelRange = Target.Address
''# This is only so you can see it working
gCount = gCount + 1
application.StatusBar = gCopySheet & ":" & gCopyRange & ", Count: " & gCount
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Source As Range)
CopyTest ''# You may need to call CopyTest from other events as well.
''# This is only so you can see it working
gCount = gCount + 1
application.StatusBar = gCopySheet & ":" & gCopyRange & ", Count: " & gCount
End Sub
Sub CopyTest()
Dim curCBText As String
Dim r As Range
DataObj.GetFromClipboard
On Error GoTo NoCBData
curCBText = DataObj.GetText
On Error Resume Next
''# Really need to test the current cells values
''# and compare as well. If identical may have to
''# update the gCopyRange etc.
If curCBText <> prevCBText Then
gCopySheet = pSelSheet
gCopyRange = pSelRange
prevCBText = curCBText
End If
Exit Sub
NoCBData:
gCopySheet = ""
gCopyRange = ""
prevCBText = ""
End Sub
Oh and excuse the wierd comments ''# they're just there to help the syntax highlighter of SO.
I think you can use this method
https://learn.microsoft.com/en-us/office/vba/api/Excel.Application.OnKey
This method assigns a function to the hot key Ctrl+C, every time this combination is used, the function will be triggered and you can get the address of the range.

Resources