COPY TO excel Sheet in foxpro - visual-foxpro

Is their any command in Foxpro that convert the DBF into a particular excel sheet.
I have three DBF(dbf_1, dbf_2, dbf_3). My current program convert the file using copy to "filename.xls" type fox2x and then I will manually copy the consolidate all the sheet into one excel. For me, this method I using is alright but what if their are 20 or more dbf that I will consolidate. Is their any command in foxpro that convert the dbf's into one excel file. I already use the foxpro Automation but it is to slow.

No there isn't.
Also "copy to ... type fox2x". although better than many other type selections (such as csv and xls) should not be chosen when there are better ways.
You are saying automation is slow, but don't know if you are really finding automation slow, or if you have tried it in the ways that you shouldn't use to transfer data to Excel. The sample below, use one of the variations of my "vfp2excel" function and automation. It transfers sample Customer, Employee, Orders, OrdItems and Products data in 2.5 seconds on my machine. If you really meant it as slow then no dice, otherwise here is the sample:
* These represent complex SQL as a sample
Select emp_id,First_Name,Last_Name,;
Title,Notes ;
from (_samples+'\data\employee') ;
into Cursor crsEmployee ;
readwrite
Replace All Notes With Chrtran(Notes,Chr(13)+Chr(10),Chr(10))
Select cust_id,company,contact,Title,country,postalcode ;
from (_samples+'\data\customer') ;
into Cursor crsCustomer ;
nofilter
Select * ;
from (_samples+'\data\orders') ;
into Cursor crsOrders ;
nofilter
Select * ;
from (_samples+'\data\orditems') ;
into Cursor crsOrderDetail ;
nofilter
Select * ;
from (_samples+'\data\products') ;
into Cursor crsProducts ;
nofilter
* Now we want to get these on 3 sheets
* Sheet1: Employees only
* Sheet2: Customers only
* Sheet3: Orders, ordItems, Products layed out horizontally
Local oExcel
oExcel = Createobject("Excel.Application")
With oExcel
.DisplayAlerts = .F.
.Workbooks.Add
.Visible = .T.
With .ActiveWorkBook
For ix = 1 To 3 && We want 3 Sheets
If .sheets.Count < m.ix
.sheets.Add(,.sheets(.sheets.Count)) && Add new sheets
Endif
Endfor
* Name the sheets
.WorkSheets(1).Name = "Employees"
.WorkSheets(2).Name = "Customers"
.WorkSheets(3).Name = "Order, OrderDetail, Products" && max sheetname is 31 chars
* Start sending data
* First one has headers specified
VFP2Excel('crsEmployee', .WorkSheets(1).Range("A1"), ;
"Id,First Name,Last Name,Employee Title,Comments about employee" ) && To sheet1, start at A1
VFP2Excel('crsCustomer', .WorkSheets(2).Range("A1") ) && To sheet2, start at A1
VFP2Excel('crsOrders', .WorkSheets(3).Range("A1") ) && To sheet3, start at A1
* Need to know where to put next
* Leave 2 columns empty - something like 'G1'
lcRange = _GetChar(.WorkSheets(3).UsedRange.Columns.Count + 3) + '1'
* To sheet3, start at next to previous
VFP2Excel('crsOrderDetail', .WorkSheets(3).Range(m.lcRange) )
lcRange = _GetChar(.WorkSheets(3).UsedRange.Columns.Count + 3) + '1'
* To sheet3, start at next to previous
VFP2Excel('crsProducts', .WorkSheets(3).Range(m.lcRange) )
#Define xlJustify -4130
#Define xlTop -4160
* I just happen to know notes in at column 5 from SQL
* No need to query from excel to keep code simple
* Lets format that column specially instead of leaving
* at the mercy of Excel's autofitting
.WorkSheets(1).UsedRange.VerticalAlignment = xlTop && set all to top
With .WorkSheets(1).Columns(5)
.ColumnWidth = 80 && 80 chars width
.WrapText = .T.
* .HorizontalAlignment = xlJustify && doesn't work good always
Endwith
* Finally some cosmetic stuff
For ix=1 To 3
With .WorkSheets(m.ix)
.Columns.AutoFit
.Rows.AutoFit
Endwith
Endfor
.WorkSheets(1).Activate
Endwith
Endwith
* Author: Cetin Basoz
* This is based on earlier VFP2Excel function codes
* that has been published on the internet, at various sites
* since 2001. Not to be messed with others' code who named the same but has
* nothing to do with the approaches taken here (unless copy & pasted and claimed
* to be their own work, < s > that happens).
Procedure VFP2Excel(tcCursorName, toRange, tcHeaders, tnPrefferredWidthForMemo)
* tcCursorName
* toRange
* tcHeaders: Optional. Defaults to field headers
* tnPrefferredWidthForMemo: Optional. Default 80
* Function VFP2Excel
tcCursorName = Evl(m.tcCursorName,Alias())
tnPrefferredWidthForMemo = Evl(m.tnPrefferredWidthForMemo,80)
Local loConn As AdoDB.Connection, loRS As AdoDB.Recordset,;
lcTemp,lcTempDb, oExcel,ix, lcFieldName, lcHeaders
lnSelect = Select()
lcTemp = Forcepath(Sys(2015)+'.dbf',Sys(2023))
lcTempDb = Forcepath(Sys(2015)+'.dbc',Sys(2023))
Create Database (m.lcTempDb)
Select * From (m.tcCursorName) Into Table (m.lcTemp) Database (m.lcTempDb)
Local Array aMemo[1]
Local nMemoCount
nMemoCount = 0
lcHeaders = ''
For ix = 1 To Fcount()
lcFieldName = Field(m.ix)
If Type(Field(m.ix))='M'
nMemoCount = m.nMemoCount + 1
Dimension aMemo[m.nMemoCount]
aMemo[m.nMemoCount] = m.ix
Replace All &lcFieldName With Chrtran(&lcFieldName,Chr(13)+Chr(10),Chr(10))
Endif
lcHeaders = m.lcHeaders + Iif(Empty(m.lcHeaders),'',',')+Proper(m.lcFieldName)
Endfor
tcHeaders = Evl(m.tcHeaders,m.lcHeaders)
Use In (Juststem(m.lcTemp))
Close Databases
Set Database To
loStream = Createobject('AdoDb.Stream')
loConn = Createobject('ADODB.Connection')
loRS = Createobject("ADODB.Recordset")
loConn.ConnectionString = "Provider=VFPOLEDB;Data Source="+m.lcTempDb
loConn.Open()
loRS = loConn.Execute("select * from "+m.lcTemp)
loRS.Save( loStream )
loRS.Close
loConn.Close
Erase (m.lcTemp)
* Use first row for headers
Local Array aHeader[1]
loRS.Open( loStream )
toRange.Offset(1,0).CopyFromRecordSet( loRS ) && Copy data starting from headerrow + 1
Set Safety Off
Delete Database (m.lcTempDb) Deletetables
Select (m.lnSelect)
For ix=1 To Iif( !Empty(m.tcHeaders), ;
ALINES(aHeader, m.tcHeaders,1,','), ;
loRS.Fields.Count )
toRange.Offset(0,m.ix-1).Value = ;
Iif( !Empty(m.tcHeaders), ;
aHeader[m.ix], ;
Proper(loRS.Fields(m.ix-1).Name) )
toRange.Offset(0,m.ix-1).Font.Bold = .T.
Endfor
#Define xlJustify -4130
#Define xlTop -4160
* This part is cosmetic
toRange.WorkSheet.Activate
With toRange.WorkSheet.UsedRange
.VerticalAlignment = xlTop && set all to top
For ix=1 To m.nMemoCount
With .Columns(aMemo[m.ix])
.ColumnWidth = m.tnPrefferredWidthForMemo && 80 chars width
.WrapText = .T.
Endwith
Endfor
.Columns.AutoFit
.Rows.AutoFit
Endwith
Endproc
* Return A, AA, BC etc noation for nth column
Function _GetChar
Lparameters tnColumn && Convert tnvalue to Excel alpha notation
If m.tnColumn = 0
Return ""
Endif
If m.tnColumn <= 26
Return Chr(Asc("A")-1+m.tnColumn)
Else
Return _GetChar(Int(Iif(m.tnColumn % 26 = 0,m.tnColumn - 1, m.tnColumn) / 26)) + ;
_GetChar((m.tnColumn-1)%26+1)
Endif
Endfunc

