Solved

SERIAL PORT WORKS ON WIN/NT AND NOT IN WIN2K

Posted on 2003-11-19
6
5,059 Views
Last Modified: 2013-12-03
Hi Experts,

It works fine on Win/NT, but when I tried it on win2k, all functions called
after CreateFile(which returns a valid handle) fail giving 1 on GetLastError.

This is a very simple old fashioned C (not MFC) program, a printer test, based
on the mttty example from MSDN. I made it to test the WIN32 API because I have to
migrate an app. from win95 to win2K.
I compiled it first using VC++6.0 Enterprise Edition running under Win/NT 4.00.1381
ServicePack 6, in a PII with 128MB RAM.
Then I compiled it with VC++.NET (MSDE 7.1.3088 and Framework 1.1.4322) on
Win2K 5.002195- ServPack 4 running in a Pentium (R)4 - 2.4GHz w/256 MB RAM.
Both times the same happened: works fine on NT4 but not on Win2K.
Attached you wil find the source code

Looking forward for your comments

Regards

// Printer Test made based on " Serial Communications in Win32 "

#define PGM_VERS                  17

#define ASCII_XON                  0x11
#define ASCII_XOFF                  0x13
#define MAX_WRITE_BUFFER      1024
#define MAX_READ_BUFFER            2048
#define MOMENTO                         200      // milliseconds
#define READ_TIMEOUT             500      // milliseconds
#define READ_RETRIES               6

#include <windows.h>  
#include <stdio.h>  

DCB dcb = {0};
DCB dcb_orig = {0};
COMMTIMEOUTS gTimeoutsDefault = { 0x01, 0, 0, 0, 0 };

struct SERIALInfoStruct
{
    HANDLE                  hCommPort ;
    DWORD                  dwBaudRate ;
    DWORD                  fParity;
      DWORD                  fDtrControl;
    DWORD                  fOutxDsrFlow;
    DWORD                  fDsrSensitivity;
    DWORD                  fRtsControl;
    DWORD                  fOutxCtsFlow;
    DWORD                  fOutX;
    DWORD                  fInX;
    DWORD                  fTXContinueOnXoff;
    WORD                  XonLim;
    WORD                  XoffLim;
    BYTE                  XonChar;
    BYTE                  XoffChar;
    BYTE                  bPort;
    BYTE                  bByteSize;
    BYTE                  bParity;
    BYTE                  bStopBits ;
    COMMTIMEOUTS      timeoutsorig;
    COMMTIMEOUTS      timeoutsnew;

} SERIALInfo;

            // Printer specifics
      #define  SLIP_UNSELECTED 0x04
      #define  SLIP_ERR        0x44
      #define  ROLL_ERR        0x60
      #define  NEAR_END        0x0C
      #define  STAT_RSP_BYTES  1

         char  s_stat_req[] = { 0x10,0x04,0x05 } ;
         char  r_stat_req[] = { 0x10,0x04,0x04 } ;
         char  reset_err[]  = { 0x10,0x05,0x01 } ;
         char  EscSecNormal[]            = { 0x1B,0x21, 0x00 } ;
         char  EscSecAlto[]            = { 0x1B,0x21, 0x10 } ;
         char  EscSecAncho[]            = { 0x1B,0x21, 0x20 } ;
         char  EscSecNegra[]            = { 0x1B,0x21, 0x08 } ;

#ifdef wstat_req
      #undef wstat_req
#endif

      #define wstat_req     s_stat_req

BOOL ReadABuffer(struct SERIALInfoStruct *pSER, char * lpBuf, DWORD dwToRead);
BOOL WriteABuffer(struct SERIALInfoStruct *pSER, char * lpBuf, DWORD dwToWrite);
BOOL reset_prt_error(struct SERIALInfoStruct *pSER);

int getch();

