Why cant code in a modeless dialog function use my external access function?

Gesperrt Why cant code in a modeless dialog function use my external access function?

  • Thursday, May 03, 2012 10:05 PM
     
     

    I have four C++ files, kb.cpp, kb.h, enumerate_launcher.cpp, enumerate_launcher.h.

    As follows:
    -------------------------------------
    // kb.cpp

    #include "kb.h"
    #include "enumerate_launcher.h"

    #define KB_G

    void enumerate_things() {
    // Uses object candidates, defined in kb.h
     return;
    }

    void make_bundles() {
    enumerate_launcher *guard= NULL;

    prep_candidates(); // Makes candidates and num_candidates.
      if ((candidates != NULL) && (num_candidates > 0)) {
    // At this point (breakpoint), candidates and num_candidates are what I expected.
        guard= new enumerate_launcher(AfxGetMainWnd()); // Bring up the modeless dialog.
        guard->Create(IDD_ENUMERATE_LAUNCHER, NULL);
        guard->ShowWindow(SW_SHOWNORMAL);
      }
      return;
    } // make_bundles

    // ACCESS FUNCTIONS

    thing **get_candidates() {
    return(candidates);
    } // get_candidates

    int get_num_candidates() {
    return(num_candidates);
    } // get_num_candidates

    -------------------------------------------------
    // kb.h

    #ifdef KB_G
    static thing **candidates= NULL;
    static int num_candidates= 0;
    #endif  

    void enumerate_bundles();

    // ACCESS FUNCTIONS
     
    thing **get_candidates(); 
    int get_num_candidates();

    ---------------------------------------------
    // enumerate_launcher.cpp

    // enumerate_launcher is a modeless dialog. When it becomes visible, I click the BEGIN button
    // IDC_BEGIN, which causes the strange behavior I'm asking about.

    enumerate_launcher::enumerate_launcher(CWnd* pParent /*=NULL*/)
    : CDialog(enumerate_launcher::IDD, pParent) {
    } // enumerate_launcher constructor

    enumerate_launcher::~enumerate_launcher() {
    } // enumerate_launcher destructor

    void enumerate_launcher::DoDataExchange(CDataExchange* pDX) {
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_BEGIN, start_enum);
    } // enumerate_launcher::DoDataExchange

    BEGIN_MESSAGE_MAP(enumerate_launcher, CDialog)
    ON_BN_CLICKED(IDC_BEGIN, &enumerate_launcher::OnClickedBegin)
    END_MESSAGE_MAP()

    // enumerate_launcher message handlers 
     
    BOOL enumerate_launcher::OnInitDialog() {
    CDialog::OnInitDialog();

    return(TRUE); 
    } // enumerate_launcher::OnInitDialog

    void enumerate_launcher::OnClickedBegin() {
    BOOL counted_them= TRUE;
    // DEBUGGERY
    thing **checkit= get_candidates();
    // END DEBUGGERY

    // When I get here (breakpoint), checkit comes out NULL!
      if ((get_candidates() != NULL) && (get_num_candidates() > 0)) {
        counted_them= enumerate_bundles(); 
    }
    } // enumerate_launcher::OnClickedBegin

    ---------------------------------------------

    // enumerate_launcher.h

    class enumerate_launcher : public CDialog {
    DECLARE_DYNAMIC(enumerate_launcher)
    public:
    enumerate_launcher(CWnd* pParent = NULL); // standard constructor
    virtual ~enumerate_launcher();
    // Dialog Data
    enum { IDD = IDD_ENUMERATE_LAUNCHER };
    protected:
    virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
    DECLARE_MESSAGE_MAP()
    public:
    virtual BOOL OnInitDialog();
    CButton start_enum;
    afx_msg void OnClickedBegin();
    }; // class enumerate_launcher

    -----------------------------------

    I have kicked out some other variables which are irrelevant.

    My problem is that the dialog knows about  the function enumerate_bundles
    defined in kb, so it should know about get_candidates, also defined in kb.
    But the access function get_candidates returns NULL to the dialog, whereas 
    if I call it after prep_candidates, it will return what I expect.

    In many of my other applications in the past, I have been able to successfully
    pass static variables to other modules this way. What am I missing?
    Is there something magic about modeless dialogs?

