Monday, November 19, 2012

Creating an IStream from a resource

In Win32, suppose you have an application resource (added with the resource compiler) and you want to open an OLE IStream interface on it. One reason might be to load an image with GDI+.
I initially made my own IStream implementation to provide read-only access to the resource data but I eventually decided to do the following, which is less code but does make a copy of the resource. The API is 'inspired by' the Shell's CreateStreamOnHGlobal function.

HRESULT CreateStreamOnResource(LPCTSTR name, LPSTREAM* stream) {
 HINSTANCE hInst = GetModuleHandle(0);
 return CreateStreamOnResource(hInst, name, stream);
}

HRESULT CreateStreamOnResource(HINSTANCE hInst, LPCTSTR name, LPSTREAM* stream)
{
 assert(hInst != 0);
 assert(name != 0);
 assert(stream != 0);
 *stream = NULL;
 HRSRC hC = FindResource(hInst, name, RT_RCDATA);
 if (hC) {
  // This is not really a HGLOBAL http://msdn.microsoft.com/en-us/library/windows/desktop/ms648046(v=vs.85).aspx
  HGLOBAL hG = LoadResource(hInst, hC);
  if (hG) {
   void* bytes = LockResource(hG);
   ULONG size = SizeofResource(hInst, hC);
   // Create a new empty stream.
   HRESULT hr = CreateStreamOnHGlobal(NULL, TRUE, stream);
   if (SUCCEEDED(hr)) {
    ULONG written;
    // Copy the resource into it.
    hr = (*stream)->Write(bytes, size, &written);
   }
   return hr;
  }
 }
 return E_INVALIDARG;
}
It should have been even easier - just get an HGLOBAL with LoadResource and pass it to CreateStreamOnHGlobal but LoadResource does not really return a handle to global memory so I needed to make the additional steps of creating a new stream, locking the resource and copying its data. I think I remember in the Win16 days you really did have to worry about loading and unloading resources from the segmented .exe but these days I think it all just gets mapped anyway.

3 comments:

  1. Thank you Peter for this post.

    I've implemented this technique in my Golang program:

    https://github.com/mysteriumnetwork/myst-launcher/blob/1.0.18/widget/impl/stateview.go#L87

    It will probably be useful to some developers who use Golang.

    ReplyDelete
  2. Anton - thanks for letting me know - I'd forgotten all about this as it's been nearly ten years. Glad it was useful to someone and good luck with your mysterium project.

    ReplyDelete
  3. These days (ok, anything later than Windows Vista!) you could use SHCreateMemStream
    https://learn.microsoft.com/en-us/windows/win32/api/shlwapi/nf-shlwapi-shcreatememstream

    ReplyDelete