[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1512
  • Last Modified:

WriteFileEx C++ and threads

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
epmcevoy
Asked:
epmcevoy
  • 2
1 Solution
 
piano_boxerCommented:
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
 
epmcevoyAuthor Commented:
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
 
piano_boxerCommented:
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: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now