All Replies

  • Friday, May 04, 2012 1:51 AM
     
     Answered
    In many of my other applications in the past, I have been able to successfully
    pass static variables to other modules this way. What am I missing?
    Is there something magic about modeless dialogs?
    If you define variables in a header file, then each .cpp file that inclides this header gets a separate copy.
     
    If you must use global variables, then use this pattern:
     
    // kb.h
     
    extern thing **candidates; // declaration
    extern int num_candidates; // declaration
     
    // kb.cpp
     
    thing **candidates = NULL; // definition
    int num_candidates = 0; // definition
     

    David Wilkinson | Visual C++ MVP
    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •  
  • Friday, May 04, 2012 4:32 AM
     
     Answered

    I made one mistake when I presented this- in kb.cpp, I really had the #define KB_G before #include "kb.h".

    Please correct me if I am wrong, but I believe that the effect of this strategy is to exclude the statics from being

    seen by the module enumerate_launcher, since there is no such define in it. However, it does know about the

    function enumerate_bundles(). Note that get_candidates() is also communicated to enumerate_launcher. Both functions are outside the ifdef in kb.h.

     get_candidates() should transmit the candidates pointer to the module enumerate_launcher, to the variable named checkit.

    In the enumerate_bundles module, I don't mention candidates- I would get an error if I did.

    The problem manifests itself with a NULL checkit in the function OnClickedBegin, and non-NULL and properly formed candidates in the module kb.

    Please take another look, with the modification of putting #define KB_G before the include, and tell me where I'm wrong.

    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •  
  • Friday, May 04, 2012 10:24 AM
     
     Answered
    I made one mistake when I presented this- in kb.cpp, I really had the #define KB_G before #include "kb.h".
    Well, yes, this was confusing. But really, if you only want to use candidates and num_candidates in kb.cpp, why not put them in kb.cpp? Once again, defining variables in header files is a bad idea.
     
    I really do not understand what your problem is. You do not show the function prep_candidates(), but you say it "makes" candidates and num_candidates. So clearly candidates is NULL unless you call prep_candidates(). If you are expecting candidates to be non-NULL, where do you think you are defining it to be non-NULL?
     

    David Wilkinson | Visual C++ MVP
    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •  
  • Friday, May 04, 2012 5:37 PM
     
     

    Since I last posted, I resolved one mystery, which might explain the problem. You interpreted my cryptic remarks correctly- prep_candidates does make it non-NULL. What I discovered is that after I raised the dialog, my own code deleted and NULLed candidates. I did not count on the asynchronous nature of the dialog- I am using the dialog's button to start a computation using candidates.

    My intent is that the dialog has a begin button, a progress bar, and a terminate-at-any-time button. I have fixed my code so that the begin button triggers what I want it to, I don't prematurely kill candidates, and the progress bar reports the progress of my computation. Unfortunately, while the progress bar is operating, my terminate button is locked out.

    A previous adviso suggested that I don't know anything about the message pump, and that I should use a thread for my computation. What I am trying to do is simple, and I think that all the other Windows apps which I have used, having a single dialog monitoring the action, maybe don't use threads. I think that developers of these GUIs must use some kind of timing tricks to prevent clogging the message pump. But, I haven't figured it out yet.

    What I would like to find is:

    An algorithm, or code, for operation of a dialog which has a simple progress bar, a begin button, and an interruptor button. For my application, I will use the interruptor button to terminate the operation being monitored. My interruptor button doesn't respond until progress is all done, at which time it is useless.

     


    • Edited by CADMeister Friday, May 04, 2012 5:38 PM mistake
    •  
  • Friday, May 04, 2012 5:46 PM
     
     Answered

    Doing the lengthy operation in a worker thread is the preferred answer, because it leaves the primary thread available to respond to a click on the interruptor button. (And also to paint the changed progress bar.)

    You could hack together a solution that uses a timer instead of a thread. The idea is that each time you receive the WM_TIMER message you do a brief portion of the lengthy operation, then return. Continue on the next WM_TIMER, again for a brief interval only.

    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •  
  • Friday, May 04, 2012 5:48 PM
     
     
    What I would like to find is:
     
    An algorithm, or code, for operation of a dialog which has a simple progress bar, a begin button, and an interruptor button. For my application, I will use the interruptor button to terminate the operation being monitored. My interruptor button doesn't respond until progress is all done, at which time it is useless.
    You need to use a worker thread. In an MFC application you should use AfxBeginThread() to start the thread.
     

    David Wilkinson | Visual C++ MVP
  • Saturday, May 05, 2012 12:06 AM
     
     Answered

    Thanx for the teach.

    I had to try my idea but it didn't work.

    With the use of one simple worker thread, triggered by the BEGIN button of my dialog, the whole thing works like a trained pig.

    Seems to me that this world could use a new kind of resource- a multi-control "true" modeless dialog, where the controls simulated parallelism for single cpu machines.

    I wonder if there is such an operating system as a multi-pump system.

    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •  
  • Saturday, May 05, 2012 12:07 AM
     
     Answered

    Thanx for the teach.

    I had to try my idea but it didn't work.

    With the use of one simple worker thread, triggered by the BEGIN button of my dialog, the whole thing works like a trained pig.

    Seems to me that this world could use a new kind of resource- a multi-control "true" modeless dialog, where the controls simulated parallelism for single cpu machines.

    I wonder if there is such an operating system as a multi-pump system.

    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •  
  • Saturday, May 05, 2012 1:16 AM
     
     Answered
    Seems to me that this world could use a new kind of resource- a multi-control "true" modeless dialog, where the controls simulated parallelism for single cpu machines.
    Worker thread works even with a single CPU, because each thread gets time from the OS. Windows is a preemptive multitasking OS:
     
    http://en.wikipedia.org/wiki/Preemptive_multitasking
     

    David Wilkinson | Visual C++ MVP
    • Marked As Answer by CADMeister Wednesday, May 09, 2012 5:09 PM
    •