Hello
So here is a fun little problem.
I have TestPluginA and TestPluginB
I get crash either inside PluginA or PLuginB, PluginA usually calls PluginB to do some work. - both PluginA/B are build-loaded via unreal startup routine, HOWEVER, I would eventually want PluginB to only be loaded via OS api as dll, so that I can dynamically load it as needed not as startup.
I’m not passing any objects/etc between the plugins, only string in form of char*.
The crash happens at random, sometimes passing someFunc(“HelloWorld”)
where void someFunc(const QString& data) is signature, or if I pass it via someFunc(QString(“lala”))) etc etc
I’ve spend days on this now, tried numerous flags and every “pro” AI model I could find to help me with it.
Unreal swaps it allocators, and then everything after has a chance of crash.
Sometimes I get crash on 1st call, other on 50th call, Russian roulette.
Can any1 help me out with this ?
REPLACEMENT_OPERATOR_NEW_AND_DELETE
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
using System.IO;public class TestPluginA : ModuleRules
{
public TestPluginA(ReadOnlyTargetRules Target) : base(Target)
{
bUseRTTI = true;
bEnableExceptions = true;
bUseUnity = false;
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;// CRITICAL: Force ANSI allocator for this module to avoid heap corruption. // Qt allocates memory inside Qt DLLs using their own heap. When Qt objects are // destroyed in our module code, UE's replaced operator delete tries to free via // FMemory::Free(), which crashes because the pointer wasn't allocated by UE. // FORCE_ANSI_ALLOCATOR=1 does TWO things: // 1. Makes FMemory use standard malloc/free instead of UE's custom allocator // 2. Disables REPLACEMENT_OPERATOR_NEW_AND_DELETE (see ModuleBoilerplate.h line 49) // This makes the module compatible with external libraries like Qt that use their own heap. //PrivateDefinitions.Add("FORCE_ANSI_ALLOCATOR=1"); //PublicDefinitions.Add("FORCE_ANSI_ALLOCATOR=1"); // Prefer an environment variable override so builds don’t depend on a machine-local path. // Falls back to your current local install. string QtPath = System.Environment.GetEnvironmentVariable("QT_DIR"); if (string.IsNullOrEmpty(QtPath)) { QtPath = System.IO.Path.Combine("C:\\Qt\\6.10.1\\msvc2022_64"); } QtPath = System.IO.Path.GetFullPath(QtPath); string MocPath = System.IO.Path.Combine(QtPath, "bin", "moc.exe"); string RccPath = System.IO.Path.Combine(QtPath, "bin", "rcc.exe"); string UicPath = System.IO.Path.Combine(QtPath, "bin", "uic.exe"); string MocDirectory = System.IO.Path.Combine(ModuleDirectory, "Mocs"); if (!System.IO.Directory.Exists(MocDirectory)) { System.IO.Directory.CreateDirectory(MocDirectory); } string QrcDirectory = System.IO.Path.Combine(ModuleDirectory, "qrc"); if (!System.IO.Directory.Exists(QrcDirectory)) { System.IO.Directory.CreateDirectory(QrcDirectory); } string UiDirectory = System.IO.Path.Combine(ModuleDirectory, "UI"); if (!System.IO.Directory.Exists(UiDirectory)) { System.IO.Directory.CreateDirectory(UiDirectory); } // Auto-build UI files BuildUiFiles(UicPath, UiDirectory); BuildMocFiles(MocPath, MocDirectory); BuildRccFiles(RccPath, QrcDirectory); PublicIncludePaths.AddRange( new string[] { // ... add public include paths required here ... System.IO.Path.Combine(QtPath, "include"), System.IO.Path.Combine(QtPath, "include\\QtCore"), System.IO.Path.Combine(QtPath, "include\\QtGui"), System.IO.Path.Combine(QtPath, "include\\QtWidgets"), System.IO.Path.Combine(QtPath, "include\\QtNetwork"), "S:\\libsExtra\\json\\active" } ); if (Target.Platform == UnrealTargetPlatform.Win64) { string QtBinPath = System.IO.Path.Combine(QtPath, "bin"); string QtLibPath = System.IO.Path.Combine(QtPath, "lib"); // Always use Release Qt - Editor uses Release CRT bool useDebugQt = Target.Configuration == UnrealTargetConfiguration.Debug || Target.Configuration == UnrealTargetConfiguration.DebugGame; string qtCoreLib = useDebugQt ? "Qt6Cored.lib" : "Qt6Core.lib"; string qtGuiLib = useDebugQt ? "Qt6Guid.lib" : "Qt6Gui.lib"; string qtWLib = useDebugQt ? "Qt6Widgetsd.lib" : "Qt6Widgets.lib"; string qtNetLib = useDebugQt ? "Qt6Networkd.lib" : "Qt6Network.lib"; PublicAdditionalLibraries.Add(System.IO.Path.Combine(QtLibPath, qtCoreLib)); PublicAdditionalLibraries.Add(System.IO.Path.Combine(QtLibPath, qtGuiLib)); PublicAdditionalLibraries.Add(System.IO.Path.Combine(QtLibPath, qtWLib)); PublicAdditionalLibraries.Add(System.IO.Path.Combine(QtLibPath, qtNetLib)); // Ensure we’re dynamically linking Qt (import libs + DLLs), not static. // NOTE: Qt cannot generally be delay-loaded on Windows because it imports data symbols // (e.g. QObject::staticMetaObject), which triggers LNK1194. So we do NOT use /DELAYLOAD. // Still stage the DLLs so the Editor/packaged build can load them normally at process start. RuntimeDependencies.Add(System.IO.Path.Combine(QtBinPath, useDebugQt ? "Qt6Cored.dll" : "Qt6Core.dll")); RuntimeDependencies.Add(System.IO.Path.Combine(QtBinPath, useDebugQt ? "Qt6Guid.dll" : "Qt6Gui.dll")); RuntimeDependencies.Add(System.IO.Path.Combine(QtBinPath, useDebugQt ? "Qt6Widgetsd.dll" : "Qt6Widgets.dll")); RuntimeDependencies.Add(System.IO.Path.Combine(QtBinPath, useDebugQt ? "Qt6Networkd.dll" : "Qt6Network.dll")); //PublicDelayLoadDLLs.Add(useDebugQt ? "Qt6Cored.dll" : "Qt6Core.dll"); //PublicDelayLoadDLLs.Add(useDebugQt ? "Qt6Guid.dll" : "Qt6Gui.dll"); //PublicDelayLoadDLLs.Add(useDebugQt ? "Qt6Widgetsd.dll" : "Qt6Widgets.dll"); //PublicDelayLoadDLLs.Add(useDebugQt ? "Qt6Networkd.dll" : "Qt6Network.dll"); // If you use Qt Widgets, you almost always need the platform plugin at runtime. // This stages it so you’re not relying on QT_PLUGIN_PATH being set globally. RuntimeDependencies.Add(System.IO.Path.Combine(QtPath, "plugins", "platforms", "qwindows.dll")); PublicDefinitions.Add("IC_INTERNAL_BUILD=1"); PublicDefinitions.Add("QT_NO_KEYWORDS"); // avoids 'signals', 'slots' macro collisions //PublicDefinitions.Add("NOMINMAX"); // avoids Windows min/max macro issues PublicDefinitions.Add("QT_NO_EXCEPTIONS=0"); // Reduce growth of macro conflicts when including Qt headers alongside UE headers. // (Main fix is to keep Qt includes behind your icQtBegin/icQtEnd wrappers.) //PublicDefinitions.Add("WIN32_LEAN_AND_MEAN"); PublicDefinitions.Add("_CRT_SECURE_NO_WARNINGS"); } PrivateIncludePaths.AddRange( new string[] { // ... add other private include paths required here ... MocDirectory, QrcDirectory, UiDirectory, } ); PublicDependencyModuleNames.AddRange( new string[] { "Core", "Projects", "InputCore", "EditorFramework", "UnrealEd", "LevelEditor", "CoreUObject", "Engine", } ); PrivateDependencyModuleNames.AddRange( new string[] { "Slate", "SlateCore", "ApplicationCore", "PythonScriptPlugin", "ToolMenus", "LevelSequenceEditor", "LevelSequence", // ... add private dependencies that you statically link with here ... } ); DynamicallyLoadedModuleNames.AddRange( new string[] { // ... add any modules that your module loads dynamically here ... } ); } private void BuildMocFiles(string MocPath, string MocDirectory) { if (!System.IO.File.Exists(MocPath)) { System.Console.WriteLine("MOC not found at: " + MocPath); return; } // Search for headers that might contain Q_OBJECT string SourcePath = ModuleDirectory; string[] HeaderFiles = System.IO.Directory.GetFiles(SourcePath, "*.h", System.IO.SearchOption.AllDirectories); foreach (string HeaderFile in HeaderFiles) { string Content = System.IO.File.ReadAllText(HeaderFile); if (Content.Contains("Q_OBJECT")) { string FileName = System.IO.Path.GetFileNameWithoutExtension(HeaderFile); string GeneratedMoc = System.IO.Path.Combine(MocDirectory, "moc_" + FileName + ".cpp"); if (!System.IO.File.Exists(GeneratedMoc) || System.IO.File.GetLastWriteTime(HeaderFile) > System.IO.File.GetLastWriteTime(GeneratedMoc)) { System.Console.WriteLine("Generating MOC for " + HeaderFile + " -> " + GeneratedMoc); System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo { FileName = MocPath, Arguments = string.Format("\"{0}\" -o \"{1}\"", HeaderFile, GeneratedMoc), UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; using (System.Diagnostics.Process Proc = System.Diagnostics.Process.Start(StartInfo)) { Proc.WaitForExit(); } } } } } private void BuildUiFiles(string UicPath, string UiDirectory) { if (!System.IO.File.Exists(UicPath)) { System.Console.WriteLine("UIC not found at: " + UicPath); return; } string SourcePath = System.IO.Path.Combine(ModuleDirectory, "Shared"); if (!System.IO.Directory.Exists(SourcePath)) { System.Console.WriteLine("Shared directory not found in: " + ModuleDirectory); return; } string[] UiFiles = System.IO.Directory.GetFiles(SourcePath, "*.ui", System.IO.SearchOption.AllDirectories); foreach (string UiFile in UiFiles) { string FileName = System.IO.Path.GetFileNameWithoutExtension(UiFile); string GeneratedHeader = System.IO.Path.Combine(UiDirectory, "ui_" + FileName + ".h"); if (!System.IO.File.Exists(GeneratedHeader) || System.IO.File.GetLastWriteTime(UiFile) > System.IO.File.GetLastWriteTime(GeneratedHeader)) { System.Console.WriteLine("Generating header for " + UiFile + " -> " + GeneratedHeader); System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo { FileName = UicPath, Arguments = string.Format("\"{0}\" -o \"{1}\"", UiFile, GeneratedHeader), UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; using (System.Diagnostics.Process Proc = System.Diagnostics.Process.Start(StartInfo)) { Proc.WaitForExit(); if (Proc.ExitCode != 0) { System.Console.WriteLine("Error generating header for " + UiFile + ": " + Proc.StandardError.ReadToEnd()); } } } } } private void BuildRccFiles(string RccPath, string QrcDirectory) { if (!System.IO.File.Exists(RccPath)) { System.Console.WriteLine("RCC not found at: " + RccPath); return; } string SourcePath = ModuleDirectory; string[] RccFiles = System.IO.Directory.GetFiles(SourcePath, "*.qrc", System.IO.SearchOption.AllDirectories); foreach (string RccFile in RccFiles) { string FileName = System.IO.Path.GetFileNameWithoutExtension(RccFile); string GeneratedRcc = System.IO.Path.Combine(QrcDirectory, "qrc_" + FileName + ".cpp"); if (!System.IO.File.Exists(GeneratedRcc) || System.IO.File.GetLastWriteTime(RccFile) > System.IO.File.GetLastWriteTime(GeneratedRcc)) { System.Console.WriteLine("Generating RCC for " + RccFile + " -> " + GeneratedRcc); System.Diagnostics.ProcessStartInfo StartInfo = new System.Diagnostics.ProcessStartInfo { FileName = RccPath, Arguments = string.Format("-name \"{0}\" \"{1}\" -o \"{2}\"", FileName, RccFile, GeneratedRcc), UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true }; using (System.Diagnostics.Process Proc = System.Diagnostics.Process.Start(StartInfo)) { Proc.WaitForExit(); } } } }}