Solved

How to maintain 'scope' when using unmanaged queue code within a .Net Form Application ?

Posted on 2006-07-14
20
214 Views
Last Modified: 2010-04-24
I have a .Net Form Application which has one main thread created within the Form1_Load() :

private:  System::Void Form1_Load(System::Object * sender, System::EventArgs * e)
{

   PanelThread = new Thread(new ThreadStart(this, MainPanelThreadProc));
   PanelThread->Start();

}

When this thread runs it polls five different sockets of unmanged code (or C/C++ files) that have been included into this project from projects of yester-year.....

It successfully polls these sockets, and the data from these sockets is transferred back to Form1 via global extern 'string' arrays.  The data is then displayed to the screen.  Hence, I know I have no problem collecting data.

I wanted to add one more unmanaged 'C' file which contains queue code responsible for collecting various elements from each socket.  I didn't want to have to learn the .Net queue functions given time, although it may have been the better way to go.  The queue has no problem displaying five icons on the Form when called by MainPanelThreadProc() during each iteration:

// global declaration
QueueData   FormAppQueue;

void Form1::MainPanelThreadProc()
{
   initializeQueue(&FormAppQueue);
    ...

     for(;;) {
           //  declared as void SetPictureStatus(...)
           SetPictureStatus(...);                //  Loads queue objects & displays pictures based on queue line up i.e. 1st, 2nd,
           
            if(Group1_SocketFlag == 1)    //  collects data and would update the head queue object if possible !!    
                    GroupOneData();            //  declared as void Form1::GroupOneData()
            if(Group2_SocketFlag == 1)     //  collects data and would update the head queue object if possible !!
                    ReadGroupTwoData()      //  declared as void Form1::GroupTwoData()
            if(Group3_SocketFlag == 1)     //  collects data and would update the head queue object if possible !!
                    GroupThreeData()           //  declared as void Form1::GroupTwoData()
             ...
      }
}

SetPictureStatus() successfully loads the queue with five objects and initializes LineUp.  To keep things simple, it looks something like this:

int QueueIndex;
QStruct   QObject;

QueueIndex = (FormAppQueue.head + 1) % 6;                // head is one greater than actual head
QObject       = FormAppQueue.elements[QueueIndex];     // get head
QObject.LineUp = LineUp;                                               // tracks total queue objects

All I want to do for the moment is prove one simple test which is to read GroupOneData, deposite one 'member' into the queue head, and remove one 'object' from the queue based on that deposit.

However, when I step through the debugger the program reveals that the data is being read and deposited, but when I circle back to SetPictureStatus() and perform a check on the 'member' deposited from GroupOneData, the data isn't there !  I  can cleary identify the head object by using the LineUp number.  In other words, the queue objects created by SetPictureStatus() maintain their existence and LineUp status but any other assigned data is not really being assigned.

All the line-up numbers are still in place but not _one_ of the queue objects has the updated member value which was clearly assigned during runtime.  I set a watch on QHead within both functions, and observe that both values reside while inside GroupOneData().  Once the program goes on to the other functions and comes back around to SetPictureStatus(), the only data that remains is the original LineUp data from the first call to load.

I'm guessing this problem revloves around the MainPanelThread and the scope it will allow for unmanaged code.  Keep in mind that SetPictureStatus() is able to call the global declared Queue and initialize it with 'LineUp.'  Why then can't any other function have the same success ?

.... I can provide a scaled down project which would be Form1.h, Form1.cpp and queue.cpp if necessary.

Thanks
0
Comment
Question by:John500
20 Comments
 
LVL 48

Expert Comment

by:AlexFM
Comment Utility
.NET framework allows to access windows only from thread which created them. Resullt of any call which uses window handle (like drawing, chamging style, image or text of any window control) is unpredictable. Such calls must be marshalled to main application thread using Invoke or PInvoke functions.
There are not any restrictions on accessing non-windows objects.
0
 

Author Comment

by:John500
Comment Utility
Concerning the use of Invoke or PInvoke functions, could you please give an example in the context of my situation?  

I'm clueless and need a code example.

Thanks !
0
 

Author Comment

by:John500
Comment Utility
AlexFM,

I read your answer over a couple of times and I understand what's going on but I could use a little more feed back.

Regarding windows handles, I know what you mean.  I had to do the following in order to get my picture boxes to display from the unmanaged code:

if(pictureBox1->IsHandleCreated && pictureBox1->InvokeRequired)
          pictureBox1->Invoke(__gc new System::ThreadingThreadStart(this, &Form1::HandlePictureBox1Update));

HandlePictureBox1Update sets a boolean accordingly.

