From my experience, the way you let the DLL call the main app's functions is via callbacks and function pointers.

// DLL
void (*g_callback)();

void SetCallbackFoo(void (*callback)())
{
    g_callback = callback;
}

void DoSomething()
{
    // do stuff

    // call callback
    g_callback();
}

// Main
void SomeFunc()
{
    SetCallback(DLLCallback);
}

void DLLCallback()
{
    // do something
}

You can extend this sort of pattern to an object that you pass to the DLL or even a whole other DLL that the main program supplies. But for the most part I think these callbacks will get the job done.