0

So I try to load an image resource from a dll file. To do this I created the method

static Bitmap GetImageResource(IntPtr handle, string resourceId)
{
    IntPtr img_ptr = NativeMethods.LoadImage(handle, "#" + resourceId, IMAGE_ICON, 16, 16, 0);

    if (img_ptr == IntPtr.Zero)
        throw new System.ComponentModel.Win32Exception((int)NativeMethods.GetLastError());

    return Image.FromHbitmap(img_ptr);
}

Which loads the image resource from the dll, given a handle and the resource id. According to this question I asked yesterday I have to prepend a # to the id, which I did. Now the handle returned by LoadImage is not zero anymore, however when I try to create Bitmap image from this handle using Image.FromHbitmap I get a System.Runtime.InteropServices.ExternalException saying

A generic error occurred in GDI+

(or something similar, I dont get the message in english so I roughly translated it)

I already read this and this question but they didn't help me.

Why is this? Thanks in advance

Community
  • 1
  • 1
Tom Doodler
  • 1,471
  • 2
  • 15
  • 41
  • That is entirely normal, you are loading an icon, not an image. Use Icon.FromHandle() instead. You'll have to destroy the icon again with DestroyIcon, later, after you are sure the Icon object cannot be used again. – Hans Passant Sep 16 '16 at 13:31

1 Answers1

0

If the Dll is a .NET assembly, you can call Assembly.GetManifestResourceStream, like so:

public static Bitmap getBitmapFromAssemblyPath(string assemblyPath, string resourceId) {
    Assembly assemb = Assembly.LoadFrom(assemblyPath);
    Stream stream = assemb.GetManifestResourceStream(resourceId);
    Bitmap bmp = new Bitmap(stream);
    return bmp;
}

If it is a native dll (not an assembly), you will have to use Interop instead. You have a solution here, and could be summarized as follows:

[DllImport("kernel32.dll", SetLastError = true)] 
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);

[DllImport("kernel32.dll")] 
static extern IntPtr FindResource(IntPtr hModule, int lpID, string lpType); 

[DllImport("kernel32.dll", SetLastError = true)] 
static extern IntPtr LoadResource(IntPtr hModule, IntPtr hResInfo); 

[DllImport("kernel32.dll", SetLastError = true)] 
static extern uint SizeofResource(IntPtr hModule, IntPtr hResInfo);

[DllImport("kernel32.dll", SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool FreeLibrary(IntPtr hModule);

static const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002;

public static Bitmap getImageFromNativeDll(string dllPath, string resourceName, int resourceId) {
    Bitmap bmp = null;
    IntPtr dllHandle = LoadLibraryEx(dllPath, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE);
    IntPtr resourceHandle = FindResource(dllHandle, resourceId, resourceName);
    uint resourceSize = SizeofResource(dllHandle, resourceHandle); 
    IntPtr resourceUnmanagedBytesPtr = LoadResource(dllHandle, resourceHandle);

    byte[] resourceManagedBytes = new byte[resourceSize]; 
    Marshal.Copy(resourceUnmanagedBytesPtr, resourceManagedBytes, 0, (int)resourceSize); 
    using (MemoryStream m = new MemoryStream(resourceManagedBytes)) {
        bmp = (Bitmap)Bitmap.FromStream(m);
    }

    FreeLibrary(dllHandle);

    return bmp;
}

No error handling was added, this is not production-ready code.

NOTE: Should you need an Icon, you can use the Icon constructor that receives a stream:

    using (MemoryStream m = new MemoryStream(resourceManagedBytes)) {
        bmp = (Icon)new Icon(m);
    }

And you should change the return type accordingly.

Augusto Ruiz
  • 136
  • 6