SetClassLong (hWnd, GCL_HICON, hIcon) не может заменить WinForms Form.Icon - PullRequest
1 голос
/ 15 февраля 2010

Я хотел бы использовать конкретный файл ICO в качестве значка в приложении WinForms. Поскольку я хочу иметь возможность указать маленькую иконку (16x16) для строки заголовка и обычную иконку (32x32) при Alt-Tabbing, я не могу использовать свойство Form.Icon, которое принимает один объект System.Drawing.Icon, что заставляет меня использовать значок низкого разрешения или обычный значок.

Я опубликовал связанный вопрос , в котором было предложено очень простое решение, которое отлично работает для родных приложений Win32:

SetClassLong(hWnd, GCL_HICON, hIcon32x32);
SetClassLong(hWnd, GCL_HICONSM, hIcon16x16);

Попытка применить тот же трюк к Form не работает. У меня есть следующие определения P / Invoke:

[DllImport ("User32.dll")]
extern static int SetClassLong(System.IntPtr hWnd, int index, int value);

const int GCL_HICON   = -14;
const int GCL_HICONSM = -34;

и я тогда просто позвоню:

System.IntPtr hIcon32x32 = ...;
System.IntPtr hIcon16x16 = ...;
SetClassLong(this.Handle, GCL_HICON, hIcon32x32.ToInt32());
SetClassLong(this.Handle, GCL_HICONSM, hIcon16x16.ToInt32());

и никогда не звоните Form.Icon. Это не работает, однако:

  1. Значок в форме по-прежнему является значком WinForms по умолчанию.
  2. При нажатии Alt-Tab я все еще вижу значок WinForms по умолчанию.

... но, что интересно, это то, что когда я нажимаю Alt-Tab, я очень очень быстро вижу значок, который я определил с помощью GCL_HICON (или GCL_HICONSM, если я не использую GCL_HICON) , Кажется, что-то происходит за кулисами, что заставляет Windows рисовать значок, используя значок по умолчанию WinForms.

Я не могу понять, что я сделал неправильно и что происходит за кулисами.

отредактировано: я действительно хочу иметь возможность предоставлять две разные иконки, созданные на лету, а не привязывать Form.Icon к иконке на диске. Вот почему я пытаюсь использовать код P / Invoke для указания значков в памяти.

Ответы [ 2 ]

3 голосов
/ 24 апреля 2013

На самом деле я не проверял это, тестируя его или просматривая дизассемблированный код WinForms, поэтому я не уверен, будет ли этот ответ удовлетворять условию "надежных и / или официальных источников". Но я думаю, что я весьма [заслуживает доверия], поэтому я все равно попробую!

Вы устанавливаете значок, связанный с окном class . Вы делаете это с помощью функции SetClassLong[Ptr] и индексов GCL_HICON / GCL_HICONSM, но это имеет тот же эффект, что и установка в структуре WNDCLASSEX во время регистрации класса , Это устанавливает значок по умолчанию для окон этого класса.

Однако отдельные окна могут устанавливать свои собственные значки, переопределяя значок по умолчанию, предоставляемый их классом. Вы делаете это, отправляя сообщение WM_SETICON, передавая либо ICON_BIG, либо ICON_SMALL как wParam, а дескриптор к значку как lParam. Предположительно, это то, что делает WinForms. Вот почему значок WinForms по умолчанию появляется вместо значка класса окна по умолчанию, который вы назначаете, потому что WinForms устанавливает свой значок по умолчанию, используя WM_SETICON, а не через класс окна. Единственное, что «по умолчанию» в значке WinForms, это то, что он автоматически назначается платформой, если вы не назначаете другой пользовательский значок. Он не подходит ни под какое другое определение «по умолчанию» - определенно не то, которое можно использовать с точки зрения Win32.

Свойство Form.Icon определенно использует WM_SETICON для изменения значка, поэтому оно работает так, как ожидается. Теперь вы говорите, что не хотите устанавливать свойство Icon, потому что

Я действительно хочу иметь возможность предоставлять две разные иконки, созданные на лету, а не привязывать Form.Icon к иконке на диске. Вот почему я пытаюсь использовать код P / Invoke для указания значков в памяти.

Но это не значит, что вы не можете установить свойство Icon. Вы можете указать дескриптор для значка (HICON) здесь, так же, как вы можете использовать P / Invoke. Все, что вам нужно, это статический метод Icon.FromHandle, который создает новый объект Icon из указанного HICON. Затем этот объект Icon назначается свойству Icon формы.

Тебе не обязательно, однако. Вы можете использовать P / Invoke, если хотите:

const int WM_SETICON = 0x80;

enum IconType
{
    ICON_BIG   = 1;
    ICON_SMALL = 0;
}

[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd,
                                 int message,
                                 IntPtr wParam,
                                 IntPtr lParam);

Затем назовите это аналогично тому, что у вас есть:

IntPtr hIcon32x32 = ...;
IntPtr hIcon16x16 = ...;
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_BIG, hIcon32x32);
SendMessage(this.Handle, WM_SETICON, (IntPtr)IconType.ICON_SMALL, hIcon16x16);

Только одна вещь, которую вы делаете неправильно: при условии, что «большая» иконка всегда будет иметь размер 32x32 пикселей, а «маленькая» иконка всегда будет иметь размер 16x16 пикселей. По крайней мере, я предполагаю, что вы делаете это из имен переменных. Если так, то это неверное предположение. Это только самые распространенные размеры. Они не гарантируются одинаковыми во всех средах. Вот почему важно, чтобы в вашем файле .ico были большие иконки; например, значок 48x48. Так как вы устанавливаете значки динамически, у Windows не будет доступа к большему значку для даунсамплинга, и вы можете получить что-то действительно размытое и безобразное, когда ваш значок 32x32 увеличен.

Чтобы получить действительные размеры , вызовите функцию GetSystemMetrics. Флаги SM_CXICON и SM_CYICON сообщат вам размеры X и Y соответственно «большого» значка. Флаги SM_CXSMICON и SM_CYSMICON сообщат вам размеры X и Y соответственно «маленького» значка.

const int SM_CXICON   = 11;
const int SM_CYICON   = 12;
const int SM_CXSMICON = 49;
const int SM_CYSMICON = 50;

[DllImport("user32.dll")]
static extern int GetSystemMetrics(int smIndex);
static Size GetBigIconSize()
{
    int x = GetSystemMetrics(SM_CXICON);
    int y = GetSystemMetrics(SM_CYICON);
    return Size(x, y);
}
static Size GetSmallIconSize()
{
    int x = GetSystemMetrics(SM_CXSMICON);
    int y = GetSystemMetrics(SM_CYSMICON);
    return Size(x, y);
}
2 голосов
/ 15 февраля 2010

Вы можете использовать Form.Icon. Вам нужен только один файл значка, который содержит версии вашего значка 16x16 и 32x32 пикселей.

Я только что попробовал, используя один файл значков, который содержит красный круг размером 32x32 пикселей и синий прямоугольник 16x16. Небольшой значок окна показывает синий прямоугольник, значок alt-tab - красный круг.

P / Invoke вообще не нужен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...