通过RDP调用Bitmap.FromHbitmap时出错

时间:2014-06-10 10:07:18

标签: c# .net winapi rdp

我们的应用程序执行一些游标操作以在WinForms上启用“相对”漂亮的拖放动画(当时WPF不是一个选项)。但是,当在RDP会话中使用应用程序时,它会抛出一个通用的GDI +异常。

抛出这个的方法是:

[DllImport("user32")]
private static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO pIconInfo);

[DllImport("user32.dll")]
private static extern IntPtr LoadCursorFromFile(string lpFileName);

[DllImport("user32.dll", SetLastError = true)]
public static extern bool DestroyIcon(IntPtr hIcon);

[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);

public static Bitmap BitmapFromCursor(Cursor cur)
{
    ICONINFO iInfo;
    GetIconInfo(cur.Handle, out iInfo);

    Bitmap bmp = Bitmap.FromHbitmap(iInfo.hbmColor);
    DeleteObject(iInfo.hbmColor);
    DeleteObject(iInfo.hbmMask);

    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
    Bitmap dstBitmap = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format32bppArgb, bmData.Scan0);
    bmp.UnlockBits(bmData);

    return new Bitmap(dstBitmap);
}

特别是这一行:

Bitmap bmp = Bitmap.FromHbitmap(iInfo.hbmColor);

当调试hbmColor0时,这意味着在通过RDP运行时,对GetIconInfo的调用不会返回所需信息。

我可以查看0并处理特殊情况,但是我能做些什么来使其在RDP上工作正常吗?

修改

这是ICONINFO结构:

[StructLayout(LayoutKind.Sequential)]
struct ICONINFO
{
     public bool fIcon;         // Specifies whether this structure defines an icon or a cursor. A value of TRUE specifies
                    // an icon; FALSE specifies a cursor.
     public Int32 xHotspot;     // Specifies the x-coordinate of a cursor's hot spot. If this structure defines an icon, the hot
                    // spot is always in the center of the icon, and this member is ignored.
     public Int32 yHotspot;     // Specifies the y-coordinate of the cursor's hot spot. If this structure defines an icon, the hot
                    // spot is always in the center of the icon, and this member is ignored.
     public IntPtr hbmMask;     // (HBITMAP) Specifies the icon bitmask bitmap. If this structure defines a black and white icon,
                    // this bitmask is formatted so that the upper half is the icon AND bitmask and the lower half is
                    // the icon XOR bitmask. Under this condition, the height should be an even multiple of two. If
                    // this structure defines a color icon, this mask only defines the AND bitmask of the icon.
     public IntPtr hbmColor;    // (HBITMAP) Handle to the icon color bitmap. This member can be optional if this
                    // structure defines a black and white icon. The AND bitmask of hbmMask is applied with the SRCAND
                    // flag to the destination; subsequently, the color bitmap is applied (using XOR) to the
                    // destination by using the SRCINVERT flag.
}        

从HABJAN的回答我已经将p / Invoke中的注释添加到上面的结构中。看起来hbmMask包含我之后的位图参考,但我担心我的位操作技能相当生疏。当p / Invoke表示上半部分/下半部分时 - 它推断出什么?

是否可以从中获取黑白位图?

2 个答案:

答案 0 :(得分:1)

我认为这是由于你的RDP颜色深度。如果光标仅为黑白(通过RDP),则不会获得hbmColor值,因为此参数是可选的。

MSDN说:

hbmColor

Type: HBITMAP

描述:图标颜色位图的句柄。如果此结构定义了黑白图标,则此成员可以是可选的。 hbmMask的AND位掩码与SRCAND标志一起应用于目标;随后,使用SRCINVERT标志将颜色位图(使用XOR)应用于目标。

修改

public static Bitmap BitmapFromCursor(Cursor cur)
{
    ICONINFO iInfo;
    GetIconInfo(cur.Handle, out iInfo);

    Bitmap bmpColor = null;

    if (iInfo.hbmColor != IntPtr.Zero) {
       bmpColor = Bitmap.FromHbitmap(iInfo.hbmColor);
    }
    else {
       bmpColor = new Bitmap(w,h);
       // fill bmpColor with white colour
    }

    Bitmap bmpMask = Bitmap.FromHbitmap(iInfo.hbmMask);
    DeleteObject(iInfo.hbmColor);
    DeleteObject(iInfo.hbmMask);

    // apply mask bitmap to color bitmap:
    // http://stackoverflow.com/questions/3654220/alpha-masking-in-c-sharp-system-drawing

    BitmapData bmData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
    Bitmap dstBitmap = new Bitmap(bmData.Width, bmData.Height, bmData.Stride, PixelFormat.Format32bppArgb, bmData.Scan0);
    bmp.UnlockBits(bmData);

    return new Bitmap(dstBitmap);
}

...我没有测试这段代码,只是为了简要介绍一下该怎么做...

答案 1 :(得分:1)

在HABJAN的帮助下,我能够找到一种方法来完成这项工作。我在这里写答案的原因是因为从句柄获得的位图掩码包含两个掩码,因此您必须选择所需的版本(根据文档)。

public static Bitmap GetBitmapFromMask(IntPtr maskH)
{
    using (var bothMasks = Bitmap.FromHbitmap(maskH))
    {
        int midY = bothMasks.Height / 2;
        using (var mask = bothMasks.Clone(new Rectangle(0, midY, bothMasks.Width, midY), bothMasks.PixelFormat))
        {
            using (var input = new Bitmap(mask.Width, mask.Height))
            {
                using (var g = Graphics.FromImage(input))
                {
                    using (var b = new SolidBrush(Color.FromArgb(255, 255, 255, 255)))
                        g.FillRectangle(b, 0, 0, input.Width, input.Height);
                }

                var output = new Bitmap(mask.Width, mask.Height, PixelFormat.Format32bppArgb);
                var rect = new Rectangle(0, 0, input.Width, input.Height);
                var bitsMask = mask.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                var bitsInput = input.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                var bitsOutput = output.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
                unsafe
                {
                    for (int y = 0; y < input.Height; y++)
                    {
                        byte* ptrMask = (byte*)bitsMask.Scan0 + y * bitsMask.Stride;
                        byte* ptrInput = (byte*)bitsInput.Scan0 + y * bitsInput.Stride;
                        byte* ptrOutput = (byte*)bitsOutput.Scan0 + y * bitsOutput.Stride;
                        for (int x = 0; x < input.Width; x++)
                        {
                            ptrOutput[4 * x] = ptrInput[4 * x];           // blue
                            ptrOutput[4 * x + 1] = ptrInput[4 * x + 1];   // green
                            ptrOutput[4 * x + 2] = ptrInput[4 * x + 2];   // red
                            ptrOutput[4 * x + 3] = ptrMask[4 * x];        // alpha
                        }
                    }
                }
                mask.UnlockBits(bitsMask);
                input.UnlockBits(bitsInput);
                output.UnlockBits(bitsOutput);
                return output;
            }
        }
    }
}

这是由HABJAN链接的答案的基本副本 - 它似乎不会对结果字节执行逻辑AND或逻辑XOR - 但似乎没有做必要的工作。