This example makes it clear to me how that the unmanaged code would not be able to paint the picture unless I performed an Invoke().  However, please consider my scenario again so that we might detect the trouble area.

First off, keep in mind that my issue is that I can't get the queue to hold data when it is assigned outside the MainPanelThreadProc() function and I'm not trying to draw, change style, change image or text of any window control:

void Form1::MainPanelThreadProc()
{
   initializeQueue(&FormAppQueue);
    ...

     for(;;) {
           //  declared as void SetPictureStatus(...)
           SetPictureStatus(...);               //  Loads queue objects & displays pictures based on queue line up i.e. 1st, 2nd,
           
            if(Group1_SocketFlag == 1)    //  collects data and would update the head queue object if possible !!    
                    GroupOneData();            //  declared as void Form1::GroupOneData()
            if(Group2_SocketFlag == 1)     //  collects data and would update the head queue object if possible !!
                    ReadGroupTwoData()      //  declared as void Form1::GroupTwoData()
            if(Group3_SocketFlag == 1)     //  collects data and would update the head queue object if possible !!
                    GroupThreeData()           //  declared as void Form1::GroupTwoData()
             ...
      }
}
 
SetPictureStatus() has no problem loading the objects of the queue and using a subfunction to paint the pictures as I show above.  My problem is the GroupOneData() and other functions which should be updating the queue.  When these functions call upon the global queue to make an assignment, the assignment does not stick outside those functions.

Does anything come to mind as you look at this again?  I realize you said there are no restrictions on accessing non-windows objects and this holds true as I watch the assignment take place in the debugger.  But why would the data be lost once outside the function which does the assignment?

When I try to access the data elsewhere, the only data available is the LineUp number which was assigned when the object was first created and stuck into the queue.

Thanks again


 




0
 

Author Comment

by:John500
Comment Utility
Here's the portion of code that should be working to update the queue.  As I say, when I step through the debugger I can see the data is being assigned to QHead.  However, once this function has completed, and the 'SetPictureStatus()' function is called, the data is not in the head or anywhere else.  The reason I know this is because I can track queue objects by the 'LineUp' number which SetPictureStatus() initialized during the creation of the queue objects:



Group3Data.cpp               // collects socket data and stores to global array
----------------------------------------------------------------------------------
extern string Group3Data[3];    // global decalaration -- stores socket data collected



Form1.cpp                  //  handles display of data and storage of data to queue
----------------------------------------------------------------------------------

string Group3Data[3];            //  global declaration



void Form1::Group3SocketProc()
{
      QueStruct QHead;
      int QueueIndex;
      long TestData1;
      
      ReadSocket();      // calls a unmanged code to collect socket info and store to global string array

      // updates queue for future testing of data

      QueueIndex                  = (FormAppQueue.head + 1) % 6;            // set index
      QHead                        = FormAppQueue.elements[QueueIndex];      // get queue head
      QHead.Test_Data1            = Group3Data[1];                  // assign data to queue head
      TestData2;                  = atol(Group3Data[2];.c_str());            // convert from char to long
      QHead.Test_Data2            = TestData2                        // assign data to queue head

      // update groupBox display by converting 'string' to '__gc string'
      listBox9->BeginUpdate();
      listBox9->Items->Clear();
      String *TestSocketData1 = __gc new String(Group3Data[1].c_str());
         listBox9->Items->Add(*TestSocketData1);
          listBox9->EndUpdate();

      // update groupBox display
      listBox17->BeginUpdate();
      listBox17->Items->Clear();
      String *TestSocketData2 = __gc new String(Group3Data[2].c_str());
          listBox17->Items->Add(*TestSocketData2);          listBox17->EndUpdate();

      
      for(int i = 0; i < 4; i++)
            Group3Data[i] = "";

      return;
}
0
 
LVL 15

Expert Comment

by:lakshman_ce
Comment Utility
Check the data in FormAppQueue before executing the code

for(int i = 0; i < 4; i++)
          Group3Data[i] = "";

If it is pointing to the data in Group3Data, it may be lost during this code execution.

0
 
LVL 15

Expert Comment

by:lakshman_ce
Comment Utility
what parameters you pass to SetPictureStatus(..) method? If you are passing FormAppQueue pass it by reference.
0
 

Author Comment

by:John500
Comment Utility
lakshman_ce,

I figured the same thing might be happening with the 'for' loop so I simply commented it out and still had the same results.  Then, on top of that, instead of assigning the values from the array, I simply hard-coded them like this:

QHead.Test_Data1   = "Test"
QHead.Test_Data2   = 0;

Still no different.

Concerning the parameters for SetPictureStatus() they look like this and are initialized within MainPaneThreadProc():

