Archive > Discussion - Legacy Software (PBWIN 9.0+/PBCC 5.0+)

Possible Istorage.Read Error

(1/5) > >>

David Maruca:
I've been beating my head over this one and would appreciate any help.

What is happening: I'm making successive calls to Istream.Read to parse an Excel file, and everything is running perfect up to one point. Once that point is reached, the next call to Read reads in stuff from who knows where!

I've edited just about everything out except what is needed for debugging, so this will be easy to see if you take the time to load it.

1) Save the bas and xls files to the same directory.
2) Load the bas file and set a breakpoint on line 66. (ghettoDebugAssert = ghettoDebugAssert)
3) Run the program in debug mode until the first break at position &H8B0. This is the last correct read incase you want to step through it. It will read a number format into an array.
4) The next break is at position &H8CE which is directly after the number format in 3. I've placed a zero seek just beforehand so you can verify that qNewPos is reading the correct position in the file.

The very next read into the Identifier type should read in 2 words which are 00E0 and 0014 once you take into account endians, but it reads in 041E and 0018 instead. I've attached the binary Workbook file if you want to fire it up in a hex editor to follow along. I've searched the entire file for this 1E041800 sequence but it does not exist. Can you account for the sudden garbage read? FYI I have manually checked the cbRead and hr result on every read call and they all return expected values so it can't be that.

I guess my next step will be to load this up in olly to see what is going on. Any help you can provide would be a lifesaver.

Frederick J. Harris:
I havn't looked at your program or data David, and I don't know if this comment will help, but about two weeks ago I was working with IStorage and IStream interfaces a good bit, and I discovered somewhat to my dismay that while the analogy to directories and files is pretty close, its not exact.  For example, there is no counterpart that I know of to the Win Api GetFileSize() function.  When an IStream is created its allocated in a 'block' type manner, say for example 2048 bytes.  If you Write, say, 256 bytes to it, there is no way to recall that number.  If you read past the 256 bytes you wrote into the 2048 byte buffer, you'll get S_OK return values, even though you've read beyond what you wrote.  So I was wondering if your bizarre read was reading some control code of COM marking the end of a IStream/IStorage block?

In terms of the problem I was having with not being able to obtain the count of bytes written into an IStream, I decided to open a seperate IStream within my IStorage to record the count of bytes written into other IStreams within the IStorage.  That worked out quite well. 

To be perfectly honest, I didn't know there was any published info on the internal structure of *.xls files.  I know there was at one time, but I thought it was all to be done now through Excel interfaces.

José Roca:

--- Quote ---For example, there is no counterpart that I know of to the Win Api GetFileSize() function.  When an IStream is created its allocated in a 'block' type manner, say for example 2048 bytes.  If you Write, say, 256 bytes to it, there is no way to recall that number.

--- End quote ---

You can use the Stat method of the IStream interface, that fills a STATSTG structure with diverse information. One of the members of this structure, cbSize, is a QUAD that specifies the size, in bytes, of the stream or byte array.

Frederick J. Harris:
That was my first thought too Jose, but it doesn't seem to work.  The number that gets returned is the block size of the IStream object, whose rough counterpart would be a sector on a file system I guess.  In fact, I asked the question over on the Code Project Forum, and the reply there I got was that it doesn't seem to be possible to determine the size written into a IStream after it is closed.  The individual there told me he checked one of his company's applications that uses IStreams, and they were storing the length written into the first four bytes.  So that's how I came up with my idea of just creating a seperate IStream within the IStorage to persist metadata, as it were, relating to other IStreams within the IStorage.  By the way, I downloaded and examined your IStorage/IStream examples and was really happy to see you had implemented those Structured Storage Interfaces in your Includes.  I had been working on this in C++ but when I did a PowerBASIC translation it was exactly the same.  I know everyone is always thanking you for doing all those wonderful translations you do, and sometimes I don't because so many other folks are always thanking you, but I too appreciate them.

If you want I can easily post an example demonstrating the problem I described above.  Maybe there is a way to do it but I sank about a day into it and finally did what I did and it worked out OK.

José Roca:
That sounds strange. There is an API function available in Shlwapi.inc, but is a wrapper that calls the Stat method and returns the cbSize value:


--- Code: ---DECLARE FUNCTION IStream_Size IMPORT "SHLWAPI.DLL" ALIAS "IStream_Size" ( _
   BYVAL pstm AS IStream _                              ' __in IStream *pstm
 , BYREF pui AS QUAD _                                  ' __out ULARGE_INTEGER *pui
 ) AS LONG                                              ' HRESULT

--- End code ---

If it also fails, then a possible workaround can be to call the Read method of the IStream interface with a buffer greater than the possible length of the stream (with a maximum of 2147483647& characters because PB quads are signed) and then the size will be the number of characters read returned in the pcbRead parameter.

I have a wrapper function in Ole2Utils.inc that has worked in my tests:


--- Code: ---' ========================================================================================
' Reads numchars at the current Seek position. If numchars = 0 or -1, it returns all the
' content of the stream.
' ========================================================================================
FUNCTION IStream_ReadText (BYVAL pIStream AS IStream, BYVAL numchars AS LONG) AS STRING

   LOCAL hr AS LONG
   LOCAL plibNewPosition AS QUAD
   LOCAL pcbRead AS DWORD
   LOCAL tstatstg AS STATSTG
   LOCAL strText AS STRING

   IF ISNOTHING(pIStream) THEN EXIT FUNCTION
   IF numchars < -1 THEN EXIT FUNCTION
   IF numchars = 0 THEN numchars = -1

   IF numchars = -1 THEN
      hr = pIStream.Seek(0, %STREAM_SEEK_SET, plibNewPosition)
      IF hr <> %S_OK THEN EXIT FUNCTION
      hr = pIStream.Stat(tstatstg, %STATFLAG_NONAME)
      IF hr <> %S_OK THEN EXIT FUNCTION
      IF tstatstg.cbsize < 1 THEN EXIT FUNCTION
      IF tstatstg.cbsize <= 2147483647& THEN
         numchars = tstatstg.cbsize
      ELSE
         numchars = 2147483647&
      END IF
   END IF

   strText = SPACE$(numchars)
   hr = pIStream.Read(STRPTR(strText), LEN(strText), pcbRead)
   IF hr <> %S_OK THEN EXIT FUNCTION
   IF pcbRead < numchars THEN strText = LEFT$(strText, pcbRead)
   FUNCTION = strText

END FUNCTION
' ========================================================================================

--- End code ---

Navigation

[0] Message Index

[#] Next page

Go to full version