This is what I was looking for :-) I was trying with my knowledge of Excel Automation programming in Visual FoxPro but always got errors. My task was to create "n" Sheets from one big cursors which I want to parse regarding customer selection of attribute name from cursor to get also "n" Sheets. This sample is for 3 cursors and 3 Sheets and it is generic. But I need this for "n" cursors and one attribute which customer select to distinct and get "n" Sheets in one Excel file. So now I have dynamic procedure. I customized this code and solve my problem which I am trying to end for about 4 days. So again thank you for this code and off course I will not modify VFP2Excel procedure and wrote somewhere else my name. Thanks for help !

There is no native VFP function to do this, BUT, there is an awesome open source project which has a class that will make this very easy:
VFPx Workbook Xlsx - See it here on Github: XLSX Workbook for FoxPro
It has 3 magical functions that will do exactly what you asked for:
CreateWorkbook()
AddSheet()
SaveTableToWorkbook()
(Repeat commands 2 and 3 above for each DBF/Sheet you want to create)
It is well documented with a 54-page PDF and code sample that explains everything you'll need to know.

Related

Is there a way to keep microsoft excel app close on visual foxpro?

I have problems with the Excel appand the program I tryin' to put together sends me a message that a document is been used.
In a form on Visual Foxpro 8, I collect some info from the tables of a db and then I assemble a worksheet with this elements.
Lately i've been testin' this part of the program but it shows me the message after it's done assemble the worksheet that the document is still been used, and on the task manager I see that the app is still running even that in the procedure there's a line telling that the Excel sheet is released.
with thisform
lcDir = "C:\Documents\PRGT\test.xls"
ExcelSheet=createobject("Excel.application")
ExcelSheet.APPLICATION.workbooks.ADD
ExcelSheet.APPLICATION.activesheet.cells(1,1).value = "info1"
ExcelSheet.APPLICATION.activesheet.cells(1,2).value = "info2"
ExcelSheet.APPLICATION.activesheet.cells(1,3).value = "info3"
ExcelSheet.APPLICATION.activesheet.cells(1,4).value = "info4"
local lnRow
store 0 to lnRow
select cursorName
go top
do while !eof()
ExcelSheet.Columns("A").Select
ExcelSheet.Selection.value = cursorName.a
ExcelSheet.Columns("B").Select
ExcelSheet.Selection.value = cursorName.b
ExcelSheet.Columns("C").Select
ExcelSheet.Selection.value = cursorName.c
ExcelSheet.Columns("D").Select
ExcelSheet.Selection.value = cursorName.d
lnRow = lnRow+1
selelect cursorName
skip
enddo
ExcelSheet.APPLICATION.activeworkbook.saveas(lcDir)
ExcelSheet.APPLICATION.visible = .f.
release ExcelSheet
/*
* some instruction to close the app
*/
endwith
Well, i found in a Visual forum that the next instruction work ExcelSheet.quit(.f.)
and I tried that line and when I was testing the program it couldn't do the procedure of assemble the excel and savin' it.
Any ideas I can try will be welcome and also it would help me to learn better about this
I couldn't understand some parts of your code, like:
lnRow = lnRow + 1 && not used at all
and selecting the Columns("A"). Why would you select the column? Wouldn't it be a Cell?
Though the way you are doing it is not suggested at all, to make it work correctly you could write as:
#Define xlWorkbookNormal -4143
LOCAL lcDir
lcDir = "C:\Documents\PRGT\test.xls"
Local excelsheet
excelsheet=Createobject("Excel.application")
With excelsheet
.DisplayAlerts = .F.
.workbooks.Add()
With .ActiveWorkBook.ActiveSheet
.cells(1,1).Value = "info1"
.cells(1,2).Value = "info2"
.cells(1,3).Value = "info3"
.cells(1,4).Value = "info4"
Select cursorName
Scan
.cells(Recno()+1,1).Value = cursorName.a
.cells(Recno()+1,2).Value = cursorName.b
.cells(Recno()+1,3).Value = cursorName.c
.cells(Recno()+1,4).Value = cursorName.d
Endscan
Endwith
.ActiveWorkBook.SaveAs(m.lcDir,xlWorkbookNormal)
.ActiveWorkBook.Saved = .T.
.Quit()
Endwith
In your code, you were setting excelSheet (which is the same as excelSheet.Application) .Visible to .F. and releasing ExcelSheet.
Setting it to Visible = .F. and releasing only the variable doesn't help, Excel instance would be left behind hidden with a lock on the document you created. You shouldn't set it to Visible=.F. but simply call Quit() method. Then also using your ExcelSheet variable local would release it by default, going out of scope.
After having said that, proper way of copying data to excel is not like this and this would be the slowest way to do that (don't try with lots of rows and columns data).
Instead you could send the data to excel using OLEDB and just use CopyFromRecordset method (using one of the variations of VFP2Excel routine that I have posted many times to many forums). ie: This sample sends 2 cursors to excel, to apply it to yourself all you need is to create your cursor(s) and set the laInfo[] accordingly to pass to "SaveCursorsToExcel":
* Our sample cursor(s)
Select Cust_Id, Company, Contact, Country From (_samples + 'data\Customer') Into Cursor crsCustomers nofilter
Select Order_Id, Cust_Id, Order_Date, Shipped_on From (_samples + 'data\Orders') Into Cursor crsOrders nofilter
Local Array laInfo[2, 3]
laInfo[1,1] = 'crsCustomers'
laInfo[1,2] = 'Customer Id,Company,Contact Name,Country' && Headers
laInfo[1,3] = 'Customers' && Sheet name
laInfo[2,1] = 'crsOrders'
laInfo[2,2] = 'Order Id, Customer Id, Order Date, Shipped On'
laInfo[2,3] = 'Orders'
Local lcSaveFilename
lcSaveFilename = "c:\temp\SampleExcelFile.xlsx"
SaveCursorsToExcel(#laInfo, m.lcSaveFilename)
Procedure SaveCursorsToExcel(taInfo, tcSaveAs)
External Array taInfo
*** We need real tables on disk to get them via VFPOLEDB
*** Assuming that there may be LFN in cursor data we create a temp DBC too
Local lcDbc, ix
lcDbc = Forcepath( Forceext( Sys(2015), 'dbc'), Sys(2023))
** Create the temp 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
*** We could send many cursors to Excel at one go.
*** taInfo passes the information of cursornames to send and their headers
Local Array laTableNames[ALEN(taInfo,1)]
For ix = 1 To Alen(laTableNames)
laTableNames[m.ix] = Forcepath( Forceext(Sys(2015), 'dbf'), Sys(2023))
Select * From (taInfo[m.ix,1]) Into Table (m.laTableNames[m.ix]) Database (m.lcDbc)
Use In (Select(Juststem(m.laTableNames[m.ix])))
Endfor
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 Array laStream[ALEN(taInfo,1)]
Local ix
For ix = 1 To Alen(taInfo,1)
laStream[m.ix] = GetDataAsAdoStream("Provider=VFPOLEDB;Data Source="+m.lcDbc, Textmerge("select * from ('<< m.laTableNames[m.ix] >>')"))
Endfor
*** 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
For ix = 1 To Alen(taInfo,1)
If .sheets.Count < m.ix
.sheets.Add(,.sheets(.sheets.Count)) && Add new sheet
Endif
.WorkSheets(m.ix).Name = taInfo[m.ix,3]
* Send the data - copy to replacement
VFP2ExcelVariation(m.laStream[m.ix], .WorkSheets[m.ix].Range("A1"), taInfo[m.ix,2])
.WorkSheets(m.ix).Columns.AutoFit()
Endfor
.WorkSheets(1).Activate
Endwith
.ActiveWorkBook.SaveAs(m.tcSaveAs)
.ActiveWorkBook.Saved = .T.
.Quit()
Endwith
ENDPROC
Function VFP2ExcelVariation(toStream, toRange, tcHeaders)
Local loRS As AdoDb.Recordset,ix
loRS = Createobject('Adodb.Recordset')
m.loRS.Open( m.toStream )
* Use 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
At first no need to put your code in with thisform and endwith block as here you are not specifying properties of a form object.
The Excel file name extension prefered to be "xlsx" not "xls" if you are using a new version of Excel.
It's better to define lcDir as local as you did with the lnRow so you need to add local lcDir before store 0 to lnRow.
Regarding the loop through records it's better and easier to replace :
select cursorName
go top
do while !eof()
.
.
.
select cursorName
skip
enddo
By
select cursorName
scan
.
.
.
endscan
To export the records data to the Excel workbook rows, use the same method you used to export the header of the columns but change the row number for each record using the lnRow variable you already defined, but initiate it's value by 2 at first rather than 0 by replacing store 0 to lnRow by store 2 to lnRow
And replace :
ExcelSheet.Columns("A").Select
ExcelSheet.Selection.value = cursorName.a
ExcelSheet.Columns("B").Select
ExcelSheet.Selection.value = cursorName.b
ExcelSheet.Columns("C").Select
ExcelSheet.Selection.value = cursorName.c
ExcelSheet.Columns("D").Select
ExcelSheet.Selection.value = cursorName.d
By :
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,1).value = cursorName.a
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,2).value = cursorName.b
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,3).value = cursorName.c
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,4).value = cursorName.d
After saving the workbook you need only to quit the Excel as you stated by using ExcelSheet.quit and no need to make the Excel hidden, so no need for ExcelSheet.APPLICATION.visible = .f.
So the complete code will look like:
local lcDir
lcDir = "C:\Documents\PRGT\test.xlsx"
ExcelSheet=createobject("Excel.application")
ExcelSheet.APPLICATION.workbooks.ADD()
ExcelSheet.APPLICATION.activesheet.cells(1,1).value = "info1"
ExcelSheet.APPLICATION.activesheet.cells(1,2).value = "info2"
ExcelSheet.APPLICATION.activesheet.cells(1,3).value = "info3"
ExcelSheet.APPLICATION.activesheet.cells(1,4).value = "info4"
local lnRow
store 2 to lnRow
select cursorName
scan
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,1).value = cursorName.a
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,2).value = cursorName.b
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,3).value = cursorName.c
ExcelSheet.APPLICATION.activesheet.cells(lnRow ,4).value = cursorName.d
lnRow = lnRow+1
endscan
ExcelSheet.APPLICATION.activeworkbook.saveas(lcDir)
ExcelSheet.quit()
release ExcelSheet

