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

EnumFontFamiliesEx fails under 32bit but fine under 64bit

Baird__David
Beginner
831 Views

Hi,

I'm using EnumFontFamiliesEx from ifwin to find the font names on the users computer.

I have the following code:

   != Process reading the Fonts on this PC
     integer function enum_fonts(lFont, pFont, fType, lParam)
        use ifwin
        implicit none
        type (T_ENUMLOGFONTEX) :: lFont
        type (T_NEWTEXTMETRICEX) :: pFont
        integer(DWORD) :: fType
        integer(LONG_PTR) :: lParam
        integer :: i,len,c

       enum_fonts = 1    
       if (npcfonts .lt. MAXPCFNT) then    
       npcfonts = npcfonts + 1
       pcfonts(npcfonts) = lFont%elfFullName(1:FNTLEN)
       do i = 2,FNTLEN ! Remove null termination
          c = ichar(pcfonts(npcfonts)(i:i))
          if (c .eq. 0) then
             pcfonts(npcfonts)(i:FNTLEN) = ' '
             return
          endif
       enddo
     endif
     end function enum_fonts

    != Read the Fonts on this PC
      subroutine GetPCFonts()
        use ifwin
        implicit none
        integer(HANDLE) hdc
     type (T_LOGFONT) LogFont;
        integer :: ret
     
        hdc = GetDC(0)
        LogFont%lfCharSet = ANSI_CHARSET
        LogFont%lfPitchAndFamily = 0
        LogFont%lfFaceName = ' '
        LogFont%lfFaceName(1:1) = achar(0)
     npcfonts = 0
     ret = EnumFontFamiliesEx(hdc,LogFont,%REF(enum_fonts),0,0)
        return    
      end subroutine GetPCFonts

Note npcfonts, pcfonts, FNTLEN(=32) and MAXPCFNT(=1024) are at the module level.
This runs as expected on the 64bit version, giving me 303 fonts on my PC
but in the 32bit version it only returns 1 font. I can't see why it's working in
one case and not the other as the integer default is 4 in both cases and
the declarations see fine otherwise, and I'm always returning 1 from
enum_fonts to continue enumeration.

Thanks for any help,
David.

Note: I'm using Intel(R) Visual Fortran Composer XE 2013 SP1 Update 6 Integration for Microsoft Visual Studio* 2008, 14.0.0100.2008, Copyright (C) 2002-2014 Intel Corporation as we haven't validated our software under a later version yet.

Also I can't find any recorded bug fixes for EnumFontFamiliesEx.

0 Kudos
1 Solution
Steven_L_Intel1
Employee
831 Views

All Windows API callback functions must use the STDCALL calling convention. On Intel 64 this doesn't matter, but it does on IA-32. Also, your callback routine doesn't specify that the last two arguments are passed by value.

Try this.In your callback routine, add:

!DEC$ ATTRIBUTES STDCALL, REFERENCE ::enum_fonts
!DEC$ ATTRIBUTES VALUE :: ftype, lParam

I did this and it works.

View solution in original post

0 Kudos
13 Replies
Steven_L_Intel1
Employee
832 Views

All Windows API callback functions must use the STDCALL calling convention. On Intel 64 this doesn't matter, but it does on IA-32. Also, your callback routine doesn't specify that the last two arguments are passed by value.

Try this.In your callback routine, add:

!DEC$ ATTRIBUTES STDCALL, REFERENCE ::enum_fonts
!DEC$ ATTRIBUTES VALUE :: ftype, lParam

I did this and it works.

0 Kudos
Baird__David
Beginner
831 Views

Thanks so much Steve, that's gold, very much appreciated. 

I've been looking but can't find much documentation on the ifwin library beyond three pages in the help, which don't give any details like this.

Is there a document that goes into more detail?

0 Kudos
Baird__David
Beginner
831 Views

Thanks, I'm very familar with the Windows Platform SDK, what I need is a document that shows how to declare the calls on the Fortran side.
 reading ifwinty.f90 gives me most of what I need but not the information on declaring the callbacks. Your comment gives me a good start. I've read the Using Intel® Visual Fortran to Create and Build Windows*-Based Applications but it's pretty lightweight, not the full reference manual I was hoping for. Thanks again, much appreciated.

0 Kudos
Steven_L_Intel1
Employee
831 Views

MSDN is the "full reference manual". There are also lots of examples we provide. It's not really possible for us to describe how to use each and every API routine from Fortran - the best we can do is some general guidelines. I'll admit that the STDCALL requirement for callbacks can be obscure, but it's a fundamental aspect of Windows API programming.

