implement OleLoadPictureFile

Michael Karcher wine at
Mon Nov 17 17:26:42 CST 2008

Am Montag, den 17.11.2008, 13:09 -0800 schrieb Jeremy Drake:
> > Be sure to write your findings like (this is hypothetical, as I did not
> > look at your patches and the OlePicture stuff till now): "Loads an image
> > from a File. This is just like OleLoadPictureStream, but with a file
> > name instead of a stream as source specification" and not like "To
> > implement the function, you must create a stream from the file (use the
> > standard OLE file stream object for that), pass the stream to
> > OleLoadPictureStream and finally release the stream. In the error case
> > you should do foo". The second form *is* just publishing what you saw in
> > disassembly, so everyone who reads this mail carefully is in my oppinion
> > everyone reading it carefully is in the same situation as you and
> > shouldn't implement the function.
> Hmm, looks like I don't really have to.  Your detailed description is
> about 90% accurate :).  But the simple version should read "This is just
> like OleLoadPicture"...  I'll let you extrapolate the change to the
> detailed version.
I just guessed from the text that accompanied your patch and the
function names.

> Note that the IDL defines the VARIANT parameter as "optional".  If the
> filename is not specified, you should pass a NULL stream to
> OleLoadPicture.
How do I know? I think you shouldn't have told these detail. I might
know what happens if I pass in VARIANTs of type VT_ERROR or VT_NULL,
like lplpdispPicture is unchanged, set to NULL or Windows crashes. But
there seems no legal way for me to find out what happens inside
Microsoft's oleaut32.dll. If it were an inter-dll call, relay or snoop
might bring me to the conclusion that you are right, but I just pretend
I never read that. Just write a couple of tests for different variant
types (VT_NULL, VT_ERROR, VT_BSTR). If they crash on Windows, put them
in an if(0) block with an appropriate comment.

With wine, it will crash in olepicture.c:1787 if I pass a NULL stream
into OleLoadPicture. If it crashes in windows too, it does not matter
how we implement the crash in Wine. I don't even think that we have to
crash everything that crashes in Windows. And if it will crash, I might
just as well assume that the VARIANT is a VT_BSTR without any checks, as
no Win32 program is going to call with wrong parameters.

You seem to have looked at the disassembled code too much to see that
"there is more than one way to do it". But exactly this seperates a
clean-room implementation from a rip-off. The clean-room reimplementers
(which could be me) must not be provided with the way how to do it, so
they can find there own way (this is the creative power that makes wine
our own work!) instead of blindly following the way Microsoft decided to

> Also, there is an OleLoadPictureFileEx, which adds the same 3 additional
> paramters as OleLoadPictureEx adds over OleLoadPicture.  You can probably
> guess how that works...
Do a three-way-merge of OleLoadPicture to OleLoadPictureEx and
OleLoadPicture to OleLoadPictureFile and you arrive at
OleLoadPictureFileEx, probably.

> For the non-Ex versions, it may help you to know about the constant
> LP_DEFAULT in olectl.h, which means "do the default thing" to the
> extra params of the Ex version.
MSDN documents LP_DEFAULT only for the palette in OleLoadPictureFileEx,
but follows your points in the documentation for OleLoadPictureEx. It
might have been that the the desired size should be just zero (which
LP_DEFAULT happens to be) to get the default.

> As far as the "standard OLE file stream object", if you know of one please
> let me know.  AFAIK, it is a pretty major oversight in the OLE IStream
> APIs that there is not a function to create an IStream given a file (name
> or handle).
Oops. I just assumed there was one, but you seem to be right. One could
try whether the Windows FileMoniker works on objects that don't
implement IPersistFile but just IPersistStream and thus
IMoniker::BindToObject from the FileMoniker does what we want. But this
is a very dangerous approach, as you can't tell BindToObject the CLSID
to use, but just the IID you want, and the Wine implementation will
happily activate and load *any* file type you pass in, and notice after
loading that the object loaded does not support the IPicture interface.
I don't know what ugly things can happen on IPersistStream::Load given
some CLSIDs no one thinks about in the moment (maybe XML documents that
cause an internet access to fetch the DTD), but I think some Media
Player/Outlook security vulnerabilities we had some years ago are
related to implementing things like this (that was IIRC attachments of
the name ".WAV" that are associated with windows media player; windows
media player checked magic numbers in that file, completely forgetting
about the file a wave file finding "MZ" in the beginning and acting
"appropriately). You will see whether OLELoadPictureFile does that if
you do a relay trace with native oleaut32 and builtin ole32, as the
CreateFileMoniker call would cross DLL boundaries. Please don't tell me
that you know that Microsoft did it another way, even if you probably

> I've always had to roll my own wrapper around the file APIs,
> or use a hack like the one in OleLoadPictureFilePath.
You probably mean OleLoadPictureFile. I am not going to look at your
patch, so I don't know what hack you meant. If the hack was your idea,
feel free to describe it. If the hack is inspired by the disassembly,
please don't tell any Wine developer how that hack worked.

> > Testcases on the other hand are fine. They just describe *what* to do,
> > but not *how*. Especially for the error case testcases might be
> > interesting.
> Quite.  Also in olectl.h are the error codes that these functions could
> return.  Unfortunately, I think it will be necessary to make some sort of
> manual translation between the GetLastError returns from the file APIs and
> these errors.  Without spelling out the mappings from the disassembly, it
> will probably be nearly impossible to get all of the cases exactly right.
That is fair. Even different Windows versions are some times
inconsistent with their error codes. It is more important what
conditions exactly count as errors, and what happens to the data pointed
to by lplpdispPicture parameter (unchanged / set to NULL / set to a
valid object implementing IDispatch and IPicture). Most prominent error
codes like "File not found" and "File is not in any supported format"
should be checked, but that's it. You shouldn't even have told that the
mapping is done directly in OleLoadPictureFile and not some lower layer
invoked by OleLoadPictureFile, except if you are sure from a relay
trace. Of course, if Wine developers find that *somewhere* error codes
seem to be mapped, they are free to do the mapping in

> I think that, as long as you are familiar with the APIs for dealing with
> VARIANTs, this should be a very simple task.
You might want to do the following test, if possible: Put the image into
a file named "33" (without extension or anything) and call
OleLoadPictureFile with a variant containing "33" as string value to
verify that OleLoadPictureFile is able to load images without extension
based on their magic bytes. If that works, retry with a variant
containing the integer "33". If it still works, chances are *VERY* high
that we need VariantChangeType. If it fails, we know that
VariantChangeType is not the way to go.

> I would provide details
> about how to deal with the VARIANT, but it may be misinterpreted as
> knowledge gained from the disassembler.  So I'll just leave you with a
> couple of links to MS docs.
If you tell me whether VariantChangeType should be used here or not, it
*is* knowledge gained from the disassembler, so it is exactly right you
don't tell me that. Write good testcases instead that make me infer how
to implement it. Thanks for the pointers to MSDN. I didn't know about

  Michael Karcher

More information about the wine-devel mailing list