Solved

In using serial port,syncnorous or asyncnorous?

Posted on 1998-07-03
1
440 Views
Last Modified: 2013-11-19
In using serial port, What is the difference between syncnorous and asyncronous ?
if the 6th argument of the function, CreateFile,  has FILE_FLAG_OVERLAPPED, that
the serial communication is asyncnorous ,is documented in HELP(F1)
but,I well can't understand it. Help me Thank you
0
Comment
Question by:aeteta
1 Comment
 
LVL 1

Accepted Solution

by:
IgorGrebnev earned 100 total points
ID: 1318705
Dear aeteta

If you use FILE_FLAG_OVERLAPPED flag you can use WriteFile and ReadFile operations asyncronously. This mean that after you call
ReadFile funciton the function returns immidiately, even if there is no enough data for you request.
If you do not use FILE_FLAG_OVERLAPPED then
WriteFile blocks until teh operation is finished and through return value you can get the number of bytes actually written to the port ( it is the same if no error occurs ).
ReadFile return and return number of bytes already in input buffer of serial port. ( There is additional buffer associated with serial port and if you do not call ReadFile the data is   not lost ).

If you use FILE_FLAG_OVERLAPPED then WriteFile and ReadFile returns immidiately with return code ERROR_IO_PENDING, it means that the output started. You get handle of event and can wait with this handle for the end of IO operation ( or program can do anything else ). Then you can get result if operation with GetOverlappedresults function.

Anyway using of overlapped IO is more difficult and would not advise to use it at the beginning.

I also send you source of my class for serial port communicatoin, hope it will help you. If you have any questions please write here or contact me igrebnev@msinetllc.com

Igor

// SerialPort.h : header file
//
/////////////////////////////////////////////////////////////////////////////
// Window for processing of posted messages of comm events.  
#ifndef __SERIALPORT_H
#define __SERIALPORT_H

class CSerialPort;

class CSerialPortWnd : public CWnd


{ friend class CSerialPort;
  CSerialPort *m_pSerialPort;
// Construction
      CSerialPortWnd(CSerialPort *pSerialPort );
      virtual ~CSerialPortWnd()  {}
      //{{AFX_MSG(CSerialPortWnd)
      afx_msg LONG OnCommEvent( UINT, LONG dwEvMask ) ;
      //}}AFX_MSG
      DECLARE_MESSAGE_MAP()
};

/////////////////////////////////////////////////////////////////////////////
// CPortThread thread
class CPortThread : public CWinThread

{friend class CSerialPort;

//-------------- Pointer to serial port that contains the thread object
   CSerialPort *m_pSerialPort;
// Attributes
   CPortThread( CSerialPort *pSerialPort );  
   virtual ~CPortThread();
//---------- Set to TRUE when the port is closed
       BOOL m_IsProcessExiting;
// Overrides
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CPortThread)
      public:
      virtual BOOL InitInstance();
      virtual int  ExitInstance();
      virtual int  Run();
      //}}AFX_VIRTUAL
};
/////////////////////////////////////////////////////////////////////////////
// CSerialPort command target

class CSerialPort : public CObject

