开发者

GdipCreateBitmapFromStream change of contract on Windows 7?

We have a perfectly functional app that just broke on Windows 7 because (the GDI+ primitive) GdipCreateBitmapFromStream refuses JPEG images presented to it (without problem on XP and even Vista).

We don't directly call GDI+, we call the documented ATL CImage class, and it calls that, and gets the error.

It's our own OLE stream implementation.

Has anyone seen a way around this?

Here is a complete test case:

#include <atlbase.h>
#include <atlimage.h>
#include <gdiplus.h>
#include <time.h>
#include <sys/stat.h>

// GSMemoryStream.h : Declaration of the GSMemoryStream

/* No ATL or class factory support is needed here.  You get one of these via "new", with zero reference count...
    Image.Load(IStreamPtr(new GSMemoryStream(ptr, len));
    and the smart pointer will provoke its deletion at the right time....
    */

// GSMemoryStream

class  GSMemoryStream : 
    public IStream
{
    private:
        ULONG m_Length;
        ULONG m_CurPtr;
        PBYTE m_Base;
        int m_rc;
public:

    GSMemoryStream(PBYTE _p, DWORD _len) {
        m_Length = _len;
        m_CurPtr = 0;
        m_Base = _p;
        m_rc = 0;
    }

    GSMemoryStream () {
        m_Length = 0;
        m_CurPtr = 0;
        m_Base = NULL;
        m_rc = 0;
    }

        STDMETHODIMP Read(void *,ULONG,ULONG *);
        STDMETHODIMP Write(const void *,ULONG,ULONG *) {return E_FAIL;}
        STDMETHODIMP Seek(LARGE_INTEGER,DWORD,ULARGE_INTEGER *);
        STDMETHODIMP SetSize(ULARGE_INTEGER) {return E_FAIL;}
        STDMETHODIMP CopyTo(IStream *,ULARGE_INTEGER,ULARGE_INTEGER *,ULARGE_INTEGER *);
        STDMETHODIMP Commit(DWORD) {return S_OK;}
        STDMETHODIMP Revert(void) {return S_OK;}
        STDMETHODIMP LockRegion(ULARGE_INTEGER,ULARGE_INTEGER,DWORD) {return S_OK;}
        STDMETHODIMP UnlockRegion(ULARGE_INTEGER,ULARGE_INTEGER,DWORD) {return S_OK;}
        STDMETHODIMP Stat(STATSTG *,DWORD);
        STDMETHODIMP Clone(IStream ** ) {return E_FAIL;}

        STDMETHODIMP QueryInterface(const IID & iid,void ** d) throw() {
            if (IsEqualGUID(iid, IID_IUnknown) || IsEqualGUID (iid, __uuidof(IStream))) {
                *d = (PVOID)this;
                AddRef();
                return S_OK;
            }
            return E_FAIL;
        }

        ULONG STDMETHODCALLTYPE AddRef(void) throw() {
            m_rc++;
            return S_OK;
        }
        ULONG STDMETHODCALLTYPE Release(void) throw() {
            if (--m_rc == 0)
                delete this; // can never go negative, because the m_rc won't be around any more once it is 0.
            // so it's not even meaningful to test for it and breakpoint or throw.
            return S_OK;
        }
};



// CGSMemoryStream

STDMETHODIMP GSMemoryStream::Read(void * p,ULONG n, ULONG * pNread) {
    ATLTRACE(L"GSMS$Read p %p  bufct %d m_curptr %d\r\n", p, n, m_CurPtr);

    if ((n + m_CurPtr) > m_Length)
        n = m_Length - m_CurPtr;
    memcpy(p, m_Base + m_CurPtr, n);
    if (pNread)
        *pNread = n;
    m_CurPtr += n;
    ATLTRACE(L"GSMS$Read(final) n %d m_CurPtr %d\r\n", n, m_CurPtr);
    return S_OK;
}