void init_puerto(struct SERIALInfoStruct * pSerial)
{
      DWORD LastError;

      pSerial->timeoutsnew = gTimeoutsDefault ;    

      pSerial->bByteSize                  = 8;
      pSerial->bParity                  = NOPARITY;
      pSerial->bStopBits                  = ONESTOPBIT;
      pSerial->dwBaudRate                  = CBR_9600;
      pSerial->fParity                  = 1;
      pSerial->fDtrControl            = DTR_CONTROL_ENABLE;
      pSerial->fOutxDsrFlow            = 1;
    pSerial->fDsrSensitivity      = 1;
    pSerial->fRtsControl            = RTS_CONTROL_ENABLE;
    pSerial->fOutxCtsFlow            = FALSE;
    pSerial->fOutxDsrFlow            = FALSE;
    pSerial->XonChar                  = ASCII_XON;
    pSerial->XoffChar                  = ASCII_XOFF;
    pSerial->XonLim                        = 0;
    pSerial->XoffLim                  = 0;

    printf("\nInit Puerto COM%d Handle=%d", pSerial->bPort+1, pSerial->hCommPort );

    memset(&dcb, 0x00, sizeof(dcb));
    dcb.DCBlength = sizeof(dcb);    

    if (!GetCommState(pSerial->hCommPort, &dcb))      // get current DCB settings
      {      LastError = GetLastError();
            printf("\nError en GetCommState da %d", LastError);
      }
      else
            printf("\n --> GetCommState OK" );

      memcpy(&dcb_orig, &dcb, sizeof(dcb));

    // update DCB rate, byte size, parity, and stop bits size
      dcb.BaudRate                  = pSerial->dwBaudRate;
    dcb.ByteSize                  = pSerial->bByteSize;
    dcb.Parity                        = pSerial->bParity;
    dcb.StopBits                  = pSerial->bStopBits;

    // update DCB flow control
      dcb.fDtrControl                  = pSerial->fDtrControl            ;
      dcb.fOutxDsrFlow            = pSerial->fOutxDsrFlow            ;
    dcb.fRtsControl                  = pSerial->fRtsControl      ;
    dcb.fOutxCtsFlow            = pSerial->fOutxCtsFlow      ;
    dcb.fOutxDsrFlow            = pSerial->fOutxDsrFlow      ;
    dcb.fDsrSensitivity            = pSerial->fDsrSensitivity      ;
    dcb.fOutX                        = pSerial->fOutX      ;
    dcb.fInX                        = pSerial->fInX      ;
    dcb.fTXContinueOnXoff      = pSerial->fTXContinueOnXoff      ;
    dcb.XonChar                        = pSerial->XonChar      ;
    dcb.XoffChar                  = pSerial->XoffChar      ;
    dcb.XonLim                        = pSerial->XonLim      ;
    dcb.XoffLim                        = pSerial->XoffLim      ;

    if (!GetCommTimeouts( pSerial->hCommPort, &(pSerial->timeoutsorig)))
      {      LastError = GetLastError();
            printf("\nError en GetCommTimeouts da %d", LastError);
      }
      else
            printf("\n --> GetCommTimeouts OK" );

    if (!SetCommState(pSerial->hCommPort, &dcb))
      {      LastError = GetLastError();
            printf("\nError en SetCommState da %d", LastError);
      }
      else
            printf("\n --> SetCommState OK" );

    if (!SetCommTimeouts(pSerial->hCommPort, &(pSerial->timeoutsnew)))
      {      LastError = GetLastError();
            printf("\nError en SetCommTimeouts da %d", LastError);
      }
      else
            printf("\n --> SetCommTimeouts OK" );

    if (!SetupComm(pSerial->hCommPort, MAX_READ_BUFFER, MAX_WRITE_BUFFER))
      {      LastError = GetLastError();
            printf("\nError en SetupComm da %d", LastError);
      }
      else
            printf("\n --> SetupComm OK" );

    if (!EscapeCommFunction(pSerial->hCommPort, SETDTR))
      {      LastError = GetLastError();
            printf("\nError en EscapeCommFunction(SETDTR) da %d", LastError);
      }
      else
            printf("\n --> EscapeCommFunction(SETDTR) OK" );

}

