0

I have a working single-threaded MFC app and am now trying to convert the worker portion to a thread. I am having some trouble with understanding some of the automatically-supplied Visual Studio C++ code. My basic problem is that the thread has to be static code, while the dialog has to be dynamic and there are variables that are global to both. Here are the minimal pieces that seem to be the cause of either undefined references or function must be non-static. I have spent several hours trying to find online references, but have not had success. How can I gain access to m_strTree_IN in both my functions (The errors are noted in my code here):

// M_exifierDlg.cpp 
#include "pch.h"
#include "framework.h"
#include "M_exifier.h"
#include "M_exifierDlg.h"
#include "afxdialogex.h"

CMexifierDlg::CMexifierDlg(CWnd* pParent /*=nullptr*/)
    : CDialogEx(IDD_M_EXIFIER_DIALOG, pParent)
**// I can’t find any guidance to the following syntax (commas after the right paren above)**
        , m_strTree_IN("") // variable attached to dialog textbox 
        , m_strTree_OUT("") // ditto
        , m_inst_text("")   // ditto
            
{
    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMexifierDlg::OnClickedCheck1()
{
    AfxBeginThread(MyThreadControllingFunction, 0);
}

  unsigned int static  MyThreadControllingFunction(LPVOID pParam)
{
      doit();
      return 0;   // thread completed successfully
  }

Void doit()
{
CString X=m_strTree_IN  **//<< ERROR identifier m_strTree_IN is undefined**
// UpDateData(); **//<< ERROR calls with TRUE and FALSE are undefined**
// but see below
}

// In m_exifier.h
Class cMexifierDlg : public CDialogEx
{.
public:
static void CMexifierDlg::doit();
    CString m_strTree_IN;       // INPUT from dialog textbox
};
DocDJ
  • 31
  • 5
  • Since `m_strTree_IN` is a non-static member of class `cMexifierDlg`, you need a class instance (i.e. an object) to use it. Maybe you can use the `LPVOID pParam` arg to pass a pointer to `cMexifierDlg` (the `this` pointer to be used to access `m_strTree_IN`). – wohlstad Apr 16 '23 at 15:55
  • See the code [here](https://learn.microsoft.com/en-us/cpp/parallel/multithreading-creating-worker-threads?view=msvc-170). Pass `this` from your dialog as the `LPVOID pParam`. You could also have used `std::bind`, maybe a callback to a member. It depends on the objective. – lakeweb Apr 16 '23 at 16:51
  • Would the elements of the object be writable from inside the thread code (assuming I know how to handle synchronization- although there will be ONLY one thread)? I have several variables to share between the main dialog code and the thread. @wohlstad: could you post a brief example of doing that? – DocDJ Apr 16 '23 at 19:54
  • 2
    *"My basic problem is that the thread has to be static code"* - There is no such thing as *"non-static"* code. *All* code is static (excepting the odd case of run-time generated code). That's not your issue. What you probably *meant* is that a thread procedure cannot be a non-static class member. Which is true, but doesn't limit you in passing arbitrary state (such as an instance pointer) to a thread procedure. You should take a detour, dropping MFC for now, and learn how to program against the Win32 API, without a framework getting in your way of grasping the fundamental concepts. – IInspectable Apr 16 '23 at 21:57
  • 2
    In general, it is not a good idea handling UI elements in a thread. (`UpdateData()` ) – Tom Tom Apr 17 '23 at 04:49
  • I have used Win32 to build a working model of a variable-size wind-turbine farm, using semaphores, mutexes and condition-variables, where main & turbine threads synchronize with each other using global data. My problem is the mechanics of MFC to do the same between dialog and thread using global data. It is the C++ & MFC framework where I need help. I asked for references and documentation. The VS error msgs led me to believe that vars with "static" type are req'd by threads, but dialogs don't like them. So I need references to MECHANICS of sharing. MS Learn pages are not much help. – DocDJ Apr 17 '23 at 09:25
  • @TomTom: My dialog gathers the data that the thread will use to do its work. The thread needs to update the textboxes to communicate with the user. I currently use MessageBoxes, but that is cumbersome, slow and annoying to the user. – DocDJ Apr 17 '23 at 09:30
  • @DocDJ You need to elaborate GUI-thread vs Worker-Thread. This is an Oldie but Godie [link](http://www.flounder.com/workerthreads.htm) – Tom Tom Apr 17 '23 at 10:46
  • 1
    @TomTom is right, the owning (UI) thread should be making all updates of its controls, the worker thread should better only be collecting and processing the data in the background, and when new data are available notify the UI thread to update the controls. This can be done by posting a custom message (`WM_APP+nnnn`) to the UI thread (the `wParam` and `lParam` params may contain info about the type of updates needed, or even hold the data themselves, like handles or pointers to memory blocks. – Constantine Georgiou Apr 17 '23 at 11:12

1 Answers1

0

The code you provided won't work at all as Void doit() should read void CMexifierDlg::doit() in order to access the m_strTree_IN member of the dialog class (also needed: an instance of the dialog handled to the worker thread via pParam in order to call the member from the worker), which is what the is undefined error message is trying to tell you. (The easier way would be to just use a static variable outside the class to communicate with the dialog.)

However, it's not a good idea to interfere with the user interface code of the dialog from within the worker thread directly. If you worked with mutexes/semaphores before, then use them to drop data into the dialog context but let the dialog's user interface thread display them, for example check if there is something to display during the dialog's WM_TIMER event or, even better, post a windows message in the worker to signal the dialog to process new data.

thomiel
  • 2,467
  • 22
  • 37
  • "an instance of the dialog handled to the worker thread via pParam". I think I understand what you are saying, but I can't seem to find the proper syntax and stmts to do it. I agree with your stmt about not messing with the dialog controls and can modify that after I figure out how to actually create the thread using the "MFC method". How can I paste what I have so far? It's too big for a comment here. – DocDJ Apr 17 '23 at 13:00
  • It's all available in the documentation. Both MFC's `AfxBeginThread()` and Win32's `CreateThread()` take an application-defined `LPVOID` "parameter" (`pParam` or `lpParameter`) which is passed to the thread procedure as is. You can cast any data type that can fit in there (int, pointer, handle etc) to `LPVOID` and pass it to the thread proc. You can find an example of how this works (incl the message notification mechanism) in my recent answer to TomTom's question [here](https://stackoverflow.com/questions/75110448/allow-start-only-one-thread). – Constantine Georgiou Apr 17 '23 at 13:34
  • yes, I have that part, but I keep getting a problem with "non-static member reference must be relative to a specific object" for every function-call (both my own functions & UpdateData) and "global" variable referenced from within my thread code. How can I make the variables global for all my thread functions (parts of the single thread), but separate from the dialog "space"? – DocDJ Apr 17 '23 at 14:42
  • This is a C++ feature. Static members are akin to global, in that they are instantiated only once, statically. Therefore. from inside a static member function you may only access static members. For the non-static ones you need a class instance (pointer or reference), as these are instantiated once for every instance of the class. So something like `pMexDlg->m_strTree_IN`. As noted above, this can be passed to the thread proc through the `LPVOID` "parameter". Check the example in the link above. – Constantine Georgiou Apr 17 '23 at 15:02
  • I suppose, one problem is that a static function member can only be a member of a static class (a class that won't be instanciated) but the `CDialog` can't be static by design as you can show more than one dialog at the same time so the dialogs are created dynamically, referred to by it's instance variable. Btw.: Outside a class or function body declaring something as `static` does change nothing, except that the object can't be accessed as global entities by ohter .cpp or .c files using the `extern` keyword. – thomiel Apr 18 '23 at 14:47
  • *) correct: "*should usually* only be a member of a static class"... you can do it in instantiated classes, but it has no real benefit. – thomiel Apr 18 '23 at 15:03
  • I have to admit I'm confused. I will post a new (short) version of my code with a restated version of my problem. I HAVE been able to get past the static vs non-static problem and my code DOES compile, but dies in the first usage of my thread parameter. Clearly, I am not properly creating the required object and have yet to find an example I can understand to show me a way to do it. – DocDJ Apr 20 '23 at 17:32
  • Here is how I interacted with a worker thread from within a dialog (GPL v3.0; code is more than 20 years old, so it's just to depict the principles): https://github.com/Thomas-Mielke-Software/RetellSoftwareCallRecorder/blob/main/AutoUpdate.cpp – thomiel Apr 21 '23 at 12:16