NO

Author Topic: IStream_ReadStr()  (Read 13266 times)

czerny

  • Guest
IStream_ReadStr()
« on: November 12, 2014, 07:10:09 PM »
Hallo,

has anybody a simple example how to use IStream_ReadStr() and  IStream_WriteStr() in C?

czerny

laurro

  • Guest
Re: IStream_ReadStr()
« Reply #1 on: November 12, 2014, 10:27:18 PM »
Code: [Select]

#define UNICODE
#define COBJMACROS
#include <windows.h>
#include <objidl.h>
#include <shlwapi.h>
#include <wchar.h>
#pragma comment(lib,"Shlwapi.lib")

#define IF_FAILED_RETURN(x)  if(FAILED((x)))\
                     return (int)(x)

int main(void)
{
wchar_t *s = NULL;
LARGE_INTEGER a = { 0 };
ULARGE_INTEGER b;
wchar_t string[] = L"test";
HRESULT hr;

IStream *istream = SHCreateMemStream(NULL, 0);
if (!istream)
return 0;

hr = IStream_WriteStr(istream, string);
IF_FAILED_RETURN(hr);

hr = IStream_Seek(istream, a, STREAM_SEEK_SET, &b);
IF_FAILED_RETURN(hr);
hr = IStream_ReadStr(istream, &s);
IF_FAILED_RETURN(hr);
wprintf(L"%ls\n\n", s);

{ //just to see where is the seek pointer
a.QuadPart = 0;
hr = IStream_Seek(istream, a, STREAM_SEEK_CUR, &b);
IF_FAILED_RETURN(hr);
wprintf(L"seek pointer at %llu \n\n", b.QuadPart);
}

IStream_Release(istream);
return 0;
}


but the minimum OS for IStream_WriteStr() is, again, Vista.

Laur

tienkhoanguyen

  • Guest
Re: IStream_ReadStr()
« Reply #2 on: November 13, 2014, 04:46:12 AM »
Hallo,

has anybody a simple example how to use IStream_ReadStr() and  IStream_WriteStr() in C?

czerny

 :)  I'm being serious.  Not joking at all.  I do not know that much about C.  I have to look up a lot of stuffs.  I am into Borland Turbo Assembler 4.1 now a days.  However, for what it is worth - you are pretty.  And if others ask the question, everyone is pretty and handsome.  God made us all gifted one way or another!

czerny

  • Guest
Re: IStream_ReadStr()
« Reply #3 on: November 13, 2014, 09:12:52 AM »
but the minimum OS for IStream_WriteStr() is, again, Vista.
That might be the problem. I tried on XP and got no runtime error about not implemented functions. But my software crashes.

The MS docs are not allways  clear in the minimum OS requirements.

As an example take 'SHCreateStreamOnFile()'. The MS docs say: Minimum supported client
Windows XP [desktop apps only].

But this function is documented in Shlwapi 5.0 and newer, which is available in win98SE and up.

czerny

  • Guest
Re: IStream_ReadStr()
« Reply #4 on: November 13, 2014, 09:16:45 AM »
Laurro: Can you please check, what exactly these functions are working with? BSTR or Zero terminated unicode? A hexdump would be nice.

laurro

  • Guest
Re: IStream_ReadStr()
« Reply #5 on: November 13, 2014, 12:55:05 PM »
The minimum OS for IStream_WriteStr() (vista) not for SHCreateStreamOnFileEx() (xp).

None of the them, the string is stored as a unicode char, but
without the NULL, instead a ENQ (enquiry (5) ) word
it is put in front of string and for example the L"Ana", instead
of 65,0, 110,0, 97,0, 0,0 it is stored  5,0, 65,0, 110,0, 97,0.
I wrote this test program

Code: [Select]
#define UNICODE
#define COBJMACROS
#include <windows.h>
#include <shlwapi.h>
#include <objidl.h>
#include <wchar.h>
//#include <stdio.h>

#pragma comment(lib,"Shlwapi.lib")

void dump_raw_dataW(void *any_kind, unsigned int size, unsigned int n_columns);

