Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

SetConsoleIcon problems in Windows 10

jirina
New Contributor I
1,530 Views

I have a console application and I change its icon at runtime. It has been working well so far, in Windows 7, 8 and 10. However, it stopped working after one of the recent updates of Windows 10 (I do not know which was it was and I do not have any possibility to use an older version of Windows 10).

This is how I change the icon in the code:

      subroutine changeConsoleWindowIcon ( )
      
      use ifwin
      
      interface
        function SetConsoleIcon ( hIcon )
        use ifwinty
        integer(BOOL) :: SetConsoleIcon
        integer(DWORD) :: hIcon
        !dec$ attributes default, stdcall :: SetConsoleIcon
        end function
      end interface
      
      integer,parameter :: IDI_ICON_MYAPP=101
      integer(kind=HANDLE) :: hModule, hLib
      integer :: hMainIcon, lRet
      pointer ( SetConsoleF1, SetConsoleIcon )
      
      
      hLib = LoadLibrary ( 'Kernel32.dll'C )
      SetConsoleF1 = GetProcAddress ( hLib, 'SetConsoleIcon'C )
      hModule = GetModuleHandle(0)
      hMainIcon = LoadIcon ( hModule, MAKEINTRESOURCE(IDI_ICON_MYAPP) )
      lRet = SetConsoleIcon ( hMainIcon )
      lRet = FreeLibrary ( hLib )
      
      return
      end subroutine changeConsoleWindowIcon

This works well in Windows 7. In Windows 10, it also works, in cases when I run the executable by double clicking it in the Windows Explorer or any file manager. If I run the command line and run my application from it, or if I run my application as a new process from another (dialog-based, C++) application, the console icon is not changed.

I checked the return value from the call to SetConsoleIcon (by writing its value to the console window) and it is 0 in all above mentioned cases.

I apologize this is not much of a Fortran question; I wonder if anyone here could at least point me in a direction leading to resolving this problem. Goggling this found one note that SetConsoleIcon might have been removed from Kernel32.dll...

Thank you in advance for any tips.

0 Kudos
1 Solution
Robert_van_Amerongen
New Contributor III
1,530 Views

Here is the code:

  PROGRAM cd
  USE IFWIN, ONLY : LRESULT, fWPARAM, HANDLE , NULL, GetModuleHandle, LoadIcon, &
                    GetConsoleWindow, SendMessage, WM_SETICON, ICON_BIG, ICON_SMALL
  IMPLICIT none
!
  INTEGER(KIND=HANDLE)  :: hInstance, hConsole, hIcon
  INTEGER(KIND=LRESULT) :: result