{friend CPortThread;
      DECLARE_DYNCREATE( CSerialPort )  
  void InitData();
 //-----------------
  HANDLE            m_hPort;
      CPortThread       m_PortThread;
      CSerialPortWnd    m_CWnd;  
  CString           m_strPortName;
  BOOL              m_IsPostingEnabled;
//---------------
  HANDLE m_hWriteEvt;
//---------------
  int m_inBufferSize;
  int m_outBufferSize;
// Attributes
public:
  CSerialPort();                      
  CSerialPort( LPCSTR lpszPortName );
  CSerialPort( LPCSTR lpszPortName, int BaudRate );
//-------- Close serial port and exit thread  
  virtual ~CSerialPort();
//--------
  BOOL Open( LPCSTR lpszPortName, LPCSTR lpszDBCDef = NULL, DWORD EventMask = NULL );
  BOOL Open( LPCSTR lpszPortName, int BaudRate );
//------------------ Closes port, exit thread and destroys window for notification
  void Close();
// Operations
  CString GetPortName()   { return m_strPortName; }
  HANDLE  GetPortHandle() { return m_hPort;       }
  BOOL    EnableMessagePosting( BOOL IsEnable );
//------- Attach port handle and creates thread and windows for communication events
  BOOL    Attach( HANDLE hPort );
//------- Close internal thread and destroy window for messges.
  HANDLE  Detach();
// Overrides
      virtual void OnReceive    ( DWORD /*dwEvMask*/ )  {;}
  virtual void OnPostReceive( DWORD /*dwEvMask*/ )  {;}
//----------- Implementation wrappers
  BOOL  Send( const void* lpBuf, DWORD nBufLen, LPDWORD lpnumbBytesWritten = NULL );
  int   Receive( void* lpBuf, DWORD nBufLen, DWORD timeout = 0, DWORD retryPeriod = 50 );
//******** API wrappers. ASSERT if file handle invalid
  BOOL ClearCommBreak();
  BOOL SetCommBreak();
  BOOL ClearCommError( LPDWORD lpErrors, LPCOMSTAT lpStat );
//---------
  BOOL EscapeCommFunction(  DWORD  dwFunc );
  BOOL GetCommModemStatus( LPDWORD  lpModemStat );
//---------
  BOOL GetCommConfig( LPCOMMCONFIG lpCC, LPDWORD lpdwSize );
  BOOL SetCommConfig( LPCOMMCONFIG lpCC );
//---------
  BOOL GetCommMask( LPDWORD  lpEvtMask );
  BOOL SetCommMask( DWORD  dwEvtMask );
//---------
  SetupComm( DWORD  dwInQueue, DWORD dwOutQueue );
//---------
  BOOL GetCommState( LPDCB  lpDCB );
  BOOL SetCommState( LPDCB  lpDCB );
//---------
  BOOL GetCommProperties( LPCOMMPROP  lpCommProp );
//---------
  BOOL GetCommTimeouts( LPCOMMTIMEOUTS );
  BOOL SetCommTimeouts( LPCOMMTIMEOUTS );
//---------
  BOOL FlushFileBuffers();
  BOOL PurgeComm( DWORD  dwFlags );
//---------
  BOOL TransmitCommChar( char  cChar );
//--------- Frequently used helpers
  BOOL      SetParirty( BOOL bParityChecking );
      BOOL  SetBaudRate( DWORD BaudRate );
      BOOL  SetStopBits( BYTE StopBits );
};

inline BOOL CSerialPort::ClearCommBreak()
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::ClearCommBreak( m_hPort );
}

inline BOOL CSerialPort::SetCommBreak()  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::SetCommBreak( m_hPort );
}

inline BOOL CSerialPort::ClearCommError( LPDWORD lpErrors, LPCOMSTAT lpStat )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::ClearCommError( m_hPort, lpErrors, lpStat );  
}

inline BOOL CSerialPort::EscapeCommFunction( DWORD dwFunc )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::EscapeCommFunction( m_hPort, dwFunc );
}

inline BOOL CSerialPort::GetCommModemStatus( LPDWORD lpModemStat )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::GetCommModemStatus( m_hPort, lpModemStat );
}

inline BOOL CSerialPort::GetCommConfig( LPCOMMCONFIG lpCC, LPDWORD lpdwSize )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::GetCommConfig( m_hPort, lpCC, lpdwSize );
}

inline BOOL CSerialPort::SetCommConfig( LPCOMMCONFIG lpCC )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::SetCommConfig( m_hPort, lpCC, sizeof( COMMCONFIG ) );
}

inline BOOL CSerialPort::GetCommMask( LPDWORD lpEvtMask )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::GetCommMask( m_hPort, lpEvtMask );
}

