Ошибка Bitmap.FromHbitmap при вызове через RDP

1

Наше приложение делает некоторые манипуляции с курсором, чтобы включить "относительно" красивую анимацию перетаскивания на 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 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 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 говорит верхнюю половину/нижнюю половину - что он выводит?

Можно ли получить черно-белое растровое изображение?

  • 0
    Можете ли вы показать свое определение структуры «ICONINFO»?
  • 0
    Игнорирование возвращаемого значения функций winapi является стандартной ошибкой. Это не является обязательным, у вас нет дружественных. NET исключений, чтобы избежать неприятностей. Бросьте Win32Exception, когда GetIconInfo () вернет false, чтобы вы знали, почему он не работает. И исправьте объявление, добавив SetLastError = true.
Показать ещё 1 комментарий
Теги:
winapi
rdp

2 ответа

1
Лучший ответ

Я думаю, что это связано с вашей глубиной цвета RDP. Если ваш курсор только черный и белый (через RDP), вы не получите значение hbmColor поскольку этот параметр является необязательным.

MSDN говорит:

hbmColor

Type: HBITMAP

Описание: дескриптор растрового изображения цвета значка. Этот член может быть необязательным, если эта структура определяет черно-белый значок. Битовая маска AND hbmMask применяется с флагом SRCAND к месту назначения; впоследствии цветовая растровая карта применяется (с использованием XOR) к месту назначения с использованием флага SRCINVERT.

РЕДАКТИРОВАТЬ:

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);
}

... я не тестировал этот код, он просто дал вам краткую информацию о том, что делать...

  • 0
    Здравствуйте, да, вы правы - я исправил свой вопрос с помощью дополнительной информации из p / Invoke - похоже, hbmMask содержит то, что мне нужно. Я не думаю, что вы знаете, могу ли я получить растровое изображение от этого?
  • 0
    @Marlon: то же самое вы делаете для «iInfo.hbmColor», вы должны сделать для «iInfo.hbmMask». Таким образом, вы получите 2 растровых изображения, одно цветное растровое изображение и второе растровое изображение маски. Итак, вам нужно проверить, если hbmColor == 0, если это так, создайте простое растровое изображение с белым фоном и примените к нему маску. Чтобы применить маску вы можете проверить этот вопрос: stackoverflow.com/questions/3654220/…
Показать ещё 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, - он, похоже, не делает логического ИЛИ или логического XOR в результирующих байтах - тем не менее, похоже, делает требуемую работу.

  • 0
    Можете ли вы вставить свое полное решение, а не только метод GetBitmapFromMask? Спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню