run a macro on particular cell selection using vbs - macros

I need to write a vbs that selects a cell and runs a macro on that cell
here's the code I found online(that doesnt work)
ActiveSheet.Cells(5, 3).Select
xl.Run "'Book1.xls'!Calc"
I am creating this vbs in Eclips(Running this VBS through
Runtime.getRuntime().exec("cmd /c RUN_MACRO.vbs "+xlsFile+" "+ macroName);
Still after running I get no error and no change in the excel as well.
Please help
Full Code for VBS for RUN_MACRO.vbs
Here Calc is the Macro name saved in some Module(as I created this macro(to multiply A2*B2) in Open Office and when I select a cell and run a macro,I the cell value gets changed)
Set xl = CreateObject(WScript.Arguments(0))
Set wb = xl.Workbooks.Open(WScript.Arguments(1))
ActiveSheet.Cells(5, 3).Select
Range("C5").Select
xl.Run "'Book1.xls'!Calc"
ActiveWorkbook.Save
wb.Close
xl.Quit
Method calling this vbs in my java file
public static void executeMacros(String xlsFile, String macroName){
try{
Runtime.getRuntime().exec("cmd /c RUN_MACRO.vbs "+xlsFile+" "+ macroName);
}catch(Exception e)
{ System.out.println(e);}
}

There are a few oddities in your VBS file. The first is that you are trying to create an object from an Excel file instead of from a Class. So the first thing that you need to do is Set xl = CreateObject("Excel.Application").
Because you are doing Set wb = xl.Workbooks.Open(WScript.Arguments(1)) and your second argument is the macro name the script is trying to open a workbook named "macroname".
You are also trying to select two different cells before actually executing the macro. So ActiveSheet.Cells(5, 3).Select doesn't seem necessary and wouldn't really work anyways.
The xl.Run "'Book1.xls'!Calc" actually needs to be in the format of xl.Run "ModuleName.MacroName"
You may need to use this code to update other cells. Therefore you may be interested in adding a third argument to the cell that actually needs to be updated.
Thus try this as your vbs:
WorkBookToOpen = WScript.Arguments(0)
MacroToRun = WScript.Arguments(1)
CellToUpdate = WScript.Arguments(2)
Set xl = CreateObject("Excel.Application")
Set wb = xl.Workbooks.Open(WorkBookToOpen)
xl.Range( CellToUpdate ).Select
xl.Run "Module1." & MacroToRun
xl.ActiveWorkbook.Save
wb.Close
xl.Quit
And then change to:
Runtime.getRuntime().exec("cmd /c RUN_MACRO.vbs "+xlsFile+" "+ macroName + " " + cellToUpdate);

Related

XLSX file via OpenXml SDK Both Valid and Invalid

I have a program which exports a System.Data.DataTable to an XLSX / OpenXml Spreadsheet. Finally have it mostly working. However when opening the Spreadsheet in Excel, Excel complains about the file being invalid, and needing repair, giving this message...
We found a problem with some content in . Do you want us to
try to recover as much as we can? If you trust the source of the
workbook, clik Yes.
If I click Yes, it comes back with this message...
Clicking the log file and opening that, just shows this...
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recoveryLog xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
<logFileName>error268360_01.xml</logFileName>
<summary>Errors were detected in file 'C:\Users\aabdi\AppData\Local\Temp\data.20190814.152538.xlsx'</summary>
<repairedRecords>
<repairedRecord>Repaired Records: Cell information from /xl/worksheets/sheet1.xml part</repairedRecord>
</repairedRecords>
</recoveryLog>
Obviously, we don't want to deploy this into a production environment like this. So I've been trying to figure out how to fix this. I threw together a quick little sample to validate the XML and show the errors, based on this link from MSDN. But when I run the program and load the exact same XLSX document that Excel complains about, the Validator comes back saying that the file is perfectly Valid. So I'm not sure where else to go from there.
Any better tools for trying to validate my XLSX XML? Following is the complete code I'm using to generate the XLSX file. (Yes, it's in VB.NET, it's a legacy app.)
If I comment out the line in the For Each dr As DataRow loop, then the XLSX file opens fine in Excel, (just without any data). So it's something with the individual cells, but I'm not really DOING much with them. Setting a value and data type, and that's it.
I also tried replacing the For Each loop in ConstructDataRow with the following, but it still outputs the same "bad" XML...
rv.Append(
(From dc In dr.Table.Columns
Select ConstructCell(
NVL(dr(dc.Ordinal), String.Empty),
MapSystemTypeToCellType(dc.DataType)
)
).ToArray()
)
Also tried replacing the call to Append with AppendChild for each cell too, but that didn't help either.
The zipped up XLSX file (erroring, with dummy data) is available here:
https://drive.google.com/open?id=1KVVWEqH7VHMxwbRA-Pn807SXHZ32oJWR
Full DataTable to Excel XLSX Code
#Region " ToExcel "
<Extension>
Public Function ToExcel(ByVal target As DataTable) As Attachment
Dim filename = Path.GetTempFileName()
Using doc As SpreadsheetDocument = SpreadsheetDocument.Create(filename, DocumentFormat.OpenXml.SpreadsheetDocumentType.Workbook)
Dim data = New SheetData()
Dim wbp = doc.AddWorkbookPart()
wbp.Workbook = New Workbook()
Dim wsp = wbp.AddNewPart(Of WorksheetPart)()
wsp.Worksheet = New Worksheet(data)
Dim sheets = wbp.Workbook.AppendChild(New Sheets())
Dim sheet = New Sheet() With {.Id = wbp.GetIdOfPart(wsp), .SheetId = 1, .Name = "Data"}
sheets.Append(sheet)
data.AppendChild(ConstructHeaderRow(target))
For Each dr As DataRow In target.Rows
data.AppendChild(ConstructDataRow(dr)) '// THIS LINE YIELDS THE BAD PARTS
Next
wbp.Workbook.Save()
End Using
Dim attachmentname As String = Path.Combine(Path.GetDirectoryName(filename), $"data.{Now.ToString("yyyyMMdd.HHmmss")}.xlsx")
File.Move(filename, attachmentname)
Return New Attachment(attachmentname, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
End Function
Private Function ConstructHeaderRow(dt As DataTable) As Row
Dim rv = New Row()
For Each dc As DataColumn In dt.Columns
rv.Append(ConstructCell(dc.ColumnName, CellValues.String))
Next
Return rv
End Function
Private Function ConstructDataRow(dr As DataRow) As Row
Dim rv = New Row()
For Each dc As DataColumn In dr.Table.Columns
rv.Append(ConstructCell(NVL(dr(dc.Ordinal), String.Empty), MapSystemTypeToCellType(dc.DataType)))
Next
Return rv
End Function
Private Function ConstructCell(value As String, datatype As CellValues) As Cell
Return New Cell() With {
.CellValue = New CellValue(value),
.DataType = datatype
}
End Function
Private Function MapSystemTypeToCellType(t As System.Type) As CellValues
Dim rv As CellValues
Select Case True
Case t Is GetType(String)
rv = CellValues.String
Case t Is GetType(Date)
rv = CellValues.Date
Case t Is GetType(Boolean)
rv = CellValues.Boolean
Case IsNumericType(t)
rv = CellValues.Number
Case Else
rv = CellValues.String
End Select
Return rv
End Function
#End Region
For anyone else coming in and finding this, I finally tracked this down to the Cell.DataType
Setting a value of CellValues.Date will cause Excel to want to "fix" the document.
(apparently for dates, the DataType should be NULL, and Date was only used in Office 2010).
Also, if you specify a DataType of CellValues.Boolean, then the CellValue needs to be either 0 or 1. "true" / "false" will also cause Excel to want to "fix" your spreadsheet.
Also, Microsoft has a better validator tool already built for download here:
https://www.microsoft.com/en-us/download/details.aspx?id=30425

How can I run a VBScript file silently in the background?

I want to run a VBScript file silently, because it is just a part of a hidden script.
I'm using the VBScript to export automatically documents out of SAP, that is working perfectly, unless showing each step in the SAP-GUI.
The VBScript file is started in a PowerShell, where I already tried to hide the process like:
$vbsPDPPath = "$env:userprofile\AppData\Roaming\KPIReport"
$vbsPDPName = "SAP-ExportPDP.vbs"
$processNamePDP = $vbsPDPPath + "\" + $vbsPDPName
Start-Process $processNamePDP -WindowStyle Hidden
Didn't work out though.
I'm looking for a solution like in VBA, where you can just add:
Application.ScreenUpdating = False
Still have no idea how to solve it. I thought it would be helpful to let you see the vbs-code, there must be the fault.
I noticed that I haven't mentioned to hide the SAP GUI as well as the Excel Application.
Dim Number_PDP
Dim testNode
Dim WshShell
Dim profile
Set WshShell = WScript.CreateObject("WScript.Shell")
profile = WshShell.ExpandEnvironmentStrings("%USERPROFILE%")
'read XML file
Set xmlDoc = CreateObject("MSXML.DomDocument")
xmlDoc.Load profile & "\AppData\Roaming\KPIReport\DIS.xml"
For Each testNode In xmlDoc.selectNodes("/Reports/Report")
Number_PDP = testNode.SelectSingleNode("DIS_PDP").Text
'connect to SAP GUI
If Not IsObject(application) Then
Set SapGuiAuto = GetObject("SAPGUI")
Set application = SapGuiAuto.GetScriptingEngine
End If
If Not IsObject(connection) Then
Set connection = application.Children(0)
End If
If Not IsObject(session) Then
Set session = connection.Children(0)
End If
If IsObject(WScript) Then
WScript.ConnectObject session, "on"
WScript.ConnectObject application, "on"
End If
SapGuiAuto.Visible = false
'not working, thought it is possible to hide SAP
session.findById("wnd[0]").resizeWorkingPane 132,31,false
session.findById("wnd[0]/tbar[0]/okcd").text = "/n cv04n"
session.findById("wnd[0]").sendVKey 0
...
'cutted a couple of rows, just opens a document in Excel with SAP
'after SAP opens Excel I used this code to save the documents
set objExcel = getobject(,"Excel.Application")
if err.number<>0 then
err.clear
end if
'this part should be hidden as well, not working
objExcel.Visible = false
objExcel.ActiveWorkbook.SaveAs profile & "\AppData\Roaming\KPIReport\" & Number_PDP
objExcel.ActiveWorkbook.Close
objExcel.Quit
Next
I would use a workaround in both cases.
For example:
. . .
'session.findById("wnd[0]").resizeWorkingPane 132,31,false
session.findById("wnd[0]").iconify
. . .
and
. . .
'objExcel.Visible = false
objExcel.WindowState = 2
. . .
Regards,
ScriptMan

Redemption in MS Access / Outlook (trying to include a separate message file in email)

This code is in MS Access (2010) VBA, using the Redemption library with Microsoft Outlook 2010.
I had this process working before, but we recently had a Citrix upgrade that I guess reset something in my Outlook and now the process no longer works.
I have a folder of .msg files which are basically pre-made email templates with all the proper formatting, images, text, etc.
This is what I was doing before:
Dim outlookApp As Object, namespace As Object
Dim oItem, MyItem
Set outlookApp = CreateObject("Outlook.Application")
Set namespace = outlookApp.GetNamespace("MAPI")
namespace.Logon
Set MyItem = outlookApp.CreateItemFromTemplate(path_to_dot_msg_file)
'And then there are many calls like this:
MyItem.HTMLBody = Replace(MyItem.HTMLBody, "Dear Person,", "Dear " & name)
'...
Set safeItem = CreateObject("Redemption.SafeMailItem")
Set oItem = MyItem
safeItem.Item = oItem
'this next line displays the email, and as of this line, it looks correct
'safeItem.Display
'but as of this line, the issue occurs
safeItem.HTMLBody = "<p>This is an extra message that shows up before the .msg file</p>" & safeItem.HTMLBody
safeItem.Recipients.ResolveAll
safeItem.Send
Now when the email is sent, the .msg contents aren't present at all -- the only thing that shows up is the "extra message" that I prepended to the HTMLBody.
What do I need to change or update? Is this something I need to change in the code, or in my Outlook settings, etc?
Extra: body insertion:
Function insertStringBodyTag(htmlBody As String, stringToInsert As String)
Dim s As String
Dim i As Integer
s = htmlBody
i = InStr(1, s, "<body")
i = InStr(i, s, ">")
s = Left(s, i) & stringToInsert & Right(s, Len(s) - i)
insertStringBodyTag = s
End Function
'Called with safeItem.htmlBody = insertStringBodyTag(safeItem.htmlBody, prefix_string)
You cannot concatenate 2 HTML strings and expect a valid HTML string back - the two must be merged - find the position of the "<body"substring in the original HTML body, then find the positon of the following ">" (this way you take care of the body element with attributes), then insert your HTML string following that ">".

Programmatically created form with name conflict

I'm creating in Excel VBA a form using code. The following code snippet presents a problem in which the form is somehow created with the name already correctly set and then afterwards, in the only place where I set the said variable, it raises an issue saying that there is a form with that name (the variable in case).
Here is my code:
Dim frmName As String
frmName = "frm_" & Replace(CStr(Nome_do_formulario), " ", "")
Set myForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
With myForm
.Properties("Caption") = Nome_do_formulario
.Properties("Width") = 300
.Properties("Height") = 270
.Properties("Name") = frmName
End With
To be clear, the error is that when it reaches the line:
Set myForm = ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm)
Somehow it already creates a form with name that's set after at the with statement:
With myForm
.Properties("Caption") = Nome_do_formulario
.Properties("Width") = 300
.Properties("Height") = 270
.Properties("Name") = frmName '<- HERE
End With
And then, when it tries to run the with statement it breaks and says that a form with that name already exists.
The whole thing is ran at another module as:
Public Sub Main()
Dim ac As autoCrud
Set ac = New autoCrud
ac.CreateCRUDView
End Sub
The form creation happens inside the ac.CreateCRUDView.
How is it pulling the name variable before it's set and then trying to use it to make another form with the same name?
VBE suffers heavily corruption when it is about UserForms collection in a VBA Project.
Even if you remove explicitly a UserForm from your project, you might get errors creating programatically (and sometimes in the normal way) another with the same name.
Try using this approach:
Dim frmName As String
Dim myForm As VBComponent
frmName = "frm_" & Replace(CStr(Nome_do_formulario), " ", "")
ThisWorkbook.VBProject.VBComponents.Add(vbext_ct_MSForm).Name = frmName
Set myForm = ThisWorkbook.VBProject.VBComponents(frmName)
With myForm
.Properties("Caption") = Nome_do_formulario
.Properties("Width") = 300
.Properties("Height") = 270
End With
Remember, if you delete the newly created userform and run this code with the same Nome_do_formulário value, you'll get an error.

How to print a file with Jscript

Goal
I want to print a file via a PDF printer which isn't the default printer. I was able to temporary change the normal printer to the PDF printer.
Problem
But I don't know how to print a .doc, .txt or .xls via Jscript. Also, I can't find a way to save the default printer name so I can switch back after I've printed the file.
Jscript code
var objShell = new ActiveXObject("Shell.Application");
var objFSO = new ActiveXObject("Scripting.FileSystemObject");
try {
var PDFCreatorQueue = new ActiveXObject("PDFCreatorBeta.JobQueue");
PDFCreatorQueue.Initialize();
var sourceFile = WScript.Arguments(0)
var sourceFolder = objFSO.GetParentFolderName(sourceFile)
var sourceName = objFSO.GetBaseName(sourceFile)
var targetFile = sourceFolder + "\\" + sourceName + ".pdf"
//HERE GOES THE COMMAND TO SAVE THE CURRENT DEFAULT PRINTER NAME TO A TEMP VARIABLE
objNet.SetDefaultPrinter("PDFCreator");
//HERE GOES THE PRINT COMMAND WHICH I DON'T KNOW
// HERE GOES THE COMMAND TO CHANGE BACK TO THE OLD DEFAULT PRINTER
if(!PDFCreatorQueue.WaitForJob(3)) {
WScript.Echo("The print job did not reach the queue within " + 3 + " seconds");
}
else {
var job = PDFCreatorQueue.NextJob;
job.SetProfileByGUID("DefaultGuid");
job.ConvertTo(targetFile);
if(!job.IsFinished || !job.IsSuccessful) {
WScript.Echo("Could not convert the file: " + targetFile);
}
}
PDFCreatorQueue.ReleaseCom();
}
catch(e) {
WScript.Echo(e.message);
PDFCreatorQueue.ReleaseCom();
}
Use the ShellFolderItem.InvokeVerbEx() function. The JScript example code in the MSDN article shows how to use it. Make the first argument "print" and the second argument the name of the printer. So you can remove the code that tinkers with the default printer.
Printing web page from js is quite easy, you could use window.print() method over an iFrame ( this works only with file format wich can be displaied into a web page so it doesn't work with .doc extension)
<iframe id="textfile" src="text.txt"></iframe>
<button onclick="print()">Print</button>
<script type="text/javascript">
function print() {
var iframe = document.getElementById('textfile');
iframe.contentWindow.print();
}
</script>
These will show you a message box to select what printer you want to use a so on.
What are you asking for seems to be silent printing but it isn't standarized over all the broswer.
P.S. I think that isn't a good idea to use the printer to save this file to pdf, I think taht you could look at jsPDF (a js tools to create pdf) or you should consider to make the pdf generation serverside.