Solved

WriteFileEx C++ and threads

Posted on 1998-05-05
3
1,404 Views
Last Modified: 2013-11-20
I have spawned four threads that write to four different serial ports. Each is spawned as an instance of the class Centrix.  I am using C++ and the call WriteFileEx to write to the ports.  The problem is with the IOCOMPLETION routine for writefileex. I seem to be only able to to declare the call back routine in the class as follows
      static void WINAPI CCentrix::WriteCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfNytesTransferred, LPOVERLAPPED pOverlapped);
 
Within the callback routine I want to compare the number of bytes transferred with the number of bytes to transfer.
void WINAPI CCentrix::WriteCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfNytesTransferred, LPOVERLAPPED pOverlapped)
{

if (dwNumberOfNytesTransferred != m_DWBytesToWrite)
      {
      TRACE("WARNING: WriteFileEx() error.. Bytes Sent: %d; Message Length: %d\n", m_DWBytesToWrite, dwNumberOfNytesTransferred);
      }
}

The variable m_DWBytesToWrite is a class member of Centrix(my own class).  Because the routine is static I don't get a this pointer so therefore I cannot access the class member variable m_DWBytesToWrite If I declare the variable static it does not exist for a particular instance of the object which defeats the purpose of having a different instance of the class for each thread.

What I want to do is to be able to access the class member variables within the callback routine.Also I want the callback routine tied to a particular instance of the object.The solution would be to delcare the callback routine as follows
void WINAPI CCentrix::WriteCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfNytesTransferred, LPOVERLAPPED pOverlapped)
but this gives me a compile error in the call to WriteFileEx. If anyone knows how I can declare the WriteCompletionRoutine as not being static or some other way of solving this problem I would be gratefull. I know that I can use the call WriteFile instead but I want to use the WriteFileEx call.

Regards,
Eddie.

Eddie
 
0
Comment
Question by:epmcevoy
  • 2
3 Comments
 
LVL 4

Accepted Solution

by:
piano_boxer earned 100 total points
ID: 1307537
Your right in that you can only declare the callback as a static member function of your class.

To get to the correct class from the callback, derive a new class from OVERLAPPED and include a pointer to the class in it.

class COverlapped : public OVERLAPPED
{
    CCentrix* m_pClass;
}

Replace your OVERLAPPED structs with this new class and before using it initialize m_pClass to the object that 'owns' the overlapped operation.

Then in your WriteCompletionRoutine cast the pOverlapped param to COverlapped

    COverlapped* pOL = (COverlapped*)pOverlapped;
    CCentrix* pClass = pOL->m_pClass;

One other thing you could do with the COverlapped class is to make the constructor initialize the OVERLAPPED struct:

COverlapped::COverlapped()
{
    Internal = 0;
    InternalHigh = 0;
    Offset = 0;
    OffsetHigh = 0;
    hEvent = NULL;
}

REMEMBER: Treat the class COverlapped as a normal overlapped structure.

   
0
 

Author Comment

by:epmcevoy
ID: 1307538
Thanks, I have implemented your soultion and I can now access the class members. I have one addition that I was wondering if you could answer.. Because the callback proc is static do I have to do some thread sychncronization before I call the WriteFileEx procedure. Reason being that each thread will be calling the same callback procedure and because it is static it is not owned by the calling class. If this is the case how do I perform simultanious writes to different comports using the WriteFileEx.

See below

void WINAPI CCentrix::WriteCompletionRoutine(DWORD dwErrorCode, DWORD dwNumberOfNytesTransferred, LPOVERLAPPED pOverlapped)
{
      COverlapped* pOL = (COverlapped*)pOverlapped;
      CCentrix* pClass = pOL->m_pClass;

if (dwNumberOfNytesTransferred != pClass->m_DWBytesToWrite)
      {
      TRACE("WARNING: WriteFileEx() error.. Bytes Sent: %d; Message Length: %d\n", pClass->m_DWBytesToWrite, dwNumberOfNytesTransferred);
      }
}


void CCentrix::WriteExCmdToPort(CCentrix* port)
{
//      unsigned short length = port->SerialCmdRec.length;
      char Data[] = "Were getting there....slowly but surely Were getting there....slowly but surely Were getting there....slowly but surely";
      BOOL bResult = TRUE;
      COverlapped            WrtExOverlapped;            // New class with overlapped structure in it.
                                                                  

      WrtExOverlapped.m_pClass = port;            // Init the centrix element of WrtExOverlapped to point to the class we are working on at the moment.

      port->m_DWBytesToWrite = port->SerialCmdRec.length;
       ResetEvent(port->m_hWriteCmdEvent);

       // Gain ownership of the critical section so that we have only one callback routine running at any one particular time.
      EnterCriticalSection(&port->m_csCommunicationSync);

      
      // Initailize variables      
      WrtExOverlapped.Offset = 0;
      WrtExOverlapped.OffsetHigh = 0;

      port->m_ov.Offset = 0;            
      port->m_ov.OffsetHigh = 0;

      // Clear buffer
      PurgeComm(port->m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

      bResult = WriteFileEx(port->m_hComm,
                                          port->m_szWriteCmdBuffer,
                                          port->m_DWBytesToWrite,
                                          &WrtExOverlapped,
                                          WriteCompletionRoutine);
      

      SleepEx(INFINITE, TRUE);
      
                  
      LeaveCriticalSection(&port->m_csCommunicationSync);



}
0
 
LVL 4

Expert Comment

by:piano_boxer
ID: 1307539
In your case I do not think you need to do any thread sync. It should be safe for you to remove the EnterCriticalSection() / LeaveCriticalSection() calls.

Each time the callback is called, you only touch data belonging to the SAME instance of the CCentrix or COverlapped class as allocated for the thread.

Remember IO completion routines are called in the context of the threads then called WriteFileEx().

Just keep in mind, if two or more threads accesses the same variable (and modyfies it), you need to protect it by a critical section (or simular).

TIP! Use the CCriticalSection/CSingleLock classes, its much easier.
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

Introduction: Ownerdraw of the grid button.  A singleton class implentation and usage. Continuing from the fifth article about sudoku.   Open the project in visual studio. Go to the class view – CGridButton should be visible as a class.  R…
Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
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.
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

14 Experts available now in Live!

Get 1:1 Help Now