This is how I upgraded SmartInspect to XE7.
Important Update!
Gurock has released SmartInspect v3.3.8, which supports XE7. So now this post is only for historical interest.
My version of SI is “SmartInspect Professional” v3.3.7.150 by Gurock software. My version of Delphi is “Embarcadero® Delphi XE7” Version 21.0.17707.5020 + Update 1. The o/s is Windows 7 Enterprise + SP1 (64-bit). This procedure was tested early March 2015.
(1) Install SmartInspect using the regular automated installer.
(2) Copy the source code for XE6 to a new and convenient directory.
The relevant files are:
- $(appdir)\source\delphi\SiAuto.pas
- $(appdir)\source\delphi\SiEncryption.pas
- $(appdir)\source\delphi\SiRijndael.inc
- $(appdir)\source\delphi\SiWinSock2.pa
- $(appdir)\source\delphi\SmartInspect.inc
- $(appdir)\source\delphi\SmartInspect.pas
- $(appdir)\source\delphi\SmartInpsectDXE6.dpk
- $(appdir)\source\delphi\SmartInspectDXE6.bdsproj
… where:
- $(appdir) is a place-marker for your SI install directory (typically something like ‘C:\Program Files (x86)\Gurock Software\SmartInspect Professional’)
- $(new-appdir) is a place-marker for where you care going to put the modified source
For now, don’t worry about the project group, the 64-bit project, nor the project heads for the other compilers.
In your new directory, structure the folders how you like. My preference is indicated below. segregate the project head files (.dpk, and .bdsproj) from the rest in accordance with your plan. For example, with my structure, everything goes in run, except for the package heads which go in packages\XE7.
Directory | What lives there? |
---|---|
$(new-appdir)\run | Source, except head files |
$(new-appdir)\packages\XE7 | XE7 head files |
$(new-appdir)\ephemeral\dcu\XE7\Win32\Debug | dcu files for Win32 platform, Debug config |
$(new-appdir)\ephemeral\dcu\XE7\Win32\Release | dcu files for Win32 platform, Release config |
$(new-appdir)\ephemeral\dcu\XE7\Win64\Debug | dcu files for Win64 platform, Debug config |
$(new-appdir)\ephemeral\dcu\XE7\Win64\Release | dcu files for Win64 platform, Release config |
(3) Open up the package, at the new location, in the XE7 IDE. If you have changed the relative location of project members to thier heads, you may have to remove and add back the members. Do not do so by directly editing the dpr, as doing so will not correctly update the droj.
The original dpr should look like this …
package SmartInspectDXE6; {$ALIGN 8} {$ASSERTIONS ON} {$BOOLEVAL OFF} {$DEBUGINFO ON} {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} {$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} {$OPTIMIZATION ON} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} {$REFERENCEINFO ON} {$SAFEDIVIDE OFF} {$STACKFRAMES OFF} {$TYPEDADDRESS OFF} {$VARSTRINGCHECKS ON} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} {$IMAGEBASE $400000} {$DESCRIPTION 'SmartInspect Delphi XE6 Runtime Package'} {$RUNONLY} {$IMPLICITBUILD OFF} requires rtl, vcl {$IFNDEF SI_DISABLE_GRAPHIC}, vclimg {$ENDIF} {$IFNDEF SI_DISABLE_DB}, dbrtl {$ENDIF} ; contains SiAuto in 'SiAuto.pas', SiWinSock2 in 'SiWinSock2.pas', SiEncryption in 'SiEncryption.pas', SmartInspect in 'SmartInspect.pas'; {$IFDEF INCLUDE_VERSION} {$R versionXE6.res} {$ENDIF} end.
Experienced Delphi Developers will notice a problem here straight away. Manual conditionals in the dpr source. The SmartInspect author should have known better. If you make any changes to the project, even just project options, the Delhi IDE will silently trash the source, and the developer will be left scratching his head, wondering why it won’t compile. We don’t need the conditions. For the one in a million developers that want vlcimg unit excluded, or the version resource included, they can work this out for themselves.
(4) If touching the project leads you to an unbalanced {$ENDIF}, balance as required, but don’t compile yet.
(5) Save the project with new project name, just ‘SmartInspect’. Delete the old named project head files. We should be left with just SmartInspect.dpr and SmartInspect.dproj .
(6) Set up project options. Set these options up for “All configurations – all platforms”. Make sure afterwards that there are no configs in which these values are overridden. Checking this can be a bit of a pain, and is a area of the Embarcadero IDE that could and should be improved.
Option | Instruction |
---|---|
dcp output directory | Set as required. Usually this is blank, and the resolved value is controlled by IDE options. |
package output directory | Set as required. Usually this is blank and the resolved value is controlled by IDE options |
unit output directory. | Set to ..\..\ephemeral\dcu\XE7\$(Platform)\$(Config) |
Description | SmartInspect Runtime Package |
LibSuffix | _XE7 |
Include Version Information | CHECKED |
Module Version Number | 3.3.7.0 |
(7) Update global IDE options for library path. Do this for all platforms. In particular ….
- Remove any references that might exist to the old SmartInspect source, dcu or dcp locations
- Remove any references that might exist to the new SmartInspect source directory. We don’t want SI units to be recompiled every time we compile our main application
- Include the path the to SmartInspect dcu files at the new location. Make use of $(Platform) and $(Config) symbols, instead of literal values, where-ever possible.
- You many also want to adjust browsing path, if you intend to debug step into the SI code. This step is optional and will not suite all developers.
(8) If you are the obsessive-compulsive type, you could edit the SmartInspect.inc file to explicitly mention the XE7 compiler, but this step is optional, as the inc file is already reasonably future-proof. I list it here for your convenience. How to extend it is self-evident.
// // <!-- Copyright (C) Gurock Software GmbH. All rights reserved. --> // // <summary> // Contains defines needed to be compatible with multiple versions of // Borland/CodeGear Delphi. // </summary> {$IFDEF CONDITIONALEXPRESSIONS} {$IF CompilerVersion >= 15} {$DEFINE DELPHI7_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 17} {$DEFINE DELPHI2005_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 18} {$DEFINE DELPHI2006_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 20} {$DEFINE DELPHI2009_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 21} {$DEFINE DELPHI2010_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 22} // XE {$DEFINE DELPHI2011_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 23} // XE2 {$DEFINE DELPHI2012_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 24} // XE3 {$DEFINE DELPHI2013_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 25} // XE4 {$DEFINE DELPHI2014_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 26} // XE5 {$DEFINE DELPHIXE5_OR_HIGHER} {$IFEND} {$IF CompilerVersion >= 27} // XE5 {$DEFINE DELPHIXE6_OR_HIGHER} {$IFEND} {$ENDIF} {.$DEFINE SI_DISABLE_DB} { Disables database methods } {.$DEFINE SI_DISABLE_RTTI} { Disables RTTI usage (LogObject) } {.$DEFINE SI_DISABLE_GRAPHIC} { Disables graphic methods } {.$DEFINE SI_DISABLE_ENCRYPT} { Disables log file encryption } {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF}
(9) In the unit SmartInspect.pas, add a line in the procedure InternalConnect(), just after making the o/s call to connect to the socket. This line will be a call to a local procedure to write some text to a temporary file. This is shown in listing 3. I have found by trial, that without this fix, on XE7, about 50% of the time, the o/s would terminate the application calling InternalConnect. No exception is raised. The application is simply terminated. I cannot explain why this fix works. I only know that it does. InternalConnect() will be called every time a SmartInspect object is switched from Enabled=False to Enabled=True.
procedure FixSocketProblem; var FN: string; TempStream: TStream; Buff: TBytes; sOutText: string; begin FN := TPath.GetTempFileName; TempStream := TFile.Create( FN, fmCreate); try sOutText := 'Writing some text to a file makes the socket problem go away.'; Buff := TEncoding.UTF8.GetBytes( sOutText); TempStream.WriteData( Buff, Length( Buff)) finally TempStream.Free end; TFile.Delete( FN) end; procedure TSiTcpClient.InternalConnect(const ASocket: TSocket; const ATo: PSockAddrIn; const ATimeout: Integer); var LConnectResult: Integer; WSAErr: integer; begin ChangeBlockingMode(ASocket, False); try // Try a non-blocking connect to the destination. LConnectResult := SiWinSock2.connect(ASocket, PSockAddr(ATo), SizeOf(ATo^)); FixSocketProblem; if LConnectResult = SOCKET_ERROR then begin // It is normal that the winsock connect function returns // SOCKET_ERROR if a socket is in non-blocking mode. This occurs // if the socket could not be connected immediately. But if the // last error isn't WSAEWOULDBLOCK we've got a real error. WSAErr := WSAGetLastError; if WSAErr <> WSAEWOULDBLOCK then begin // And indeed, we've got a real error, so // raise the last socket error accordingly. RaiseLastSocketError; end; // We are not connected yet, so we need to wait // until the socket connection has been established // or the timeout has been reached. WaitForConnect(ASocket, ATimeout); end; finally // Restore the normal blocking mode. ChangeBlockingMode(ASocket, True); end; end;
(10) Modify TSiTcpClient.ReceiveLn() to put a reasonable cap on the text line returned by sending tcp client (usually the SI Console). The adjustment is shown in listing 4.
function TSiTcpClient.ReceiveLn: AnsiString; var C: AnsiChar; begin SetLength(Result, 0); repeat if Receive(C, 1) <> 1 then begin // The socket connection has been closed. raise ESmartInspectError.Create(SiSocketClosedError); end else begin if not (C in [#10, #13]) then begin // Append the new character, unless it // represents a newline (char #10 or #13). Result := Result + C; end; end; until (C = #10) or (Length( result) >= 1000); end;
(11) Compile with Platform=Win32 and Config=Debug.
(12) Test. Make a Win32 desktop application that does some SI stuff.
(13) Compile for Release.
(14) Add Win64 platform within the same project. No need to create a separate project. Compile for both configs.
The new dpr should look like this …
package SmartInspect; {$R *.res} {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} {$ALIGN 8} {$ASSERTIONS ON} {$BOOLEVAL OFF} {$DEBUGINFO ON} {$EXTENDEDSYNTAX ON} {$IMPORTEDDATA ON} {$IOCHECKS ON} {$LOCALSYMBOLS ON} {$LONGSTRINGS ON} {$OPENSTRINGS ON} {$OPTIMIZATION OFF} {$OVERFLOWCHECKS OFF} {$RANGECHECKS OFF} {$REFERENCEINFO ON} {$SAFEDIVIDE OFF} {$STACKFRAMES ON} {$TYPEDADDRESS OFF} {$VARSTRINGCHECKS ON} {$WRITEABLECONST OFF} {$MINENUMSIZE 1} {$IMAGEBASE $400000} {$DEFINE DEBUG} {$ENDIF IMPLICITBUILDING} {$DESCRIPTION 'SmartInspect Runtime Package'} {$RUNONLY} {$IMPLICITBUILD OFF} requires rtl, vcl, vclimg, dbrtl; contains SiAuto in 'SiAuto.pas', SiWinSock2 in 'SiWinSock2.pas', SiEncryption in 'SiEncryption.pas', SmartInspect in 'SmartInspect.pas'; end.