inline BOOL CSerialPort::SetCommMask( DWORD dwEvtMask )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::SetCommMask( m_hPort, dwEvtMask );
}

inline BOOL CSerialPort::SetupComm( DWORD dwInQueue, DWORD  dwOutQueue)  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::SetupComm( m_hPort, dwInQueue, dwOutQueue);
}

inline BOOL CSerialPort::GetCommState( LPDCB  lpDCB )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::GetCommState( m_hPort, lpDCB );
}

inline BOOL CSerialPort::SetCommState( LPDCB  lpDCB )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::SetCommState( m_hPort, lpDCB );
}

inline BOOL CSerialPort::GetCommProperties( LPCOMMPROP  lpCommProp )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::GetCommProperties( m_hPort, lpCommProp );
}

inline BOOL CSerialPort::GetCommTimeouts( LPCOMMTIMEOUTS lpCommTimeOuts )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::GetCommTimeouts( m_hPort, lpCommTimeOuts);
}

inline BOOL CSerialPort::SetCommTimeouts( LPCOMMTIMEOUTS lpCommTimeOuts )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::SetCommTimeouts( m_hPort, lpCommTimeOuts );
}

inline BOOL CSerialPort::FlushFileBuffers()  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::FlushFileBuffers( m_hPort );
}

inline BOOL CSerialPort::PurgeComm( DWORD  dwFlags )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::PurgeComm( m_hPort, dwFlags );
}

inline BOOL CSerialPort::TransmitCommChar( char cChar )  
{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  return ::TransmitCommChar( m_hPort, cChar );
}

/////////////////////////////////////////////////////////////////////////////
#define WM_SERCOMMEVENT    (WM_USER + 998)
 
#endif // __SERIALPORT_H

// SerialPort.cpp : implementation file
//

#include "stdafx.h"
#include "SerialPort.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#pragma warning(disable: 4355)

/////////////////////////////////////////////////////////////////////////////
// CSerialPortWnd

CSerialPortWnd::CSerialPortWnd( CSerialPort *pSerialPort)

{ m_pSerialPort = ( CSerialPort * )pSerialPort;
}

BEGIN_MESSAGE_MAP(CSerialPortWnd, CWnd)
      //{{AFX_MSG_MAP(CSerialPortWnd)
  ON_MESSAGE( WM_SERCOMMEVENT, OnCommEvent )
  //}}AFX_MSG_MAP
END_MESSAGE_MAP()
 
LONG CSerialPortWnd::OnCommEvent( UINT, LONG dwEvMask )

{ m_pSerialPort->OnPostReceive( dwEvMask );
  return 0L;
}    
/////////////////////////////////////////////////////////////////////////////
// CPortThread
CPortThread::CPortThread(CSerialPort *pSerialPort)

{      m_IsProcessExiting = FALSE;
  m_pSerialPort = pSerialPort;
  m_bAutoDelete = FALSE;
}

CPortThread::~CPortThread()

{
}

BOOL CPortThread::InitInstance()

{ SetThreadPriority( THREAD_PRIORITY_TIME_CRITICAL );
  return TRUE;
}

int CPortThread::ExitInstance()
{
      return CWinThread::ExitInstance();
}
//------------------ Loop with waiting for communication events
int CPortThread::Run()

{      DWORD dwEvMask;
      while ( !m_IsProcessExiting )
      {      WaitCommEvent(m_pSerialPort->m_hPort, &dwEvMask, NULL ) ;
            m_pSerialPort->OnReceive( dwEvMask );
    if ( m_pSerialPort->m_CWnd.m_hWnd != NULL && m_pSerialPort->m_IsPostingEnabled )
    { m_pSerialPort->m_CWnd.PostMessage( WM_SERCOMMEVENT, dwEvMask, 0 );
    }
      }
  return 0L;
}
/////////////////////////////////////////////////////////////////////////////
// CSerialPort

IMPLEMENT_DYNCREATE(CSerialPort, CObject)


void CSerialPort::InitData()

