Solved

WriteFileEx C++ and threads

Posted on 1998-05-05
3
1,463 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Announcing the Most Valuable Experts of 2016

MVEs are more concerned with the satisfaction of those they help than with the considerable points they can earn. They are the types of people you feel privileged to call colleagues. Join us in honoring this amazing group of Experts.

Question has a verified solution.

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

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: 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.
A short tutorial showing how to set up an email signature in Outlook on the Web (previously known as OWA). For free email signatures designs, visit https://www.mail-signatures.com/articles/signature-templates/?sts=6651 If you want to manage em…

732 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