There once was a book "Compaq Visual Fortran: A Guide to Creating Windows Applications" that went into this topic in great detail. It is still mostly applicable to Intel Visual Fortran, but it hasn't been published in a while and copies are expensive.

You might want to start with https://msdn.microsoft.com/library/windows/desktop/ff381399(v=vs.85).aspx - while this is C++ oriented, a lot of the text is generally applicable to Fortran. Even here, though, the need for STDCALL is obscure - possibly because this is hidden in the C++ header files. We've started to add abstract interfaces for callbacks to our modules, specifying STDCALL, so that may help, but it wouldn't have helped you here. Luckily, once you become aware of this it becomes second-nature. And, as I said, it is a 32-bit issue only since on x64 there is only one calling convention.

0 Kudos
Robert_van_Amerongen
New Contributor III
831 Views

David,

the Lawrence book is still available. Amazon ask prices up to USD 80, our local Dutch (book)store (bol.com) has a price euro 60. You can obtain the examples of the book via the site  http://booksite.elsevier.com/9781555582494/

Unfortunately, the book is out-of-data. It is published in 2002 and thus has not used Fortran 2003 with its ISO_C_BINDING.stuff. It thus does not show you how to wrtite a window program in a strict standard-conforming and compiler-independent way. It uses the Compaq compiler now known as  Intel. The Fortran introduction  aspect is the weak point. But there are a number of more advanced topics that are handled very well. For that reason I can recomment it. (Of course, always besides the Petzold book!)

Robert

0 Kudos
Steven_L_Intel1
Employee
831 Views

There isn't a way to write a Windows API program in a compiler-independent way. You can generally avoid nonstandard code in your own application with some effort. The Lawrence book is still very applicable.

0 Kudos
Baird__David
Beginner
831 Views

Thanks everyone for the input, I've ordered a copy from Better World Books who sell second hand books and have free shipping to any country:
http://www.betterworldbooks.com/Compaq-Visual-FORTRAN--A-Guide-to-Creating-Windows-Applications-id-9781555582494.aspx
Mine cost US$31 including shipping. Better Wortd also donate a book to charity for each one you buy.

0 Kudos
Robert_van_Amerongen
New Contributor III
831 Views

In my post #7 I was not clear. With "compiler-independent" I did mean the situation of having only one Fortran file(or set of files) that can be used in combination with more that one compiler. My experience with the WinAPI, OpenGL, freeglut is that for 64 bits it always works fine. For 32 bits you need to add directives. A drawback, of course, is that you have to write yourself a module with the named constants, structs and interfaces .

Robert

0 Kudos
JVanB
Valued Contributor II
831 Views

The nice thing is that !DEC$ ATTRIBUTES STDCALL and !GCC$ ATTRIBUTES STDCALL are pretty much compatible for procedures with the BIND attribute, so a single source with both ATTRIBUTES declarations generally works fine in both gfortran and ifort, 32 bits and 64 bits. The worst incompatibility I can recall is that C_LONG ends up being 8 in cygwin, so Windows data types that are dependent on this constant should be made dependent on another constant for Windows code intended for use in cygwin. Doesn't Windows C code potentially suffer for the same problem with Windows header files?

 

0 Kudos
Steven_L_Intel1
Employee
831 Views

I don't see how C_LONG can change based on using Cygwin - at least using Intel Fortran. Maybe gfortran handles it differently.

In Intel Fortran, using our modules, you'd be using named constants such as DWORD that always are the right kind. Windows C code would similarly be using typedefs declared in windows.h.

0 Kudos
JVanB
Valued Contributor II
831 Views

The issue is that if you look in https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx it says that DWORD is defined in IntSafe.h as typedef unsigned long DWORD; and the cygwin headers end up with sizeof(long) = 8. Thus cygwin+windows headers produce the wrong effective KIND for DWORD, and if the programmer says something like INTEGER, PARAMETER, PUBLIC :: DWORD = C_LONG, the result would be DWORD = 8 in 64-bit code.

BTW, gfortran looks like they are finally going to change the KIND of the internal LEN of CHARACTER variables to be C_PTRDIFF_T instead of the previous C_INT. I would have thought that C_SIZE_T (doesn't ifort use this?) were more appropriate.

 

0 Kudos
Steven_L_Intel1
Employee
831 Views

Ah, Cygwin C headers - certainly if you're using gcc in Cygwin I could see (!) a problem there. Doesn't affect ifort. Yes, ifort uses C_SIZE_T for character lengths.

0 Kudos
Reply