STDMETHODIMP GSMemoryStream::Seek(LARGE_INTEGER pos,DWORD type,ULARGE_INTEGER * newpos) {
    LONG lpos = (LONG)pos.LowPart;
    ATLTRACE(L"GSMS$Seek type %d lpos %d m_CurPtr %d\r\n", type, lpos, m_CurPtr);
    switch (type) {
        case STREAM_SEEK_SET:
            if (lpos < 0 || lpos > (LONG) m_Length)
                return E_POINTER;
            m_CurPtr = (ULONG)lpos;
            break;

        case STREAM_SEEK_CUR:
            if (lpos + m_CurPtr < 0 || lpos + m_CurPtr > m_Length)
                return E_POINTER;
            m_CurPtr += lpos;
            break;

        case STREAM_SEEK_END:
            if (lpos > 0)
                lpos = -lpos;
            if (lpos + m_Length < 0)
                return E_POINTER;
            m_CurPtr = m_Length + lpos;
            break;
        default:
            return E_FAIL;

    }
    ATLTRACE(L"GSMS$Seek end m_CurPtr %d\r\n", m_CurPtr);
    if (newpos) {
        newpos->HighPart = 0;
        newpos->LowPart = m_CurPtr;
    }

    return S_OK;
}

STDMETHODIMP GSMemoryStream::CopyTo(IStream * pstm,ULARGE_INTEGER cb,ULARGE_INTEGER * pNread,ULARGE_INTEGER * pNwritten){
    ATLTRACE("GSMS$CopyTo\r\n");
    if (cb.HighPart)
        return E_INVALIDARG;
    ULONG n = cb.LowPart;
    if ((n + m_CurPtr) > m_Length)
        n = m_Length - m_CurPtr;
    ULONG nwritten = 0;
    HRESULT hr = pstm->Write(m_Base+m_CurPtr, n, &nwritten);
    if (nwritten < n)
        nwritten = n;

    if (pNread) {
        pNread->HighPart = 0;
        pNread->LowPart = n;
    }
    if (pNwritten) {
        pNwritten->HighPart = 0;
        pNwritten->LowPart = nwritten;
    }
    m_CurPtr += n;
    return hr;
}

STDMETHODIMP GSMemoryStream::Stat(STATSTG * ps,DWORD krazyflag) {
    ATLTRACE(L"GSMS$Stat kf %d\r\n", krazyflag);
    memset(ps, 0, sizeof(STATSTG));
    ps->type = STGTY_STREAM;
    ps->cbSize.LowPart = m_Length;
    ps->cbSize.HighPart = 0;
#if 0
    ps->mtime = (DWORD)time(NULL);
    ps->ctime = (DWORD)time(NULL);
    ps->atime = (DWORD)time(NULL);
#endif
    return S_OK;
}






int main (int argc, char ** argv) {

    if (argc < 2) {
        fprintf(stderr, "Need image file pathname\n");
        exit(2);
    }
    struct _stat SSTAT;
    const char* fn = argv[1];
    int failed = _stat(fn, &SSTAT);
    if (failed) {
        fprintf(stderr, "Can't open file: %s\n", fn);
        exit(3);
    }
    size_t len = SSTAT.st_size;
    printf ("Len = %d\n", len);
    FILE* f = fopen(fn, "rb");
    unsigned char * buf = new unsigned char [len];
    size_t got = fread (buf, 1, len, f);
    printf ("Got = %d\n", got);
    fclose(f);

    CoInitialize(NULL);
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;

    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    GSMemoryStream G(buf, 开发者_JAVA技巧len);
    CImage cim;
    HRESULT hr = cim.Load(&G);
    printf("HRESULT = 0x%08X\n", hr);
    delete [] buf;
    CoUninitialize();
    return 0;
}


Have you tried using the documented Bitmap object instead of using the undocumented GDI+ entrypoints?


Further research reveals that W7 queries an additional optional interface on the stream, and it's essential to return E_NOINTERFACE instead of E_NOTIMPL for it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