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;
}
}