int main(void)
{
wchar_t *s = NULL;
LARGE_INTEGER a = { 0 };
ULARGE_INTEGER b;
HRESULT hr;

IStream *istream = NULL;
hr = SHCreateStreamOnFileEx(L"stream", STGM_READWRITE | STGM_FAILIFTHERE, FILE_ATTRIBUTE_NORMAL, FALSE, NULL, &istream);

if (hr) //file(stream) doesn't exist, create one
{
SHCreateStreamOnFileEx(L"stream", STGM_READWRITE | STGM_CREATE, FILE_ATTRIBUTE_NORMAL, FALSE, NULL, &istream);
}
else //use the existing one
{
IStream_Seek(istream, a, STREAM_SEEK_END, &b);
}

IStream_WriteStr(istream, L"test0");
IStream_WriteStr(istream, L"test1");
IStream_WriteStr(istream, L"test2");
IStream_WriteStr(istream, L"test3");
IStream_WriteStr(istream, L"test4");

IStream_Seek(istream, a, STREAM_SEEK_SET, &b);
while (IStream_ReadStr(istream, &s) == S_OK)
wprintf(L"%ls\n", s);

{ //dump the content of stream (a copy) to console
wprintf(L"%\n\n");
unsigned start /*of stream */ ;
unsigned end /*of stream */ ;
IStream_Seek(istream, a, STREAM_SEEK_SET, &b);
start = b.QuadPart;
IStream_Seek(istream, a, STREAM_SEEK_END, &b);
end = b.QuadPart;
size_t size = end - start;
BYTE *content = malloc(size);
if (content)
{
ULONG pcbRead;
IStream_Seek(istream, a, STREAM_SEEK_SET, &b);
if (SUCCEEDED(istream->lpVtbl->Read(istream, content, size, &pcbRead)))
{
dump_raw_dataW(content, size, 12);
wprintf(L"%\n\n");
}
free(content);
}
}

IStream_Release(istream);

return 0;
}

void dump_raw_dataW(void *any_kind, unsigned int size, unsigned int n_columns)
{
if (!any_kind || !size)
return;
unsigned char *p = any_kind, c;
long i, h, t, j, k, i_col, n = size, n_ = n - 1, col = n_columns ? n_columns : 16, stop = 1;
for (i = 0; stop; i += col)
{
wprintf(L" %.8x     ", i);
i_col = i + col;
j = 0;
for (h = i; h < i_col; h++)
{
c = p[h];
wprintf(L"%.2x ", c);
j++;
if (h == n_)
{
stop = 0;
break;
}
}
k = col - j;
if (k != 0 && n > col)
{
for (int m = 0; m < k; m++)
wprintf(L"   ");
}
wprintf(L"    ");
for (t = i; t < i_col; t++)
{
c = p[t];
if (c >= 32 && c < 127)
wprintf(L"%c", c);
else
wprintf(L".");
if (t == n_)
break;
}
wprintf(L"\n");
}
}

but you shouldn't be able to run on your machines so here is
the binary dump after 3 runs.
Code: [Select]
test0
test1
test2
test3
test4
test0
test1
test2
test3
test4
test0
test1
test2
test3
test4
 00000000     05 00 74 00 65 00 73 00 74 00 30 00 05 00 74 00     ..t.e.s.t.0...t.
 00000010     65 00 73 00 74 00 31 00 05 00 74 00 65 00 73 00     e.s.t.1...t.e.s.
 00000020     74 00 32 00 05 00 74 00 65 00 73 00 74 00 33 00     t.2...t.e.s.t.3.
 00000030     05 00 74 00 65 00 73 00 74 00 34 00 05 00 74 00     ..t.e.s.t.4...t.
 00000040     65 00 73 00 74 00 30 00 05 00 74 00 65 00 73 00     e.s.t.0...t.e.s.
 00000050     74 00 31 00 05 00 74 00 65 00 73 00 74 00 32 00     t.1...t.e.s.t.2.
 00000060     05 00 74 00 65 00 73 00 74 00 33 00 05 00 74 00     ..t.e.s.t.3...t.
 00000070     65 00 73 00 74 00 34 00 05 00 74 00 65 00 73 00     e.s.t.4...t.e.s.
 00000080     74 00 30 00 05 00 74 00 65 00 73 00 74 00 31 00     t.0...t.e.s.t.1.
 00000090     05 00 74 00 65 00 73 00 74 00 32 00 05 00 74 00     ..t.e.s.t.2...t.
 000000a0     65 00 73 00 74 00 33 00 05 00 74 00 65 00 73 00     e.s.t.3...t.e.s.
 000000b0     74 00 34 00                                         t.4.

Laur
« Last Edit: November 15, 2014, 07:31:04 PM by laurro »

czerny

  • Guest
Re: IStream_ReadStr()
« Reply #6 on: November 13, 2014, 01:18:57 PM »
Thank you! That help's a lot.
So it's not difficult to make this with
Code: [Select]
stream->lpVtbl->Write(stream, ...).

Offline jj2007

  • Member
  • *
  • Posts: 536
Re: IStream_ReadStr()
« Reply #7 on: November 16, 2014, 06:22:10 PM »
BSTR or Zero terminated unicode?
None of the them, the string is stored as a unicode char, but without the NULL, instead a ENQ (enquiry (5) ) word
it is put in front of string and for example the L"Ana", instead
of 65,0, 110,0, 97,0, 0,0 it is stored  5,0, 65,0, 110,0, 97,0.