int revisa_prt_on(struct SERIALInfoStruct *pSER)
{
    char status[4];
    long conta=0L;
    int ret;
      int Istatus;

      BOOL RC;

    printf("\nREVISA PRT_ON  COM%d Handle=%d",
            pSER->bPort+1, pSER->hCommPort );

    ret = 0x0000;

  inicio_prueba :                      /* pedir status del slip */
 /*************/

    RC = WriteABuffer(pSER, &wstat_req[0], sizeof(wstat_req) ) ;
      printf( "\nREVISA PRT_ON: WriteABuffer da %d (%d bytes)", RC, sizeof(wstat_req) );
            
      // read printer status (slip)
    RC = ReadABuffer(pSER, status,1);
      Istatus = (int) status;
      printf( "\nREVISA PRT_ON: ReadABuffer da %d (Status = %d)", RC, Istatus );
            
    if (Istatus & 0x8000)
    {
            printf("\nPrinter desconectada/apagada");
            conta++;
            printf("\n printer disconnected/off");
            goto inicio_prueba ;
    }
    else
      {       if ( conta )
            { conta = 0L;              
              Sleep(MOMENTO);      
            }
           
         return(ret) ;
      }
}
                     
BOOL reset_prt_error(struct SERIALInfoStruct *pSER)
{  
      BOOL RC;

      printf("\nRESET_PRT_ERROR: COM%d Handle=%d", pSER->bPort+1, pSER->hCommPort );      
      RC = WriteABuffer(pSER, &reset_err[0], sizeof(reset_err) ) ;
      return RC;
}


BOOL revisa_papel(struct SERIALInfoStruct *pSER, unsigned nretries, char tipo_salida)
{
    unsigned wretries;
    unsigned Istatslip = 0, Istatpapel = 0;
      BOOL       RC;
    char     statslip[4];
    char     statpapel[4];
    int      no_lista , wtest ;

    printf("\nREVISA PAPEL");

    for ( no_lista=1 ; no_lista ; )
        {                                  /* pedir status del slip */
            RC = WriteABuffer(&SERIALInfo, &s_stat_req[0], sizeof(s_stat_req) );
                  if(!RC) return FALSE;
                                                /* Leer status del slip */
            RC= ReadABuffer(&SERIALInfo, statslip, sizeof(s_stat_req));
                  if(!RC) return FALSE;

            Istatslip = statslip[0];

                  printf( "\nSLIP_status=%04X (SLIP_UNSEL=%04X)=> Es <%s>... Slip esta <%s>",
                         Istatslip ,
                         SLIP_UNSELECTED ,
                         (Istatslip & SLIP_UNSELECTED) ? "ROLL" : "SLIP"  ,
                         (Istatslip & 0x0010) ? "ON" : "OFF" );
               
            if (Istatslip & SLIP_UNSELECTED)  /* slip no seleccionado */
               {
                 printf("\nES ROLL ... "); getch();

               chk_roll :                     /* pedir y leer status del ROLL */

                 RC = WriteABuffer(&SERIALInfo, &r_stat_req[0], sizeof(r_stat_req) ) ;
                         if(!RC) return FALSE;

                 RC = ReadABuffer(&SERIALInfo, statpapel, STAT_RSP_BYTES);  
                         if(!RC) return FALSE;

                 Istatpapel = (unsigned)statpapel[0];
                 if (Istatpapel & ROLL_ERR)    /* rollo acabado */
                          {  printf("\n ROLLO TERMINADO");      
                             goto chk_roll ;
                          }
                 else if (Istatpapel & NEAR_END)
                          {  printf("\n ROLLO CASI TERMINADO");
                          }

                 return TRUE ;
               }

                  printf("\nES SLIP... "); getch();

            wretries = nretries ;

            do {                               /* pedir y leer status del slip */
                 RC = WriteABuffer(&SERIALInfo, &s_stat_req[0],sizeof(s_stat_req) ) ;
                         if(!RC) return FALSE;

                 RC = ReadABuffer(&SERIALInfo, statpapel,STAT_RSP_BYTES);
                         if(!RC) return FALSE;

                 --wretries ;
                 Istatpapel = (unsigned)statpapel[0];
                 wtest = ((Istatpapel & SLIP_ERR)!=0) ;
                 printf( "\nVERIF.SLIP(en el while):stat=%04X (ERR=%04X) ==> Test=%s retries=%d",
                         statpapel,
                         SLIP_ERR,
                         (wtest)?"OK":"ERR" ,
                         wretries
                          );
                 if (wtest)
                     Sleep(MOMENTO);
               } while( wtest && wretries );

            printf( "\nVERIF.SLIP(en el while):stat=%04X (ERR=%04X) ==> Test=%s retries=%d",
                     statpapel,
                     SLIP_ERR,
                     ((Istatpapel & SLIP_ERR)!=0)?"OK":"ERR" ,
                     wretries
                      );

        if ( (wretries < 1) && ((Istatpapel & SLIP_ERR)!=0) )
            {
                        printf( "\nREVISE IMPRESORA" );
            }
        else no_lista = 0 ;
                                         // reset printer error
        RC = reset_prt_error(&SERIALInfo);
        printf( "\nluego ResetErr=%s", (RC)?"OK":"MAL" );

        } // while paper not positioned

    printf("\nTERMINA OK ... ");
    return TRUE ;

//  return_err :
// /**********/
    printf("\nTERMINA ERR ... ");
    return FALSE ;
}