SAS - Reading a specific file from a FTP location

I am able to read my files from the FTP location if I specify the exact filename. My problem is that I'm trying to automate this process where I have to read these files week over week and the filename changes randomly. There is no specific pattern to it, so it can't be predetermined.
Is there a way in SAS where I can read the name of all the files present at an FTP location and give the user a dialog box with this information, for them to enter the filename they want to read.
In the SAS Display Manager interface you can use a data step WINDOW or macro %WINDOW statements to define a simple picker, and DISPLAY or %DISPLAY to raise it. The simple picker is really simple, no scrollers or other modern adornments.
An FTP folder listing is retrieved using the filename FTP engine option LS
Sample code:
/**/
* location of FTP folder;
filename folder ftp
user = 'anonymous'
host = 'ftp.cdc.gov'
cd = '/pub/Health_Statistics/NCHS/Publications/ICD9-CM/2011'
ls
;
* retrieve listing;
data files;
infile folder;
input;
order + 1;
fileinfo = _infile_;
run;
/**/
%macro picker(
/* Dynamically build a %WINDOW definition, display it and return the last selected item */
name=, /* Name of window */
title=, /* First line text */
data=, /* data set containing items */
order=order, /* variable for ordering items in the picker*/
item=, /* variable to pick a value of */
result= /* name of macro variable that will contain the picked item, must pre-exist in caller scope */
);
%* field definitions will look like
%* #2 #2 field<i> 1 color=blue attr=rev_video " &filename<i>" ;
%local n i row field_def;
proc sql noprint;
select count(*) into :n trimmed from &data;
%do i = 1 %to &n;
%local field&i item&i;
%end;
select
&order, &item
into
:order1-, :item1-
from
&data;
quit;
%do i = 1 %to &N;
%let field_def = &field_def #%eval(&i+1) #2 field&i 1 color=blue attr=rev_video " &&item&i";
%end;
%WINDOW PICKER rows=30 columns=80
#1 #1 "&title. - Mark an item and Press F3"
&field_def
;
%display PICKER;
%do i = 1 %to &N;
%if %length(&&field&i) %then %let &result=&&item&i;
%end;
%mend;
%let selected=;
%picker(name=PICKER, title=Pick a file, data=files, item=fileinfo, result=selected);
%put &=selected;
More sophisticated pickers can be built using SAS/AF. Other possibilities include Stored Process prompt dialogs, SAS Studio snippets, or a SAS server page.

