After twelve hours finally found why it is NULL. The module is started in EngineLoop::PreInit and GUnrealEd is created in EngineLoop::Init. Changed LoadingPhase to PostEngineInit and it is working now!
You cannot rely on setting the plugin’s loading phase to PostEngineInit to guarantee the existence of GEditor inside StartupModule()!
Plugins will load at the earliest loading phase in their chain of dependencies, meaning the actual loading phase used by a plugin can become earlier than the loading phase set in the .uplugin settings!
i.e. GEditor may not exist (yet) when the plugin is initialized.
Example
Suppose we have a Plugin A which loads in Default.
A new Plugin B is added that depends on Plugin A. Plugin B loads in PreDefault.
Now Plugin A will be loaded in PreDefault, along with Plugin B.
Plugin A may suddenly and mysteriously start breaking as a result.
This can be rather surprising and is hard to figure out what’s wrong if you weren’t thinking about Plugin A when changing Plugin B’s loading phase or dependencies.
Workaround
A more reliable way to access GEditor on module startup is to listen on the PostEngineInit delegate explicitly using FCoreDelegates::OnPostEngineInit.
Do note that if the loading phase isPostEngineInit, then the OnPostEngineInit delegate will have already fired! so your OnPostEngineInit callback will not be executed in that case.
If you’re not worried about this, you can simply use an earlier loading phase, e.g. "Default" and always use the delegate, as the loading phase cannot get later than what’s specified in .uplugin, only earlier, IIUC.
void FMyModule::OnStartup() {
// wait for GEditor to be ready
FCoreDelegates::OnPostEngineInit.AddRaw(this, &FMyModule::OnPostEngineInit);
}
However if you want to ensure fewer surprises when changing loading phases in the future, you can wait for GEditor if it’s missing, or run your init immediately if GEditor is already around:
void FMyModule::OnStartup() {
if (GEditor) {
OnPostEngineInit(); // GEditor ready, init immediately
} else {
// wait for GEditor to be ready
FCoreDelegates::OnPostEngineInit.AddRaw(this, &FMyModule::OnPostEngineInit);
}
}
With this you should be able to adjust dependencies and loading phases without creating as many load order issues in other plugins.
edit: Also see this good suggestion from @Engelard:
@errata Thanks! Your post helped a lot in my situation.
I would even go further and recommend FCoreDelegates::OnBeginFrame , it is the very last point in the entire Editor loading process, when even viewports loaded and Editor displayed to the user.
P.S. Don’t forget to FCoreDelegates::OnBeginFrame.Remove(MyHandle); once it reached your target part of code.