Thread: Help - how can I pass an array through functions?

  1. #1
    Registered User
    Join Date
    Feb 2011
    Posts
    96

    Help - how can I pass an array through functions?

    I've got a project involving Microsoft print.spooler.api.
    In a nutshell, I have a:
    proc that declares an array of a struct.
    Then I pass that array to a Microsoft function that enumerates fonts and for each iteration sends the array to a
    callback function I created.

    If I understand correctly, the original proc's array should be filled with each font found when the Microsoft proc is complete.
    That's what I need.

    I don't know how to set pointers, instantiate, type cast (LPVOID) or allocate memory for this.

    Also, I think Microsoft's stuff is in C++.

    Code:
    #include <windows.h>
    #include <wingdi.h>
    #include <winspool.h>
    
    typedef struct
    {
        LONG  lfWeight;
        BYTE  lfItalic;
        BYTE  lfUnderline;
        BYTE  lfStrikeOut;
        TCHAR lfFaceName[LF_FACESIZE];
    } font_params
    
    font_params[] printer_list_fonts()
    {
        font_params arFontParams[1000];
    
        EnumFontFamilies(resource->dc, (LPCTSTR) NULL,(FONTENUMPROC) enumFamCallBack, (LPARAM) arFontParams);
    
        return arFontParams;
    }
    
    int enumFamCallBack(LPLOGFONT lplf, LPNEWTEXTMETRIC lpntm, DWORD FontType, LPVOID arFontParams)
    {
        font_params prmFont;
    
        prmFont->lfWeight = lplf->lfWeight;
        prmFont->lfItalic = (lplf->lfItalic ? 1 : 0);
        prmFont->lfUnderline = (lplf->lfUnderline ? 1 : 0);
        prmFont->lfStrikeOut = (lplf->lfStrikeOut ? 1 : 0);
        prmFont->lfFaceName = lplf->lfFaceName;
    
        nMax = sizeof(arFontParams);
        for(i=0;i<nMax;i++)
        {
            if(arFontParams[i] == NULL) //How do I check for an empty slot?
            {
                arFontParams[i] = prmFont;
                break;
            }
        }
    
        return 1;
    }
    The Microsoft struct that is sent to my callback is:
    Code:
    typedef struct tagLOGFONT 
    {
      LONG  lfHeight;
      LONG  lfWidth;
      LONG  lfEscapement;
      LONG  lfOrientation;
      LONG  lfWeight;
      BYTE  lfItalic;
      BYTE  lfUnderline;
      BYTE  lfStrikeOut;
      BYTE  lfCharSet;
      BYTE  lfOutPrecision;
      BYTE  lfClipPrecision;
      BYTE  lfQuality;
      BYTE  lfPitchAndFamily;
      TCHAR lfFaceName[LF_FACESIZE];
    } LOGFONT, *PLOGFONT;
    The Microsoft explanation is here:
    Enumerating the Installed Fonts (Windows)
    Last edited by MAtkins; 05-13-2016 at 03:36 PM.

  2. #2
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    You can't return a local array from a function (font_params[] is not a valid return type).

    The code below is just a sketch of ideas. You'll need to flesh it out.

    Unfortunately I can't really run this right now (I could but my win10 laptop is way over there), so I easily could have made a mistake or two. Let me know.

    BTW, just because MS uses the sickening "Hungarian notation" doesn't mean you have to!
    Code:
    #define MAX_FONTS 1000
    
    typedef struct {
        LONG  weight;
        BYTE  italic;
        BYTE  underline;
        BYTE  strikeOut;
        TCHAR faceName[LF_FACESIZE];
    } FontParams;
    
    typedef struct {
        FontParams list[MAX_FONTS];
        int count;
    } Fonts;
    
    int CALLBACK
    enumFamCallback(const LOGFONT *font, const TEXTMETRIX *tm,
                    DWORD type, LPARAM lParam) {
        Fonts *f = (Fonts*)lParam;
    
        if (f->count >= MAX_FONTS)
            return 0;  // list is full; stop enumeration
    
        FontParams *fp = f->fonts[f->count++];
    
        fp->weight = font->lfWeight;
        fp->italic = font->lfItalic;
        fp->underline = font->lfUnderline;
        fp->strikeOut = font->lfStrikeOut;
        strcpy(fp->faceName, font->lfFaceName); // or some more "secure" function
    
        return 1;
    }
    
    void listFonts(Fonts *fonts) {
        fonts->count = 0; // must zero the count
        EnumFontFamilies(resource->dc,  // I don't know where this comes from.
                                        // Should probably be passed in, but here assumed global.
                         (LPCTSTR)NULL,
                         (FONTENUMPROC)enumFamCallBack,
                         (LPARAM)fonts);
    }
    
    void someFunc() {
        Fonts fonts;
        listFonts(&fonts);
        printf("Font families found: %d\n", fonts.count);
        for (int i = 0; i < fonts.count; i++)
            printf("%s\n", fonts.list[i].
    }

  3. #3
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Thanks so much for your detailed reply.

    This forum will not let me post code, even surrounded with code tags.

    I changed someFunc to main & returned an int.
    I also included the windows .h files that *should* have all the MS definitions.

    I tried changing line 30 to match the list param in the structure:
    thinking that was a problem (got an error) but that didn't fix it.

    The error is on line: #30
    Error: incompatible types when initializing type ‘FontParams . . .

    I tried to post my code but it insists that I must enclose it in code tags except that it won't let me do it :-\

    So, I tried to attach the .c file; hope that works.
    ctest..c

  4. #4
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    I forgot to put an ampersand in that line. That's the problem with not being able to test (or even compile) code. Line 30 in your code should be:
    Code:
        FontParams *fp = &f->fonts[f->count++];
    Also you'll have to define resource->dc somehow. I have no idea where that's coming from so I don't know how to tell you to define it. Is it just from some code you copied? This page might help: How To: Retrieve a Printer Device Context

    And main should probably return 0 at the end.

  5. #5
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    There were a couple of other errors in my code. I was able to get the following to run on windows. For simplicity I created a "DISPLAY" hdc but you might want one specific to your printer.
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <wingdi.h>
    #include <winspool.h>
    
    #define MAX_FONTS 1000
     
    typedef struct {
        LONG  weight;
        BYTE  italic;
        BYTE  underline;
        BYTE  strikeOut;
        TCHAR faceName[LF_FACESIZE];
    } FontParams;
     
    typedef struct {
        FontParams fonts[MAX_FONTS];
        int count;
    } Fonts;
    
    int CALLBACK
    enumFamCallback(const LOGFONT *font, const NEWTEXTMETRIC *tm,
                    DWORD type, LPARAM lParam) {
        Fonts *f = (Fonts*)lParam;
     
        if (f->count >= MAX_FONTS)
            return 0;  // list is full; stop enumeration
     
        FontParams *fp = &f->fonts[f->count++];
     
        fp->weight = font->lfWeight;
        fp->italic = font->lfItalic;
        fp->underline = font->lfUnderline;
        fp->strikeOut = font->lfStrikeOut;
        _tcscpy_s(fp->faceName, LF_FACESIZE, font->lfFaceName);
     
        return 1;
    }
     
    void listFonts(Fonts *fonts) {
        fonts->count = 0; // must zero the count
        HDC hdc = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
        EnumFontFamilies(resource->dc,
                         (LPCTSTR)NULL,
                         (FONTENUMPROC)enumFamCallBack,
                         (LPARAM)fonts);
        DeleteDC(hdc);
    }
     
    int main() {
        Fonts fonts;
    
        listFonts(&fonts);
        _tprintf(_T("Font families found: %d\n"), fonts.count);
        for (int i = 0; i < fonts.count; i++)
            _tprintf(_T("%s\n"), fonts.list[i].faceName);
    
        return 0;
    }

  6. #6
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Thanks again for your reply.

    I can't compile your code (verbatim copy & paste) using cygwin on Windows 7
    Here are the errors I got

    Code:
    $ gcc /c/php-dev/CExamples/printer7/ctest.c
    /c/php-dev/CExamples/printer7/ctest.c: In function ‘enumFamCallback’:
    /c/php-dev/CExamples/printer7/ctest.c:36:5: warning: implicit declaration of function ‘_tcscpy_ ’ [-Wimplicit-function-declaration]
         _tcscpy_s(fp->faceName, LF_FACESIZE, font->lfFaceName);
         ^
    /c/php-dev/CExamples/printer7/ctest.c: In function ‘listFonts’:
    /c/php-dev/CExamples/printer7/ctest.c:43:24: warning: implicit declaration of function ‘_T’ [-Wimplicit-function-declaration]
         HDC hdc = CreateDC(_T("DISPLAY"), NULL, NULL, NULL);
                            ^
    /c/php-dev/CExamples/printer7/ctest.c:43:24: warning: passing argument 1 of ‘CreateDCA’ makes pointer from integer without a cast [-Wint-conversion]
    In file included from /usr/include/w32api/windows.h:71:0,
                     from /c/php-dev/CExamples/printer7/ctest.c:3:
    /usr/include/w32api/wingdi.h:2693:24: note: expected ‘LPCSTR {aka const char *}’ but argument is of type ‘int’
       WINGDIAPI HDC WINAPI CreateDCA(LPCSTR pwszDriver,LPCSTR pwszDevice,LPCSTR pszPort,CONST DEVMODEA *pdm);
                            ^
    /c/php-dev/CExamples/printer7/ctest.c:44:22: error: ‘resource’ undeclared (first use in this function)
         EnumFontFamilies(resource->dc,
                          ^
    /c/php-dev/CExamples/printer7/ctest.c:44:22: note: each undeclared identifier is reported only once for each function it appears in
    /c/php-dev/CExamples/printer7/ctest.c:46:36: error: ‘enumFamCallBack’ undeclared (first use in this function)
                          (FONTENUMPROC)enumFamCallBack,
                                        ^
    /c/php-dev/CExamples/printer7/ctest.c: In function ‘main’:
    /c/php-dev/CExamples/printer7/ctest.c:55:5: warning: implicit declaration of function ‘_tprintf’ [-Wimplicit-function-declaration]
         _tprintf(_T("Font families found: %d\n"), fonts.count);
         ^
    /c/php-dev/CExamples/printer7/ctest.c:57:35: error: ‘Fonts {aka struct <anonymous>}’ has no member named ‘list’
             _tprintf(_T("%s\n"), fonts.list[i].faceName);

  7. #7
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    There are two outright errors in the code I posted (stupidly I didn't actually copy/paste it from what I ran). I left "resource->dc" which should have been changed to "hdc". And I spelled enumFamCallBack once like that and the other time like enumFamCallback (lowercase b). They need to be the same.

    All the other errors are about the _T() and _txxx function stuff, which is about allowing the character set to be ANSI or UNICODE, but you seem to be using ANSI.

    First try including <tchar.h>.

    If that doesn't work, then try removing all the TCHAR, _T and _txxx function stuff like this (I fixed the two outright errors, too) :
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    #include <wingdi.h>
    #include <winspool.h>
     
    #define MAX_FONTS 1000
      
    typedef struct {
        LONG  weight;
        BYTE  italic;
        BYTE  underline;
        BYTE  strikeOut;
        char  faceName[LF_FACESIZE];
    } FontParams;
      
    typedef struct {
        FontParams fonts[MAX_FONTS];
        int count;
    } Fonts;
     
    int CALLBACK
    enumFamCallback(const LOGFONT *font, const NEWTEXTMETRIC *tm,
                    DWORD type, LPARAM lParam) {
        Fonts *f = (Fonts*)lParam;
      
        if (f->count >= MAX_FONTS)
            return 0;  // list is full; stop enumeration
      
        FontParams *fp = &f->fonts[f->count++];
      
        fp->weight = font->lfWeight;
        fp->italic = font->lfItalic;
        fp->underline = font->lfUnderline;
        fp->strikeOut = font->lfStrikeOut;
        strcpy_s(fp->faceName, LF_FACESIZE, font->lfFaceName);
        // or maybe just: strcpy(fp->faceName, font->lfFaceName);
      
        return 1;
    }
      
    void listFonts(Fonts *fonts) {
        fonts->count = 0; // must zero the count
        HDC hdc = CreateDC("DISPLAY", NULL, NULL, NULL);
        EnumFontFamilies(hdc,
                         (LPCSTR)NULL,
                         (FONTENUMPROC)enumFamCallback,
                         (LPARAM)fonts);
        DeleteDC(hdc);
    }
      
    int main() {
        Fonts fonts;
     
        listFonts(&fonts);
        printf("Font families found: %d\n", fonts.count);
        for (int i = 0; i < fonts.count; i++)
            printf("%s\n", fonts.list[i].faceName);
     
        return 0;
    }

  8. #8
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Ack! Boy I sure appreciate your patience here.
    I wish I knew more about what you're doing.
    I hope to research each line once I can compile & run it.

    I made 2 changes to your code.
    1. changed fonts to list in the Fonts struct along with the references to 'fonts'.
    2. changed strcpy_s, which threw an error to strcpy

    I don't know how you got this code to compile.
    I'm still getting errors. This time I have no idea what the problem might be.
    Here are the errors and the attached c file.



    I ran into other errors (again verbatim copy & paste)
    This time I included the actual file.

    Code:
    $ gcc /c/php-dev/CExamples/printer7/ctest.c
    /tmp/cc9TjZ7D.o:ctest.c:(.text+0x100): undefined reference to `__imp_CreateDCA'
    /tmp/cc9TjZ7D.o:ctest.c:(.text+0x100): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `__imp_CreateDCA'
    /tmp/cc9TjZ7D.o:ctest.c:(.text+0x127): undefined reference to `__imp_EnumFontFamiliesA'
    /tmp/cc9TjZ7D.o:ctest.c:(.text+0x127): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `__imp_EnumFontFamiliesA'
    /tmp/cc9TjZ7D.o:ctest.c:(.text+0x137): undefined reference to `__imp_DeleteDC'
    /tmp/cc9TjZ7D.o:ctest.c:(.text+0x137): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `__imp_DeleteDC'
    collect2: error: ld returned 1 exit status
    Attached Files Attached Files

  9. #9
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    You've actually made some progress here. The code has in fact compiled for you, but now your linker is complaining. You need to explicitly link to the required libraries. Try this:
    Code:
    gcc -o fontlister fontlister.c -lgdi32
    Or maybe it should be gdi64, if there is such a thing. I don't really know. I compiled it using Visual Studio, not cygwin.
    (fontlister and fontlister.c are just placeholders for whatever you've called your program.)
    And you don't need the tchar.h include since you're not using it.

  10. #10
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    I've run it in VS 2015 Developer command line.
    I need to move this into a php extension that can be built for x64 or x32.

    So, I need to know why these links are failing.
    It can see the wingdi.h file but is apparently missing something.
    Why is it failing?

    Here are the errors:
    Code:
    C:\CProjects\printer>cl ctest.c
    Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23918 for x86
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    ctest.c
    Microsoft (R) Incremental Linker Version 14.00.23918.0
    Copyright (C) Microsoft Corporation.  All rights reserved.
    
    /out:ctest.exe
    ctest.obj
    ctest.obj : error LNK2019: unresolved external symbol __imp__CreateDCA@16 referenced in function _listFonts
    
    ctest.obj : error LNK2019: unresolved external symbol __imp__DeleteDC@4 referenced in function _listFonts
    ctest.obj : error LNK2019: unresolved external symbol __imp__EnumFontFamiliesA@16 referenced in function _l
    istFonts
    ctest.exe : fatal error LNK1120: 3 unresolved externals

  11. #11
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    I told you why it's failing. You need to explicitly tell the linker which library files to link with (some library files are implicitly linked, but that's not the case with gdi32).

    wingdi.h is not a library file. It's a header file. Its purpose is to provide the information necessary for the compiler to compile the source file into an object file. The linker then links the object file(s) together with library files to produce the executable.

    I don't know how to tell cl about library files. You'll have to look that up yourself. I showed you how to do it for gcc. (I thought you were using cygwin.)

    A quick search suggests that for cl, it might be as simple as:
    cl ctest.c gdi32.lib

  12. #12
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Thanks again for your help & patience.
    Getting real help from a real programmer makes my job so much easier.

    I started by using a php nmake to build the extension - failed.
    I switched to using cl with VS 2015 command line - failed.
    I switched again to cygwin - failed.

    I have upgraded from Windows 7 to Windows 10 (which *so far* I actually like except that it's awful busy when I'm not using it).
    I downloaded & installed Microsoft's SDK for Windows 10.
    No help - all attempts still failed.
    I can't just use the link switches because in the long run, I have to use php7's nmake.

    My big problem was that I eventually had to make it work with the php7 nmake.

    Using variations on your code I was finally actually able to make it compile and work using nmake.
    I got a php Web page to display all the fonts available for a given printer.

    Now I have to learn how to make c concatenate all the FontParam values into a single | delimited string:
    "<FontFace>|<Weight>|<Italic>|<Underline>|<StrikeT hrough>"

    Everything I've tried so far (reading bogus responses in stackoverflow and forums) has failed.
    I have to convert a LONG and 3 BOOL values and then get them + the faceName into a single | delimited string.

  13. #13
    Registered User
    Join Date
    Jun 2015
    Posts
    1,643
    Quote Originally Posted by MAtkins View Post
    Now I have to learn how to make c concatenate all the FontParam values into a single | delimited string:
    "<FontFace>|<Weight>|<Italic>|<Underline>|<StrikeT hrough>"

    Everything I've tried so far (reading bogus responses in stackoverflow and forums) has failed.
    I have to convert a LONG and 3 BOOL values and then get them + the faceName into a single | delimited string.
    That's sounds simple enough. You didn't say how you wanted the booleans written in the string (e.g., true could be 1, 'T', "true", etc.). Here's an example where true/false are written as 'T'/'F':
    Code:
    sprintf(str, "%s|%ld|%c|%c|%c",
            fp->fontName,
            fp->weight,
            fp->italic    ? 'T' : 'F',
            fp->underline ? 'T' : 'F',
            fp->strikeOut ? 'T' : 'F');

  14. #14
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Thanks, that works great.
    It looks like I need to learn sprintf and remember to include stdio.h

    Now I'll have to figure out how to select a font from the list (I think I've got that) and then use wingdi to print text.
    If I can accomplish that I can finally put this project to bed!

    Thanks again

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. how to pass arrays to functions?
    By Mohit Saxena in forum C Programming
    Replies: 11
    Last Post: 09-23-2014, 04:35 AM
  2. Replies: 3
    Last Post: 05-09-2012, 06:41 AM
  3. Replies: 4
    Last Post: 02-14-2012, 07:45 PM
  4. Pass array through functions
    By willc0de4food in forum C Programming
    Replies: 13
    Last Post: 03-06-2005, 10:53 AM
  5. Replies: 3
    Last Post: 04-02-2002, 01:39 PM

Tags for this Thread