VB6 MSFlexGrid - Unable to set columns and rows count at runtime

I have a Visual Basic 6 form with a MSFlexGrid control inside, which takes data from a record set(ADODB) and displays them.
Before starting the copy of data to the FlexGrid, I'm trying to set the rows count, depending on records count. Also I have a collection which contains columns' names, then I can get the number of columns from here.
The following is a code snippet:
v_colsCount = UBound(aCols) + 2 // aCols = array with columns' names
v_regCount = rs.RecordCount // rs = my ADODB record set
myFlexGrid.Rows = 0 // for cleaning rows from a previous display
myFlexGrid.Rows = IIf(v_regCount > 0, v_regCount + 1, 2)
myFlexGrid.Cols = v_colsCount
myFlexGrid.FixedRows = 1
myFlexGrid.FixedCols = 0
There are 7532 rows and 52 columns. The problem comes when I run the application and try to execute this part of the code (fill the FlexGrid with data from the record set):
For iRow = 1 To v_regCount
For iCol = 0 To v_colsCount -2
sAux = ConvStr(rs.Fields(aCols(iCol)).Value)
myFlexGrid.TextMatrix(iRow, iCol) = sAux
I notice that
v_regCount = 7532 but v_colsCount = 2 ,
and I get an error ("Substring out of range"). If I swap the settings order (i.e. if I set myFlexGrid.Cols after set myFlexGrid.Rows), then
v_regCount = 0 and v_colsCount = 52
I don't understand why I can't set rows and columns count at the same time.
Any ideas?
Thanks in advance

