http://bugs.winehq.org/show_bug.cgi?id=16187
Anastasius Focht focht@gmx.net changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |focht@gmx.net
--- Comment #2 from Anastasius Focht focht@gmx.net 2008-12-19 18:36:53 --- Hello,
gdiplus bug: unsupported image stream codec requested.
--- snip --- ... 0032:Call ole32.CreateStreamOnHGlobal(00000000,00000001,0032d580) ret=00528955 ... 0032:Ret ole32.CreateStreamOnHGlobal() retval=00000000 ret=00528955 0032:Call gdiplus.GdipGetImageEncodersSize(0032d564,0032d568) ret=0052888f 0032:trace:gdiplus:GdipGetImageEncodersSize 0x32d564 0x32d568 0032:Ret gdiplus.GdipGetImageEncodersSize() retval=00000000 ret=0052888f ... 0032:Call gdiplus.GdipGetImageEncoders(00000001,0000004c,01014698) ret=005288af 0032:trace:gdiplus:GdipGetImageEncoders 1 76 0x1014698 0032:Ret gdiplus.GdipGetImageEncoders() retval=00000000 ret=005288af ... 0032:Call gdiplus.GdipSaveImageToStream(068da908,06af41a0,0032d584,00000000) ret=00528981 0032:trace:gdiplus:GdipSaveImageToStream 0x68da908 0x6af41a0 0x32d584 (nil) 0032:Ret gdiplus.GdipSaveImageToStream() retval=0000000d ret=00528981 ... 0032:Ret KERNEL32.SetLastError() retval=00000578 ret=79e74af7 0032:CALL gdiplus.GdipLoadImageFromStream(0568d0d0,0032d580) ret=0147a1d4 0032:RET gdiplus.GdipLoadImageFromStream() retval=00000002 ret=0147a1d4 --- snip ---
A default (invalid, see later comment) CLSID is passed which leads to:
GdipSaveImageToStream -> UnknownImageFormat
Due to unhandled image format, the stream parameter will be NULL which leads to:
GdipLoadImageFromStream -> InvalidParameter
and managed exception which goes by unhandled. The app's own crash hander finally catches it, hence the error dialog.
---
The real problem is actually not visible with trace, one has to debug through.
What really happens is that the app tries to resolve gdiplus image codec for specific MIME formats by looping through the list of encoders returned by GdipGetImageEncoders(). It compares the MIME type of each returned encoder entry to the given MIME. On match the class identifier of that encoder is used. The default (invalid) CLSID gets overwritten with the one found by successful lookup.
The MIME type for requested codec "bad guy" is actually "image/tiff". Wine's gdiplus misses encoders/decoders for various famous image formats. Maybe additional image formats support can be added by the use of libtiff (and libjpeg for jpeg stuff, ...).
FYI it also suffers from bug 13462
=================
--- snip --- Installing gdiplus seems to make things worse; even after uninstalling it, layout crashes much sooner. --- snip ---
A bit lengthy story which might deserve its own bugzilla bug but I will explain here ...
This app early binds to gdiplus.dll (using dllimports) while parts the .NET runtime also bind to gdiplus (manifest+activation context mechanism).
When you do the usual native gdiplus override (using winetricks gdiplus) you install/copy gdiplus.dll into system32 folder. With the installation of the .NET Framework another copy will be installed in private load path: C:\windows\Microsoft.NET\Framework\v2.0.50727\gdiplus.dll (due to installer winver w2k hack -> missing junction API).
When the app load time reference to gdiplus is resolved (explicit dll import), gdiplus is loaded from system32. Later when parts of the .NET runtime are initialized, another copy will be loaded by .NET from private load path "C:\windows\Microsoft.NET\Framework\v2.0.50727". This happens because .NET initially tries to load the SxS version using manifest/activation context and this fails because SxS version not present.
As fallback method, gdiplus library is directly loaded with private load path, resulting in another copy loaded with different base address. This fallback to private load path exists to prevent dll pollution from unknown/unwanted library version in system32. Result: two copies exist in memory with different base addresses.
Gdiplus API relies on Gdiplusstartup()/shutdown() being called, otherwise most of its API won't work and you see all sorts of side-effects/crashes. Try to call native gdiplus API before Gdiplusstartup() and you will see...
Now consider the following:
--- Scenario 1 (one gdiplus dll from SxS present in memory because of manifest/activation context/SxS):
When the app tries to use gdiplus API for the first time, .NET already initialized gdiplus (for itself) hence the app can successfully use the API too. Because of late .NET unload mechanism, the bug (scenario 3) goes unnoticed.
--- Scenario 2 (two gdiplus dlls present in memory, wine builtin + .NET native from private load path):
Because Wine doesn't employ gdiplus object tracking/reference counting (see comments in gdiplusstartup/shutdown) it doesn't impose restrictions when API can be called (like allocation, ..). The dll loaded by app will be used by app code while .NET will use the dll from private load path and call API on it. -> not really good but acceptable for now
--- Scenario 3 (two gdiplus dlls present in memory, wine native override from system32 + .NET native from private load path):
Both libraries are present in memory at different base addresses. The app will call API on the gdiplus loaded by itself and goes *boom* because it didn't explicitly call gdiplusstartup. .NET runtime will initialize its own loaded version and call API on it.
In short: Technically it's an g00gle application bug which is hidden in Windows due to scenario 1 but exposed in Wine due to scenario 3.
Regards