void Form1::MainPanelThreadProc()
{
   initializeQueue(&FormAppQueue);
    int LineUp = 0;
    int SideNumber = 499;
    bool AddAircraft = true;

    for(;;){

            SetPictureStatus(SideNumber, LineUp, AddAircraft)
      ...
      ...
    }
}

A point worth noting here is that MainPanelThreadProc() tracks LienUp and SideNumber and these are the only values that stay within each queue object.  In other words, I can see these values from any othe function that calls the queue.

If the problem doesn't become obvious, I post Form1.h, Form1.cpp & queue.cpp.  You already ran the queue code a little while back if you remember................
0
 
LVL 15

Expert Comment

by:lakshman_ce
Comment Utility
where you are calling Group3SocketProc method?

Another possiblity is InitializeQueue code  locking the object and not making it available for update.

Synchoronize the global FormAppQueue object between the InitializeQueue and Group3SocketProc methods.
0
 

Author Comment

by:John500
Comment Utility
lakshman_ce,

To answer your question, I'm calling 'initializeQueue(&FormAppQueue) within MainPanelThreadProc() like this which would seem to be synchronized with Group3SocketProc() right ?

void Form1::MainPanelThreadProc()
{
    initializeQueue(&FormAppQueue);
    int LineUp = 0;
    int SideNumber = 499;
    bool AddAircraft = true;

        for(;;) {
           //  declared as void SetPictureStatus(...)
           SetPictureStatus(...);               //  Loads queue objects & displays pictures based on queue line up i.e. 1st, 2nd,
           
            if(Group1_SocketFlag == 1)    //  collects data and would update the head queue object if possible !!    
                    GroupOneData();            //  declared as void Form1::GroupOneData()
            if(Group2_SocketFlag == 1)     //  collects data and would update the head queue object if possible !!
                    ReadGroupTwoData()      //  declared as void Form1::GroupTwoData()
            if(Group3_SocketFlag == 1)     //  collects data and would update the head queue object if possible !!
                    GroupThreeData()           //  declared as void Form1::GroupTwoData()
             ...
      }
}

Also, remember that the queue (FormAppQueue) is declared globally within Form1.cpp.  I tried putting the call to initializeQueue() like this:

