Pelles C forum

C language => Windows questions => Topic started by: czerny on November 12, 2014, 07:10:09 PM

Title: IStream_ReadStr()
Post by: czerny 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
Title: Re: IStream_ReadStr()
Post by: laurro 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
Title: Re: IStream_ReadStr()
Post by: tienkhoanguyen 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!
Title: Re: IStream_ReadStr()
Post by: czerny 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.
Title: Re: IStream_ReadStr()
Post by: czerny 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.
Title: Re: IStream_ReadStr()
Post by: laurro 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
Title: Re: IStream_ReadStr()
Post by: czerny 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, ...).
Title: Re: IStream_ReadStr()
Post by: jj2007 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 (http://msdn.microsoft.com/en-us/library/windows/desktop/ms221069%28v=vs.85%29.aspx). The "B" stands for Basic. However, it seems the terminator (two zero bytes) is missing in your example.
Title: Re: IStream_ReadStr()
Post by: frankie 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:
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).
Title: Re: IStream_ReadStr()
Post by: laurro 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
Title: Re: IStream_ReadStr()
Post by: aardvajk 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.
Title: Re: IStream_ReadStr()
Post by: jj2007 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.
Title: Re: IStream_ReadStr()
Post by: frankie 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 (http://msdn.microsoft.com/en-us/library/windows/desktop/bb773802(v=vs.85).aspx) I found this:
"On success, the returned string should be freed with CoTaskMemFree."
Title: Re: IStream_ReadStr()
Post by: czerny 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.
Title: Re: IStream_ReadStr()
Post by: jj2007 on November 17, 2014, 01:51:04 PM
Best description of BSTR found so far is Eric Lippert's blog (http://ericlippert.com/2003/09/12/bstr-semantics/):

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.
Title: Re: IStream_ReadStr()
Post by: czerny on November 17, 2014, 03:04:55 PM
Does anybody know an example where IStream_WriteStr or IStream_ReadStr is used by the OS?
Title: Re: IStream_ReadStr()
Post by: jj2007 on November 17, 2014, 04:34:03 PM
You mean "an example where IStream_WriteStr or IStream_ReadStr is used by the OS?"

Google IStream_ReadStr include windows.h - the top hit tells you how popular this function is in the coding world ;D
Title: Re: IStream_ReadStr()
Post by: czerny on November 18, 2014, 01:31:45 PM
You mean "an example where IStream_WriteStr or IStream_ReadStr is used by the OS?"
No, I mean by the OS. Most interfaces and apis are used by the OS himself in any way.
Title: Re: IStream_ReadStr()
Post by: jj2007 on November 19, 2014, 03:10:39 AM
You mean "an example where IStream_WriteStr or IStream_ReadStr is used by the OS?"
No, I mean by the OS. Most interfaces and apis are used by the OS himself in any way.

Of course, but for CreateWindowEx etc you'll find a Million hits, while the top hit for IStream_ReadStr in connection with "windows.h" points straight to your post. Which implies that absolutely nobody uses it, unless of course Microsoft developers are secretly using it ;-)
Title: Re: IStream_ReadStr()
Post by: aardvajk on November 19, 2014, 04:13:48 AM
Here's where you can look. This is all the places ReadStr has been used since being invented, the WriteStr list is pretty much the same.
Code: [Select]
Module Os
ieframe.dll 8 SP0
ieframe.dll 8.1
mssrch.dll 8.1
searchfolder.dll 7 SP0
searchfolder.dll 7 SP1
searchfolder.dll 8 SP0
searchfolder.dll 8.1
searchprotocolhost.exe 8.1
shell32.dll 7 SP0
shell32.dll 7 SP1
shell32.dll 8 SP0
shell32.dll 8.1
shell32.dll Server2008 SP1
shell32.dll Server2008 SP2
shell32.dll Vista SP0
shell32.dll Vista SP1
shell32.dll Vista SP2
twinui.dll 8 SP0
twinui.dll 8.1
urlmon.dll 8 SP0
urlmon.dll 8.1
wsshared.dll 8 SP0
wsshared.dll 8.1
Edit: Here (http://msdn.microsoft.com/en-us/library/windows/desktop/bb773802(v=vs.85).aspx) I found this:
"On success, the returned string should be freed with CoTaskMemFree."
Mystery solved
Title: Re: IStream_ReadStr()
Post by: jj2007 on November 20, 2014, 07:14:27 PM
Just for curiosity: How did you produce that list? I can't find IStream_ReadStr in my Win7-64 Shell32.dll, just ReadStrAlloc...
Title: Re: IStream_ReadStr()
Post by: TimoVJL on November 21, 2014, 12:05:49 PM
I can't find IStream_ReadStr in my Win7-64 Shell32.dll, just ReadStrAlloc...
Check Shlwapi.dll
Title: Re: IStream_ReadStr()
Post by: jj2007 on November 21, 2014, 02:35:05 PM
I can't find IStream_ReadStr in my Win7-64 Shell32.dll, just ReadStrAlloc...
Check Shlwapi.dll

Thanks. I got it in shlwapi and urlmon but not in the others listed above.
Title: Re: IStream_ReadStr()
Post by: TimoVJL on November 21, 2014, 04:01:38 PM
Shell32.dll imports IStream_ReadStr from Shlwapi.dll by ordinal 0254h ?
Title: Re: IStream_ReadStr()
Post by: aardvajk on November 21, 2014, 09:18:39 PM
czerny asked for examples so I presume (s)he meant where it's used by the OS. All those modules listed import it in some fashion. Whereabouts in those modules I don't know, but with something like the free version of IDA it's shouldn't be hard to find.

As for how, it's not too hard to scan the imports and exports of an OS's dll's though you have to take time to install them all or at least expand them from the ISO/WIM. As I found out when I was halfway done, someone's already done it (http://blog.airesoft.co.uk/windows-platform-changes/). I used the WhoImportsFunctionAllOS query within that.