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 @@
frmInstallOptions
- @@ -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 @@ -
Form1
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"));