void main()
{
    unsigned port = 0;  /* COM1 */
    char portStr[6];

    char reset[]    = { 0x1B ,0x30 };
    char feed[]     = { 0x0C };
    char expulsa[]  = { 0x1B ,0x4F };

        // Initialize  port
    char reset_error[]  = { 0x1B ,0x6C };
    char F_ini[6] = { 0x1B, 0x4C, 0x00, 0x00 }; // frontal
    char R_ini[6] = { 0x1B, 0x43, 0x00, 0x00 }; // roll

    int c ;
      BOOL RC;

   preguntar1 :

      printf("\nGIOSPSR3.vc++.NET(v%04d)\nCOM1 OR COM2 (1/2) ? " ,PGM_VERS) ;
    c = toupper(getch() );            
//      sprintf(portStr, "COM%c:", c ) ;
    sprintf(portStr, "COM%c", c ) ;
    printf("Escogiste %s\n", portStr ) ;

    if (c == '1')
      { SERIALInfo.bPort = 0;                    
      }
    else if (c == '2')
            { SERIALInfo.bPort = 1;                  
            }
         else
            { printf("Valor incorrecto!!\n" ) ;
              goto preguntar1;                  
            }
   
    printf("\nAntes de Open %s", portStr);
      SERIALInfo.hCommPort = CreateFile( portStr,  
                                                GENERIC_READ | GENERIC_WRITE,
                                                0,                  // exclusive access
                                                NULL,            // no security attrs
                                                OPEN_EXISTING,
                                                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
                                                0);
      if (SERIALInfo.hCommPort == INVALID_HANDLE_VALUE)
      {      printf("\nOpen dio INVALID_HANDLE_VALUE (hnd=%d)", SERIALInfo.hCommPort );
            return ;
    }
    printf("\nOpen dio hnd=%d", SERIALInfo.hCommPort );

    init_puerto(&SERIALInfo);

   preguntar2 :

    printf("\n(R)oll o (F)ront ? " ) ;
    c = toupper(getch() );
    printf("  %c\n", c ) ;

    if (c == 'F')
      { WriteABuffer(&SERIALInfo, F_ini, 3 );
        RC = revisa_papel(&SERIALInfo, 100, (char)c );
      }
    else if (c == 'R')
            { WriteABuffer(&SERIALInfo, R_ini, 3 );
              RC = revisa_papel(&SERIALInfo, 100, (char)c );
            }
    else goto preguntar2;
      if(!RC)
            printf("\nERROR INICIALIZANDO IMPRESORA!");

    printf("\nAny key para datos masivos..." ) ; getch();

#define ESC_SEC(a) WriteABuffer(&SERIALInfo,a,sizeof(a))

    ESC_SEC(EscSecNormal);
    WriteABuffer(&SERIALInfo, "1 printer test\n",16);

    ESC_SEC(EscSecAlto);
    WriteABuffer(&SERIALInfo, "2 printer test\n",16);

    ESC_SEC(EscSecNormal);
    ESC_SEC(EscSecAncho);
    WriteABuffer(&SERIALInfo, "3 printer test\n",16);

    ESC_SEC(EscSecNormal);
    ESC_SEC(EscSecNegra);
    WriteABuffer(&SERIALInfo, "4 printer test\n",16);

    ESC_SEC(EscSecNormal);
    WriteABuffer(&SERIALInfo, "5 printer test\n",16);
    WriteABuffer(&SERIALInfo, "6 printer test\n",16);

    printf("\nAny key para expulsar papel ..." ) ; getch();


     if (c=='F')
       WriteABuffer(&SERIALInfo, expulsa,2 );
     else
       WriteABuffer(&SERIALInfo, feed,1);

       getch();

}


