Solved

WriteFileEx C++ and threads

Posted on 1998-05-05
3
1,447 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

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Looking for a specific application/software 2 114
unable to delete all specified values regedit 38 544
List out all word 7 316
Message not shown 5 67
Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
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 an interesting question (https://www.experts-exchange.com/questions/29008360/) here at Experts Exchange, a member asked how to split a single image into multiple images. The primary usage for this is to place many photographs on a flatbed scanner…

839 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