int APIENTRY _tWinMain(hinstance hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{

     initializeQueue(&FormAppQueue);

     Application::Run(new Form1());
     ...

     return;
}
 
.... but this didn't do the trick either.  Also, please note that I needed to start the MainPanelThread() from :

System::Void Form1_Load(System::Object * sender, System::EventArgs * e)
{
    PanelThread == new Thread(new ThreadStart(this, MainPanelThreadProc));
    PanelThread->Start();
}

Do you think this is causing a lock ?  How else could I start this thread?
0
 
LVL 49

Expert Comment

by:DanRollins
Comment Utility
First, this may be an issue similar to that of one of your previous questions -- perhaps the data is there, but you are mis-interpretting what the debugger is telling you.

One tip:  
In your code that removes an item from the queue, you should first set it to a value like "Nothing here folks!" and a recognizable status of 0x123456789, then delete it.  If you later accidentally try to access an element that has been removed, that fact will jump out immediately.
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 

Author Comment

by:John500
Comment Utility
>> First, this may be an issue similar to that of one of your previous questions --
>> perhaps the data is there, but you are mis-interpretting what the debugger is telling you.

That was my first impression but that's not the case.  The reason ->  in the previous case when the data was undetectable, I was able to perform logic successfully.  So for instance, even though I couldn't see the value '1' within the 'watch', I was able to perform the test:

If(WaveOff == 1) ....

In this case however, the succeeding logic always fails.  Since I can see the correct value being assigned, and I can view it within the 'watch' while the function is in scope, I know it should be working outside of the scope but it's not.

>>  you should first set it to a value like "Nothing here folks!"
In this case I'm not even making it that far.  Nothing is being deleted.  The queue is filled first and then I attempt to delete one object based upon criteria in the queue... but that criteria isn't getting into the queue or it isn't actually assigned or it isn't accessible out of scope.
0
 

Author Comment

by:John500
Comment Utility
... at this point I'm persuaded to believe this problem centers around where I'm declaring the queue (FormAppQueue).  It is declared globally in Form1.cpp, initialized & destroyed within _tWinMain().

It appears the first function to access the queue - SetPictureStatus() - allocates memory that no other function can access for the sake of making assignments.  All other functions within Form1.cpp can see the queue because it is declared globally... but none of them can access the memory to assign data.  The assignment appears to be made but when the succeeding function calls the queue, the data is not there !@#$%!!

In spite of the fact the queue is declared globally, I have tried passing the queue from MainPanelThreadProc() to SetPictureStatus() and then to GroupThreeData() but that doesn't make any difference.

Should I be declaring the queue from Form1.h ... is this even possible ?   I thought the problem might be due to MainPanelThreadProc() being started from Form1_Load() but I moved it to Form1.cpp under a menu item and that still makes no difference!
0
 
LVL 15

Expert Comment

by:lakshman_ce
Comment Utility
You can try this

1. Add FormAppQueue as member for Form1 class (i.e in Form1.h)
2. Call InitializeQueue method in Form1_Load() before spawning the thread


If this is not working you can use the Collections::Queue and see if you can work it out. Here is a sample of how the queue is used with Monitors to prevent the loss of data

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemthreadingmonitorclassentertopic.asp
0
 

Author Comment

by:John500
Comment Utility
lakshman_ce,


>> 1. Add FormAppQueue as member for Form1 class (i.e in Form1.h)

I wanted to do this but how is it declared ??  I get the following error when I make the declaration public or private:

public OR private:

         static QueueData   FormAppQueue;                 // QueueData is a struct accessed via #include "queue_code.h"

System::Void Form1_Load(.......)
{
   // error C2664: 'initializeQueue': cannot convert param 1 from' QueueData   __gc*' to 'QueueData  *'
    initialize Queue(&FormAppQueue) ;    
   ...
}

or if I remove 'static':

public OR private:

        QueueData   FormAppQueue;

// error on class declaration
public __gc class Form1 : public System:: Windows::Forms::Form
{
    priavate:

    public:
            // Create main menu objects
            ...
             Form1(void) {                      //  ERROR ON THIS LINE

error C2662: 'QueueData::~QueueData' : cannot convert 'this' pointer from 'QueueData' to 'QueueData &'




0
 
LVL 15

Accepted Solution

by:
lakshman_ce earned 500 total points
Comment Utility
Try
private : static QueueData *myqueue;
initializeQueue(myqueue)
0
 

Author Comment

by:John500
Comment Utility
lakshman_ce,

Your suggestion worked but it leaves me with issues on these queue functions:

bool empty(QueueData q);             // determine if queue is empty --- no pointer
bool full(QueueData q);                 //  determine if queue is full     --- no pointer

'empty()' would be used in the Form's destructor:

while(!empty(FormAppQueue))    
         delet(QueueData, QTerminator)

Can you shoot me a response letting me know if a work-around comes to mind for such functions that require the actual queue rather than a pointer?

In the mean time, I'll accept your last post on this question and I've opened a new one for help getting the 'Collections::Queue' compiled in my project.

Take a look when you get the chance.  Thanks

http://Q_21923471.html

0
 
LVL 15

Expert Comment

by:lakshman_ce
Comment Utility
Pass *myqueue to the
empty and full functions.

empty(*myqueue);
full(*myqueue);
0
 

Author Comment

by:John500
Comment Utility
The more I think about this the more I think my problem may have boiled down to not having a constructor or the equivalent.

In other words, we agree I should delcare like this:

private :
        static QueueData *myqueue;

but what does '*myqueue' point to ?  I included at the top of Form1.h the appropriate header file which contains the definition of a queue called 'QueueData' but the function called 'initializeQueue() looks like this:

void initializeQueue(QueueData *q)
{
       q->head = q->tail = MAX_Q_SIZE -1;
}

The queue looks like this:

typedef struct{
       QueueStruct   elements[MAX_Q_SIZE];
       int head;
       int tail;
}QueueData

My point in all this is that although I can declare a pointer in Form1.h, it doesn't point to anything !  Since initializeQueue() only sets the head and tail, that doesn't take care of allocating a 'new' QueueData structure - right ?


0
 
LVL 15

Expert Comment

by:lakshman_ce
Comment Utility
You can allocate memory before calling InitializeQueue in Form_Load method

myqueue = new QueueData();
InitializeQueue(myqueue);



0
 

Author Comment

by:John500
Comment Utility
lakshman_ce,

Thanks, the program compiles.  I'm going to test it out shortly.  I think this was my whole problem in terms of being able to store data !!!

Thanks again.




0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

The following diagram presents a diamond class hierarchy: As depicted, diamond inheritance denotes when two classes (e.g., CDerived1 and CDerived2), separately extending a common base class (e.g., CBase), are sub classed simultaneously by a fourt…
In Easy String Encryption Using CryptoAPI in C++ (http://www.experts-exchange.com/viewArticle.jsp?aid=1193) I described how to encrypt text and recommended that the encrypted text be stored as a series of hexadecimal digits -- because cyphertext may…
This video discusses moving either the default database or any database to a new volume.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

763 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

11 Experts available now in Live!

Get 1:1 Help Now