diff --git a/windows/src/desktop/kmbrowserhost/kmbrowserhost.dpr b/windows/src/desktop/kmbrowserhost/kmbrowserhost.dpr
index 0f3562ea6f..12d3693be9 100644
--- a/windows/src/desktop/kmbrowserhost/kmbrowserhost.dpr
+++ b/windows/src/desktop/kmbrowserhost/kmbrowserhost.dpr
@@ -8,7 +8,6 @@ uses
DebugPaths in '..\..\global\delphi\general\DebugPaths.pas',
ErrorControlledRegistry in '..\..\global\delphi\vcl\ErrorControlledRegistry.pas',
VersionInfo in '..\..\global\delphi\general\VersionInfo.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
utilsystem in '..\..\global\delphi\general\utilsystem.pas',
utilexecute in '..\..\global\delphi\general\utilexecute.pas',
Unicode in '..\..\global\delphi\general\Unicode.pas',
diff --git a/windows/src/desktop/kmbrowserhost/kmbrowserhost.dproj b/windows/src/desktop/kmbrowserhost/kmbrowserhost.dproj
index d1183d67b4..cef1206670 100644
--- a/windows/src/desktop/kmbrowserhost/kmbrowserhost.dproj
+++ b/windows/src/desktop/kmbrowserhost/kmbrowserhost.dproj
@@ -111,7 +111,6 @@
-
diff --git a/windows/src/desktop/kmbrowserhost/kmbrowserhost.res b/windows/src/desktop/kmbrowserhost/kmbrowserhost.res
index 6876088a66..08809368b7 100644
Binary files a/windows/src/desktop/kmbrowserhost/kmbrowserhost.res and b/windows/src/desktop/kmbrowserhost/kmbrowserhost.res differ
diff --git a/windows/src/desktop/kmshell/kmshell.dpr b/windows/src/desktop/kmshell/kmshell.dpr
index 0e245a118a..9242a2d5e4 100644
--- a/windows/src/desktop/kmshell/kmshell.dpr
+++ b/windows/src/desktop/kmshell/kmshell.dpr
@@ -70,7 +70,6 @@ uses
utilcheckfonts in '..\..\global\delphi\general\utilcheckfonts.pas',
wininet5 in '..\..\global\delphi\general\wininet5.pas',
GlobalProxySettings in '..\..\global\delphi\general\GlobalProxySettings.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
UFixupMissingFile in '..\..\global\delphi\ui\UFixupMissingFile.pas',
UImportOlderVersionKeyboards in 'main\UImportOlderVersionKeyboards.pas',
UImportOlderKeyboardUtils in 'main\UImportOlderKeyboardUtils.pas',
diff --git a/windows/src/desktop/kmshell/kmshell.dproj b/windows/src/desktop/kmshell/kmshell.dproj
index 25335be6f3..c8b286254f 100644
--- a/windows/src/desktop/kmshell/kmshell.dproj
+++ b/windows/src/desktop/kmshell/kmshell.dproj
@@ -197,7 +197,6 @@
-
diff --git a/windows/src/desktop/kmshell/kmshell.res b/windows/src/desktop/kmshell/kmshell.res
index 6356d2bcc6..a4ee2686e6 100644
Binary files a/windows/src/desktop/kmshell/kmshell.res and b/windows/src/desktop/kmshell/kmshell.res differ
diff --git a/windows/src/desktop/kmshell/main/UImportOlderVersionKeyboards.pas b/windows/src/desktop/kmshell/main/UImportOlderVersionKeyboards.pas
index 1feb5f6676..23f8e59914 100644
--- a/windows/src/desktop/kmshell/main/UImportOlderVersionKeyboards.pas
+++ b/windows/src/desktop/kmshell/main/UImportOlderVersionKeyboards.pas
@@ -69,9 +69,9 @@ implementation
uses
Winapi.Messages,
- ErrLogPath,
GetOsVersion,
IniFiles,
+ KeymanPaths,
kmpinffile,
Keyman.System.UpgradeRegistryKeys,
KPInstallFontKMShell,
@@ -384,7 +384,7 @@ begin
FAdmin := AAdmin; // I2361
for i := 0 to 5 do
try
- FLogFile := TFileStream.Create(GetErrLogFileName('keymanimport'), fmCreate); // I2792
+ FLogFile := TFileStream.Create(TKeymanPaths.ErrorLogPath('keymanimport'), fmCreate); // I2792
Break;
except
on E:EFCreateError do
diff --git a/windows/src/desktop/setup/RunTools.pas b/windows/src/desktop/setup/RunTools.pas
index e3b1a51bd1..1580257aeb 100644
--- a/windows/src/desktop/setup/RunTools.pas
+++ b/windows/src/desktop/setup/RunTools.pas
@@ -136,7 +136,6 @@ uses
jwawintype,
bootstrapmain,
- errlogpath,
GetOsVersion,
HTTPUploader,
Keyman.System.UpgradeRegistryKeys,
@@ -288,7 +287,7 @@ begin
ShowMessageW(msg);
if not Assigned(FErrorLog) then
begin
- path := GetErrLogPath + 'setup.log'; // I2314
+ path := TKeymanPaths.ErrorLogPath + 'setup.log'; // I2314
if SysUtils.FileExists(path) then
begin
@@ -636,7 +635,7 @@ begin
{ Log the install to the diag folder }
- FLogFileName := GetErrLogFileName(ChangeFileExt(ExtractFileName(FInstallInfo.MSIFileName), '')); // I1610 // I2755 // I2792
+ FLogFileName := TKeymanPaths.ErrorLogPath(ChangeFileExt(ExtractFileName(FInstallInfo.MSIFileName), '')); // I1610 // I2755 // I2792
//ForceDirectories(GetErrLogPath); // I2768
MsiEnableLogW(INSTALLLOGMODE_VERBOSE, PWideChar(FLogFileName), 0);
diff --git a/windows/src/desktop/setup/setup.dpr b/windows/src/desktop/setup/setup.dpr
index 39e23fe92a..80b7cf9271 100644
--- a/windows/src/desktop/setup/setup.dpr
+++ b/windows/src/desktop/setup/setup.dpr
@@ -25,7 +25,6 @@ uses
UfrmRunDesktop in 'UfrmRunDesktop.pas' {frmRunDesktop},
UfrmInstallOptions in 'UfrmInstallOptions.pas' {frmInstallOptions},
RunTools in 'RunTools.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
ShellUserRegistry in '..\..\global\delphi\general\ShellUserRegistry.pas',
RegistryHelpers in 'RegistryHelpers.pas',
ErrorControlledRegistry in '..\..\global\delphi\vcl\ErrorControlledRegistry.pas',
diff --git a/windows/src/desktop/setup/setup.dproj b/windows/src/desktop/setup/setup.dproj
index d2e023712f..d95ce319f5 100644
--- a/windows/src/desktop/setup/setup.dproj
+++ b/windows/src/desktop/setup/setup.dproj
@@ -7,7 +7,7 @@
1
Console
VCL
- 18.4
+ 18.8
Win32
@@ -122,7 +122,6 @@
-
@@ -249,7 +248,6 @@
1
- Contents\MacOS
0
@@ -258,6 +256,20 @@
classes
1
+
+ classes
+ 1
+
+
+
+
+ res\xml
+ 1
+
+
+ res\xml
+ 1
+
@@ -270,90 +282,242 @@
library\lib\armeabi
1
+
+ library\lib\armeabi
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
library\lib\mips
1
+
+ library\lib\mips
+ 1
+
library\lib\armeabi-v7a
1
+
+ library\lib\arm64-v8a
+ 1
+
+
+
+
+ library\lib\armeabi-v7a
+ 1
+
res\drawable
1
+
+ res\drawable
+ 1
+
res\values
1
+
+ res\values
+ 1
+
+
+
+
+ res\values-v21
+ 1
+
+
+ res\values-v21
+ 1
+
+
+
+
+ res\values
+ 1
+
+
+ res\values
+ 1
+
res\drawable
1
+
+ res\drawable
+ 1
+
res\drawable-xxhdpi
1
+
+ res\drawable-xxhdpi
+ 1
+
res\drawable-ldpi
1
+
+ res\drawable-ldpi
+ 1
+
res\drawable-mdpi
1
+
+ res\drawable-mdpi
+ 1
+
res\drawable-hdpi
1
+
+ res\drawable-hdpi
+ 1
+
res\drawable-xhdpi
1
+
+ res\drawable-xhdpi
+ 1
+
+
+
+
+ res\drawable-mdpi
+ 1
+
+
+ res\drawable-mdpi
+ 1
+
+
+
+
+ res\drawable-hdpi
+ 1
+
+
+ res\drawable-hdpi
+ 1
+
+
+
+
+ res\drawable-xhdpi
+ 1
+
+
+ res\drawable-xhdpi
+ 1
+
+
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+ res\drawable-xxhdpi
+ 1
+
+
+
+
+ res\drawable-xxxhdpi
+ 1
+
+
+ res\drawable-xxxhdpi
+ 1
+
res\drawable-small
1
+
+ res\drawable-small
+ 1
+
res\drawable-normal
1
+
+ res\drawable-normal
+ 1
+
res\drawable-large
1
+
+ res\drawable-large
+ 1
+
res\drawable-xlarge
1
+
+ res\drawable-xlarge
+ 1
+
+
+
+
+ res\values
+ 1
+
+
+ res\values
+ 1
+
@@ -371,6 +535,10 @@
1
.framework
+
+ 1
+ .framework
+
0
@@ -380,6 +548,10 @@
1
.dylib
+
+ 1
+ .dylib
+
0
.dll;.bpl
@@ -402,6 +574,10 @@
1
.dylib
+
+ 1
+ .dylib
+
0
.bpl
@@ -411,6 +587,9 @@
0
+
+ 0
+
0
@@ -423,6 +602,9 @@
0
+
+ 0
+
0
@@ -438,6 +620,17 @@
1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
1
@@ -449,6 +642,39 @@
1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
1
@@ -460,6 +686,61 @@
1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
1
@@ -471,6 +752,116 @@
1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
1
@@ -504,10 +895,35 @@
1
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
1
+
+ 1
+
@@ -533,6 +949,7 @@
1
+
@@ -540,12 +957,20 @@
Contents\Resources
1
+
+ Contents\Resources
+ 1
+
library\lib\armeabi-v7a
1
+
+ library\lib\arm64-v8a
+ 1
+
1
@@ -561,10 +986,19 @@
1
+
+ 1
+
0
+
+
+ library\lib\armeabi-v7a
+ 1
+
+
1
@@ -600,7 +1034,9 @@
+
+
12
diff --git a/windows/src/developer/TIKE/Tike.dpr b/windows/src/developer/TIKE/Tike.dpr
index b5fa89ede5..9bddda6fc2 100644
--- a/windows/src/developer/TIKE/Tike.dpr
+++ b/windows/src/developer/TIKE/Tike.dpr
@@ -167,7 +167,6 @@ uses
wininet5 in '..\..\global\delphi\general\wininet5.pas',
TextFileTemplates in 'main\TextFileTemplates.pas',
GlobalProxySettings in '..\..\global\delphi\general\GlobalProxySettings.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
UfrmFontHelper in 'dialogs\UfrmFontHelper.pas' {Form1},
VKeyChars in '..\..\global\delphi\general\VKeyChars.pas',
usp10 in '..\..\global\delphi\general\usp10.pas',
diff --git a/windows/src/developer/TIKE/Tike.dproj b/windows/src/developer/TIKE/Tike.dproj
index 47805230c7..f0a23717b2 100644
--- a/windows/src/developer/TIKE/Tike.dproj
+++ b/windows/src/developer/TIKE/Tike.dproj
@@ -348,7 +348,6 @@
-
diff --git a/windows/src/engine/keyman/keyman.dpr b/windows/src/engine/keyman/keyman.dpr
index 0c86528aba..2c731890d8 100644
--- a/windows/src/engine/keyman/keyman.dpr
+++ b/windows/src/engine/keyman/keyman.dpr
@@ -75,7 +75,6 @@ uses
utilcheckfonts in '..\..\global\delphi\general\utilcheckfonts.pas',
findfonts in '..\..\global\delphi\general\findfonts.pas',
WideStringClass in '..\..\global\delphi\general\WideStringClass.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
UFixupMissingFile in '..\..\global\delphi\ui\UFixupMissingFile.pas',
utiluac in '..\..\global\delphi\general\utiluac.pas',
VKeyChars in '..\..\global\delphi\general\VKeyChars.pas',
diff --git a/windows/src/engine/keyman/keyman.dproj b/windows/src/engine/keyman/keyman.dproj
index 910efae67b..486334338d 100644
--- a/windows/src/engine/keyman/keyman.dproj
+++ b/windows/src/engine/keyman/keyman.dproj
@@ -179,7 +179,6 @@
-
@@ -307,9 +306,9 @@
False
-
+
- keyman.exe
+ .\
true
@@ -319,9 +318,9 @@
true
-
+
- .\
+ keyman.exe
true
diff --git a/windows/src/engine/kmcomapi/com/keyman_implementation.pas b/windows/src/engine/kmcomapi/com/keyman_implementation.pas
index 96beab889c..550da16c99 100644
--- a/windows/src/engine/kmcomapi/com/keyman_implementation.pas
+++ b/windows/src/engine/kmcomapi/com/keyman_implementation.pas
@@ -90,7 +90,6 @@ implementation
uses
Classes,
ComServ,
- errlogpath,
sysutils,
klog,
utilhandleexception;
@@ -102,9 +101,6 @@ procedure TKeyman.Initialize;
begin
KL.MethodEnter(Self, 'Initialize', []);
try
- // I1642 move GetErrLogPath out of DllMain - causes memory issues later due to COM calls
- ForceDirectories(GetErrLogPath);
-
inherited;
try
@@ -122,7 +118,6 @@ begin
on E:Exception do
begin
LogException('TKeyman', E, ExceptAddr);
- SysUtils.ShowException(ExceptObject, ExceptAddr);
FInitialized := False;
end;
end;
@@ -245,6 +240,7 @@ end;
function TKeyman.Get_AutoApply: WordBool;
begin
+ if not FInitialized then raise Exception.Create(SErrorUninitialised);
Result := FControl.AutoApply;
end;
@@ -252,6 +248,7 @@ procedure TKeyman.Apply;
var
AutoApply: Boolean;
begin
+ if not FInitialized then raise Exception.Create(SErrorUninitialised);
AutoApply := FControl.AutoApply;
FControl.AutoApply := False;
try
@@ -267,6 +264,7 @@ end;
procedure TKeyman.Set_AutoApply(Value: WordBool);
begin
+ if not FInitialized then raise Exception.Create(SErrorUninitialised);
FControl.AutoApply := Value;
end;
diff --git a/windows/src/engine/kmcomapi/kmcomapi.dpr b/windows/src/engine/kmcomapi/kmcomapi.dpr
index f63bdb6008..9c1fb43e59 100644
--- a/windows/src/engine/kmcomapi/kmcomapi.dpr
+++ b/windows/src/engine/kmcomapi/kmcomapi.dpr
@@ -112,7 +112,6 @@ uses
UnicodeBlocks in '..\..\global\delphi\general\UnicodeBlocks.pas' { UnicodeBlock: CoClass},
IntegerArray in '..\..\global\delphi\general\IntegerArray.pas',
KeymanControlMessages in '..\..\global\delphi\general\KeymanControlMessages.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
internalinterfaces in 'util\internalinterfaces.pas',
VKeyChars in '..\..\global\delphi\general\VKeyChars.pas',
UserMessages in '..\..\global\delphi\general\UserMessages.pas',
diff --git a/windows/src/engine/kmcomapi/kmcomapi.dproj b/windows/src/engine/kmcomapi/kmcomapi.dproj
index 0e54799bf5..9bbd862e8f 100644
--- a/windows/src/engine/kmcomapi/kmcomapi.dproj
+++ b/windows/src/engine/kmcomapi/kmcomapi.dproj
@@ -3,7 +3,7 @@
{A20773C5-2124-434E-9F3C-27DC9EBC85EE}
kmcomapi.dpr
True
- Debug
+ Release
3
Library
VCL
@@ -110,12 +110,14 @@
false
false
CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName)
+ 2
CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=
1
0
false
+ 2
DEBUG;KLOG;$(DCC_Define)
@@ -251,7 +253,6 @@
-
@@ -348,8 +349,8 @@
www.greatis.com
- Microsoft Office 2000 Sample Automation Server Wrapper Components
- Microsoft Office XP Sample Automation Server Wrapper Components
+ Microsoft Office 2000 Sample Automation Server Wrapper Components
+ Microsoft Office XP Sample Automation Server Wrapper Components
diff --git a/windows/src/engine/kmcomapi/processes/keyboard/kprecompilemnemonickeyboard.pas b/windows/src/engine/kmcomapi/processes/keyboard/kprecompilemnemonickeyboard.pas
index 5f28e7de27..30d0c275cc 100644
--- a/windows/src/engine/kmcomapi/processes/keyboard/kprecompilemnemonickeyboard.pas
+++ b/windows/src/engine/kmcomapi/processes/keyboard/kprecompilemnemonickeyboard.pas
@@ -41,7 +41,6 @@ uses
System.Win.Registry,
Winapi.Windows,
- errlogpath,
errorcontrolledregistry,
keymancontext,
keymanerrorcodes,
@@ -106,7 +105,7 @@ begin
with TStringStream.Create(FLogText, TEncoding.UTF8) do // I4174
try
- SaveToFile(GetErrLogFileName('mcompile-'+ChangeFileExt(ExtractFileName(FileName),'')));
+ SaveToFile(TKeymanPaths.ErrorLogPath('mcompile-'+ChangeFileExt(ExtractFileName(FileName),'')));
finally
Free;
end;
@@ -125,7 +124,7 @@ begin
with TStringStream.Create(FLogText, TEncoding.UTF8) do // I4174
try
- SaveToFile(GetErrLogFileName('mcompile-d-'+ChangeFileExt(ExtractFileName(FileName),'')));
+ SaveToFile(TKeymanPaths.ErrorLogPath('mcompile-d-'+ChangeFileExt(ExtractFileName(FileName),'')));
finally
Free;
end;
diff --git a/windows/src/engine/kmcomapi/processes/package/kpinstallpackage.pas b/windows/src/engine/kmcomapi/processes/package/kpinstallpackage.pas
index 368fc8ef39..530130a8b8 100644
--- a/windows/src/engine/kmcomapi/processes/package/kpinstallpackage.pas
+++ b/windows/src/engine/kmcomapi/processes/package/kpinstallpackage.pas
@@ -388,3 +388,4 @@ begin
end;
end.
+
diff --git a/windows/src/engine/kmcomapi/util/utilhandleexception.pas b/windows/src/engine/kmcomapi/util/utilhandleexception.pas
index 7334fea15c..3b150a42ec 100644
--- a/windows/src/engine/kmcomapi/util/utilhandleexception.pas
+++ b/windows/src/engine/kmcomapi/util/utilhandleexception.pas
@@ -32,79 +32,78 @@ implementation
uses
System.Classes,
+ System.JSON,
Winapi.Windows,
- ErrLogPath,
+ KeymanPaths,
+ JsonUtil,
KLog,
utildir,
VersionInfo;
-function ConvertedExceptAddr(ExceptAddr: Pointer): Pointer;
-
- function GetLogicalAddr( Address: Pointer ): Pointer;
- const
- CODE_OFFSET = $1000;
- begin
- {
- hard-coded $1000 instead of more correct FPImgHdr^.OptionalHeader.BaseOfCode
- because there are problems with corrupted header in packed EXEs
- BTW Inprise linkers always set code base = $1000 :)
- }
- if Address <> nil then Result := Pointer(Cardinal(Address)-CODE_OFFSET)
- else Result := nil;
- end;
+function RtlCaptureStackBackTrace(FramesToSkip, FramesToCapture: DWORD; BackTrace: Pointer; BackTraceHash: PDWORD): WORD; stdcall; external 'ntdll.dll';
+//
+// Capture a stack trace and include the offending crash address at the top of
+// the trace. Apart from skipping frames and the inclusion of TopAddr, this is
+// very similar to sentry_event_value_add_stacktrace.
+//
+function CaptureStackTrace(TopAddr: Pointer; FramesToSkip: DWORD): TJSONArray;
var
- Info: TMemoryBasicInformation;
+ walked_backtrace: array[0..255] of Pointer;
+ frameCount: Word;
+ i: Integer;
begin
- VirtualQuery(ExceptAddr, Info, sizeof(Info));
- if Info.State <> MEM_COMMIT then
- Result := GetLogicalAddr(ExceptAddr)
- else
- Result := GetLogicalAddr( Pointer(Integer(ExceptAddr)-Integer(Info.AllocationBase)) );
+ Result := TJSONArray.Create;
+
+ frameCount := RtlCaptureStackBackTrace(FramesToSkip, 256, @walked_backtrace[0], nil);
+ if frameCount = 0 then
+ Exit;
+
+ for i := Integer(frameCount) - 1 downto 0 do
+ Result.Add(Format('0x%x', [NativeUInt(walked_backtrace[i])]));
+
+ // Insert the except address at the top of the stack
+ if TopAddr <> nil then
+ Result.Add(Format('0x%x', [NativeUInt(TopAddr)]));
end;
procedure LogException(const SourceClassName: string; E: Exception; ExceptAddr: Pointer);
-{$IFDEF CPUX64}
+const
+ Size = 1024;
var
- msg, errlogfile: string;
errlog: TStringList;
+ errlogfile: string;
+ stack: TJSONArray;
+ o: TJSONObject;
+ Buffer: array[0..Size-1] of Char;
begin
+ stack := CaptureStackTrace(ExceptAddr, 0);
+
+ errlog := TStringList.Create;
try
- if E = nil
- then msg := Format('Exception in %s at %p', [SourceClassName, ConvertedExceptAddr(ExceptAddr)])
- else msg := Format('Exception in %s at %p (%s): %s', [SourceClassName, ConvertedExceptAddr(ExceptAddr), E.ClassName, (E as Exception).Message]);
- KL.LogError(msg);
+ errlogfile := TKeymanPaths.ErrorLogPath('kmcomapi'); // I2824
- errlog := TStringList.Create;
+ o := TJSONObject.Create;
+ o.AddPair('sourceClassName', SourceClassName);
+ o.AddPair('exception', E.ClassName);
+
+ if ExceptionErrorMessage(E, ExceptAddr, Buffer, Size) > 0
+ then o.AddPair('message', Buffer)
+ else o.AddPair('message', E.Message);
+ o.AddPair('stack', stack);
+ PrettyPrintJSON(o, errlog, 2);
+
+ with TStringStream.Create(errlog.Text, TEncoding.UTF8) do
try
- errlogfile := GetErrLogFileName('kmcomapi'); // I2824
-
- errlog.Text :=
- 'Crash Identifier: kmcomapi.dll_'+GetVersionString+'_'+IntToHex(Integer(ExceptAddr),8)+#13#10#13#10+
- 'KMCOMAPI EXCEPTION AT '+FormatDateTime('yyyy-mm-dd hh:nn:ss', Now) + #13#10 +
- 'kmcomapi.dll version ' + GetVersionString + #13#10 +
- msg + #13#10#13#10;
-
- if FileExists(errlogfile) then
- with TStringList.Create do
- try
- LoadFromFile(errlogfile); // use prolog encoding
- errlog.Text := Text + errlog.Text;
- finally
- Free;
- end;
- errlog.SaveToFile(errlogfile, TEncoding.UTF8); // I3337
+ // Use TStringStream to avoid BOM from TStringList
+ SaveToFile(errlogfile);
finally
- errlog.Free;
+ Free;
end;
- except
- ;
+ finally
+ errlog.Free;
end;
-{$ELSE}
-begin
- {$MESSAGE HINT 'TODO: Write a raw call stack to diag folder which can then be sucked in by client app and reported'}
-{$ENDIF}
end;
procedure LogException(E: Exception);
diff --git a/windows/src/engine/tsysinfo/UfrmEmail.pas b/windows/src/engine/tsysinfo/UfrmEmail.pas
index c0f9b39cf1..7a7acf91fc 100644
--- a/windows/src/engine/tsysinfo/UfrmEmail.pas
+++ b/windows/src/engine/tsysinfo/UfrmEmail.pas
@@ -66,7 +66,6 @@ type
implementation
uses
- ErrLogPath,
GlobalProxySettings,
Math,
UfrmProgress,
diff --git a/windows/src/engine/tsysinfo/sysinfo_main.pas b/windows/src/engine/tsysinfo/sysinfo_main.pas
index b4864e2bfc..a37dacb92a 100644
--- a/windows/src/engine/tsysinfo/sysinfo_main.pas
+++ b/windows/src/engine/tsysinfo/sysinfo_main.pas
@@ -105,7 +105,7 @@ implementation
uses
// keymanstrings,
- ErrLogPath, ActiveX, ComObj,
+ KeymanPaths, ActiveX, ComObj,
ErrorControlledRegistry, RegistryKeys,
shlobj,
UframeAttachedFiles,
@@ -234,7 +234,7 @@ var
m: Integer;
begin
FSIList.Files.Clear;
- FPath := GetErrLogPath;
+ FPath := TKeymanPaths.ErrorLogPath;
if FindFirst(FPath + '*', 0, f) = 0 then
begin
i := 0;
diff --git a/windows/src/engine/tsysinfo/tsysinfo.dpr b/windows/src/engine/tsysinfo/tsysinfo.dpr
index ebead23a3f..29226a45da 100644
--- a/windows/src/engine/tsysinfo/tsysinfo.dpr
+++ b/windows/src/engine/tsysinfo/tsysinfo.dpr
@@ -26,7 +26,6 @@ uses
DebugPaths in '..\..\global\delphi\general\DebugPaths.pas',
GetOsVersion in '..\..\global\delphi\general\GetOsVersion.pas',
GlobalProxySettings in '..\..\global\delphi\general\GlobalProxySettings.pas',
- ErrLogPath in '..\..\global\delphi\general\ErrLogPath.pas',
klog in '..\..\global\delphi\general\klog.pas',
utildir in '..\..\global\delphi\general\utildir.pas',
utilsystem in '..\..\global\delphi\general\utilsystem.pas',
diff --git a/windows/src/engine/tsysinfo/tsysinfo.dproj b/windows/src/engine/tsysinfo/tsysinfo.dproj
index ee685968da..c217fda389 100644
--- a/windows/src/engine/tsysinfo/tsysinfo.dproj
+++ b/windows/src/engine/tsysinfo/tsysinfo.dproj
@@ -170,7 +170,6 @@
-
diff --git a/windows/src/ext/sentry/Sentry.Client.pas b/windows/src/ext/sentry/Sentry.Client.pas
index d63e26419e..5d134445d3 100644
--- a/windows/src/ext/sentry/Sentry.Client.pas
+++ b/windows/src/ext/sentry/Sentry.Client.pas
@@ -36,7 +36,13 @@ type
TSentryClientEventType = (scetException, scetMessage);
TSentryClientEventAction = (sceaContinue, sceaTerminate);
- TSentryClientEvent = procedure(Sender: TObject; EventType: TSentryClientEventType;
+
+ TSentryClientBeforeEvent = procedure(Sender: TObject; EventType: TSentryClientEventType;
+ event: sentry_value_t;
+ const EventClassName, Message: string;
+ var EventAction: TSentryClientEventAction) of object;
+
+ TSentryClientAfterEvent = procedure(Sender: TObject; EventType: TSentryClientEventType;
const EventID, EventClassName, Message: string;
var EventAction: TSentryClientEventAction) of object;
@@ -46,11 +52,12 @@ type
FInstance: TSentryClient;
private
options: psentry_options_t;
- FOnBeforeEvent: TSentryClientEvent;
- FOnAfterEvent: TSentryClientEvent;
+ FOnBeforeEvent: TSentryClientBeforeEvent;
+ FOnAfterEvent: TSentryClientAfterEvent;
procedure DoAfterEvent(const EventID, ExceptionClassName, Message: string;
EventType: TSentryClientEventType);
- procedure DoBeforeEvent(const EventID, ExceptionClassName, Message: string;
+ procedure DoBeforeEvent(event: sentry_value_t;
+ const ExceptionClassName, Message: string;
EventType: TSentryClientEventType);
procedure DoTerminate;
function EventIDToString(Guid: TGUID): string;
@@ -61,8 +68,8 @@ type
function MessageEvent(Level: TSentryLevel; const Logger, Message: string; IncludeStack: Boolean = False): TGUID;
function ExceptionEvent(const ExceptionClassName, Message: string; AExceptAddr: Pointer = nil): TGUID;
- property OnBeforeEvent: TSentryClientEvent read FOnBeforeEvent write FOnBeforeEvent;
- property OnAfterEvent: TSentryClientEvent read FOnAfterEvent write FOnAfterEvent;
+ property OnBeforeEvent: TSentryClientBeforeEvent read FOnBeforeEvent write FOnBeforeEvent;
+ property OnAfterEvent: TSentryClientAfterEvent read FOnAfterEvent write FOnAfterEvent;
end;
TSentryClientClass = class of TSentryClient;
@@ -222,7 +229,8 @@ begin
ExitProcess(1);
end;
-procedure TSentryClient.DoBeforeEvent(const EventID, ExceptionClassName, Message: string;
+procedure TSentryClient.DoBeforeEvent(event: sentry_value_t;
+ const ExceptionClassName, Message: string;
EventType: TSentryClientEventType);
var
EventAction: TSentryClientEventAction;
@@ -230,7 +238,7 @@ begin
if Assigned(FOnBeforeEvent) then
begin
EventAction := sceaContinue;
- FOnBeforeEvent(Self, EventType, EventID, ExceptionClassName, Message, EventAction);
+ FOnBeforeEvent(Self, EventType, event, ExceptionClassName, Message, EventAction);
if EventAction = sceaTerminate then
DoTerminate;
end;
@@ -275,16 +283,16 @@ const
// A pseudo-frame is inserted at the top of the stack which points
// to the address of the code that caused the exception
begin
- DoBeforeEvent('', ExceptionClassName, Message, scetException);
-
event := sentry_value_new_event;
+ DoBeforeEvent(event, ExceptionClassName, Message, scetException);
+
(*
When we set exception information, the report is corrupted. Not sure why. So
for now we won't create as an exception event. We still get all the information
we want from this.
- Investigating this further.
+ Investigating this further at https://forum.sentry.io/t/corrupted-display-when-exception-data-is-set-using-native-sdk/9167/2
exc := sentry_value_new_object;
sentry_value_set_by_key(exc, 'type', sentry_value_new_string(PAnsiChar(UTF8Encode(ExceptionClassName))));
@@ -317,14 +325,14 @@ const
// Sentry.Client.CaptureStackTrace,
// Sentry.Client.TSentryClient.MessageEvent
begin
- DoBeforeEvent('', Logger, Message, scetMessage);
-
event := sentry_value_new_message_event(
{* level *} sentry_level_t(Level),
{* logger *} PAnsiChar(UTF8Encode(Logger)),
{* message *} PAnsiChar(UTF8Encode(Message))
);
+ DoBeforeEvent(event, Logger, Message, scetMessage);
+
if IncludeStack then
begin
threads := CaptureStackTrace(nil, FRAMES_TO_SKIP);
diff --git a/windows/src/global/delphi/debug/Keyman.System.DebugLogManager.pas b/windows/src/global/delphi/debug/Keyman.System.DebugLogManager.pas
index ef939dc453..9937d476c3 100644
--- a/windows/src/global/delphi/debug/Keyman.System.DebugLogManager.pas
+++ b/windows/src/global/delphi/debug/Keyman.System.DebugLogManager.pas
@@ -81,7 +81,7 @@ uses
Accctrl,
AclApi,
ErrorControlledRegistry,
- ErrLogPath,
+ KeymanPaths,
RegistryKeys,
Unicode;
@@ -237,7 +237,7 @@ const
function TDebugLogManager.DebugLogFileName(n: Integer): string;
begin
- Result := GetErrLogPath + 'system' + IntToStr(n) + '.etl';
+ Result := TKeymanPaths.ErrorLogPath + 'system' + IntToStr(n) + '.etl';
end;
procedure TDebugLogManager.FindFirstLogFileName;
diff --git a/windows/src/global/delphi/general/ErrLogPath.pas b/windows/src/global/delphi/general/ErrLogPath.pas
deleted file mode 100644
index 1b73f84312..0000000000
--- a/windows/src/global/delphi/general/ErrLogPath.pas
+++ /dev/null
@@ -1,48 +0,0 @@
-(*
- Name: ErrLogPath
- Copyright: Copyright (C) SIL International.
- Documentation:
- Description:
- Create Date: 28 Jul 2008
-
- Modified Date: 26 Jun 2012
- Authors: mcdurdin
- Related Files:
- Dependencies:
-
- Bugs:
- Todo:
- Notes:
- History: 28 Jul 2008 - mcdurdin - I1574 - Initial version
- 18 Mar 2011 - mcdurdin - I2768 - Fix crash in installer when Diag folder does not exist
- 18 Mar 2011 - mcdurdin - I2824 - Consolidate logging of diagnostics to Diag folder in appdata
- 26 Jun 2012 - mcdurdin - I3379 - KM9 - Remove old Winapi references now in Delphi libraries
-*)
-unit ErrLogPath;
-
-interface
-
-function GetErrLogPath: string;
-function GetErrLogFileName(app: string): string;
-
-implementation
-
-uses
- Winapi.ShlObj,
- Winapi.Windows,
- System.SysUtils,
- RegistryKeys,
- utilsystem;
-
-function GetErrLogPath: string;
-begin
- Result := GetFolderPath(CSIDL_LOCAL_APPDATA) + SFolderKeymanEngineDiag + '\';
- ForceDirectories(Result); // I2768
-end;
-
-function GetErrLogFileName(app: string): string; // I2824
-begin
- Result := GetErrLogPath + app + '-' + IntToStr(GetCurrentProcessId) + '-' + FormatDateTime('yyyy-mm-dd-hh-nn-ss', Now) + '.log';
-end;
-
-end.
diff --git a/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas b/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas
index ca029d29fb..b2ff7ed738 100644
--- a/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas
+++ b/windows/src/global/delphi/general/Keyman.System.KeymanSentryClient.pas
@@ -19,6 +19,7 @@ type
const EventID, EventClassName, Message: string;
var EventAction: TSentryClientEventAction);
constructor Create(SentryClientClass: TSentryClientClass; AProject: TKeymanSentryClientProject; AFlags: TKeymanSentryClientFlags);
+ procedure ReportRemoteErrors(const childEventID: string);
public
destructor Destroy; override;
class procedure Start(SentryClientClass: TSentryClientClass; AProject: TKeymanSentryClientProject; AFlags: TKeymanSentryClientFlags = [kscfCaptureExceptions, kscfShowUI, kscfTerminate]);
@@ -31,13 +32,18 @@ type
implementation
uses
- sentry,
-
+ System.Classes,
+ System.Generics.Collections,
+ System.JSON,
System.SysUtils,
{$IF NOT DEFINED(CONSOLE)}
+ System.UITypes,
+ Vcl.Dialogs,
Vcl.Forms,
{$ENDIF}
+ sentry,
+
KeymanPaths,
KeymanVersion,
utilexecute;
@@ -83,14 +89,18 @@ var
begin
if EventType = scetException then
begin
+ // We need to look for a kmcomapi errlog and report that
+ ReportRemoteErrors(EventID);
+
if kscfShowUI in FFlags then
begin
{$IF DEFINED(CONSOLE)}
// Write to console
- writeln('Fatal error '+EventClassName+': '+Message);
- writeln;
+ writeln(ErrOutput, 'Fatal error '+EventClassName+': '+Message);
+ writeln(ErrOutput, 'This error has been automatically reported to the Keyman team.');
+ writeln(ErrOutput);
{$ELSE}
- // Launch external gui exception handler.
+ // Launch external gui exception dialog app.
// Usage: tsysinfo -c [sentryprojectname [classname [message]]]
AppID := LowerCase(ChangeFileExt(ExtractFileName(ParamStr(0)), ''))+'-'+CKeymanVersionInfo.VersionWithTag;
if Assigned(Application)
@@ -114,13 +124,9 @@ begin
if not TUtilExecute.Shell(0, TKeymanPaths.KeymanEngineInstallPath('tsysinfo.exe'), // I3349
TKeymanPaths.KeymanEngineInstallPath(''), CommandLine) then
begin
- {$MESSAGE HINT 'Show a message before aborting here?'}
- {MessageDlg(Application.Title+' has had a fatal error. An additional error was encountered starting the exception manager ('+SysErrorMessage(GetLastError)+'). '+
- #13#10'Error log is stored in '#13#10#13#10+' '+FLogFile+#13#10#13#10+
- message+#13#10+
- detail+#13#10+
- 'Please send this information to Keyman Support',
- mtError, [mbOK], 0);}
+ MessageDlg(Application.Title+' has had a fatal error. An additional error was encountered '+
+ 'starting the exception manager ('+SysErrorMessage(GetLastError)+'). '+
+ 'This error has been automatically reported to the Keyman team.', mtError, [mbOK], 0);
end;
{$ENDIF}
end;
@@ -130,6 +136,78 @@ begin
end;
end;
+procedure TKeymanSentryClient.ReportRemoteErrors(const childEventID: string);
+var
+ errlogfile: string;
+ o: TJSONObject;
+ event: sentry_value_t;
+ stack: TJSONArray;
+ i: Integer;
+ frames: sentry_value_t;
+ stacktrace: sentry_value_t;
+ threads: sentry_value_t;
+ thread: sentry_value_t;
+ frame: sentry_value_t;
+begin
+ errlogfile := TKeymanPaths.ErrorLogPath('kmcomapi'); // I2824
+ if FileExists(errlogfile) then
+ begin
+ // We'll use the Sentry API directly here to construct a crash report from
+ // kmcomapi.
+ with TStringStream.Create('', TEncoding.UTF8) do
+ try
+ LoadFromFile(errlogfile);
+ o := TJSONObject.ParseJSONValue(DataString) as TJSONObject;
+ finally
+ Free;
+ end;
+
+ DeleteFile(errlogfile);
+
+(*
+ When we set exception information, the report is corrupted. Not sure why. So
+ for now we won't create as an exception event. We still get all the information
+ we want from this.
+
+ Investigating this further at https://forum.sentry.io/t/corrupted-display-when-exception-data-is-set-using-native-sdk/9167/2
+
+ exc := sentry_value_new_object;
+ sentry_value_set_by_key(exc, 'type', sentry_value_new_string(PAnsiChar(UTF8Encode(ExceptionClassName))));
+ sentry_value_set_by_key(exc, 'value', sentry_value_new_string(PAnsiChar(UTF8Encode(Message))));
+ sentry_value_set_by_key(event, 'exception', exc);
+*)
+
+ event := sentry_value_new_event;
+ sentry_value_set_by_key(event, 'message', sentry_value_new_string(PAnsiChar(UTF8Encode(o.Values['message'].Value))));
+ // Construct the stack trace
+
+ stack := o.Values['stack'] as TJSONArray;
+
+ frames := sentry_value_new_list;
+
+ for i := 0 to stack.Count - 1 do
+ begin
+ frame := sentry_value_new_object;
+ sentry_value_set_by_key(frame, 'instruction_addr', sentry_value_new_string(PAnsiChar(AnsiString(stack.Items[i].Value))));
+ sentry_value_append(frames, frame);
+ end;
+
+ stacktrace := sentry_value_new_object;
+ sentry_value_set_by_key(stacktrace, 'frames', frames);
+
+ threads := sentry_value_new_list;
+ thread := sentry_value_new_object;
+ sentry_value_set_by_key(thread, 'stacktrace', stacktrace);
+ sentry_value_append(threads, thread);
+
+ sentry_value_set_by_key(event, 'threads', threads);
+
+ sentry_set_extra('child_event', sentry_value_new_string(PAnsiChar(AnsiString(childEventID))));
+
+ sentry_capture_event(event);
+ end;
+end;
+
constructor TKeymanSentryClient.Create(SentryClientClass: TSentryClientClass; AProject: TKeymanSentryClientProject; AFlags: TKeymanSentryClientFlags);
var
o: TSentryClientOptions;
diff --git a/windows/src/global/delphi/general/KeymanPaths.pas b/windows/src/global/delphi/general/KeymanPaths.pas
index 06025cc68f..71e706a9fb 100644
--- a/windows/src/global/delphi/general/KeymanPaths.pas
+++ b/windows/src/global/delphi/general/KeymanPaths.pas
@@ -25,6 +25,7 @@ type
const S_FallbackKeyboardPath = 'Keyboards\';
const S__Package = '_Package\';
const S_MCompileExe = 'mcompile.exe';
+ class function ErrorLogPath(const app: string = ''): string; static;
class function KeymanDesktopInstallPath(const filename: string = ''): string; static;
class function KeymanEngineInstallPath(const filename: string = ''): string; static;
class function KeymanDesktopInstallDir: string; static;
@@ -290,4 +291,12 @@ begin
Result := Result + Filename;
end;
+class function TKeymanPaths.ErrorLogPath(const app: string): string;
+begin
+ Result := GetFolderPath(CSIDL_LOCAL_APPDATA) + SFolderKeymanEngineDiag + '\';
+ ForceDirectories(Result); // I2768
+ if app <> '' then
+ Result := Result + app + '-' + IntToStr(GetCurrentProcessId) + '.log';
+end;
+
end.
diff --git a/windows/src/global/vc/keymansentry.cpp b/windows/src/global/vc/keymansentry.cpp
index 66c99a5c8b..50b866233b 100644
--- a/windows/src/global/vc/keymansentry.cpp
+++ b/windows/src/global/vc/keymansentry.cpp
@@ -86,7 +86,7 @@ void keyman_sentry_report_exception(DWORD ExceptionCode, PVOID ExceptionAddress)
for now we won't create as an exception event. We still get all the information
we want from this.
- Investigating this further.
+ Investigating this further at https://forum.sentry.io/t/corrupted-display-when-exception-data-is-set-using-native-sdk/9167/2
sentry_value_t exc = sentry_value_new_object();
sentry_value_set_by_key(exc, "type", sentry_value_new_string("Exception"));