{ m_hPort = INVALID_HANDLE_VALUE;
  m_hWriteEvt = CreateEvent( NULL, TRUE, FALSE, NULL );
  m_inBufferSize  =  0;
  m_outBufferSize =  0;
  m_IsPostingEnabled = TRUE;
}


CSerialPort::CSerialPort() : m_CWnd( this ), m_PortThread( this )

{ InitData();
}

CSerialPort::CSerialPort( LPCSTR lpszPortName ) : m_CWnd( this ), m_PortThread( this )

{      InitData();
  m_strPortName = lpszPortName;
  Open( lpszPortName );
}

CSerialPort::CSerialPort( LPCSTR lpszPortName, int BaudRate ) : m_CWnd( this ), m_PortThread( this )

{      InitData();
  m_strPortName = lpszPortName;
  Open( lpszPortName, BaudRate );
}

CSerialPort::~CSerialPort()

{ if ( m_hWriteEvt != NULL )
  { CloseHandle( m_hWriteEvt );
  }
  Close();
}

/////////////////////////////////////////////////////////////////////////////
// CSerialPort message handlers

BOOL CSerialPort::Open( LPCSTR lpszPortName, LPCSTR lpszDBCDef, DWORD EventMask )

{      if ( lpszPortName == NULL )                         goto FileOpenError;
//-------------- Saves the port name and buffers sizes.  
  m_strPortName   =  lpszPortName;
//------------- Opens
  m_hPort = CreateFile
  ( lpszPortName,
            GENERIC_READ | GENERIC_WRITE,
            0,                                                                              // exclusive access                
            NULL,                           // no security attributes
            OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL,
            NULL                            // No template
  );
      if ( m_hPort == INVALID_HANDLE_VALUE )              goto FileOpenError;
//---------------      
  if ( lpszDBCDef != NULL )
  { DCB commSetings;
        GetCommState( &commSetings );
    BuildCommDCB( lpszDBCDef, &commSetings );
    if ( !SetCommState( &commSetings ) )              goto FileOpenError;
  }
//---------------      
  m_inBufferSize  =  1024 ;
  m_outBufferSize =  1024;
  if ( !SetupComm( m_inBufferSize, m_outBufferSize ) )goto FileOpenError;
//----------------- Set comm timeouts.
  COMMTIMEOUTS commTimeOuts;
      ZeroMemory( &commTimeOuts, sizeof( commTimeOuts ) );
      commTimeOuts.ReadIntervalTimeout = MAXDWORD;
  if ( !SetCommTimeouts( &commTimeOuts ) )            goto FileOpenError;
//------------------ Event mask
  if ( !SetCommMask( EventMask ) )                    goto FileOpenError;
//-------------------- Creates invisble window for posting of messages with desctop parent      
  if ( !m_CWnd.Create
       ( NULL,
         "Serial Port Notification Wnd",
         NULL,
         CRect( 0, 0, 100, 100 ),
         CWnd::GetDesktopWindow(), 0
       )
     )  goto FileOpenError;
//-------------------- Creates thread waiting for communication event      
  if ( !m_PortThread.CreateThread() )                 goto FileOpenError;
  return TRUE;
//---------------- If error occured during opening, the control jump here.
FileOpenError:
  char buffer[0x50];
  sprintf( buffer, "Error opening the port :%s", lpszPortName );
  AfxMessageBox( buffer );
  Close();      
  return FALSE;
}

BOOL CSerialPort::Open( LPCSTR lpszPortName, int BaudRate )

{ BOOL retVal = Open( lpszPortName );
  if ( retVal )
  { SetBaudRate( BaudRate );
  }
  return retVal;
}

void CSerialPort::Close()

