| xlsgen > overview > Memory generation of Excel files |
There is basically 3 method calls related to working with Excel spreadsheets in memory rather than actual files :
Usually the notion of creating an Excel workbook ends up materializing as a file in the file system. Whether you are creating an Excel workbook from scratch or reuse an existing one, the deliverable of a generator is a file. Depending on your deployment, you may be using the generator under an environment which provides no write access anywhere on the file system. This is a blocking problem unless you target a network fileshare instead of your own file system. Examples of such are constrained ASP/PHP/JSP applications that, for security reasons, are not given any permission to write on the file system.
Well, it's no more a problem. xlsgen has two new API methods that write in a byte array, and it's up to the client application to do whatever it wants with it, including perhaps writing the byte array back in a file!
The nice thing about the byte array used in the API is that it is a growable buffer, so the client application needs not worry about whether the size will fit the content of the workbook. This growable byte array capabilities is provided by the Windows OLE ILockBytes interface.
Note : this method can target Excel97, 2000, XP, 2003 and 2007 versions (with the 2007 version being a drastically different underlying file format).
| VB.NET code |
Imports System.Runtime.InteropServices
Imports System.IO
Imports xlsgen
Public Class Win32
<DllImport("ole32.dll")> _
Public Shared Function CreateILockBytesOnHGlobal(ByVal hGlobal As IntPtr, _
ByVal fDeleteOnRelease As Boolean, _
<Out()> ByRef ppLkbyt As IlockBytes) _
As Integer
End Function
End Class
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown), _
Guid("0000000a-0000-0000-C000-000000000046")> _
Public Interface IlockBytes
Function ReadAt(ByVal ulOffset As Long, ByVal pv As IntPtr, ByVal cv As Integer, <Out()> ByRef pcbRead As Long) As Integer
Function WriteAt(ByVal ulOffset As Long, ByVal pv As IntPtr, ByVal cb As Integer) As UIntPtr
Sub Flush()
Sub SetSize(ByVal cb As Long)
Sub LockRegion(ByVal libOffset As Long, ByVal cb As Long, ByVal dwLockType As Integer)
Sub UnlockRegion(ByVal libOffset As Long, ByVal cb As Long, ByVal dwLockType As Integer)
Sub Stat(ByRef pstatstg As STATSTG, ByVal grfStatFlag As Integer)
End Interface
Module Module1
Sub Main()
Dim engine As New CoXlsEngine
' create a buffer in memory
Dim lockbytes As IlockBytes = Nothing
Dim hr As Integer
hr = Win32.CreateILockBytesOnHGlobal(IntPtr.Zero, True, lockbytes)
' create a simple Excel file in memory
Dim wbk As IXlsWorkbook
wbk = engine.NewInMemory(lockbytes, enumExcelTargetVersion.excelversion_97)
Dim wksht As IXlsWorksheet
wksht = wbk.AddWorksheet("samplesheet")
wksht.Label(1, 2) = "Hello world!"
wbk.Close()
' read the resulting buffer
Dim statstg As New STATSTG
lockbytes.Stat(statstg, 0)
Dim offset As Int64
Dim buf As IntPtr = Marshal.AllocHGlobal(&H2000)
Dim fileBytes As Byte() = New Byte(statstg.cbSize - 1) {}
Dim dwRead As Long
Do While (lockbytes.ReadAt(offset, buf, &H2000, dwRead) = 0)
If dwRead = 0 Then
Exit Do
End If
Marshal.Copy(buf, fileBytes, CInt(offset), CInt(dwRead))
offset = (offset + dwRead)
Loop
' write the buffer in a file
Dim fs As New FileStream("myfile.xls", FileMode.CreateNew)
Dim bw As New BinaryWriter(fs)
bw.Write(fileBytes)
bw.Close()
fs.Close()
Marshal.FreeHGlobal(buf)
End Sub
End Module
|
| VBScript code |
' xlsgen_clientside_read.vbs
' to run this code, double-click on the .vbs file
'
' demonstrates how to update an existing .xls file in memory using VBScript
' for the purpose of the sample, the output buffer is written back to a file
Dim szProcessingFolder
szProcessingFolder = "c:\"
Dim engine
Set engine = CreateObject("ExcelGenerator.ARsTdesign")
' create a memory buffer host
Dim lb
lb = engine.helpers.ILockBytes_New
' open an existing Excel file, and retrieve a workbook
Dim wbk
Set wbk = engine.OpenInMemory(szProcessingFolder & "input.xls", lb, 3)
' create a new worksheet
Dim wksht
Set wksht = wbk.AddWorksheet("sheet1")
' sample code
wksht.Label(9,1) = "hello world!"
' your code begins here
' ...
wbk.Close
' convert the output to a regular memory buffer (byte array)
Dim byteArrayBuffer
byteArrayBuffer = engine.helpers.ILockBytes_Write(lb)
' for the purpose of the sample, write back to a regular file
engine.helpers.WriteFile szProcessingFolder & "myfile.xls", byteArrayBuffer
' free the memory
Set engine = Nothing
|
| C# code |
using System.Runtime.InteropServices;
using System.IO;
using xlsgen;
[DllImport("ole32.dll")]
static extern int CreateILockBytesOnHGlobal(IntPtr hGlobal,
bool fDeleteOnRelease,
out ILockBytes ppLockbytes);
[Guid("0000000a-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ILockBytes
{
int ReadAt([In] UInt64 olOffset, [In] IntPtr pv, [In] uint cb, [Out] out uint pcbRead);
int WriteAt([In] UInt64 ulOffset, [In] IntPtr pv, [In] uint cb, [Out] out uint pcbWritten);
int Flush();
int SetSize([In] UInt64 cb);
int LockRegion([In] UInt64 libOffset, [In] UInt64 cb, [In] int dwLockType);
int UnlockRegion([In] UInt64 libOffset, [In] UInt64 cb, [In] int dwLockType);
int Stat([Out, MarshalAs(UnmanagedType.Struct)] out STATSTG pstatstg, [In] int grfStatFlag);
}
// this samples creates an Excel workbook in memory,
// and then, as an example, writes it back into a file.
xlsgen.CoXlsEngine engine = new xlsgen.CoXlsEngine();
ILockBytes lockbytes = null;
int hr = CreateILockBytesOnHGlobal(IntPtr.Zero, true, out lockbytes);
IXlsWorkbook book = engine.NewInMemory(lockbytes, enumExcelTargetVersion.excelversion_2003);
IXlsWorksheet sheet = book.AddWorksheet("sheet1");
// add some content to our worksheet
for (int curRow = 0; curRow < 8000; curRow++)
{
sheet.set_Label(5 + curRow, 1, "Hello");
sheet.set_Number(5 + curRow, 2, 5 + curRow + (int)(Math.Pow(-1d, (double)curRow)));
}
book.Close();
// how big is the file?
STATSTG statstg = new STATSTG();
lockbytes.Stat(out statstg, 0);
UInt64 offset = 0;
IntPtr buf = Marshal.AllocHGlobal(8192);
uint dwRead;
byte[] fileBytes = new byte[statstg.cbSize];
while (lockbytes.ReadAt(offset, buf, 8192, out dwRead) == 0 && dwRead > 0)
{
Marshal.Copy(buf, fileBytes, (int)offset, (int)dwRead);
offset += dwRead;
}
FileStream fs = new FileStream(@"sample.xls", FileMode.CreateNew);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(fileBytes);
bw.Close();
fs.Close();
Marshal.FreeHGlobal(buf);
System.Runtime.InteropServices.Marshal.ReleaseComObject(engine);
System.Diagnostics.Process.Start( @"sample.xls" );
|
| C++ code |
{
// the following code creates a simple Excel workbook in memory
// with xlsgen, then writes the buffer into a file.
ILockBytes* lockbytes = NULL;
do
{
// make a Windows API call to create a ILockBytes instance
// see : http://msdn.microsoft.com/library/en-us/stg/stg/createilockbytesonhglobal.asp
// the ILockBytes interface is defined here :
// http://msdn.microsoft.com/library/en-us/stg/stg/ilockbytes.asp
HRESULT hr = CreateILockBytesOnHGlobal(NULL, // HGLOBAL
TRUE, // fDeleteOnRelease
&lockbytes);
if (!lockbytes)
break;
//
// Excel workbook creation
//
xlsgen::IXlsEnginePtr engine( __uuidof(xlsgen::CoXlsEngine) );
xlsgen::IXlsWorkbookPtr wbk;
wbk = engine->NewInMemory( lockbytes, xlsgen::excelversion_2003 );
xlsgen::IXlsWorksheetPtr wksht;
wksht = wbk->AddWorksheet( L"Sheet1" );
//
// Worksheet "Sheet1"
//
// a very simple workbook...
wksht->Label[5][1] = L"Hello";
for (long r = 5; r < 100; r++)
for (long c = 2; c < 5; c++)
wksht->Number[r][c] = r * c;
//
// Excel workbook epilogue
//
wbk->Close();
//
// sample usage : write the byte array in a file
//
STATSTG statstg;
::ZeroMemory(&statstg, sizeof(STATSTG));
hr = lockbytes->Stat(&statstg, STATFLAG_DEFAULT);
HANDLE hFile = ::CreateFile("myfile.xls",
GENERIC_WRITE,
0 /*exclusive access*/,
NULL, // default security descriptor
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (!hFile)
break;
BYTE buf[4096];
DWORD dwRead = 0;
ULARGE_INTEGER offset;
offset.QuadPart = 0;
while( SUCCEEDED(lockbytes->ReadAt(offset,buf,4096,&dwRead)) && (dwRead > 0) )
{
offset.QuadPart += dwRead;
LPBYTE lpBuf = buf;
DWORD nCount = dwRead;
long nOffset = 0;
int nbTries = 0;
// write chunk by chunk (let the underlying I/O decide the size of the chunk)
while (nCount > 0 && nbTries < 3)
{
DWORD nWritten = 0;
if ( !::WriteFile(hFile, lpBuf + nOffset, nCount, &nWritten, NULL) )
{
nbTries++; // retry
continue;
}
nCount -= nWritten;
nOffset += nWritten;
}
} // end while
::CloseHandle(hFile);
}
while (0) ;
if (lockbytes)
lockbytes->Release();
lockbytes = NULL;
}
|
| Delphi code |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, xlsgen_TLB, ComOBJ, ComServ, ActiveX;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
engine : IXlsEngine ;
wbk : IXlsWorkbook ;
wksht : IXlsWorksheet ;
output : ILockBytes;
outputFile: file;
statstg : TStatStg;
filelen : Integer;
begin
OleCheck(CoCreateInstance(
Class_CoXlsEngine,
nil,
CLSCTX_ALL,
IXlsEngine,
engine));
ActiveX.CreateILockBytesOnHGlobal(0, True, output);
// we open input file 'input.xls', make changes,
// and make sure the output is a memory buffer
wbk := engine.OpenInMemory('input.xls', output, xlsgen_TLB.excelversion_2007);
wksht := wbk.AddWorksheet('Sheetsss2');
wksht.Label_[2,3] := 'Hello World' ;
wbk.Close();
output.Stat(statstg, ActiveX.STATFLAG_DEFAULT);
filelen := statstg.cbSize;
// the output is a memory buffer,
// we can for instance write it back to a regular file on disk
// ...
engine := Nil ;
end;
end.
|
| Java code |
XlsEngine engine = new XlsEngine("./../../../xlsgen.dll");
XlsWorkbook workbook = null;
int outputLockBytes = engine.NewILockBytes();
try
{
// for the sake of the example, we build a byte[] from the contents of a .XLS file
// but the point is to pass a byte[] to OpenFromMemory()
FileInputStream fis = new FileInputStream("input.xls");
int fileSize = fis.available();
byte[] inputBuffer = new byte[fileSize];
fis.read(inputBuffer);
fis.close();
workbook = engine.OpenFromMemory(inputBuffer, outputLockBytes, xlsgen.excelversion_2003);
}
catch(Exception e)
{
}
try
{
if (workbook != null)
{
// dummy update of the workbook
workbook.getWorksheetByIndex(1).putLabel(2,1, "Inserted comment");
workbook.Close();
}
}
catch(Exception e)
{
}
// obtain the output byte[]
byte[] outputBytes = engine.GetBytesFromILockBytes(outputLockBytes);
// write it back to a file. Again that is only for the sake of the example
try
{
FileOutputStream fos = new FileOutputStream("output.xls");
fos.write(outputBytes);
fos.close();
}
catch(Exception e)
{
}
// do not forget to release the working buffer
engine.ReleaseILockBytes(outputLockBytes);
|
| VB code |
Sub demo()
' this sample code works with buffers to read and update a spreadsheet
' the input parameter is a memory buffer (a byte array), and the output
' parameter is a memory buffer as well (a byte array)
'
' for the sake of filling the input parameter with an actual spreadsheet, we read a file
' but it should be obvious that this scenario would also work if this function
' is being passed a memory buffer in parameter, for instance in a server scenario
' with no access to files.
Dim Line As String * 4096
Dim s As String
' read a spreadsheet in memory
Open "C:\Book1.xls" For Binary Access Read As #1
Do While Not EOF(1)
Get #1, , Line
s = s & Line
Loop
Close #1
Dim buffer_input() As Byte
ReDim buffer_input(Len(s))
For i = 1 To Len(s)
buffer_input(i - 1) = Asc(Mid(s, i, 1))
Next
' the output should be large enough to store the updated spreadsheet
Dim buffer_output(32768) As Byte
' Create an xlsgen engine instance
Dim engine As CoXlsEngine
Set engine = CreateObject("ExcelGenerator.ARsTDesign")
Dim wbk As IXlsWorkbook
Set wbk = engine.OpenFromMemory(buffer_input, buffer_output, enumExcelTargetVersion.excelversion_2000)
Dim wksht As IXlsWorksheet
Set wksht = wbk.WorksheetByIndex(1)
wksht.Label(2, 1) = "Some new label"
wbk.Close
' at this point, the "buffer_output" structure holds the updated spreadsheet
' for the sake of doing something with it, we
' save the updated buffer back to a file
Open "c:\Book1_updated.xls" For Binary As #1
Put #1, 1, buffer_output
Close #1
End Sub
|
| VB.NET code |
Imports System.IO
Imports System.Runtime.InteropServices
Imports xlsgen
Public Class Win32
<DllImport("ole32.dll")> _
Public Shared Function CreateILockBytesOnHGlobal(ByVal hGlobal As IntPtr, _
ByVal fDeleteOnRelease As Boolean, _
<Out()> ByRef ppLkbyt As IlockBytes) _
As Integer
End Function
End Class
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown), _
Guid("0000000a-0000-0000-C000-000000000046")> _
Public Interface IlockBytes
Function ReadAt(ByVal ulOffset As Long, ByVal pv As IntPtr, ByVal cv As Integer, <Out()> ByRef pcbRead As Long) As Integer
Function WriteAt(ByVal ulOffset As Long, ByVal pv As IntPtr, ByVal cb As Integer) As UIntPtr
Sub Flush()
Sub SetSize(ByVal cb As Long)
Sub LockRegion(ByVal libOffset As Long, ByVal cb As Long, ByVal dwLockType As Integer)
Sub UnlockRegion(ByVal libOffset As Long, ByVal cb As Long, ByVal dwLockType As Integer)
Sub Stat(ByRef pstatstg As STATSTG, ByVal grfStatFlag As Integer)
End Interface
Module Module1
Sub Main()
' this sample code demonstrates the use of the OpenFromMemory() method
' where byte arrays are used as input and output parameters.
' For the sake of filling the input parameter with meaningful content, we
' load an existing Excel spreadsheet in memory, but that's just one example
' (accessing the file system defeats the point of using OpenFromMemory() in
' the first place).
' Likewise, after xlsgen is done with the updated spreadsheet, we simply create
' a file from the byte array.
Dim inputFile As String = "c:\Book1.xls"
Dim fileSize As Integer = New FileInfo(inputFile).Length
Dim arrInputBytes(fileSize) As Byte
Dim fs As New FileStream(inputFile, FileMode.Open, FileAccess.Read)
Dim r As New BinaryReader(fs)
r.Read(arrInputBytes, 0, fileSize)
fs.Close()
' create a buffer in memory
Dim lockbytes As IlockBytes = Nothing
Dim hr As Integer
hr = Win32.CreateILockBytesOnHGlobal(IntPtr.Zero, True, lockbytes)
Dim engine As New xlsgen.CoXlsEngine
Dim wbk As IXlsWorkbook = engine.OpenFromMemory(arrInputBytes, lockbytes, xlsgen.enumExcelTargetVersion.excelversion_2000)
wbk.WorksheetByIndex(1).Label(2, 1) = "some new label"
wbk.Close()
' read the resulting buffer
Dim statstg As New STATSTG
lockbytes.Stat(statstg, 0)
Dim offset As Int64
Dim buf As IntPtr = Marshal.AllocHGlobal(&H2000)
Dim fileBytes As Byte() = New Byte(statstg.cbSize - 1) {}
Dim dwRead As Long
Do While (lockbytes.ReadAt(offset, buf, &H2000, dwRead) = 0)
If dwRead = 0 Then
Exit Do
End If
Marshal.Copy(buf, fileBytes, CInt(offset), CInt(dwRead))
offset = (offset + dwRead)
Loop
Dim fs2 As New FileStream("c:\Book1_updated.xls", FileMode.Create, FileAccess.Write)
Dim w As New BinaryWriter(fs2)
w.Write(fileBytes)
w.Close()
fs2.Close()
End Sub
End Module
|
| VBScript code |
' xlsgen_clientside.vbs
' to run this code, double-click on the .vbs file
Dim xlsgen
Set xlsgen = CreateObject("ExcelGenerator.ARsTdesign")
' phase 1 : build a memory buffer from an Excel file
'
Dim inputStreamer
Set inputStreamer = CreateObject("ADODB.Stream")
inputStreamer.Type = 1 'adTypeBinary
inputStreamer.Open
inputStreamer.LoadFromFile "c:\Book1.xls"
Dim inputByteArray
inputByteArray = inputStreamer.Read
inputStreamer.Position = 0
' bogus method call only meant to create a byte array internally
Dim outputByteArray
outputByteArray = inputStreamer.Read
inputStreamer.Close
Set inputStreamer = Nothing
' phase 2 : update the spreadsheet
'
Dim wbk
Set wbk = xlsgen.OpenFromMemory(inputByteArray, outputByteArray, 3)
Dim wksht
Set wksht = wbk.AddWorksheet("new_sheet")
' sample code
wksht.Label(9,1) = "hello world!"
' your code begins here
' ...
wbk.Close
' phase 3 : write back to a file
'
Dim outputStreamer
Set outputStreamer = CreateObject("ADODB.Stream")
outputStreamer.Type = 1 'adTypeBinary
outputStreamer.Open
outputStreamer.Write outputByteArray
outputStreamer.SaveToFile "c:\Book1_updated.xls"
outputStreamer.Close
Set outputStreamer = Nothing
|
| C# code |
using System.IO;
using System.Runtime.InteropServices;
using xlsgen;
class Program
{
// this sample code demonstrates the use of the OpenFromMemory() method
// where byte arrays are used as input and output parameters.
// For the sake of filling the input parameter with meaningful content, we
// load an existing Excel spreadsheet in memory, but that's just one example
// (accessing the file system defeats the point of using OpenFromMemory() in
// the first place).
// Likewise, after xlsgen is done with the updated spreadsheet, we simply create
// a file from the byte array.
[DllImport("ole32.dll")]
static extern int CreateILockBytesOnHGlobal(IntPtr hGlobal,
bool fDeleteOnRelease,
out ILockBytes ppLockbytes);
[Guid("0000000a-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ILockBytes
{
int ReadAt([In] UInt64 olOffset, [In] IntPtr pv, [In] uint cb, [Out] out uint pcbRead);
int WriteAt([In] UInt64 ulOffset, [In] IntPtr pv, [In] uint cb, [Out] out uint pcbWritten);
int Flush();
int SetSize([In] UInt64 cb);
int LockRegion([In] UInt64 libOffset, [In] UInt64 cb, [In] int dwLockType);
int UnlockRegion([In] UInt64 libOffset, [In] UInt64 cb, [In] int dwLockType);
int Stat([Out, MarshalAs(UnmanagedType.Struct)] out STATSTG pstatstg, [In] int grfStatFlag);
}
static void Main(string[] args)
{
String inputFile = @"c:\Book1.xls";
int fileSize = (int) new FileInfo(inputFile).Length;
Byte[] arrInputBytes = new Byte[fileSize];
FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read);
BinaryReader r = new BinaryReader(fs);
r.Read(arrInputBytes, 0, fileSize);
fs.Close();
xlsgen.CoXlsEngine engine = new xlsgen.CoXlsEngine();
ILockBytes lockbytes = null;
int hr = CreateILockBytesOnHGlobal(IntPtr.Zero, true, out lockbytes);
IXlsWorkbook wbk = engine.OpenFromMemory(arrInputBytes, lockbytes, xlsgen.enumExcelTargetVersion.excelversion_2000);
wbk.get_WorksheetByIndex(1).set_Label(2, 1, "some new label");
wbk.Close();
// how big is the memory buffer?
STATSTG statstg = new STATSTG();
lockbytes.Stat(out statstg, 0);
UInt64 offset = 0;
IntPtr buf = Marshal.AllocHGlobal(8192);
uint dwRead;
byte[] fileBytes = new byte[statstg.cbSize];
while (lockbytes.ReadAt(offset, buf, 8192, out dwRead) == 0 && dwRead > 0)
{
Marshal.Copy(buf, fileBytes, (int)offset, (int)dwRead);
offset += dwRead;
}
FileStream fs2 = new FileStream(@"c:\Book1_updated.xls", FileMode.Create, FileAccess.Write);
BinaryWriter w = new BinaryWriter(fs2);
w.Write(fileBytes);
w.Close();
fs2.Close();
Marshal.FreeHGlobal(buf);
System.Runtime.InteropServices.Marshal.ReleaseComObject(engine);
}
}
|
| C++ code |
xlsgen::IXlsEnginePtr engine( __uuidof(xlsgen::CoXlsEngine) );
HANDLE hFile = ::CreateFile("C:\\Book1.xls",
FILE_READ_DATA,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if ( hFile )
{
DWORD len = ::GetFileSize( hFile, NULL); // only 32-bit of the actual file size is retained
if (len != 0)
{
HGLOBAL hGlobal = ::GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, len);
if ( !hGlobal )
{
::CloseHandle(hFile);
}
else
{
char* lpBuffer = reinterpret_cast<char*> ( ::GlobalLock(hGlobal) );
DWORD dwBytesRead = 0;
char* p = lpBuffer;
while ( ::ReadFile(hFile, p, len, &dwBytesRead, NULL) )
{
p += dwBytesRead;
if (dwBytesRead == 0)
break;
dwBytesRead = 0;
}
SAFEARRAY* pSA = ::SafeArrayCreateVector(VT_UI1, 0, len);
BYTE HUGEP *plong = NULL;
::SafeArrayAccessData(pSA, (void HUGEP**)&plong);
if (plong)
{
memcpy(plong, lpBuffer, len);
::SafeArrayUnaccessData(pSA);
VARIANT vinput;
vinput.vt = VT_ARRAY | VT_UI1;
vinput.parray = pSA;
_variant_t v(vinput);
{
ILockBytes* lockbytes = NULL;
HRESULT hr = CreateILockBytesOnHGlobal(NULL, // HGLOBAL
TRUE, // fDeleteOnRelease
&lockbytes);
_variant_t voutput;
voutput.vt = VT_UNKNOWN;
voutput.punkVal = lockbytes;
xlsgen::IXlsWorkbookPtr wbk;
wbk = engine->OpenFromMemory(v, voutput, xlsgen::excelversion_2000);
wbk->Close();
HANDLE hFile = ::CreateFile("C:\\Book1_updated.xls",
GENERIC_WRITE, 0,
NULL, // default security descriptor
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, NULL);
if (!hFile)
{
}
else
{
BYTE buf[4096 + 1];
DWORD dwRead = 0;
ULARGE_INTEGER offset;
offset.QuadPart = 0;
while( SUCCEEDED(lockbytes->ReadAt(offset,buf,4096,&dwRead)) && (dwRead > 0) )
{
offset.QuadPart += dwRead;
LPBYTE lpBuf = buf;
DWORD nCount = dwRead;
long nOffset = 0;
int nbTries = 0;
// write chunk by chunk (let the underlying I/O decide the size of the chunk)
while (nCount > 0 && nbTries < 3)
{
DWORD nWritten = 0;
if ( !::WriteFile(hFile2, lpBuf + nOffset, nCount, &nWritten, NULL) )
{
nbTries++; // retry
continue;
}
nCount -= nWritten;
nOffset += nWritten;
}
} // end while
::CloseHandle(hFile);
}
}
SafeArrayDestroy(pSA);
}
::GlobalUnlock(hGlobal);
}
::CloseHandle(hFile);
}
}
|
xlsgen documentation. © ARsT Design all rights reserved.