!
  hInstance = GetModuleHandle(NULL)
  hIcon     = LoadIcon(hInstance, "MY_ICON"//CHAR(0))
!
  hConsole = GetConsoleWindow()
  result = SendMessage(hConsole, WM_SETICON, INT(ICON_SMALL,KIND=fWPARAM), hIcon)
  result = SendMessage(hConsole, WM_SETICON, INT(ICON_BIG,KIND=fWPARAM),   hIcon)
!
  END PROGRAM cd

Be sure you have a resource file where you define the name of the icon resource, i.e. "MY_ICON". Note that the ICOM_SMALL is for the icon in the console itself and ICON_BIG for the icon in the task bar. The latter, however, does not work in this example. In a window applications where a console is created, it works fine.

 

View solution in original post

0 Kudos
11 Replies
Robert_van_Amerongen
New Contributor III
1,529 Views

Irina,

I met the same problem. In the mean time I found another, much more simple, solution. Try as follows:

hConsole = GetConsoleWindow()

iResult = SendMessage(hConsole, WM_SETICON, ICON_BIG, hMainIcon)

iResult = SendMessage(hConsole, WM_SETICON, ICON_SMALL, hMainIcon)

 

For me it works fine!

Robert

0 Kudos
jirina
New Contributor I
1,529 Views

Could you please post the complete code? I appreciate your idea, but I am not able to finish your snippet to make things work. :-[

0 Kudos
Robert_van_Amerongen
New Contributor III
1,531 Views

Here is the code:

  PROGRAM cd
  USE IFWIN, ONLY : LRESULT, fWPARAM, HANDLE , NULL, GetModuleHandle, LoadIcon, &
                    GetConsoleWindow, SendMessage, WM_SETICON, ICON_BIG, ICON_SMALL
  IMPLICIT none
!
  INTEGER(KIND=HANDLE)  :: hInstance, hConsole, hIcon
  INTEGER(KIND=LRESULT) :: result
!
  hInstance = GetModuleHandle(NULL)
  hIcon     = LoadIcon(hInstance, "MY_ICON"//CHAR(0))
!
  hConsole = GetConsoleWindow()
  result = SendMessage(hConsole, WM_SETICON, INT(ICON_SMALL,KIND=fWPARAM), hIcon)
  result = SendMessage(hConsole, WM_SETICON, INT(ICON_BIG,KIND=fWPARAM),   hIcon)
!
  END PROGRAM cd

Be sure you have a resource file where you define the name of the icon resource, i.e. "MY_ICON". Note that the ICOM_SMALL is for the icon in the console itself and ICON_BIG for the icon in the task bar. The latter, however, does not work in this example. In a window applications where a console is created, it works fine.

 

0 Kudos
jirina
New Contributor I
1,530 Views

Robert, thank you for posting the complete code. While testing it, I encountered several interesting things and difficulties:

  • In IPS 2016, Update 4, "GetConsoleWindow" is an unresolved external.
  • In IPS 2017, Update 2, "GetConsoleWindow" compiles well, but I had to change the call to LoadIcon to MAKEINTRESOURCE(MY_ICON) instead of "MY_ICON"//CHAR(0) to work as required.

Icon is now changed even in Windows 10; thank you very much again!

Finally, it is interesting to see that SendMessage returns 0 in Windows 7, while it return a nonzero value in Windows 10.

0 Kudos
andrew_4619
Honored Contributor II
1,530 Views

@robert, thanks for the useful tip.

0 Kudos
Robert_van_Amerongen
New Contributor III
1,530 Views

Jirina,

thanks for letting me know. I cannot comment on the unresolved external referewnce. According to MS GetConsoleWindow is valid since Windows XP. Maybe Intel added it somewhat later??

On the icon resource: you have a choice: if the resource is known by its name only, so not by a numerical value, you can use the LoadIcon function call only with a nul-terminated string as the second argument. If you have a resource known by a numerical value, you MUST use that value by calling LoadIcon with a 32 bit integer as the second argument of which the high order word must be zero. I presume you used the second method. I assumed a resource with name so used the first method.

The return value of the Sendmessage is, according to the specs, the handle to the previous console icon. If there was not such an icon, it will retrurn NULL.

Robert

 

0 Kudos
Steve_Lionel
Honored Contributor III
1,530 Views

GetConsoleWindow is defined by kernel32.lib which comes from Microsoft. Intel doesn't touch this. It could be that you declared it incorrectly. What was the exact error message? 

0 Kudos
jirina
New Contributor I
1,530 Views

Robert, thank you for your kind and detailed explanation.

Steve, this is what I have in my code:

      use ifwin
      integer(kind=HANDLE) :: hInstance
      hInstance = GetModuleHandle ( NULL )

Compiling this in IPS XE 2016, Update 4 issues warning #6717: This name has not been given an explicit type.   [GETCONSOLEWINDOW]

Linking then issues error LNK2019: unresolved external symbol GETCONSOLEWINDOW referenced in function CONSOLEWINDOW_mp_CHANGECONSOLEWINDOWICON    window.obj

 

 

0 Kudos
Lorri_M_Intel
Employee
1,530 Views

I don't want to sound like I'm making a silly statement, but that code snippet doesn't even have the string "getconsolewindow" in it (lowercase, uppercase, or mixed case)

In your real code, make sure that you "use kernel32" as well as "ifwinty".  That will add the declaration and make sure the correct library is linked against.

              --Lorri

0 Kudos
Steve_Lionel
Honored Contributor III
1,530 Views

IFWIN includes KERNEL32 and IFWINTY. Most likely the declaration of that routine was missing from module KERNEL32 (looking at the current source I see it is one I added with BIND(C), but I am not sure when it was added.) If it was missing when you compiled, you get the Intel Fortran defaults which are not correct for this routine and hence the link error.

0 Kudos
jirina
New Contributor I
1,530 Views

Lorri, I am very sorry for the mistake I made when posting the code snippet. It contained GetModuleHandle instead of GetConsoleWindow. The right snippet is

use ifwin
integer(kind=HANDLE) :: hInstance
hInstance = GetConsoleWindow( NULL )

I think using IFWIN should be sufficient, as Steve explained, because the code snippet works well in IPS XE 2017 Update 2, but not in IPS XE 2016 Update 4. I think the declaration of the routine has been added recently, making it work in IPS XE 2017 Update 2.

Thank you for your quick and kind support.

0 Kudos
Reply