{      if ( m_CWnd.m_hWnd != NULL )
      {      VERIFY( m_CWnd.DestroyWindow() );
      }
//------------- Breaks the loop in comm. thread and closes the port
  m_PortThread.m_IsProcessExiting = TRUE;
  if ( m_hPort != INVALID_HANDLE_VALUE ) // You either didn't open the port or port open error occured
  { SetCommMask( 0 );
    PurgeComm( PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR );
    CloseHandle( m_hPort );  
    m_hPort = INVALID_HANDLE_VALUE;
  }
//------------ Waits until thread waiting for comm event exits.
      if ( m_PortThread.m_hThread != NULL )
  { while ( WAIT_TIMEOUT == WaitForSingleObject( m_PortThread.m_hThread, 0 ) )
    { static MSG msg;
      while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
        DispatchMessage ( &msg );
    }
    m_PortThread.m_hThread = NULL;
  }
}                  

HANDLE CSerialPort::Detach()

{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
  HANDLE ret_hPort = m_hPort;
  m_hPort = INVALID_HANDLE_VALUE;
  Close();
  return ret_hPort;
}

BOOL CSerialPort::Attach( HANDLE hPort )

{ if ( m_hPort != INVALID_HANDLE_VALUE )
  { ASSERT( FALSE );
    return FALSE;
  }
  ASSERT( hPort != INVALID_HANDLE_VALUE );
  m_hPort = hPort;
//-------------------- Creates invisble window for posting of messages with desctop parent      
  if ( !m_CWnd.Create( NULL, NULL, NULL, CRect( 0, 0, 100, 100 ), CWnd::GetDesktopWindow(), 0 ) )
  { return FALSE;
  }
//-------------------- Creates thread waiting for communication event      
  if ( !m_PortThread.CreateThread() )
  { return FALSE;
  }
//---------------- Success!!!
  return TRUE;
}
           
BOOL CSerialPort::Send( const void* lpBuf, DWORD nBufLen, LPDWORD lpnumbBytesWritten )

{ ASSERT( m_hPort != INVALID_HANDLE_VALUE );
//----------- Preparing of ovelapped strcture
  OVERLAPPED lv_overlap;
  lv_overlap.Internal = lv_overlap.InternalHigh = lv_overlap.Offset = lv_overlap.OffsetHigh = 0;
  lv_overlap.hEvent = m_hWriteEvt;
//---------- Now start loop for writing of data. One cycle for small chunks.
  DWORD bytesWritten = 0;
  for ( BYTE *pCurPos = (BYTE *)lpBuf; pCurPos < (BYTE *)lpBuf + nBufLen;   )
  { DWORD bytesLeft =  nBufLen - ( pCurPos - (BYTE*)lpBuf );
    bytesLeft = bytesLeft < (DWORD)m_outBufferSize ? bytesLeft : m_outBufferSize;
    DWORD singleBytesWritten = 0;
    ResetEvent( m_hWriteEvt );                  
   if ( !WriteFile( m_hPort, pCurPos, bytesLeft, &singleBytesWritten, &lv_overlap ) )
        { if ( GetLastError() == ERROR_IO_PENDING )
              { DWORD waitResult = WaitForSingleObject( m_hWriteEvt, 5000 );
//--------- Internal error occured. The wait should not fail.
        if ( waitResult == WAIT_FAILED )
        { return FALSE;
        }
//--------- If the wait timed out, we promote the pointer by the number of data written and continue
        if ( waitResult == WAIT_TIMEOUT )
        { if( !GetOverlappedResult( m_hPort, &lv_overlap, &singleBytesWritten, FALSE ) )
          { return FALSE;
          }
        }
//--------- Wait succeded, the data was written and we can contunue.        
        if ( waitResult == WAIT_OBJECT_0 )
        { singleBytesWritten = bytesLeft;
        }
//------  No Error, the data was written to the port
              }
              else  // The WriteFile failed not due to overlapped IO.
              { return FALSE;
              }
        }
    bytesWritten += singleBytesWritten;
    pCurPos += singleBytesWritten;
  }
//---------- If pointer for number of bytes written was suuplied, we fill it
  if( lpnumbBytesWritten )
  { *lpnumbBytesWritten = bytesWritten;
  }
      return TRUE;
}