BOOL ReadABuffer(struct SERIALInfoStruct *pSER, char * lpBuf, DWORD dwToRead)
{
      DWORD dwRes;
      DWORD dwRead;
      DWORD nretries = 0;
      DWORD LastError;
      BOOL fWaitingOnRead = FALSE;
      BOOL ret;
      OVERLAPPED osReader = {0};
      DWORD ii;

    printf("\nReadABuffer COM%d Handle=%d",
            pSER->bPort+1, pSER->hCommPort );

      // Create the overlapped event. Must be closed before exiting
      // to avoid a handle leak.
      osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

      if (osReader.hEvent == NULL)
        {
            printf("\nCreateEvent(READER) da NULL" );
            ret = FALSE;
        }

      if (!fWaitingOnRead)
      {
         // Issue read operation.
         if (!ReadFile(pSER->hCommPort, lpBuf, dwToRead, &dwRead, &osReader))
         {
              LastError = GetLastError();
              if (LastError != ERROR_IO_PENDING)     // read not delayed?
                    {
                        printf("\nReadFile da %d", LastError);
                        ret = FALSE;
                    }
              else
                   fWaitingOnRead = TRUE;
         }
         else
         {    
              ret = TRUE;
         }
      }

      if (fWaitingOnRead)
      {
    DoWait:
   /*******/
         dwRes = WaitForSingleObject(osReader.hEvent, READ_TIMEOUT);
         switch(dwRes)
         {
              // Read completed.
              case WAIT_OBJECT_0:
                    if (!GetOverlappedResult(pSER->hCommPort, &osReader, &dwRead, FALSE))
                        {  ret = FALSE;
                           printf("\nReadFile da WAIT_OBJECT_0");
                        }
                    else
                         ret = TRUE;      // Read completed successfully.                        

                    //  Reset flag so that another opertion can be issued.
                    fWaitingOnRead = FALSE;
                    break;

              case WAIT_TIMEOUT:
                    // Operation isn't complete yet. fWaitingOnRead flag isn't
                    // changed since I'll loop back around, and I don't want
                    // to issue another read until the first one finishes.
                    //
                    // This is a good time to do some background work.
                    if (++nretries < READ_RETRIES)
                          goto DoWait;
                    ret = FALSE;
                    printf("\nReadFile da TimeOut");
                    break;                      

              default:
                    // Error in the WaitForSingleObject; abort.
                    // This indicates a problem with the OVERLAPPED structure's
                    // event handle.
                    ret = FALSE;
                    break;
         }
      }

      CloseHandle(osReader.hEvent);

      for(ii=0; ii<dwRead; ii++)
        if(ii < 20)
             printf(" %02X", *(lpBuf+ii) ) ;

      return ret;
}


