Search code examples
c++visual-studioclassmfccalling-convention

MFC MDI Function calling vs SendMessage. C programmer trying to understand a basic concept


As a Senior hardware EE, most of my programming time uses C for firmware and inline assembly as needed. I've been working on an MFC MDI which a few of you have helped me out beautifully. I am creating a settings dialog and moving away from the menu items to write to the registry for storage and retrieval of the apps user settings. I've Googled six ways to Sunday to try and understand basic "Calling a function from another .cpp file class" and many other variants of that to get my answer.....failed (Mostly like because I probably didn't understand their answers).

I have a master program reset that nukes the HKEY_CURRENT_USER key my app creates. Works great, no issues. Now, instead of being an item in the "Settings" menu with an item "Reset App", I am moving that to a button press in one of the property sheet/page dialogs.

I have it working but I am unclear as to why my original thought of how to execute it fails.

So, in my application, I trigger this function:

void CApplication::OnResetProgramSettings() //Truncated function for StackOF clarity
{    
    // ask the user
    int DisableUserWarnings = FALSE;       
    if (DisableUserWarnings == FALSE) {

        if (IDNO == AfxMessageBox(IDS_WARNING_RESET_APP, MB_ICONQUESTION | MB_YESNO))
            return;
    }

    LSTATUS status = SHDeleteKey(HKEY_CURRENT_USER, _T("SOFTWARE\\Trains"));    
}

This is called from:

BEGIN_MESSAGE_MAP(CApplication, CWinAppEx)
   ...other commands
    ON_COMMAND(ID_SETTINGS_RESET_REGISTRY, OnResetProgramSettings)
    ...other commands
    ON_UPDATE_COMMAND_UI(ID_SETTINGS_RESET_REGISTRY, OnUpdateOnResetProgramSettings)
END_MESSAGE_MAP()

To make this work from my new Dialog Settings section, I implemented this:

void CSettingsReset::OnBnClickedButtonReset()
{
    // Global reset
    AfxGetMainWnd()->SendMessage(WM_COMMAND, ID_SETTINGS_RESET_REGISTRY);

    //CApplication::OnResetProgramSettings();
    //&CApplication::OnResetProgramSettings();
}

It works exactly as if I selected the menu item "Reset App" as mentioned above.

The property page is called SettingsReset.cpp and the above lives in Application.cpp.

From C land I would normally just call the function with the application.h included in the SettingsReset.h file like this:

OnResetProgramSettings();

I realize that doesn't work the same way in C++. You can see the two line items commented out from code above and uncomment below:

    CApplication::OnResetProgramSettings(); //Doesn't get called, static error if memory serves
    &CApplication::OnResetProgramSettings(); //Doesn't get called
    CApplication OnResetProgramSettings; //Creates an object I don't want/need... or do I?
    

So I am sending AFX messages to "achieve" the same operational result. My ultimate question what is the correct way to "call" the OnResetProgramSettings() in the Application.cpp file FROM the SettingsReset.cpp file? I've read about making the function "Static", but I am unclear how to do that and/or why...and now would have two separate handlers for each function....i.e. OnResetProgramSettings() and OnResetProgramSettings1()....is that correct?

Is there a way to do this without having to use the AFX messaging to trigger that function?


Solution

  • There are two easy ways you can call a non-static member function of your application class from 'outside' that class. But they are essentially the same, in that you just need to call that member function via the actual application object (instance).

    First, you can use that object itself. Somewhere in your code, you will have an application variable like the following:

    CApplication MyApp;
    

    This will probably be in the "CApplication.cpp" file if your project was created using a Visual Studio wizard, and there will be a declaration in the "CApplication.h" header. Any source file that includes that header can then simply call a public member function like this:

    MyApp.OnResetProgramSettings();
    

    Alternatively, you can get a pointer to the current application instance (from pretty much anywhere) using the AfxGetApp() call, defined in the MFC headers; but note that this will have to be cast to a pointer to your derived class:

    static_cast<CApplication*>(AfxGetApp())->OnResetProgramSettings();
    

    However, there is a very important caveat on calling members like this: you can't do it for functions that take arguments which, in the 'normal' calling method, are provided by the framework. So, for example, it simply won't work for an ON_UPDATE_COMMAND_UI message handler (which needs the CCmdUI* argument, provided by the framework).


    Note that message handlers are declared as protected when created with the class wizard, so you'll have to change your CApplication class a bit to make those you need access to in the above way public. But that's trivial.