How to convert visual foxpro 6 report to word

I have used following code to show a report.
select mem_no,com_name,owner,owner_cate,iif(empty(photo),"c:\edrs\memphoto\void.JPG",iif(file(photo),photo,"c:\edrs\memphoto\void.JPG")) as photo from own1 into curs own
REPO FORM c:\edrs\reports\rptsearch.frx TO PRINT PREVIEW NOCONS
Here rptsearch.frx contains some image. The following code export data to excel except image.
COPY TO "c:\documents and settings\administrator\desktop\a.xls" TYPE XLS
In case of image it shows only the path name. Now I need to know how I can convert this report in word so that I can have the images in the word report.
It looks like that you are creating a simple list with pictures. One of the easiest ways to do that is to use automation. ie:
Select mem_no,com_name,owner,owner_cate,;
iif(Empty(photo) Or !File(photo),"c:\edrs\memphoto\void.JPG",photo) As photo ;
from own1 ;
into Curs crsData ;
nofilter
#Define wdWord9TableBehavior 1
#Define wdAutoFitWindow 2
#Define wdStory 6
#Define wdCollapseEnd 0
#Define wdCellAlignVerticalCenter 1
#Define CR Chr(13)
Local Array laCaptions[5]
laCaptions[1] = 'Mem No'
laCaptions[2] = 'Com Name'
laCaptions[3] = 'Owner'
laCaptions[4] = 'Owner Cate'
laCaptions[5] = 'Photo'
Local nRows, nCols, ix
nRows = Reccount('crsData')+1
nCols = Fcount('crsData')+1
oWord = Createobject('Word.Application')
With m.oWord
oDocument = .Documents.Add
With m.oDocument.Tables.Add( m.oWord.Selection.Range, m.nRows, m.nCols)
.BorderS.InsideLineStyle = .F.
.BorderS.OutsideLineStyle = .F.
For ix=1 To Alen(laCaptions)
**** Add captions *****
.Cell(1,m.ix).Range.InsertAfter( laCaptions[m.ix] )
Endfor
Select crsData
Scan
For ix=1 To Fcount()-1 && last one is photo path
**** Add values to the different cells *****
.Cell(Recno()+1,m.ix).Range.InsertAfter( Eval(Field(m.ix)) )
Endfor
lcPhoto = crsData.photo
If File(m.lcPhoto) && Add photo if any
oDocument.InlineShapes.AddPicture( m.lcPhoto, .T., .T.,;
.Cell(Recno()+1,Fcount()).Range)
Endif
.Rows(Recno()+1).Cells.VerticalAlignment = wdCellAlignVerticalCenter
Endscan
Endwith
.Visible = .T.
Endwith
However, sending data to word this way would suffer from performance if you have many rows. You can use this for small data like an employee table or so. With larger data, instead of using automation, you could simply create an HTML document and word would open an HTML document.
There is no native way to do it. I would investigate FoxyPreviewer and use that to report to RTF, which Word can open.
Or do it the other way round with a mail merge in Word.
In addition to FoxyPreviewer, you could also use OLE Office automation to programmatically build the report. There are numerous examples online, and even a book, Microsoft Office Automation, written by Tamar E. Granor & Della Martin.
I have not done a lot with automation, only enough to get it basically verify it works, and discover it was very slow for what I was attempting to do.

