Thread: How can I get const from a variable?

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

    How can I get const from a variable?

    I'm trying to compile and use an app that uses the win.spooler.api.

    I'm trying to access this function:
    GetPrinter function (Windows)
    The Microsoft Windows types are here:
    Windows Data Types (Windows)

    I run the function twice, first to determine the size of the buffer.
    That shows up on fail (returns 0) when the buffer is too small.

    Once I retrieve the size in the variable pcbNeeded I need to redefine the buffer and the buffer length variables and run it again to get the printer.

    I'm using a php 7 nmake in VS 2015 command line to compile it.
    I've tried lots of different things and I keep getting an error saying
    'expected constant expression' on the line:
    Code:
    byte piBuf[pcbNeeded];
    Please tell me how to fix this. At this point, I have no idea.

    Code:
        typedef unsigned char byte;
    
        byte bufEmpty[2];
        long cbBufEmpty = sizeof(bufEmpty);
        unsigned long pcbNeeded;
    
        GetPrinter((HANDLE *)le_printer, 2, (LPBYTE)bufEmpty, (DWORD)cbBufEmpty, (LPDWORD)pcbNeeded);
        //This does return the bytes needed for the given box, printer, etc. Bytes Needed: 3435973836
    			
        byte piBuf[pcbNeeded];
        long cbBuf = sizeof(piBuf);
    
        if(GetPrinter((HANDLE *)le_printer, 2, (LPBYTE)piBuf, (DWORD)cbBuf, (LPDWORD)pcbNeeded) == 0)
        {
            return 0;
        }
    //. . .
    Last edited by MAtkins; 06-07-2016 at 03:50 PM.

  2. #2
    Registered User taazz's Avatar
    Join Date
    May 2016
    Posts
    50
    What the error message means is that the way you declare your array the compiler needs to know the size at compile time. You need to declare and use what I know as a dynamic array, something along the lines of (code from an other language in an other era so any input is welcome)
    Code:
      byte* piBuf;
      piBuf = malloc(pcbNeeded+1);//just in case.
      long cbBuf = pcbNeeded;
    As far as I can understand (and please keep in mind my C/C++ skills are probably worst that yours) lpbyte is pointer type variable and the code above should fit perfectly in it use.

  3. #3
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Thanks, this worked - but not.
    That is, it compiled fine this time but when I ran it, it failed again.
    I've tried everything I could think of, verified that size is the size they required (even tried 10000 higher).
    I verified all the parameters as best I can and I'm pretty sure they're right.

    I get a 0 for a response and can't find a readable error anywhere, including the event log.


    The le_printer parameter is a legitimate handle to a printer. I use it in other functions and it works fine. Also, this function does return a number for pcbNeeded.

    Code:
    typedef unsigned char byte;
    
    byte bufEmpty[2];
    long cbBufEmpty = sizeof(bufEmpty);
    unsigned long pcbNeeded;
    byte *piBuf;
    long cbBuf;
    
    GetPrinter((HANDLE *)le_printer, 2, (LPBYTE)bufEmpty, (DWORD)cbBufEmpty, (LPDWORD)pcbNeeded);			
    piBuf = malloc(pcbNeeded+10000);//just in case.
    cbBuf = pcbNeeded+10000;
    			
    if(GetPrinter((HANDLE *)le_printer, 2, (LPBYTE)piBuf, (DWORD)cbBuf, (LPDWORD)pcbNeeded) == 0)
    {
        //Returns: pi2 failed! Required = 3435973836, Given: 3435973836
        sprintf(sRtn, "pi2 failed! Required = %lu, Given: %lu", pcbNeeded, cbBuf);			
        
    }
    PRINTER_INFO_2 *pi2 = (PRINTER_INFO_2 *) piBuf;

  4. #4
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Don't use HANDLE*. It's just HANDLE, which I assume is what le_printer is anyway. And it looks like the structure is a fixed size, so try this simplification:
    Code:
    PRINTER_INFO_2 pi2;
    DWORD cb;
    if (GetPrinter(le_printer, 2, (LPBYTE)&pi2, sizeof pi2, &cb) == 0) {
        // does control reach here???
    }

  5. #5
    Registered User taazz's Avatar
    Join Date
    May 2016
    Posts
    50
    try this function to see what really goes wrong.
    Code:
    #include <windows.h>
    
    TCHAR* GetLastErrorMessage(){
      DWORD   dwLastError = GetLastError();
      TCHAR*   lpBuffer =  (TCHAR*) malloc(sizeof(TCHAR)*1024);
      if(dwLastError != 0)    // Don't want to see a "operation done successfully" error ;-)
          FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,                 // It/s a system error
                           NULL,                                      // No string to be formatted needed
                           dwLastError,                               // Hey Windows: Please explain this error!
                           MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),  // Do it in the standard language
                           lpBuffer,              // Put the message here
                           sizeof(TChar)*1020,                     // Number of bytes to store the message
                           NULL);
      return lpBuffer;
    }
    I have not tested it yet, my current projects are windows agnostic and I can't test it fast I based the code at Retrieving the Last-Error Code (Windows) So I'm guessing that it should work unless I did something wrong.

  6. #6
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Thanks guys but neither answer worked.
    Both compiled but the first just returned that it failed.
    YES - the control ends up inside the if that checks for 0.

    The GetLastErrorMessage just threw the browser into the Web servers 'there's a problem' page.

  7. #7
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Quote Originally Posted by MAtkins View Post
    Thanks guys but neither answer worked.
    Both compiled but the first just returned that it failed.
    YES - the control ends up inside the if that checks for 0.

    The GetLastErrorMessage just threw the browser into the Web servers 'there's a problem' page.
    What type is le_printer? Where did you get it? Are you sure it's correct?

    You've sprint'ed something into the variable sRtn. Can you view it's contents somewhere? Try putting the value of GetLastError into it as well. What's the value?
    Code:
    sprintf(sRtn, "error code: %u", GetLastError());
    Look up the error code here: System Error Codes (Windows)

  8. #8
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    le_printer is an int. It's declared above all the functions used.
    static int le_printer;

    le_printer definitely equates to a valid printer handle, it's used throughout the app successfully.

    I tried this and it still just pulls up the "HTTP Error 500.0 - Internal Server Error" and won't give me any valid information about the error.

    Code:
    PRINTER_INFO_2 pi2;
    DWORD cb;
    if (GetPrinter(le_printer, 2, (LPBYTE)&pi2, sizeof pi2, &cb) == 0) 
    {
        // does control reach here???
        sprintf(sRtn, "error code: %u", GetLastError());
        RETURN_STRING(sRtn); //zend (php7) code that returns the error to the Web page
    }

  9. #9
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Try printing to a file:
    Code:
    FILE *f = fopen("somefile.txt", "w");  // put a full file path to some place you can write to and find
    fprintf(f, "error code: %u\n", GetLastError());
    fclose(f);

  10. #10
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > le_printer is an int. It's declared above all the functions used.
    > static int le_printer;
    So why is it so hard to make it a HANDLE* ?

    Have you verified that HANDLE* and int are indeed the same size on your machine.

    > le_printer definitely equates to a valid printer handle, it's used throughout the app successfully.
    That might just make you lucky (up to now), and your luck just ran out.

    > RETURN_STRING(sRtn); //zend (php7) code that returns the error to the Web page
    OK, what do either of these things do?
    Please don't tell me you're returning a pointer to a local variable.

    > GetPrinter((HANDLE *)le_printer, 2, (LPBYTE)bufEmpty, (DWORD)cbBufEmpty, (LPDWORD)pcbNeeded);
    > //This does return the bytes needed for the given box, printer, etc. Bytes Needed: 3435973836
    Try to actually think for a second.
    Do you seriously imagine that returning information about a single printer takes 3GB of memory!?

    What your call does demonstrate is a clear lack of understanding, hidden by indiscriminate casting.

    Write the code without casts, then THINK about the error messages and understand what they're trying to tell you.

    Code:
    BYTE dummy = 0;
    DWORD cbNeeded = 0; // check to see if anything is actually assigned
    BOOL s = GetPrinter((HANDLE *)le_printer, 2, &dummy, sizeof(dummy), &cbNeeded);
    if ( s ) {
      // success
      PRINTER_INFO_2 p = malloc( cbNeeded );
      s = GetPrinter((HANDLE *)le_printer, 2, p, cbNeeded, &cbNeeded);
    } else {
      // fail
    }
    See, you can't just go declaring variables with the same name as the parameters, then use casting to magic away all the compilation warnings.

    When something is labelled an OUT parameter, and it's got a p in front of the name, then you need to declare a variable with the type WITHOUT THE LP prefix, then use the & (address of) operator when passing the variable into the function.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  11. #11
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    Quote Originally Posted by Salem View Post
    So why is it so hard to make it a HANDLE* ?
    Have you verified that HANDLE* and int are indeed the same size on your machine.
    It should just be HANDLE, not HANDLE*. A HANDLE is actually a void* (or in windows terminology, a PVOID).
    I'd like to know where le_printer came from and why it's an int.

  12. #12
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    OK, this much works fine.
    As you can see the le_printer is filled from a Zend 'resource'.
    It comes from an argument in the function call and le_printer is set to the argument.
    If it doesn't work this function fails when trying to retrieve the printer. It doesn't fail and I use it to open a printer and to list printer fonts with no problem.

    It returns
    "Got printer"

    Code:
    zval *arg1;			
    printer *resource;
    
    byte bufEmpty[2];
    long cbBufEmpty = sizeof(bufEmpty);
    unsigned long pcbNeeded;
    byte *piBuf;
    long cbBuf;
    long nNumRtnBytes;
    char arCapabilities[2500];
    char sErr;
    char sRtn;
    
    if( zend_get_parameters_ex(1, &arg1) == FAILURE )
    {
        WRONG_PARAM_COUNT;
    }
    
    if ((resource = (printer *)zend_fetch_resource_ex(arg1, "Printer Handle", le_printer)) == NULL) 
    {
        RETURN_STRING("Didn't get printer");
    }
    
    //sprintf(sRtn, "le_printer: %u", le_printer);
    RETURN_STRING("Got printer");

  13. #13
    Registered User
    Join Date
    Jun 2015
    Posts
    1,640
    sRtn needs to be a char array, not just a single char. Try char sRtn[250]. You should've received a compiler warning about that. And the return should be RETURN_STRING(sRtn, 1). The 1 means to make a copy of the string.

    You still haven't explained where le_printer comes from. Nothing in the code you've shown is setting it. If it's just an int then you probably can't cast it to a HANDLE and expect it to work.

    In your post #3, if you initialized pcbNeeded to 0 you probably wouldn't see such a ridiculous number. I.e., the function is probably failing because of a bad HANDLE and not setting pcbNeeded at all, so you're just seeing the random uninitialized value it contained.

  14. #14
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Quote Originally Posted by algorism View Post
    You still haven't explained where le_printer comes from.
    OK, this is lengthy.
    Here's the func that opens the printer based on its name and returns the handle to php:
    Code:
    PHP_FUNCTION(printer_open)
    {
    zval *arg1;
    printer *resource;
    
    int argc = ZEND_NUM_ARGS();
    
    resource = (printer *)emalloc(sizeof(printer));
    resource->dmModifiedFields = 0;
    
    if( argc == 1 && zend_get_parameters_ex(1, &arg1) != FAILURE ) {
    	convert_to_string_ex(arg1);
    	resource->name = Z_STRVAL_P(arg1);
    }
    else if( argc == 0 ) {
    	resource->name = PRINTER7_G(default_printer);
    }
    else {
    	WRONG_PARAM_COUNT;
    }
    
    if (OpenPrinter(resource->name, &resource->handle, NULL) != 0) {
    	resource->pi2 = (PRINTER_INFO_2 *)emalloc(sizeof(PRINTER_INFO_2));
    	resource->pi2->pDevMode = (DEVMODE *)emalloc(DocumentProperties(NULL, NULL, resource->name, NULL, NULL, 0));
    	if (DocumentProperties(NULL, resource->handle, resource->name, resource->pi2->pDevMode, NULL, DM_OUT_BUFFER) == IDOK) {
    		resource->info.lpszDocName	= estrdup("PHP generated Document");
    		resource->info.lpszOutput	= NULL;
    		resource->info.lpszDatatype = estrdup("TEXT");
    		resource->info.fwType		= 0;
    		resource->info.cbSize		= sizeof(resource->info);
    		resource->dc = CreateDC(NULL, resource->name, NULL, resource->pi2->pDevMode);
    		RETURN_RES(zend_register_resource(resource, le_printer));
    	}
    }
    else {
    	php_error_docref(NULL TSRMLS_CC, E_WARNING, "couldn't connect to the printer [%s]", resource->name);
    	RETURN_FALSE;
    }
    }
    Here's the php that retrieves the handle from the above function and then uses the handle returned to call printer_get_papersources which is the function we've been working on:
    Code:
    $hndlPrinter = printer_open($PrinterName);
    $sRtn = printer_get_papersources($hndlPrinter);
    I use the printer_open with success to print basic text and (as you know) to list the printer's fonts.

  15. #15
    Registered User
    Join Date
    Feb 2011
    Posts
    96
    Quote Originally Posted by Salem View Post
    >Have you verified that HANDLE* and int are indeed the same size on your machine.
    The type is provided above, in my original post. The HANDLE type is actually a void & Microsoft claims that it is 'A handle to an object.' typed from Microsoft's PVOID which is typed as VOID.
    This has to work on any machine 2013+.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Changing a const variable?
    By jw232 in forum C++ Programming
    Replies: 14
    Last Post: 09-01-2008, 09:53 AM
  2. how to make a non const copy of a const variable?
    By sept in forum C++ Programming
    Replies: 6
    Last Post: 02-25-2008, 02:11 AM
  3. const variable...
    By technosavvy in forum C Programming
    Replies: 9
    Last Post: 02-18-2008, 06:05 AM
  4. const variable
    By vaibhav in forum C++ Programming
    Replies: 3
    Last Post: 10-24-2005, 08:06 AM
  5. problem with the const variable
    By ssharish in forum C Programming
    Replies: 2
    Last Post: 01-28-2005, 09:53 AM

Tags for this Thread