int CSerialPort::Receive( void* lpBuf, DWORD nBufLen, DWORD timeout, DWORD retryPeriod )

{      ASSERT( m_hPort != INVALID_HANDLE_VALUE );
//--------- Now reading of data.
  int bytesRead = 0;
//----------- Preparing of ovelapped strcture
  OVERLAPPED lv_overlap;
  lv_overlap.Internal = lv_overlap.InternalHigh = lv_overlap.Offset = lv_overlap.OffsetHigh = 0;
  lv_overlap.hEvent = m_hWriteEvt;
//*********** Start the loop for reading of data from teh port. Usually only one cycle
  DWORD startTime = GetTickCount();
  for ( BYTE *pCurPos = (BYTE *)lpBuf; pCurPos < (BYTE *)lpBuf + nBufLen;   )
  { DWORD bytesLeft =  nBufLen - ( pCurPos - (BYTE*)lpBuf );
    bytesLeft = bytesLeft < (DWORD)m_inBufferSize ? bytesLeft : m_inBufferSize;
    DWORD singleBytesRead = 0;
    BOOL res = ResetEvent( m_hWriteEvt );                  
//*********** Here the single read of data. IF any of API fails - return -1
    if ( !ReadFile( m_hPort, pCurPos, bytesLeft, &singleBytesRead, &lv_overlap ) )
    {//------- If file was opened with overlapped flag, then the function allways enter here  
      if ( GetLastError() == ERROR_IO_PENDING )
      { DWORD waitResult = WaitForSingleObject( m_hWriteEvt, timeout );
//--------- Internal error occured. The wait should not fail.
        if ( waitResult == WAIT_FAILED )
        { return -1;
        }
//--------- If the wait timed out, there was not enough data received
        if ( waitResult == WAIT_TIMEOUT ) // During timeout the data was not read
        { if( !GetOverlappedResult( m_hPort, &lv_overlap, &singleBytesRead, FALSE ) )
          { return -1;
          }
        }
//---------- Wait succeded, we heve requested data
        ASSERT( waitResult == WAIT_OBJECT_0 );
        { singleBytesRead = bytesLeft;
        }
      }
      else // The error is not IO_PENDING. Some other error on serial port.
      { return -1;
      }
    }
//----------------- Advance position in buffer and number of bytes read.
    bytesRead += singleBytesRead;
    pCurPos += singleBytesRead;
//----------------- Check timeout and waits.
    if ( timeout != INFINITE )
    { if ( timeout <= GetTickCount() - startTime )
      { break;
      }
    }
//----------------- If the last read didn't bring any data, it make sense to wait.
    if ( singleBytesRead == 0 )
    { Sleep( retryPeriod );
    }
  }
  return bytesRead;
}

BOOL CSerialPort::SetBaudRate( DWORD BaudRate )

{ DCB structDCB;
      GetCommState( &structDCB );
  structDCB.BaudRate = BaudRate;
      return SetCommState( &structDCB );
}

BOOL CSerialPort::SetParirty( BOOL bParityChecking )

{ DCB structDCB;
      GetCommState( &structDCB );
  structDCB.fParity = bParityChecking;
      return SetCommState( &structDCB );
}

BOOL CSerialPort::SetStopBits( BYTE StopBits )

{ DCB structDCB;
      GetCommState( &structDCB );
  structDCB.StopBits = StopBits;
      return SetCommState( &structDCB );
}

BOOL CSerialPort::EnableMessagePosting( BOOL IsEnable )

{ BOOL retVal = m_IsPostingEnabled;
  m_IsPostingEnabled = IsEnable;
  return retVal;
}

 

0

Featured Post

How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
if loop error 4 77
Turn a spreadsheet into a vba executable. 2 66
bigDiff challenge 17 75
wordcount challenge 11 76
In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…

706 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

19 Experts available now in Live!

Get 1:1 Help Now