How to save ole object (contains jpg,excel, pdf file)to file in powerbuilder

I have saved data in format of blob using powerbuilder ole control in oracle.
Now we want to convert these blob to files,
files are of different format(PDF, JPG,EXCEL,TEXT,DOC)
There are more than 1 Million files so it is not easy to do manually open and save using olecontrol.
Can we do it through script auto saving of blob to file in powerbuilder
Yes, it is possible:
Write a cursor in PowerBuilder embedded SQL to get for each record in your blob table the key and the and file extension (if you have those). The syntax for that kind of thing looks like this:
Long ll_Key
String ls_Ext
DECLARE GetBlobCursor CURSOR FOR
SELECT blob_key,
blob_extension
FROM blob_table ;
/* need to loop here while SQLCA.SQLCode is good */
FETCH GetBlobCursor
INTO :ll_Key,
:ls_Ext ;
Use a SELECTBLOB embedded SQL statement to get the blob data into a PowerBuilder BLOB variable:
Blob lblob_File
SELECTBLOB fileblob
INTO :lblob_File
FROM blobtable
WHERE blob_key = :ll_Key ;
Use FileOpen and FileWrite to write the blob with a valid file name and extension:
Long ll_Loops, ll_Step
Int li_File
String ls_Path
ls_Path = "<where do you want me?>." + String(ll_Key) + "." + ls_Ext
li_File = FileOpen(ls_Path, StreamMode!, Write!, LockWrite!, Append!)
If li_File > 0 Then
// Determine how many times to call FileWrite
ll_FileLen = Len(lblob_File)
If ll_FileLen > 32765 Then
If Mod(ll_FileLen, 32765) = 0 Then
ll_Loops = ll_FileLen/32765
Else
ll_Loops = (ll_FileLen/32765) + 1
End If
Else
ll_Loops = 1
End If
For ll_Step = 1 To ll_Loops
FileWrite(li_File,BlobMid(lblob_File,((ll_Step - 1)*32765) + 1, 32765))
Next
Else
//log the error, or handle
End If
FileClose(li_File)
Hope that gets you started.

Resources