A windows application exports data into a new excel workbook.
I need to save that generated workbook using AutoIt.
I tried the below code.But it is not working.
Local $oExcel = ObjGet("","Excel.Application")
For $oWb in $oExcel.Workbooks
$oWb.Activate()
_Excel_BookSaveAs($oWb,"File name with full path",Default,True)
$oWb.Application.Quit
ExitLoop
Next
According to the documentation, you have to make sure your file extension and excel type match - i.e. Excel 2007 = xlsx
A valid path/file name, a valid type ( optional ) , overwrite = true.
Activate isn't necessary.
#include <excel.au3>
#include <ExcelConstants.au3>
Local $oExcel = ObjGet("","Excel.Application")
if NOT #error then
for $Workbook in $oExcel.Workbooks
; optional type $xlExcel7
; optional overwrite = true
_Excel_BookSaveAs($Workbook,"c:\drivers\keeper3.xlsx" )
next
_Excel_Close($oExcel,false,true)
EndIf
ConsoleWrite("END" & #CRLF)
Related
How can I copy data from a cursor file in Visual FoxPro to an Excel template?
A relatively easy way of creating Excel spreadsheets is the XML Spreadsheet format, or XMLSS for short. It is Microsoft's first attempt to add an XML format to Office products. XMLSS debuted with Office XP and is supported back to Office 2000.
Unlike in XLSX, which is a full replacement for the XLS format, you don't deal with multiple files compressed into a ZIP folder that XLSX is behind the scenes. It's also pretty straight forward to generate.
Excel opens the file without issues if you open it within Excel. Recent versions of Excel check wether the file extension matches the format. Excel expects an XML file extension for XMLSS files which isn't mapped to Excel by default. Therefore you cannot double-click a file in Explorer and expect it to open in Excel. An earlier work around was to rename the file to XLS, but that triggers a warning now when opening the file.
You can, however, use Excel automation to convert the XMLSS Excel file into a XLSX file.
Here's a very simple sample that converts the Northwind customer table into a spreadsheet with alternating coloring. The code would work with any open cursor if you remove the USE statement.
Use Northwind\Customers
Set Point To "."
Local lcFile
lcFile = GetEnv("USERPROFILE")+"\Desktop\Customers.xls"
If File(m.lcFile)
Erase (m.lcFile)
EndIf
Local lcRows, lnField, luValue, lcStyle, lcData
lcRows = ""
Scan
lcRows = m.lcRows + "<Row>"
For lnField = 1 to Fcount()
luValue = Evaluate(Field(m.lnField))
lcStyle = Iif(Recno()%2==0,"even","odd")
Do case
Case InList(Vartype(m.luValue),"C")
lcData = ;
[<Data ss:Type="String">]+Strconv(Alltrim(m.luValue),9)+[</Data>]
Case InList(Vartype(m.luValue),"N")
lcData = ;
[<Data ss:Type="Number">]+Transform(Nvl(m.luValue,0))+[</Data]
Otherwise
Loop
EndCase
lcRows = m.lcRows + ;
[<Cell ss:StyleID="]+m.lcStyle+[">]+m.lcData+[</Cell>]
EndFor
lcRows = m.lcRows + "</Row>"
endscan
Local lcXML
Text to m.lcXML Noshow Textmerge
<?xml version="1.0"?>
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
<Styles>
<Style ss:ID="even">
<Font ss:FontName="Tahoma" ss:Size="13" ss:Bold="1" />
</Style>
<Style ss:ID="odd">
<Font ss:FontName="Tahoma" ss:Size="13" ss:Color="red" />
</Style>
</Styles>
<Worksheet ss:Name="Sheet1">
<Table><<m.lcRows>></Table>
</Worksheet>
</Workbook>
EndText
StrToFile(m.lcXml,m.lcFile)
You find more information about XMLSS and potential issues you might run into in my Excelporting white paper.
The resulting file is not connected to the cursor or the data source in any way and therefore suitable to be sent around or be based on a cursor.
You can use CopyFromRecordSet or QueryTables.Add. The problem with QueryTables.Add is:
You need to have access to source data at all times. I mean you can't simply hand over your XLS or XLSX file to someone else.
You would need to use a driver to connect to VFP data, which is likely the VFPOLEDB driver, and it is 32 bits. Your Excel edition might be 64 bits.
CopyFromRecordSet is a good choice IMHO, and you could use one of my VFP2Excel routines that have been posted on various forums many times. I.e.:
Select Cust_Id As CustomerId, ;
Val(Order_Id) As OrderId, ;
Order_Date As OrderDate, ;
Cast(Evl(Shipped_On, .Null.) As Datetime) As ShippedOn ;
From (_samples + 'data\Orders') ;
Into Cursor crsToExcel ;
nofilter
*** We need real tables on disk to get them via VFPOLEDB
Local lcDbc, lcDBF
lcDbc = Forcepath( Forceext( Sys(2015), 'dbc'), Sys(2023))
lcDBF = Forcepath( Forceext(Sys(2015), 'dbf'), Sys(2023))
** Create the temporary dbc
Create Database (m.lcDbc)
** And set it as the default database
Set Database To (m.lcDbc)
** And create tables from cursors as part of this new dbc
Select * From crsToExcel Into Table (m.lcDBF) Database (m.lcDbc)
Use In (Select(Juststem(m.lcDBF)))
Close Database
** Ready for sending the data to Excel.
** We also assume that the Excel on this machine could be a 64 bit version
** thus we don't do a direct VFPOLEDB transfer, but wrap it in a ADODB.Stream.
** We could as well use an ADODB.RecordSet.
Local ix, loStream As 'Adodb.stream'
m.loStream = GetDataAsAdoStream("Provider=VFPOLEDB;Data Source="+m.lcDbc, Textmerge("select * from ('<< m.lcDBF >>')"))
*** Now that we have the data in streams, we can
*** get rid of the temp database and tables.
Local lcSafety
lcSafety = Set("Safety")
Set Safety Off
Delete Database (m.lcDbc) Deletetables
Set Safety &lcSafety
*** Main Excel automation part now
oExcel = Createobject("Excel.Application")
With oExcel
.DisplayAlerts = .F.
.Workbooks.Add
.Visible = .T.
With .ActiveWorkBook.ActiveSheet
.Name = 'SampleSheet'
* Send the data - copy to replacement
VFP2ExcelVariation(m.loStream, .Range("A1"), "Customer ID, Order ID, Ordered On, Shipped On")
.Columns.AutoFit()
.Activate
Endwith
Endwith
Function VFP2ExcelVariation(toStream, toRange, tcHeaders)
Local loRS As AdoDb.Recordset,ix
loRS = Createobject('Adodb.Recordset')
m.loRS.Open( m.toStream )
* Use the first row for headers
Local Array aHeader[1]
m.toRange.Offset(1,0).CopyFromRecordSet( m.loRS ) && Copy data starting from headerrow + 1
For ix=1 To Iif( !Empty(m.tcHeaders), ;
ALINES(aHeader, m.tcHeaders,1,','), ;
m.loRS.Fields.Count )
m.toRange.Offset(0,m.ix-1).Value = ;
Iif( !Empty(m.tcHeaders), ;
aHeader[m.ix], ;
Proper(m.loRS.Fields(m.ix-1).Name) )
m.toRange.Offset(0,m.ix-1).Font.Bold = .T.
Endfor
m.loRS.Close()
Endfunc
Procedure GetDataAsAdoStream(tcConnection, tcSQL)
Local loStream As 'AdoDb.Stream', ;
loConn As 'AdoDb.Connection', ;
loRS As 'AdoDb.Recordset'
loStream = Createobject('AdoDb.Stream')
loConn = Createobject("Adodb.connection")
loConn.ConnectionString = m.tcConnection
m.loConn.Open()
loRS = loConn.Execute(m.tcSQL)
m.loRS.Save( loStream )
m.loRS.Close
m.loConn.Close
Return m.loStream
Endproc
I'm attempting to add sheets to an excel file.
It should be fairly straightforward; however, the following minimal working example below fails when attempting to skip optional arguments using empty braces. (Is this not correct? Source 1 Source 2)
clc
clear
% if COM error occurs, excel process remains open.
% use task manager to end process, else 'a.xlsx' file remains "in use".
srvr = actxserver('excel.application');
wbks = srvr.workbooks;
pth = fullfile(pwd, 'a.xlsx');
if ~exist(pth, 'file')
wbk = wbks.add;
wbk.activate;
wbk.saveAs( pth );
wbk.close;
end
wbk = wbks.open( pth, 0, false );
wbk.activate;
wshts = wbk.worksheets;
shts = wbk.sheets;
wsht = wshts.item(1);
wsht.activate;
sht = shts.item(1);
wsht.select(true);
sht.select(true);
%{
https://learn.microsoft.com/en-us/office/vba/api/excel.sheets.add
https://learn.microsoft.com/en-us/office/vba/api/excel.worksheets.add
%}
shts.add(sht); % functions
shts.add(wsht); % functions
shts.add([],sht); % fails
shts.add([],wsht); % fails
shts.count
wbk.save;
srvr.quit;
Here is the error:
error: com_invoke: property/method invocation on the COM object failed with error `0x800a03ec' - Z
error: called from
trash at line 46 column 1
Note that the add functions until skipping the first input. Are square brackets the wrong method to skip an input?
Original source.
I have also posted this in Octave forum.
As per comments under the question,
this was a bug, which has been reported by OP and fixed in the development branch.
Let me first say that I saw the other two threads that mentioned this issue here and here, but they didn't help me solve my problem.
I've been testing a program for several weeks in the on-prem Excel 2016 environment (32-bit) with no problems. My company is making the move to Office 365 soon, so I decided to test it over there as well. On that system, I'm getting a run-time error on the line Functions.Connection = objConnection
Option Explicit
Public Functions As SAPFunctionsOCX.SAPFunctions
Private LogonControl As SAPLogonCtrl.SAPLogonControl
Private objConnection As SAPLogonCtrl.Connection
Public Func As SAPFunctionsOCX.Function
Public Commit As SAPFunctionsOCX.Function
Public TableFactory As SAPTableFactory
Public silentLogon As Boolean
Public tblReadTableOptions, tblReadTableFields, tblReadTableData As SAPTableFactoryCtrl.Table
Sub ExtractProjectData()
If objConnection Is Nothing Then LogonToSAP
InitiateSAPVariables
Set Func = Functions.Add("BBP_RFC_READ_TABLE")
Set tblReadTableOptions = Func.Tables("OPTIONS")
Set tblReadTableFields = Func.Tables("FIELDS")
Set tblReadTableData = Func.Tables("DATA")
'extract/transform data from SAP tables
End Sub
Function InitiateSAPVariables()
Set Functions = Nothing
Set TableFactory = Nothing
Set Func = Nothing
Set Functions = CreateObject("SAP.Functions")
Set TableFactory = CreateObject("SAP.TableFactory.1")
Functions.Connection = objConnection 'run-time error here in Office 365 but not in on-prem
End Function
Function LogonToSAP()
Dim establishConnection As Boolean
silentLogon = false
Set LogonControl = CreateObject("SAP.LogonControl.1")
Set objConnection = LogonControl.NewConnection
objConnection.Client = "###"
objConnection.Language = "EN"
objConnection.SystemNumber = "##"
objConnection.User = ""
objConnection.Password = ""
objConnection.HostName = "###############"
objConnection.System = "###"
objConnection.ApplicationServer = "###.###.#.##"
establishConnection = objConnection.Logon(0, silentLogon)
End Function
A quick check of objConnection tells me that logon was successful...so I know that part is working on 365. For some reason though, it doesn't like assigning the Connection property of the Functions SAPFunctionsOCX.SAPFunctions object in the 365 environment (please feel free to correct my verbiage on that...I know it's not quite right).
Note that I'm not seeing any reference issues nor am I getting any compile errors in either environment. The first sign of trouble is on execution of Functions.Connection = objConnection
There's one more twist here and that is that I have another older VBA program that logs into SAP and runs remote function calls that doesn't use SAPFunctionsOCX.SAPFunctions, but rather declares variable R3 as Public R3 As Object and then sets R3 later in the logon code as Set R3 = CreateObject("SAP.Functions")...it does not use OCX. In other words, the old routine uses late binding. When the Functions object (R3 in this case) is set this way, I am able to run RFCs in both on prem and Office 365 environments.
Function LogonProdSAP(Optional SuppressLoginScreen As Boolean)
Application.ScreenUpdating = False
'**********************************************
'Create Server object and Setup the connection for DEV
'**********************************************
Set R3 = CreateObject("SAP.Functions")
If SuppressLoginScreen Then
R3.Connection.System = "###"
R3.Connection.HostName = "###################"
R3.Connection.SystemNumber = "##"
R3.Connection.Client = "###"
R3.Connection.User = "##########"
R3.Connection.Password = "#########"
R3.Connection.Language = "EN"
' Call Logger("LogonProdSAP> " & GetUserName)
End If
LogonProdSAP = R3.Connection.logon(0, SuppressLoginScreen)
If LogonProdSAP <> True Then MsgBox ("Logon error"): Exit Function
End Function
I could just go back to doing it this way, but I'd rather not have to reconfigure all of the code I just set up. In addition, I prefer binding early so Intellitype works to show all properties/methods available to that object. I'm sure there are other benefits as well.
What do I have to do to get the early-binding technique to work on Office 365?
It's due to the fact that your Office is in 64 bits version, and SAP GUI for Windows up to version 7.60 is in 32 bits (next SAP GUI version 7.70 should be in 64 bits, so it should work again).
You have a workaround to make VBA work with SAP GUI 32-bits DLL, by using DLL Surrogate, i.e. by editing the Windows Registry of all incompatible SAP GUI DLL. The original solution was proposed here at SAP Community.
To simplify the task, you may create my .REG file, execute it to update automatically the Windows Registry, and your VBA macro should then work.
I duplicate here my .REG file:
; ====================================================================================
; SAP Logon Unicode Control %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtlogU.ocx {0AAF5A11-8C04-4385-A925-0B62F6632BEC}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{0AAF5A11-8C04-4385-A925-0B62F6632BEC}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{0AAF5A11-8C04-4385-A925-0B62F6632BEC}]
"AppID"="{0AAF5A11-8C04-4385-A925-0B62F6632BEC}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{0AAF5A11-8C04-4385-A925-0B62F6632BEC}]
; ====================================================================================
; SAP Remote Function Call Unicode Control %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtfuncu.ocx {0AF427E7-03B9-4673-8F21-F33A683BCE28}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{0AF427E7-03B9-4673-8F21-F33A683BCE28}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{0AF427E7-03B9-4673-8F21-F33A683BCE28}]
"AppID"="{0AF427E7-03B9-4673-8F21-F33A683BCE28}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{0AF427E7-03B9-4673-8F21-F33A683BCE28}]
; ====================================================================================
; SAP Logon Control (not Unicode) %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtlog.ocx {B24944D6-1501-11CF-8981-0000E8A49FA0}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{B24944D6-1501-11CF-8981-0000E8A49FA0}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{B24944D6-1501-11CF-8981-0000E8A49FA0}]
"AppID"="{B24944D6-1501-11CF-8981-0000E8A49FA0}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{B24944D6-1501-11CF-8981-0000E8A49FA0}]
; ====================================================================================
; SAP Remote Function Call Control (not Unicode) %ProgramFiles(x86)%\SAP\FrontEnd\SAPgui\wdtfuncs.ocx {5B076C03-2F26-11CF-9AE5-0800096E19F4}
; ====================================================================================
[HKEY_CLASSES_ROOT\WOW6432Node\AppID\{5B076C03-2F26-11CF-9AE5-0800096E19F4}]
"DllSurrogate"=""
[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{5B076C03-2F26-11CF-9AE5-0800096E19F4}]
"AppID"="{5B076C03-2F26-11CF-9AE5-0800096E19F4}"
[HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppID\{5B076C03-2F26-11CF-9AE5-0800096E19F4}]
I have a CSV with semicolon seperators that I would like to convert to a regular Excel sheet. I managed to do this with the code below, but I must have made a mistake because numbers with decimals in the original file that don't start with a zero are shown in Excel as number without the decimal separator. When I open the CSV manually in Excel the result will be fine, so it must be a side-effect of doing it with a script.
For example:
In the CSV there is a line:
2013-03-10 17:00:15; idle; 2,272298;; 0,121860
In the Excel sheet this becomes:
2013-03-10 17:00 | idle | 2.272.298| | 0,121860
Opened manually in excel gives:
2013-03-10 17:00 | idle | 2,272298| | 0,121860
Could somebody please tell me what I could/should change to keep the decimals as decimals in Excel? Possibly a way to tell Excel which symbol represents the decimal separator or an argument to force it into using European formats?
Kind regards, Nico
This is the script I currently have, where csvFile is a string with the full path to the original file and excelFile is a string with the full path to the location where I want to store the new excel sheet.
Set objExcel = CreateObject("Excel.Application") 'use excel
objExcel.Visible = true 'visible
objExcel.displayalerts=false 'no warnings
objExcel.Workbooks.Open(csvFile) 'open the file
objExcel.ActiveWorkbook.SaveAs excelFile, -4143, , , False, False 'save as xls
objExcel.Quit 'close excel
Create a schema.ini file in the folder your csvFile lives in and describe it according to the rules given here.
Further reading: import, text files
There are several approaches possible, I will cover one that I favor:
Start Recording a macro
Create a new workbook
From that workbook go to Data > From Text and there you select the CSV file, then you can do all the required settings regarding Value separators, Decimal separators, Thousands separators. Also the specific data type can be selected for each column.
When the CSV content is added go to Data > Connections and Remove
the connection. The data will stay in the worksheet, but there is no
longer an active connection.
Save the workbook under the xls name
Stop the Recording
Now tweak the script a bit to your liking.
In general Excel honors the system's regional settings. The CSV import, however, sometimes has its own mind about the "correct" format, particularly when the imported file has the extension .csv.
I'd try the following. Rename the file to .txt or .tsv and import it like this:
objExcel.Workbooks.OpenText csvFile, , , 1, 1, False, False, True
I made a work around. I now create a copy of the CSV file where I replace all commas followed by a number by points. While not very effective it does give Excel what it wants and it is simple enough for an inexperienced programmer like me to use.
When doing so a college asked me to also remove white spaces and entries with duplicate values in the first column (the timestamp in this case).
The result was this script
'csvFile is a string with the full path to the file. e.g. "C:\\Program Files\\Program\\data.csv"
'tempFile is a string with the full path to the file. e.g. "C:\\Temp\\temp.csv"
'excelfile is a string with the full path to the file. e.g. "D:\\Data\\sheet.xls"
Set fs=CreateObject("Scripting.FileSystemObject")
Set writeFile = fs.CreateTextFile(tempFile,True)
Set readFile = fs.OpenTextFile(csvFile)
' regular expression to remove leading whitespaces
Set regular_expression = New RegExp
regular_expression.Pattern = "^\s*"
regular_expression.Multiline = False
' regular expression to change the decimal seperator into a point
Set regular_expression2 = New RegExp
regular_expression2.Global = True
regular_expression2.Pattern = ",(?=\d)"
regular_expression2.Multiline = False
'copy the original file to the temp file and apply the changes
Do Until readFile.AtEndOfStream
strLine= readFile.ReadLine
If (StrComp(current_timestamp,Mid(strLine, 1, InStr(strLine,";")),1)<>0) Then
If (Len(previous_line) > 2) Then
previous_line = regular_expression2.replace(previous_line,".")
writeFile.Write regular_expression.Replace(previous_line, "") & vbCrLf
End if
End if
current_timestamp = Mid(strLine, 1, InStr(strLine,";"))
previous_line = strLine
Loop
readFile.Close
writeFile.Close
Set objExcel = CreateObject("Excel.Application") ' use excel
objExcel.Visible = true ' visible
objExcel.displayalerts=false ' no warning pop-ups
objExcel.Workbooks.Open(tempFile) ' open the file
objExcel.ActiveWorkbook.SaveAs excelfile, -4143, , , False, False 'save as excelfile
fs.DeleteFile tempFile ' clean up the temp file
I hope this will also be useful for someone else.
I've an Excel 2010 spreadsheet with an XML map defined within it. Using Perl I want to save the worksheet as XML Data. I do not need to export the XML map file. From within Excel I can select "File > Save As > Save as type : XML Data". This is the output I want to create, but from my Perl script.
I can output the worksheet in CSV format using the SaveAs command with enum 6. I can also output the spreadsheet in XML format using SaveAs with enum 46, but this is not what I want. I want just the XML Data..
There appears to be a SaveAsXMLData function but I'm unable to get it working. Any help appreciated.
use strict;
use warnings;
use Win32::OLE qw(in with);
use Win32::OLE::Const 'Microsoft Excel';
use Win32::OLE::Variant;
use Win32::OLE::NLS qw(:LOCALE :DATE);
$Win32::OLE::Warn = 3; # Die on Errors.
my $Excel = Win32::OLE->GetActiveObject('Excel.Application')
|| Win32::OLE->new('Excel.Application', 'Quit');
$Excel->{DisplayAlerts}=0;
my $excel_file = 'c:\\temp\\master.xlsx';
my $csv_file = 'c:\\temp\\master.csv';
my $xml_file = 'c:\\temp\\master.xml';
my $workbook = $Excel->Workbooks->Open($excel_file);
# Alt+F11 in Excel to start VBA and after that F2 to start Object browser.
# 6 is CSV format
# 46 is XML spreadsheet
$workbook->SaveAs( $csv_file, 6 );
# Now just the XML Data
# The map is called MDBAC_Map
my $objMapToExport = $Excel->Workbooks->XmlMaps("MDBAC_Map");
$workbook->SaveAsXMLData( $xml_file, $objMapToExport );
$workbook->Close();
$Excel->Quit();
Fixed this myself (I was 99% there!). Using the macro recorder within Excel confirmed the required function calls as follows:
ChDir "C:\temp"
ActiveWorkbook.SaveAsXMLData Filename:="C:\temp\master.xml", Map:= _
ActiveWorkbook.XmlMaps("MDBAC_Map")
The line of code for exporting the XML map is wrong. Changed the above code as follows and the script works fine:
my $objMapToExport = $workbook->XmlMaps("MDBAC_Map");