BOOL WriteABuffer(struct SERIALInfoStruct *pSER, char * lpBuf, DWORD dwToWrite)
{
   DWORD ii;
   DWORD LastError;
   BOOL fRes;

    printf("\nWriteABuffer COM%d Handle=%d",
            pSER->bPort+1, pSER->hCommPort );

   fRes = TRUE;

   for(ii=0; ii < dwToWrite; ii++)
   {
            if (!TransmitCommChar(pSER->hCommPort, *(lpBuf+ii) ) )
            {  LastError = GetLastError();
              printf ("\nTransmitCommChar: Error=%d", LastError);
              fRes = FALSE;
              break;
            }
            else
            {      if(ii < 20)
                        printf(" %02X", *(lpBuf+ii) ) ;
            }
   }

   return fRes;
}
/*
            BOOL WriteABuffer(struct SERIALInfoStruct *pSER, char * lpBuf, DWORD dwToWrite)
            {
                  OVERLAPPED osWrite = { 0 };
               DWORD dwWritten;
               DWORD dwRes;
               BOOL fRes;

               // Create this write operation's OVERLAPPED structure's hEvent.
               osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
               if (osWrite.hEvent == NULL)      // error creating overlapped event handle      
                    return FALSE;

               // Issue write.
               if (!WriteFile(pSER->hCommPort, lpBuf, dwToWrite, &dwWritten, &osWrite))
               {
                    if (GetLastError() != ERROR_IO_PENDING)
                    {  // WriteFile failed, but isn't delayed. Report error and abort.
                         fRes = FALSE;
                    }
                    else
                    { // Write is pending.
                         dwRes = WaitForSingleObject(osWrite.hEvent, INFINITE);
                         switch(dwRes)
                         {
                              // OVERLAPPED structure's event has been signaled.
                              case WAIT_OBJECT_0:
                                     if (!GetOverlappedResult(pSER->hCommPort, &osWrite, &dwWritten, FALSE))
                                             fRes = FALSE;
                                     else
                                      // Write operation completed successfully.
                                      fRes = TRUE;
                                     break;
           
                              default:
                                     // An error has occurred in WaitForSingleObject.
                                     // This usually indicates a problem with the
                                    // OVERLAPPED structure's event handle.
                                     fRes = FALSE;
                                     break;
                         }
                    }
               }
               else   // WriteFile completed immediately  
                    fRes = TRUE;

               CloseHandle(osWrite.hEvent);
               return fRes;
            }

*/
0
Comment
Question by:juliogonzalez
  • 4
  • 2
6 Comments
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9781777
I've compiled the code.
And it looks like it works fine (under Win2k workstation):

GIOSPSR3.vc++.NET(v0017)
COM1 OR COM2 (1/2) ? 1
Escogiste COM1

Antes de Open COM1
Open dio hnd=76
Init Puerto COM1 Handle=76
 --> GetCommState OK
 --> GetCommTimeouts OK
 --> SetCommState OK
 --> SetCommTimeouts OK
 --> SetupComm OK
 --> EscapeCommFunction(SETDTR) OK
(R)oll o (F)ront ?


So may be the problem is not in the code, but in hardware, settings, ...
0
 

Author Comment

by:juliogonzalez
ID: 9784601
Hi GloomyFriar,

Hey, you really took the time to compile and test my program, I really appreciatte this!
Thank you very much for your help, your time and for answering so quickly.  Now you are making thing clear to me.  I was going out of my head with this problem.
Tomorrow I´ll try with a different pc.
There are two things I´d like to ask you : what compiler did you use(not much important) and what your port settings are(much more important).

Best regards

Julio
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9787266
>what compiler did you use
MSVC++ 6.0
>what your port settings
Default ;-)
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 6

Accepted Solution

by:
GloomyFriar earned 500 total points
ID: 9787283
>all functions called  after CreateFile(which returns a valid handle) fail giving 1 on GetLastError.
It's rather strange because of:
#define ERROR_INVALID_FUNCTION           1L    // dderror
0
 

Author Comment

by:juliogonzalez
ID: 9793211
It may sound silly now,  because it should have been one of the first things to check, but you know it´s normal to blame the program you´re testing rather than any other thing.
Well, there was a printer driver installed on the win2k. After removing it, the problem was solved. It seems to me that this driver holds the serial driver exclusively and causes the win2k to reject all subsequent requests, yielding this ERROR_INVALID_FUNCTION code.
I´m still in trouble because this driver is needed by the regular apps. and can´t be happily removed, but that is another problem.
As long as  you not only solved my question, but were also this supportive,  I´m grading you an "A".

Thank you very much
0
 
LVL 6

Expert Comment

by:GloomyFriar
ID: 9796306
Glad to help ;-)
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

This article shows a few slightly more advanced techniques for Windows 7 gadget programming, including how to save and restore user settings for your gadget and how to populate the "details" panel that is displayed in the Windows 7 gadget gallery.  …
A theme is a collection of property settings that allow you to define the look of pages and controls, and then apply the look consistently across pages in an application. Themes can be made up of a set of elements: skins, style sheets, images, and o…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now