【问题标题】:Bitmap.FromHbitmap erroring when called over RDP通过 RDP 调用 Bitmap.FromHbitmap 时出错
【发布时间】:2014-06-10 10:07:17
【问题描述】:

我们的应用程序进行了一些光标操作,以在 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);

当调试 hbmColor 时为 0,这意味着当通过 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 中的 cmets 添加到上面的结构中。看起来hbmMask 包含我所追求的位图参考,但我担心我的位操作技能相当生疏。当 p/Invoke 说上半部分/下半部分时 - 它在推断什么?

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

【问题讨论】:

  • 你能展示你的'ICONINFO'结构定义吗?
  • 忽略 winapi 函数的返回值是一个标准错误。它不是可选的,您没有友好的 .NET 异常来避免麻烦。当 GetIconInfo() 返回 false 时抛出 Win32Exception 以便您知道失败的原因。并修复声明以添加 SetLastError = true。
  • 干杯汉斯,这是我需要更熟悉的东西,而不仅仅是从 p/Invoke 复制和粘贴。下次我会记住这一点..

标签: c# .net winapi rdp


【解决方案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);
}

...我没有测试这段代码,只是为了给你一个简短的信息来做什么...

【讨论】:

  • 您好,是的,您是正确的 - 我已经使用来自 p/Invoke 的额外信息修改了我的问题 - 看起来 hbmMask 包含我需要的内容。我想你不知道我能不能从中得到位图?
  • @Marlon:你对“iInfo.hbmColor”做同样的事情,你需要对“iInfo.hbmMask”做同样的事情。这样,您将获得 2 个位图,一个颜色位图和第二个蒙版位图。因此,您需要检查 hbmColor 是否 == 0,如果是,则创建具有白色背景的简单位图并在其上应用蒙版。要应用面具,您可以查看以下问题:stackoverflow.com/questions/3654220/…
  • @Marlon:我用简短的示例编辑了我的答案应该是什么样子。
【解决方案2】:

在 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 - 但似乎完成了所需的工作。

【讨论】:

  • 您能否粘贴完整的解决方案,而不仅仅是 GetBitmapFromMask 方法?谢谢!
猜你喜欢
  • 1970-01-01
  • 2020-03-15
  • 1970-01-01
  • 2018-01-15
  • 2018-11-05
  • 1970-01-01
  • 1970-01-01
  • 2014-11-23
  • 2015-04-24
相关资源
最近更新 更多