diff --git a/src/DynamoCore/Models/DynamoModel.cs b/src/DynamoCore/Models/DynamoModel.cs index 4ca4738f112..0ace9f0fc8c 100644 --- a/src/DynamoCore/Models/DynamoModel.cs +++ b/src/DynamoCore/Models/DynamoModel.cs @@ -475,7 +475,7 @@ public enum DynamoModelState { NotStarted, StartedUIless, StartedUI }; public bool CLIMode { get; internal set; } /// - /// The Autodesk CrashReport tool location on disk (directory that contains the "senddmp.exe") + /// The Autodesk CrashReport tool location on disk (directory that contains the "cer.dll") /// public string CERLocation { get; internal set; } diff --git a/src/DynamoCoreWpf/DynamoCoreWpf.csproj b/src/DynamoCoreWpf/DynamoCoreWpf.csproj index d35debff055..c622aea3e2b 100644 --- a/src/DynamoCoreWpf/DynamoCoreWpf.csproj +++ b/src/DynamoCoreWpf/DynamoCoreWpf.csproj @@ -232,6 +232,7 @@ InPortContextMenu.xaml + diff --git a/src/DynamoCoreWpf/Utilities/CerDLL.cs b/src/DynamoCoreWpf/Utilities/CerDLL.cs new file mode 100644 index 00000000000..31d7a7315d8 --- /dev/null +++ b/src/DynamoCoreWpf/Utilities/CerDLL.cs @@ -0,0 +1,379 @@ +using System; +using System.Runtime.InteropServices; + +namespace Dynamo.Wpf.Utilities +{ + internal class DLL + { + [DllImport("kernel32.dll")] + public static extern IntPtr LoadLibrary(string dllToLoad); + + [DllImport("kernel32.dll")] + public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName); + + [DllImport("kernel32.dll")] + public static extern bool FreeLibrary(IntPtr hModule); + } + + internal class CerDLL : IDisposable + { + private IntPtr m_dll = IntPtr.Zero; + + public string DllFilePath { get; } + + public CerDLL(string dllFilePath) + { + DllFilePath = dllFilePath; + } + + public void Dispose() + { + if (m_dll != IntPtr.Zero) + { + DLL.FreeLibrary(m_dll); + m_dll = IntPtr.Zero; + } + } + + private static bool Initialized; + private static readonly object InitLocker = new(); + private bool Init() + { + if (!Initialized) + { + lock (InitLocker) + { + if (!Initialized) + { + if (m_dll == IntPtr.Zero) + m_dll = DLL.LoadLibrary(DllFilePath); + Initialized = true; + } + } + } + + return m_dll != IntPtr.Zero; + } + + private TDelegate GetDelegate() where TDelegate : Delegate + + { + IntPtr funcAddr = DLL.GetProcAddress(m_dll, typeof(TDelegate).Name); + if (funcAddr == IntPtr.Zero) + return null; + + return Marshal.GetDelegateForFunctionPointer(funcAddr); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_EnableUnhandledExceptionFilter(); + public void EnableUnhandledExceptionFilter() + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_ToggleCER(bool enable); + public void ToggleCER(bool enable) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(enable); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool CER_IsCEREnabled(); + public bool IsCEREnabled() + { + if (!Init()) + return false; + + var func = GetDelegate(); + if (func == null) + return false; + return func(); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetSenddmpPath(string senddmpExePath); + public void SetSenddmpPath(string senddmpExePath) + { + if (!Init()) + return; + + + var func = GetDelegate(); + if (func == null) + return; + func(senddmpExePath); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_RegisterUPI(string upiConfigFilePath); + public void RegisterUPI(string upiConfigFilePath) + { + if (!Init()) + return; + + + var func = GetDelegate(); + if (func == null) + return; + func(upiConfigFilePath); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool CER_SendReport(IntPtr exceptionPointers, bool suspendProcess); + public bool SendReport(Exception e, bool suspendProcess) + { + if (!Init()) + return false; + + var exceptionPointers = Marshal.GetExceptionPointers(); + if (exceptionPointers == IntPtr.Zero) + { + try + { + var exInfo = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(e); + exInfo.Throw(); + } + catch + { + exceptionPointers = Marshal.GetExceptionPointers(); + } + } + + var func = GetDelegate(); + if (func == null) + return false; + return func(exceptionPointers, suspendProcess); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate bool CER_SendReportWithDump(string dumpFile, bool suspendProcess); + public bool SendReportWithDump(string dumpFile, bool suspendProcess) + { + if (!Init()) + return false; + + var func = GetDelegate(); + if (func == null) + return false; + return func(dumpFile, suspendProcess); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetTheme(int theme); + public void SetTheme(int theme) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(theme); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetMultiStringParam(int key, string value, int maxCount); + public void SetMultiStringParam(ReportMultiStringParamKey key, string value, int maxCount) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func((int)key, value, maxCount); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetStringParam(int key, string value); + public void SetStringParam(ReportStringParamKey key, string value) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func((int)key, value); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetIntParam(int key, int value); + public void SetIntParam(ReportIntParamKey key, int value) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func((int)key, value); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetBoolParam(int key, bool value); + public void SetBoolParam(ReportBoolParamKey key, bool value) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func((int)key, value); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetAdditionalStringParam(string key, string value, int maxCount); + public void SetAdditionalStringParam(string key, string value, int maxCount) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(key, value, maxCount); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetAdditionalIntParam(string key, int value, int maxCount); + public void SetAdditionalIntParam(string key, int value, int maxCount) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(key, value, maxCount); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetAdditionalBoolParam(string key, bool value, int maxCount); + public void SetAdditionalBoolParam(string key, bool value, int maxCount) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(key, value, maxCount); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_RemoveAdditionalParam(string key); + public void RemoveAdditionalParam(string key) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(key); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate void CER_SetLegacyOptionsByString(string str); + public void SetLegacyOptionsByString(string str) + { + if (!Init()) + return; + + var func = GetDelegate(); + if (func == null) + return; + func(str); + } + } + + public enum ThemeType : int + { + Light, + Dark, + Blue, + }; + + public enum ReportStringParamKey : int + { + StringKeyBegin = 10000, + StringKeyUpiToken, // will be deprecated, please call RegisterUPI function as instead + StringKeyCalUptime, + StringKeyProductKey, + StringKeySerialNum, + StringKeyFeatureName, + StringKeyFeatureVer, + StringKeyLicenseBehavior, + StringKeyLicExp, + StringKeyLicUsage, + StringKeyDwg, + StringKeyAutoSend, + StringKeyAppXMLFile, + StringKeyAvailPhysicalMem, + StringKeyAvailPageFile, + StringKeyAvailVirtualMem, + StringKeyLastError, + StringKeyErrNo, + StringKeyUserSubscriptionEmail, + StringKeyErrorDescription, + StringKeyGraphicsDriver, + StringKeyCadSettingsRegPath, + StringKeyOptionsFile, + StringKeyMc3SessionId, + StringKeyMc3UserId, + StringKeyAppIcon, + StringKeyGdiObjects, + StringKeyGdiUserObjects, + StringKeyGdiObjectsPeak, + StringKeyGdiUserObjectsPeak, + StringKeyUnknown = 19998, + StringKeyEnd = 19999 + }; + + public enum ReportMultiStringParamKey + { + MultiStringKeyBegin = 20000, + MultiStringKeyAppCData, + MultiStringKeyAppName, + MultiStringKeyAppXML, + MultiStringKeyExtraFile, + MultiStringKeyLastCommand, + MultiStringKeyUnknown = 29998, + MultiStringKeyEnd = 29999 + }; + + public enum ReportIntParamKey : int + { + IntKeyBegin = 30000, + IntKeyCrashCount, + IntKeyAppLocaleId, + IntKeyAppIconId, + IntKeyUnknown = 39998, + IntKeyEnd = 39999 + }; + + public enum ReportBoolParamKey : int + { + BoolKeyBegin = 40000, + BoolKeyAltForceSend, + BoolKeyUseExceptionTrace, + BoolKeyUnknown = 49998, + BoolKeyEnd = 49999 + }; +} diff --git a/src/DynamoCoreWpf/Utilities/CrashReportTool.cs b/src/DynamoCoreWpf/Utilities/CrashReportTool.cs index 057f84592bd..d92dbc790c1 100644 --- a/src/DynamoCoreWpf/Utilities/CrashReportTool.cs +++ b/src/DynamoCoreWpf/Utilities/CrashReportTool.cs @@ -10,13 +10,14 @@ using System.Linq; using System.Reflection; using System.Runtime.InteropServices; +using CoreNodeModels.HigherOrder; namespace Dynamo.Wpf.Utilities { internal class CrashReportTool { private static List ProductsWithCER => new List() { "Revit", "Civil", "Robot Structural Analysis" }; - private static readonly string CERExeName = "senddmp.exe"; + private static readonly string CERDllName = "cer.dll"; private static string CERInstallLocation = null; @@ -143,7 +144,7 @@ private static string FindCERToolInInstallLocations() throw new MissingMethodException("Method 'DynamoInstallDetective.Utilities.FindProductInstallations' not found"); } - var methodParams = new object[] { ProductsWithCER, CERExeName }; + var methodParams = new object[] { ProductsWithCER, CERDllName }; var installs = installationsMethod.Invoke(null, methodParams) as IEnumerable; CERInstallLocation = installs.Cast>>().Select(x => x.Key).LastOrDefault() ?? string.Empty; @@ -173,7 +174,7 @@ internal static bool ShowCrashErrorReportWindow(DynamoViewModel viewModel, Crash string cerToolDir = !string.IsNullOrEmpty(model.CERLocation) ? model.CERLocation : FindCERToolInInstallLocations(); - var cerToolPath = Path.Combine(cerToolDir, CERExeName); + var cerToolPath = Path.Combine(cerToolDir, CERDllName); if (string.IsNullOrEmpty(cerToolPath) || !File.Exists(cerToolPath)) { model?.Logger?.LogError($"The CER tool was not found at location {cerToolPath}"); @@ -233,30 +234,42 @@ internal static bool ShowCrashErrorReportWindow(DynamoViewModel viewModel, Crash filesToSend.Add(viewModel.DumpRecordedCommands()); } - var extras = string.Join(" ", filesToSend.Select(f => "/EXTRA \"" + f + "\"")); - string appConfig = ""; if (model != null) { var appName = GetHostAppName(model); - appConfig = $@""; + appConfig = $""; } - string dynConfig = string.Empty; string dynName = viewModel?.Model.CurrentWorkspace.Name; - if (!string.IsNullOrEmpty(dynName)) - { - dynConfig = $"/DWG {dynName}"; - } var miniDumpFilePath = CreateMiniDumpFile(cerDir.FullName); var upiConfigFilePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "upiconfig.xml"); - var cerArgs = $"/UPITOKEN \"{upiConfigFilePath}\" /DMP \"{miniDumpFilePath}\" /APPXML \"{appConfig}\" {dynConfig} {extras} /USEEXCEPTIONTRACE"; + using (var cerDLL = new CerDLL(cerToolPath)) + { + cerDLL.ToggleCER(true); + cerDLL.RegisterUPI(upiConfigFilePath); + + if (!string.IsNullOrEmpty(dynName)) + { + cerDLL.SetStringParam(ReportStringParamKey.StringKeyDwg, dynName); + } + + foreach (var file in filesToSend) + { + cerDLL.SetMultiStringParam(ReportMultiStringParamKey.MultiStringKeyExtraFile, file, filesToSend.Count + 1); + } + + cerDLL.SetMultiStringParam(ReportMultiStringParamKey.MultiStringKeyAppXML, appConfig, 1); + cerDLL.SetBoolParam(ReportBoolParamKey.BoolKeyUseExceptionTrace, true); + model?.Logger?.LogError(cerDLL.SendReportWithDump(miniDumpFilePath, true) + ? $"Successfully sent CER error report" + : $"Failed to send CER error report"); + } - Process.Start(new ProcessStartInfo(cerToolPath, cerArgs) { UseShellExecute = true }).WaitForExit(); return true; } }