That is commonly called a BSTR. The "B" stands for Basic. However, it seems the terminator (two zero bytes) is missing in your example.

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: IStream_ReadStr()
« Reply #8 on: November 16, 2014, 07:57:01 PM »
Per sure internally it should use the BSTR format, but when writing on the stream it should use a slightly different format.
I don't agree with Laurr speculation for some reasons:
  • The ENQ charactere can be legitimately included in a string, using it as a delimiter is too limiting on string contents.
  • To clearly identify a string you have to define its start position in memory and its length
  • With the proposed layout when accessing the last string you cannot know its end because there is no other ENQ to delimit its end (a scan loop can potentially crash the program reading in unexistent memory).
As JJ isaid we have to consider a kind of BSTR (basic or Binary string) encoding.
Considering that the strings used by Laurr are always 5 unicode chars long that 0x0005 we read at beginning of the string can be its length. In this case the encoding can be described by a struct like this:
Code: [Select]
typedef _IOSTREAM_BSTR
{
    USHORT cStrLen;
    WCHAR ucStr[];
} IOSTREAM_BSTR;
The first two bytes (a short integer) are the length of string in wide chars count, followed by the unicode chars string without ending null wide char that have nosense in a stream ;).
This is just a supposition that you can test (I cannot run that code on my XP system).
« Last Edit: November 17, 2014, 09:22:20 AM by frankie »
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

laurro

  • Guest
Re: IStream_ReadStr()
« Reply #9 on: November 16, 2014, 08:29:15 PM »
JJ, msdn say

Quote
A BSTR is a composite data type that consists of a length prefix, a data string, and a terminator

Length prefix  A four-byte integer that contains the number of bytes in the following data string.
                      It appears immediately before the first character of the data string. This value
                      does not include the terminating null character.
Data string    A string of Unicode characters. May contain multiple embedded null characters.
Terminator    Two null characters.

1: it is a two-byte integer not a four-byte (word not dword), and at this point I made a mistake,
sometimes I'm incredible stupid, sorry czerny, I assume a fixed marker, in reality it is length word;

2: yes, it is an array of unicode chars;

3: two null characters? Like 00,00,00,00 ? They never appears in stream, not even one.

I don't consider this will qualify the output to be a single or multiple BSTR, but more a custom
and particular way to store strings, specially designed for IStream_WriteStr/IStream_ReadStr.
I'm not sure if you already run my code, but if so, inside the project folder you will find
a file "stream", open it in any binary editor and convince yourself.

@frankie you are right, except the integer part.

@czerny  :'(

Laur

aardvajk

  • Guest
Re: IStream_ReadStr()
« Reply #10 on: November 16, 2014, 09:03:09 PM »
Now all you need to know is how to free the string returned by ReadStr. Since it doesn't take an allocated buffer as a parameter, the memory from the string has to come from somewhere, and presumably freed back to it somehow.

Offline jj2007

  • Member
  • *
  • Posts: 536
Re: IStream_ReadStr()
« Reply #11 on: November 16, 2014, 09:41:07 PM »
1: it is a two-byte integer not a four-byte (word not dword)

Interesting. So in principle it's a BSTR with word size but no terminator. Looks like a tailored design; but at least we now know what this function expects.

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: IStream_ReadStr()
« Reply #12 on: November 16, 2014, 11:40:47 PM »
Laurro, I'm sorry for the error I wrote 0x0005 as a short, but I declared it as an integer.
I have corrected the typedef now.

@aardvajk: you're right and it will not be so simple.
The only supposition I can make is that it is a standard BSTR, in this case we have to check that the four bytes preceding the string are the string length in bytes. In that case can be used the function SysFreeString to release memory.

Edit: Here I found this:
"On success, the returned string should be freed with CoTaskMemFree."
« Last Edit: November 17, 2014, 12:05:56 AM by frankie »
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

czerny

  • Guest
Re: IStream_ReadStr()
« Reply #13 on: November 17, 2014, 08:28:45 AM »
JJ, msdn say

Quote
A BSTR is a composite data type that consists of a length prefix, a data string, and a terminator

Length prefix  A four-byte integer that contains the number of bytes in the following data string.
                      It appears immediately before the first character of the data string. This value
                      does not include the terminating null character.
Data string    A string of Unicode characters. May contain multiple embedded null characters.
Terminator    Two null characters.

1: it is a two-byte integer not a four-byte (word not dword), and at this point I made a mistake,
sometimes I'm incredible stupid, sorry czerny, I assume a fixed marker, in reality it is length word;
No problem! I had understand it as a 16-bit length.

Offline jj2007

  • Member
  • *
  • Posts: 536
Re: IStream_ReadStr()
« Reply #14 on: November 17, 2014, 01:51:04 PM »
Best description of BSTR found so far is Eric Lippert's blog:

Quote
BSTRs are always allocated and freed with SysAllocString, SysAllocStringLen, SysFreeString and so on. The underlying memory is cached by the operating system and it is a serious, heap-corrupting error to call free or delete on a BSTR. Similarly it is also an error to allocate a buffer with malloc or new and cast it to a BSTR. Internal operating system code makes assumptions about the layout in memory of a BSTR which you should not attempt to simulate.

PWSZs on the other hand can be allocated with any allocator or allocated off the stack.