//**************************************************************************** // $Module type: Package // $Module name: RegCheck2.pkg - Evolved from RegCheck.pkg introduced in 1989! // Uses RegCheck2.inc, cOSVersionInfo.pkg, _and_ // Language.pkg & LangSymb.pkg from VdfQuery (by Sture Andersen). // // $Author : Nils G. Svedmyr, RDC Tools International. // Copyright (c) 1998-2012 RDC Tools International // E-mail : nils.svedmyr@rdctools.com // Web-site : http://www.rdctools.com // // Created : 1998-12-21 @ 15:04 (Military date format - Year-Month-Day) // // This is free software; you can redistribute it and/or modify it under the // terms of the GNU Lesser General Public License. // // The code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // There should be a copy "GNU Lesser General Public License.txt" in the Help directory of the RegCheck2 workspace. // If not, Write to: The Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // Sponsors: There was a call on the newsgroup for sponsorship for the update of the RegCheck package to RegCheck2 and // I was very pleased to see that quite a few were willing to make donations to make this project come true. // Newsgroup link: http://support.dataaccess.com/Forums/showthread.php?48205-RegCheck2-sponsorship-invitation&p=240435#poststop // // And the proud sponsors are: (Thanks to all of you!) // =================================================== // Wil van Antwerpen, Garret Mott, Richard Hogg, Dave Porter, Todd Forsberg, Renato Villa, // Michael Mullan, James Albright, Jim Ward, Robert Worsley, Larry Pint, Anders ™hrt, // Dan Walsh, Riaz Merchant, Dave Robinson, Mike Cooper, Pieter van Dieren and Mika Vainio. // These persons are also listed as sponsors at vdf-guidance: // http://www.vdf-guidance.com/ContribPage.asp?Page=PKGCLSREGCHECK2N&ContribRecId=130 // // Contributors: (To the code) // Frank G. Vandervelpen, Peter H. van Wijk, Vincent Oorsprong, Ulbe Stellema, Peter Tawse, // Sergey Natarov, SJK, Bob Worsley and Wil van Antwerpen and Chuck Atkinson. // Thanks also goes to Dennis Piccioni at Data Access Worldwide for putting together // the white paper: "Opportunistic Locking and Read Caching on Microsoft Windows Networks" // http://www.dataaccess.com/whitepapers/opportunlockingreadcaching.html // Quote from the above white paper: // ================================= // - "Improperly configured Windows networks can lead to data corruption in any file system // database, including the embedded (DataFlex) database. Two Windows networking behaviors, // opportunistic locking (on Windows servers) and read caching (on Windows clients) are sources // for corruption potential." // - "Windows Terminal Services and Citrix:" // "Under normal use for these environments, users log onto a Windows server and run applications // locally on that server. If, however, an embedded database is located on another server than the // one running WTS/Citrix, oplocks between the WTS/Citrix server and the database server must be disabled." // // // Purpose: This package only applies to the embedded DataFlex database. It does NOT apply if // you are running MS-SQL, Pervasive, IBM DB2 or some other database system. // Why use it? Even If you're careful to implement the correct registry settings when deploying // your VDF application with the embedded DataFlex DBMS, some weirdo - e.g. a system administrator ;-) // or other installed software - e.g. Norton Security Suite - will apply changes that potentially // will be very dangerous to the integrity of your database. This package - when put at the top of // your program - will simply detect the client/server Windows version and check that the machine // registry settings are correct. If applicable it will perform necessary registry changes, although // it will - by default - ask the user for confirmation first. Changes to the registry is performed by // an external program also included in the RegCheck2 workspace - REGCHECKCHANGER.SRC // You will need to deploy the RegCheckChanger program with your application. // // // IMPORTANT: If you run this code on a workstation - the package will change // settings for that workstation only! // *** If you need to change settings on a SERVER, you need to // run your program on the server as well! // To clarify: Newer server editions of Windows supported by this // package does _not_ need to be changed - UNLESS you are // also using the server as a workstation. // After registry changes has been made by RegCheck2, the USER IS ASKED TO REBOOT THE MACHINE // for changes to take effect. // // Compatability: // Structs are used by this package and were introduced in VDF 11.0 so it is the // earliest VDF version that this code can be used with. // // Usage: Put a "Use RegCheck.pkg" at the top code of your VDF .src file, after // the cWorkspace or cApplication Object and add the following line to run the logic: // "Send DoCheckRegistrySettings of ghoRegCheck2 // Start registry checking (Main procedure)" // That's all there is to it! NOTE! This is a change from how the older RegCheck version // worked, were the DoCheckRegistrySettings was send automatically - it no longer is! // You probably also wants to adjust some of the default settings, in case you // need to inspect the cRegCheck2 class properties and change them in the ghoRegCheck2 // object at the bottom of this file. You also need to deploy a compiled version of // the RegCheckChanger program that is also included in the RegCheck2 workspace. // // Main messages:The two main procedures is: // DoCheckRegistrySettings - Reads registry and sets properties // DoWriteRegistrySettings // // Bugs: There's a WhatsNew.txt in the AppSrc folder that describes changes and // known bugs. // // Note: If you are using *SAMBA* you need to turn off oplocks by placing the following // lines in your [Global] section or in the section relevant to the share your data resides // RegCheck2 does NOT do this for you - You need to do it yourself manually. // - Actually I dont' know where the SAMBA ini file is stored... // Code: // oplocks = No // level2 oplocks = No // Samba is used on Linux/Unix machines and on many NAS devices and provides file and print services for // various Microsoft Windows clients and can integrate with a Windows Server domain. It // can also be part of an Acive Directory domain. // // // $Rev History (Most of this is from the old RegCheck package) // 1998-12-21 Module header created // 1999-01-02 NGS Changed package into a class. // 1999-01-03 NGS Added standard status log support. // 1999-01-08 NGS Removed hard-coded channels for Direct_Input and // Direct_Output. The Seq_Chnl package is now used instead. // This is to avoid possible conflicts with other packages or // source code that might use the same channels. // 1999-01-28 NGS Added the function FindNetwareConnection to check if user is // logged on to a Novell Server (Not only has Client32 installed). // 1999-02-08 NGS Added public properties psComputerName psUserLoginName (Strings). // 1999-02-21 NGS Added property to force the possible change of the setting even if there are // two database drivers loaded. It is possible that a (example accounting) BTrieve // database get consulted for info while the rest of the data is in DataFlex // Property Integer pbDriverBruteForce PUBLIC TRUE // Also added piFiles property, which is the number of FILES setting for Config.sys // 2000-11-16 PHvW Made Changed for Windows NT 2K. // Added test for NT sp1. // Ironed out compiler error for VDF 7. So this is possible not working for VDF6 // 2001-11-22 PHvW Added logic for NT2k and higher. Not tested for XP // I would like this pkg to work in a way that it does stop the program or gives a warning // when run on ME and XP-Home. Do not think any vdf software should run on it. // 2002-04-22 PHvW Changed a lot of code. Romeved the stuff mentioned above. // Added logic for detecting the windows version. Added properties to select // On what windows version you want to allow your application to run. // Added several error messages. Did not translate them. And are // for improvement. // Cleaned up the code for remarked not needed code since vdf7 // Not tested on vdf8, minor testing (only on win2k sp2) // So far debug mode is set to on // 2002-09-29 PHvW Removed stuff because of VDF8 // 2002-11-26 WvA Added compiler directive to avoid redefine errors for external // function Win32_WNetGetUser // Reintroduced VDF7 compatibility for now. Be aware that this // is not going to last for ever. // 2003-05-05 WvA Changed references to .NET server into 2003 Server // 2003-05-05 WvA Windows server 2000, 2003 and XP honour the traditional NT settings // but for these platforms there is a new setting // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MRXSmb\Parameters\OplocksDisabled // See also: http://support.microsoft.com/default.aspx?scid=kb;en-us;Q296264 // This setting has been added for Clients only // // 2003-05-22 WvA Fixed a problem with the translations for the english language (Thanks Peter Tawse for finding this) // 2003-06-09 WvA The DriveWriteBehind settings were wrongly written out as string while // the value should have been binary. (Thanks Renato Villa) // Renamed function ComputerName into NetbiosComputerName in order to avoid // a naming clash with Tufware's report classes. // 2004-01-30 PAT Added/Improved support for windows 2003 and netware // 2005-11-19 SJK Changed "Run_With_Warning" behaviour so that it doesn't bail out, but actually runs // 2005-11-19 WvA Added a new constant string csRegCheckStopBoxCaption which is used for all stop boxes by regcheck // so that it is more clear where the error originates from. // Changed Steve's suggestion a little bit to still write out a logfile entry. // 2006-09-02 RLW Add "RegCheck" to all stop_box captions so we can tell where the error comes from // 2007-08-21 WvA Added support for detecting Windows Vista and windows // Server editions. Replaced OS strings with constants // Fixed uppercase for W2K stop run detection and changed defaults // A unknown windows version will warn not stop, same for the home version. // 2007-09-24 WvA The Defaults were also set in the oRegCheck object which i missed to amend. // The introduction of the OS string constants missed another W2k typo in the tests. // 2007-09-25 WvA There was some really funky code for detecting if run on a server, // this is now simplified and uses results from the standard API. // Fixed so that any changes for setting server properties are logged (wrong condition) // Fixed text in english/dutch, you need to restart the PC not just the program in order // to see the difference in oplock settings! // // 2012-07-23 NGS *** MAJOR OVERHAUL *** Class is now called RegCheck2 to distinguish from earlier version. // IMPORTANT! This package now starts with Windows 2003 Server & Windows XP. // *** It does not check for earlier versions - nor does it check for Netware *** // Removed all Netware checking and for earlier Windows versions than NT 3.1 & Windows 95. // It uses a new cOSVersionInfo class to retrieve all the OS version info. // Added support for Vista, Windows 7, up to and including Windows 8 and Server 2012. // Here is some explanatory text about the changes in newer Windows versions - Vista and up - // that is important to this package: // http://technet.microsoft.com/en-us/library/ff686200(WS.10).aspx // With the advent of Windows Vista and Windows 7, Microsoft introduced a new network // protocol (SMB2) to optimize file sharing for WAN and low bandwidth and high latency // scenarios. to optimize these types of file access scenarios, Microsoft performed design // decisions which lead to the inability of the new SMB2 protocol to handle cache coherency // of file meta information such as the file size, the last update time and whether the file // actually exists on the server ("file not found" status). // As a result of this design decision made by Microsoft, the SMB2 protocol with its default // configuration breaks any application relying on shared, concurrent data access. It is therefore // absolutely required to reconfigure the SMB2 cache of the local _workstation_ to not cache // file meta information - the server is not affected by these settings, unless used as a workstation also... // The workstation settings can be found under this key: // HKEY_LOCAL_MACHINE\system\CurrentControlSet\Services\LanmanWorkstation\Parameters // - For server versions the following key is used: // HKEY_LOCAL_MACHINE\system\CurrentControlSet\Services\LanmanServer\Parameters // The following values needs to be created and set to zero. // Microsoft Recommendations: // "If you suspect metadata caching in the redirector as cause for misbehaving applications, disable the // caches in the following order to determine which of these caches is affecting the application. Disabling the // file information cache can have significant effect on client performance and show an increase in the number // of metadata requests that are sent to the server." // 1. DirectoryCacheLifetime REG_DWORD=0 // 2. FileNotFoundCacheLifetime REG_DWORD=0 // 3. FileInfoCacheLifetime REG_DWORD=0 // Also note: Vista and Win 7 have automatic failover so if SMB 2 is not available, // then SMB 1 would be used instead. // // The RegCheckChanger.exe manifest file has the following settings: // // // // // *** Administrator rights *** // // // // Failure to provide confirmation results in the program not being launched. // // //**************************************************************************** Use Windows Use Seq_Chnl.pkg Use cRegistry.pkg // The class code now uses the cRegistry class instead of registry commands. (Introduced with VDF 8) Use RegCheck2.inc // All language text strings for the package (Multiple languages) // Also, all define statements were moved into the include file! //******************* EXTERNAL FUNCTIONS ************************************* #IFNDEF get_GetComputerName External_Function GetComputerName "GetComputerNameA" Kernel32.Dll ; Pointer lpComputerName ; DWord nNameSize ; Returns Integer #ENDIF #IFNDEF get_Win32_WNetGetUser External_Function Win32_WNetGetUser 'WNetGetUserA' MPR.dll ; Pointer lpName; Pointer lpUserName; String lpnLength; Returns DWord #ENDIF #IFNDEF get_ExitWindowsEx External_Function ExitWindowsEx "ExitWindowsEx" User32.dll ; UInteger uFlags ; DWord dwReason ; Returns Boolean #ENDIF #IF (FMAC_VERSION < 14) External_Function IsUserAnAdmin "IsUserAnAdmin" shell32.dll Returns Boolean Function IsAdministrator Global Returns Boolean Integer iMajor iMinor Boolean bUnderstands // IsUserAnAdmin can only be used on XP or above. If running // 2000 or below, just return true. Move (SysConf(SYSCONF_OS_MAJOR_REV)) to iMajor Move (SysConf(SYSCONF_OS_MINOR_REV)) to iMinor // Check for XP or above (5.1 or above) Move (iMajor>5 or (iMajor=5 and iMinor>0)) to bUnderstands Function_Return (If(bUnderstands,IsUserAnAdmin(),True)) End_Function #ENDIF // This _must_ be after the IsAdministrator function. Use cOSVersionInfo.pkg // New OS Version checking class! //************************** Main Class **************************************** // Note: An instantition of the class is made at the bottom of this package. { DesignerClass=None } //{ HelpTopic=??? } Class cRegCheck2 is a cObject Procedure Construct_Object Forward Send Construct_Object // *** This property is set by the DoCheckRegistrySettings and // is set to True if one or more registry change are needed. // See also tRegChangesToDo below. // Registry changes are made by the CS_RegChangeProgram program, // and is triggered by the DoCheckRegistrySettings procudure. // // The CS_RegChangeProgram program is not run automatically // by the test program: RegCheck2Text.src. Instead there's a button to click. // The pbReportOnlyState property can be used for this purpose. Property Boolean pbRegistryChangesNeeded False // This struct property will hold all necessary registry changes to do, // after the DoCheckRegistrySettings has been send. Property tRegChangesToDo ptRegChangesToDo // NGS 24/08/2012 // Set to true if you only want to report current // registry settings - but not try to change them. Property Boolean pbReportOnlyState False // Property set by the DoWriteRegistrySettings message - that is called // from the CS_RegChangeProgram program - if a registry value couldn't be set Property Boolean pbRegistryChangeFailed False // Property set by the DoWriteRegistrySettings message - that is called // from the CS_RegChangeProgram program - if writing was denied to the registry Property Boolean pbRegistryWriteAccessDenied False // *** This property is set automatically when the object is created. // It is set to True if a service pack needs to be installed. Property Boolean pbServicePackUpdateNeeded False // Make Server registry changes on all machines? // A machine might be used both as a client and a server, so it safest to // always make registry changes as if the machine is a server. It won't affect // performance if the machine is not used as a server machine. Property Integer pbDoServerRegistryChanges True // Ask user before making changes? Property Integer pbSilentMode False // To force a change of the registry even if there are more than the 'DATAFLEX' database driver loaded. // It is possible that a (example accounting) Pervasive database get consulted for info while // the rest of the data is the embedded DataFlex database. // Note: If you set it to FALSE, and you have more than the 'DATAFLEX' loaded, // NO changes to the registry will be made! (In case this whole package is meaningless) Property Integer pbDriverBruteForce True // PHvW 22/04/2002 17:50 // The Windows version is a server version Property Integer pbNTServer False // PHvW 22/04/2002 17:50 // Running on a Windows HOME version. That is Windows ME and Windows XP Home // Both are _not_ recommended platforms for business (networking is limited). // So not for running VDF programs on. Property Integer pbWindowsHome False // pbX64 is set to true if your host OS is x64 Property Boolean pbX64 False // pbRemoteDesktop is set to true if your application currently runs under a remote desktop session Property Boolean pbRemoteDesktop False // PHvW 22/04/2002 17:51 // Should we check for the service pack and if it is to low // deny the running of the application? Property Boolean pbTestServicePack True // Is the program run with administrator rights? Property Boolean pbAdminRights (IsAdministrator()) // If this property is True, the DF_HIGH_DATA_INTEGRITY will // be set to True. If set to false; DF_HIGH_DATA_INTEGRITY will not be changed. // From the VDF help: // If DF_HIGH_DATA_INTEGRITY is True, all disk writes (save, delete, etc.) are immediately // followed by a low level file close. This forces the O/S to Write the table's buffers // to disk and to update the table's folder entry, while this offers some protection from // power failures and system crashes, it also degrades performance. // This setting only applies to Embedded Database tables. Property Boolean pbChangeHighDataIntegrity False // Log file object. By default it is a text file. // Can be changed to be a database table. Property Integer phoLogObject 0 // These two properties are set automatically by the // end_construct_object procedure. Property String psComputerName "" Property String psUserLoginName "" // The next serious of properties gives info about the Operating System. // They are set by the procedure DoSetOSProperties, which is send from // the automatically invoked DoCheckRegistrySettings. // // Identifies the major version number of the operating system. // Example: For Windows XP the major version number is 5. Property Integer piMajorVersion 0 // Identifies the minor version number of the operating system. For example, // Example: For Windows XP the minor version number is 1. Property Integer piMinorVersion 0 // Identifies the build number of the operating system. Property Integer piBuildNumber 0 // Identifies the operating system platform. This member can be one of the // following values: // VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95 or Windows 98. // VER_PLATFORM_WIN32_NT Win32 on Windows NT. Property Integer piPlatFormId 0 // Contains a string, such as "Service Pack 3", // that indicates the latest Service Pack installed on the system. If no // Service Pack has been installed, the string is empty. Property String psCSDVersion "" // PHvW 20/04/2002 14:37 // Identifies the major version number of the latest Service Pack installed // on the system. For example, for Service Pack 3, the major version // number is 3. If no Service Pack has been installed, the value is zero. Property Integer piServicePackMajor 0 // PHvW 20/04/2002 14:37 // Identifies the minor version number of the latest Service Pack installed // on the system. For example, for Service Pack 3, the minor version // number is 0. Property Integer piServicePackMinor 0 // PHvW 20/04/2002 14:37 // A set of bit flags that identify the product suites available on the system. Property Integer piSuiteMask 0 // The psSuite string only contains features which can be enabled or not, so it is NOT // a direct mapping to piSuiteMask. An example of this is if you are in a remote desktop // session or not. Property String psSuite "" // The Edition string contains specific details about the platform that cannot be // enabled as a role. So "Home" Edition or "Datacenter" edition are good examples. // R2 is a peOSVersion Property String psEdition "" // PHvW 20/04/2002 14:37 // Indicates additional information about the system. Property Integer piProductType 0 // PHvW 22/04/2002 17:48 // Contains which version of windows is running as a TEXT string. // If it can not sort it out it will be CS_OSVERSION_UNKNOWN (="Windows Version UNKNOWN") Property String psOSVersion CS_OSVERSION_UNKNOWN Property Integer peOSVersion CE_OSVERSION_UNKNOWN // 2012-08-29 NGS This is the windows version in text only e.g "Windows 7" Property String psOSShortVersion CS_OSVERSION_UNKNOWN // Default Settings for on which Windows version a VDF program is allowed to run. // Home versions and not known windows version runs with a warning. // Do NOT make changes here, make them in the RegCheck2 object instead. Property Integer piRunOnUnknownVersion cx_Run_With_Warning // However, if older than XP we never allow program to run. Property Integer piRunOnHomeVersion cx_Run_With_Warning Property Integer piRunOnWindowsXP cx_Run_Normal Property Integer piRunOnWindowsVista cx_Run_Normal Property Integer piRunOnWindows7 cx_Run_Normal Property Integer piRunOnWindows8 cx_Run_Normal // Minimum service packs for various windows versions. // Do NOT make changes here. Change properties in the RegCheck2 object instead. // Client OS's: Property Integer piMinXP_ServicePack 0 Property Integer piMinVista_ServicePack 0 Property Integer piMinWindows7_ServicePack 0 Property Integer piMinWindows8_ServicePack 0 // Server OS's: // Windows Server 2003 (released with Windows XP) Property Integer piMinServer2K3_ServicePack 0 // Windows Server 2008 (released with Windows Vista) Property Integer piMinServer2K8_ServicePack 0 // Windows Server 2008 R2 (released with Windows 7) Property Integer piMinServer2K8R2_ServicePack 0 // Windows Server 2012 (released with Windows 8) Property Integer piMinServer2K12_ServicePack 0 End_Procedure // ***** MAIN READ Procedure! ***** // Checks which Windows version is running and calls the appropiate procedures & functions // to change the registry. // This must be send from your program after the Use of this package (see top of package) // Syntax: // Use RegCheck2.pkg // Send DoCheckRegistrySettings of ghoRegCheck2 // N.B. // Note: There is also a DoWriteRegistrySettings message but you should _not_ send or use // that message. It is used by the external program CS_RegChangeProgram that is // used to make registry changes. (Due to the need for administrator rights) Procedure DoCheckRegistrySettings Boolean bNTServer bX64 bHome bDriverBruteForce bState bTestServicePack bDoServerRegistryChanges Boolean bRegistryChangesNeeded bServicePackUpdateNeeded bSilentMode bReportOnlyState String sUser sText sDriverName sOSShortVersion sVersion Integer iRetval iCount iMajorVersion iMinorVersion iCurrentMinServicePack iServicePackMajor Integer iRunOnHomeVersion iRunOnUnknownVersion eOSVersion // We only change the high data integrity setting if this property is True. Get pbChangeHighDataIntegrity to bState If (bState = True) Begin Set_Attribute DF_HIGH_DATA_INTEGRITY to True End // ********* DBMS DRIVERS CHECKING ************* Get pbDriverBruteForce to bDriverBruteForce Get_Attribute DF_NUMBER_DRIVERS to iRetval // Check if the only driver loaded is 'DATAFLEX'. For iCount from 1 to iRetval Get_Attribute DF_DRIVER_NAME of iCount to sDriverName Loop If (iRetval > 1 and bDriverBruteForce = False) Begin Move "Another driver in addition to the DATAFLEX DBMS driver is loaded and" to sText Move (sText * "NO checking of this machines registry settings was performed.") to sText Move (sText * "Note: The driver(s) might have been loaded automatically by") to sText Move (sText * "entries in your DFini.cfg in the VDF Bin folder or Data or Programs folder") to sText Move (sText * "If you want to turn on checking of the registry you need to set the RegCheck2 object property pbDriverBruteForce = True") to sText Send DoWriteLog sText // Setting pbReportOnlyState = True will enable us to execute the rest of // the DoCheckRegistrySettings message _without_ making any changes to the registry. Set pbReportOnlyState to True End // ********** SET PUBLIC OS PROPERTIES ****************** Set pbRegistryChangesNeeded to False Set pbServicePackUpdateNeeded to False Send DoSetOSProperties // Sets a series of public properties about the OS. // If pbReportOnlyState = True we write what's wrong to the log file, but // don't stop the program from running or make any changes to the registry. Get pbReportOnlyState to bReportOnlyState Get piMajorVersion to iMajorVersion Get piMinorVersion to iMinorVersion // Version = 0 is considered to be fatal and we end the program. // This shouldn't really be possible... If (iMajorVersion = 0) Begin Move (csOSversionNotRetrieved * csProgramWillBeTerminated) to sText Send DoWriteLog sText If (bReportOnlyState = False) Begin Send Stop_Box sText Send Exit_Application End End Get peOSVersion to eOSVersion // We don't support earlier versions than Windows XP If (eOSVersion < CE_OSVERSION_WXP) Begin Move (csDoesNotRunLowerThanXP + csProgramWillBeTerminated) to sText Send DoWriteLog sText If (bReportOnlyState = False) Begin Send Stop_Box sText Send Exit_Application End End If (eOSVersion = CE_OSVERSION_UNKNOWN) Begin Get piRunOnUnknownVersion to iRunOnUnknownVersion If (iRunOnUnknownVersion = cx_Run_With_Warning) Begin Send DoWriteLog csUnknownWindowsVersion If (bReportOnlyState = False) Begin Get Message_Box csUnknownWindowsVersion "Warning" MB_OK MB_ICONWARNING to iRetval End End Else If (iRunOnHomeVersion = cx_Run_Do_Not_Run) Begin Send DoWriteLog (csUnknownWindowsVersionQuestion + csProgramWillBeTerminated) If (bReportOnlyState = False) Begin Send Stop_Box (csUnknownWindowsVersionQuestion + csProgramWillBeTerminated) Send Exit_Application End End End // Get pbNTServer to bNTServer // Get pbX64 to bX64 If (bReportOnlyState = False) Begin Get pbWindowsHome to bHome If (bHome = True) Begin Get piRunOnHomeVersion to iRunOnHomeVersion If (iRunOnHomeVersion = cx_Run_With_Warning) Begin Send DoWriteLog csOSVersionIsAHomeVersionWarning If (bReportOnlyState = False) Begin Get Message_Box csOSVersionIsAHomeVersionWarning "Warning" MB_OK MB_ICONWARNING to iRetval End End Else If (iRunOnHomeVersion = cx_Run_Do_Not_Run and bReportOnlyState = False) Begin Send DoWriteLog (csOSVersionIsAHomeVersion + csProgramWillBeTerminated) If (bReportOnlyState = False) Begin Send Stop_Box (csOSVersionIsAHomeVersion + csProgramWillBeTerminated) Send Exit_Application End End End End // Note: Check of Client registry settings is performed unconditionally // of the Windows version. The rational behind this is that if this // is a Windows Server OS it may still be used as a client and // thus we need to check/adjust client registry settings as well as server settings. Send DoCheckClientRegistrySettings // Should be check for Server registry settings? Get pbDoServerRegistryChanges to bDoServerRegistryChanges If (bDoServerRegistryChanges = True) Begin Send DoCheckServerRegistrySettings End Get pbTestServicePack to bTestServicePack If (bTestServicePack = True) Begin Get piServicePackMajor to iServicePackMajor Get CurrentMinServicePack to iCurrentMinServicePack If (iServicePackMajor < iCurrentMinServicePack) Begin // *** STOP RUNNING PROGRAM! *** // The rational behind stopping the program here is that it is // best to upgrade to the correct service pack before fixing the registry. Send DoWriteLog ("OS Service pack needs to be updated. Current service pack is:" * String(iServicePackMajor) * "but it needs to be:" * String(iCurrentMinServicePack)) Set pbServicePackUpdateNeeded to True If (bReportOnlyState = False) Begin Send Stop_Box (csServicePackNotInstalledStart + String(iCurrentMinServicePack) + csServicePackNotInstalledMiddle + sOSShortVersion + csServicePackNotInstalledEnd + "\n" + csContactYourSystemAdministrator + csProgramWillBeTerminated) // Start Windows Update! Runprogram CS_WindowsUpdate Send Exit_Application End End End If (bReportOnlyState = False) Begin Get pbRegistryChangesNeeded to bRegistryChangesNeeded If (bRegistryChangesNeeded = True) Begin // Silent mode means that we dont' ask the user before changes are made. Get pbSilentMode to bSilentMode If (bSilentMode = False) Begin Get YesNo_Box (csRegistryChangesNeeded + csMakeChanges) to iRetval If (iRetval = MBR_No) Begin Send Exit_Application End End Runprogram CS_RegChangeProgram // After changes are made to the registry the machine needs to // be rebooted, so we might as well stop running this program now. Send Exit_Application End End End_Procedure // ***** MAIN WRITE Procedure! ***** // Here registry changes are made (written to the registry) // Note: You should _not_ need to send this message. It is used by the external // program CS_RegChangeProgram - that is automatically run to make registry changes. // This logic is due to the need for administrator rights. // Client Settings: // The following registry changes are made on Windows clients if applicable: // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MRXSmb\Parameters\OplocksDisabled = "1" (Type = String) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\UseOpportunisticLocking = "0" (Type = String) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\DirectoryCacheLifetime = 0 (Type = Dword) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\FileNotFoundCacheLifetime = 0 (Type = Dword) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\FileInfoCacheLifetime = 0 (Type = Dword) Procedure DoWriteRegistrySettings Integer iRetval String sValue sText sLogText Boolean bReportOnlyState bChange bNTServer bDoServerRegistryChanges tRegChangesToDo RegChangesToDo // Temporary save the pbReportOnlyState Get pbReportOnlyState to bReportOnlyState Set pbReportOnlyState to True // This will set all necessary registry changes Send DoCheckRegistrySettings Set pbReportOnlyState to bReportOnlyState Get ptRegChangesToDo to RegChangesToDo // *** Client settings: *** // The OplocksDisabled registry value should be "1" (one) // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MRXSmb\Parameters\OplocksDisabled = "1" (Type = String) Move RegChangesToDo.bOplocksDisabled to bChange If (bChange = True) Begin Move "1" to sValue Get WriteRegistryValue CS_MRXSmb CS_Parameters CS_OplocksDisabled sValue True to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_MRXSmb - CS_Parameters * CS_OplocksDisabled * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_MRXSmb - CS_Parameters * CS_OplocksDisabled * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End // The UseOpportunisticLocking registry setting is defined as a string and should be "0" (zero) (Type = String) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\UseOpportunisticLocking = "0" (Type = String) Move RegChangesToDo.bUseOpportunisticLocking to bChange If (bChange = True) Begin Move "0" to sValue Get WriteRegistryValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_UseOpportunisticLocking sValue True to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_UseOpportunisticLocking * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_UseOpportunisticLocking * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End // The DirectoryCacheLifetime registry setting is defined as type Dword and should be set to 0 (zero). // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\DirectoryCacheLifetime = 0 (Type = Dword) Move RegChangesToDo.bDirectoryCacheLifetime to bChange If (bChange = True) Begin Move "0" to sValue Get WriteRegistryValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_DirectoryCacheLifetime sValue False to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_DirectoryCacheLifetime * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_DirectoryCacheLifetime * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End // The FileNotFoundCacheLifetime registry setting is defined as type Dword and should be set to 0 (zero). // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\FileNotFoundCacheLifetime =0 (Type = Dword) Move RegChangesToDo.bFileNotFoundCacheLifetime to bChange If (bChange = True) Begin Move "0" to sValue Get WriteRegistryValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_FileNotFoundCacheLifetime sValue False to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_FileNotFoundCacheLifetime * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_FileNotFoundCacheLifetime * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End // The FileInfoCacheLifetime registry setting is defined as type Dword and should be set to 0 (zero). // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\FileInfoCacheLifetime =0 (Type = Dword) Move RegChangesToDo.bFileInfoCacheLifetime to bChange If (bChange = True) Begin Move "0" to sValue Get WriteRegistryValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_FileInfoCacheLifetime sValue False to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_FileInfoCacheLifetime * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_FileInfoCacheLifetime * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End // *** Server Registry changes: *** Get pbDoServerRegistryChanges to bDoServerRegistryChanges If (bDoServerRegistryChanges = False) Procedure_Return // We now do these Server Registry Changes unconditionally if the machine is // reported to be a "client" or "server" version. // If this machine is used as a server for storing embedded DataFlex data files, // but the OS version reported is "client" they are still needed. // Get pbNTServer to bNTServer // If (bNTServer = False) Procedure_Return // To disable oplocks on a Windows server (a Windows PC that hosts an embedded database table // accessed from another PC), change or add the following Registry values: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters EnableOplocks = "0" (Type = String) Move RegChangesToDo.bOplocksDisabled to bChange If (bChange = True) Begin Move "0" to sValue Get WriteRegistryValue CS_LanmanServerRegistryPath CS_Parameters CS_EnableOplocks sValue True to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_LanmanServerRegistryPath - CS_Parameters * CS_EnableOplocks * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_LanmanServerRegistryPath - CS_Parameters * CS_EnableOplocks * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End // To disable SMB2 on a Windows Server 2008 and later, change or add the following registry value: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters SMB2 = "0" (Type = String) Move RegChangesToDo.bSMB2 to bChange If (bChange = True) Begin Move "0" to sValue Get WriteRegistryValue CS_LanmanServerRegistryPath CS_Parameters CS_SMB2 sValue True to iRetval // Failed: If (iRetval < 0) Begin Get ConvertRegistryErrCodeToText iRetval to sText Move (CS_RegistryBranch * CS_LanmanServerRegistryPath - CS_Parameters * CS_SMB2 * CS_Error * sText) to sLogText Send DoWriteLog sLogText Set pbRegistryChangeFailed to True If (iRetval = Ci_RegistryAccessDenied) Begin Set pbRegistryWriteAccessDenied to True End End // Success: Else Begin Move (CS_ChangesMade * CS_RegistryBranch * CS_LanmanServerRegistryPath - CS_Parameters * CS_SMB2 * CS_WasSetTo * String(sValue)) to sLogText Send DoWriteLog sLogText End End End_Procedure Procedure DoCheckClientRegistrySettings Integer iRetval Integer iMajorVersion iMinorVersion iCreated Integer iErrCode String sRetval sText sRegPath sKey sValue sLogText sCSDVersion sRegPath2 Boolean bRunOnNew bOk bRegistryChangeNeeded Boolean bXP bVista bWin7 bWin8 bX64 DWord dwRetval tRegChangesToDo RegChangesToDo Get piMajorVersion to iMajorVersion Get piMinorVersion to iMinorVersion Move (iMajorVersion = 5 and iMinorVersion = 1) to bXP Move (iMajorVersion = 6 and iMinorVersion = 0) to bVista Move (iMajorVersion = 6 and iMinorVersion = 1) to bWin7 Move (iMajorVersion = 6 and iMinorVersion = 2) to bWin8 Get pbX64 to bX64 // Oportunistic locking on Windows Client PCs: // The OplocksDisabled registry value should be "1" (one) // HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MRXSmb\Parameters\OplocksDisabled = "1" (Type = String) Move False to bOk Get IsRegistryStringValue CS_MRXSmb CS_Parameters CS_OplocksDisabled (&iErrCode) to sRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_MRXSmb - CS_Parameters * CS_OplocksDisabled * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (sRetval = "1") to bOk If (bOk = False) Begin Send DoWriteLog (CS_OplocksDisabled * CS_InThe * (CS_MRXSmb - CS_Parameters) * CS_RegBranchNeedsChange * "1" * CS_TypeString) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bOplocksDisabled End // The UseOpportunisticLocking registry setting is defined as a string and should be "0" (zero) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\UseOpportunisticLocking = "0" (Type = String) Move False to bOk Get IsRegistryStringValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_UseOpportunisticLocking (&iErrCode) to sRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_UseOpportunisticLocking * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (sRetval = "0") to bOk If (bOk = False) Begin Send DoWriteLog (CS_UseOpportunisticLocking * CS_InThe * (CS_LanmanWorkStationRegistryPath - CS_Parameters) * CS_RegBranchNeedsChange * "0" * CS_TypeString) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bUseOpportunisticLocking End // *** If XP We're out of here! *** If (iMajorVersion < 6) Begin Set ptRegChangesToDo to RegChangesToDo Procedure_Return End // The DirectoryCacheLifetime registry setting is defined as type Dword and should be set to 0 (zero). // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\DirectoryCacheLifetime = 0 (Type = Dword) Move False to bOk Get IsRegistryDwordValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_DirectoryCacheLifetime (&iErrCode) to dwRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_DirectoryCacheLifetime * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (dwRetval = 0) to bOk If (bOk = False) Begin Send DoWriteLog (CS_DirectoryCacheLifetime * CS_InThe * (CS_LanmanWorkStationRegistryPath - CS_Parameters) * CS_RegBranchNeedsChange * "0" * CS_TypeDword) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bDirectoryCacheLifetime End // The FileNotFoundCacheLifetime registry setting is defined as type Dword and should be set to 0 (zero). // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\FileNotFoundCacheLifetime =0 (Type = Dword) Move False to bOk Get IsRegistryDwordValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_FileNotFoundCacheLifetime (&iErrCode) to dwRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_FileNotFoundCacheLifetime * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (dwRetval = 0) to bOk If (bOk = False) Begin Send DoWriteLog (CS_FileNotFoundCacheLifetime * CS_InThe * (CS_LanmanWorkStationRegistryPath - CS_Parameters) * CS_RegBranchNeedsChange * "0" * CS_TypeDword) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bFileNotFoundCacheLifetime End // The FileInfoCacheLifetime registry setting is defined as type Dword and should be set to 0 (zero). // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\FileInfoCacheLifetime =0 (Type = Dword) Move False to bOk Get IsRegistryDwordValue CS_LanmanWorkStationRegistryPath CS_Parameters CS_FileInfoCacheLifetime (&iErrCode) to dwRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_LanmanWorkStationRegistryPath - CS_Parameters * CS_FileInfoCacheLifetime * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (dwRetval = 0) to bOk If (bOk = False) Begin Send DoWriteLog (CS_FileInfoCacheLifetime * CS_InThe * (CS_LanmanWorkStationRegistryPath - CS_Parameters) * CS_RegBranchNeedsChange * "0" * CS_TypeDword) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bFileInfoCacheLifetime End Set ptRegChangesToDo to RegChangesToDo End_Procedure // ************************* Server Settings ************************* // VDF requires Opportunistic file locking be disabled at an NT based // server when using the embedded database. // // The following registry changes are applied by this procedure: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters EnableOplocks = "0" (Type = String) // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters SMB2 = "0" (Type = String) Procedure DoCheckServerRegistrySettings Integer iRetval iServicePack iErrCode eOSVersion String sText sValue sLogText sRetval sVersion Boolean bOk tRegChangesToDo RegChangesToDo // To disable oplocks on a Windows server (a Windows PC that hosts an embedded database table // accessed from another PC), change or add the following Registry values: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters EnableOplocks = "0" (Type = String) Move False to bOk Get IsRegistryStringValue CS_LanmanServerRegistryPath CS_Parameters CS_EnableOplocks (&iErrCode) to sRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_LanmanServerRegistryPath - CS_Parameters * CS_EnableOplocks * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (sRetval = "0") to bOk If (bOk = False) Begin Send DoWriteLog (CS_UseOpportunisticLocking * CS_InThe * (CS_LanmanServerRegistryPath - CS_Parameters) * CS_RegBranchNeedsChange * "0" * CS_TypeString) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bOplocksDisabled Set ptRegChangesToDo to RegChangesToDo End Get peOSVersion to eOSVersion If (eOSVersion < CE_OSVERSION_2K8) Procedure_Return // If earlier than Server 2008 we're out of here. // To disable SMB2 on a Windows Server 2008 and later, change or add the following registry value: // HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters SMB2 = "0" (Type = String) Move False to bOk Get IsRegistryStringValue CS_LanmanServerRegistryPath CS_Parameters CS_SMB2 (&iErrCode) to sRetval // If an error occured we want to log it. If (iErrCode < 0) Begin Get ConvertRegistryErrCodeToText iErrCode to sText Move (CS_RegistryBranch * CS_LanmanServerRegistryPath - CS_Parameters * CS_SMB2 * CS_Error * sText) to sLogText Send DoWriteLog sLogText End Move (sRetval = "0") to bOk If (bOk = False) Begin Send DoWriteLog (CS_UseOpportunisticLocking * CS_InThe * (CS_LanmanServerRegistryPath - CS_Parameters) * CS_RegBranchNeedsChange * "0" * CS_TypeString) Set pbRegistryChangesNeeded to True Move True to RegChangesToDo.bSMB2 Set ptRegChangesToDo to RegChangesToDo End End_Procedure // *** Generalized messages to Get & Set values of the ini file. // *** The ini file is CS_IniFileName and is kept in the Data folder. // Set a value in RegCheck2's ini file (write) // Pass a section name, the value name parameter and the value itself to be written. // Returns the value Procedure Set IniFileValue String sSection String sValueName String sValue String sIniFile sPath Handle hIniFile // The RefClass syntax was introduced in VDF 15.1. To be able to use // this code with earlier VDF versions we use the older "U_" syntax. //Get Create (RefClass(cIniFile)) to hIniFile Get Create U_cIniFile to hIniFile Get psDataPath of (phoWorkspace(ghoApplication)) to sPath If (Right(sPath, 1) <> "\") Move (sPath + "\") to sPath Move (sPath + CS_IniFileName) to sIniFile Set psFileName of hIniFile to sIniFile Send WriteString of hIniFile sSection sValueName sValue Send Destroy of hIniFile End_Procedure // Get a value from RegCheck2's ini-file (read) // Pass a section name and the name of the value parameter & a default value. // Returns the value Function IniFileValue String sSection String sValueName String sDefaultValue Returns String String sValue sIniFile sPath Handle hIniFile // The RefClass syntax was introduced in VDF 15.1. To be able to use // this code with earlier VDF versions we use the older "U_" syntax. //Get Create (RefClass(cIniFile)) to hIniFile Get Create U_cIniFile to hIniFile Get psDataPath of (phoWorkspace(ghoApplication)) to sPath If (Right(sPath, 1) <> "\") Move (sPath + "\") to sPath Move (sPath + CS_IniFileName) to sIniFile Set psFileName of hIniFile to sIniFile Get ReadString of hIniFile sSection sValueName sDefaultValue to sValue Send Destroy of hIniFile Function_Return sValue End_Function // *** The following two functions are refined versions of the more generalized IsRegistryValue function // Their sole purpose is to return a registry value of the right type (STRING or DWORD) // For a deeper explanation of this function and its params see the IsRegistryValue function. // This function is only here to return a correct type value (STRING) // Important: The iErrCode must be passed as a ByRef parameter! It will contain the // error code - if any - or zero if successful. // If iErrCode <> 0 the return value has no meaning. // Example: Get IsRegistryStringValue CS_MRXSmb CS_Parameters CS_OplocksDisabled (&iErrCode) to sRetval // If iErrCode < 0 an error occured. Function IsRegistryStringValue String sKey String sSubKey String sValueName Integer ByRef iErrCode Returns String String sRetval Integer iRetval Move "" to sRetval Get IsRegistryValue sKey sSubKey sValueName True to iRetval If (iRetval < 0) Move iRetval to iErrCode Move iRetval to sRetval Function_Return sRetval End_Function // For a deeper explanation of this function and its params see the IsRegistryValue function. // This function is only here to return a correct type value (DWORD) // Important: The iErrCode must be passed as a ByRef parameter! It will contain the // error code - if any - or zero if successful. // If iErrCode <> 0 the return value has no meaning. // Example: Get IsRegistryDwordValue CS_MRXSmb CS_Parameters CS_OplocksDisabled (&iErrCode) to dwRetval // If iErrCode < 0 an error occured. Function IsRegistryDwordValue String sKey String sSubKey String sValueName Integer ByRef iErrCode Returns DWord Integer iRetval DWord dwRetval Move 0 to dwRetval Get IsRegistryValue sKey sSubKey sValueName False to iRetval If (iRetval < 0) Move iRetval to iErrCode Move iRetval to dwRetval Function_Return dwRetval End_Function // To fetch a value from the HKEY_LOCAL_MACHINE registry branch. // Do _not_ use this function - instead use one of the more specialized // functions IsRegistryStringValue or IsRegistryDwordValue above. // // Parameters: // sKey - e.g. "System\CurrentControlSet\Services\MRXSmb" // sSubKey - e.g. "\Parameters" // sValueName - e.g. "OplocksDisabled" // bStringValue - True = String value. False = Dword value. // Note: The passed sSubkey _must_ start with a "\" (backslash). // Returns: // Integer equal to 0 or higher if operation was successful! (the actual registry value is returned) // CI_RegObjectNotCreated if the registry object couldn't be created. // CI_KeyDoesNotExist if the sKey doesn't exist // CI_KeyWasNotOpened if the sKey couldn't be opened // CI_SubKeyDoesNotExist if the sSubKey doesn't exist // CI_SubKeyWasNotOpened if the sSubKey couldn't be opened // CI_ValueNameEmpty if the sValueName was empty (program error) // * I don't think this should be returned - it just creates an extra logfile row: CI_ValueNameDoesntExist if the sValueName doesnt' exist Function IsRegistryValue String sKey String sSubKey String sValueName Boolean bStringValue Returns Integer Integer iRetval String sRetval Handle hoRegistry Boolean bExists bOpened Move CI_Success to iRetval // Helper function to check if a key exists and can be opened. Get IsRegistryKey sKey to iRetval If (iRetval < 0) Begin Function_Return iRetval End // Helper function to check if a key exists and can be opened. // Check sSubKey Get IsRegistryKey (sKey + sSubKey) to iRetval If (iRetval < 0) Begin // Adjust the return value to represent a faulty sSubKey If (iRetval = CI_KeyDoesNotExist) Move CI_SubKeyDoesNotExist to iRetval If (iRetval = CI_KeyWasNotOpened) Move CI_SubKeyWasNotOpened to iRetval Function_Return iRetval End // Check for programming error If (sValueName = "") Begin Function_Return CI_ValueNameEmpty End // The RefClass syntax was introduced in VDF 15.1. To be able to use // this code with earlier VDF versions we use the older "U_" syntax. //Get Create (RefClass(cRegistry)) to hoRegistry Get Create U_cRegistry to hoRegistry If (hoRegistry = 0) Begin Send DoWriteLog CS_RegObjectNotCreated Function_Return CI_RegObjectNotCreated End // Set the default of phRootKey... Set phRootKey of hoRegistry to HKEY_LOCAL_MACHINE // Set access rights to read Set pfAccessRights of hoRegistry to Key_Read // Open the Key... Get OpenKey of hoRegistry (sKey + sSubKey) to bOpened // This shouldn't be possible, we have already checked this above... If (bOpened = False) Begin Move CI_SubKeyWasNotOpened to iRetval End // The key was opened successfully Else Begin // Does the value name exists? Get ValueExists of hoRegistry sValueName to bExists // Yes, value name exist; Read it If (bExists = True) Begin If (bStringValue = True) Begin Get ReadString of hoRegistry sValueName to sRetval Move (sRetval) to iRetval End Else Begin Get ReadDword of hoRegistry sValueName to iRetval End End // No, value name doesn't exist Else Begin Move CI_ValueNameDoesntExist to iRetval End // Don't forget to close the registry key Send CloseKey of hoRegistry End Send Destroy of hoRegistry Function_Return iRetval End_Function // To create a new registry dword value name and/or set it's value in // Windows HKEY_LOCAL_MACHINE registry branch. // // Parameters: // sKey - e.g. "System\CurrentControlSet\Services\MRXSmb" // sSubKey - e.g. "\Parameters" // sValueName - e.g. "OplocksDisabled" // dWordValue - e.g. 1 // bStringValue - True = String value. False = Dword value. // Note: The passed sSubkey _must_ start with a "\" (backslash). // Returns: // CI_Success IF OPERATION WAS SUCCESSFUL (=0) // CI_RegObjectNotCreated if the registry object couldn't be created. // CI_KeyDoesNotExist if the sKey doesn't exist // CI_KeyWasNotOpened if the sKey couldn't be opened // CI_SubKeyDoesNotExist if the sSubKey doesn't exist // CI_SubKeyWasNotOpened if the sSubKey couldn't be opened // CI_ValueNameEmpty if the sValueName was empty (program error) // CI_ValueNameNotCreated if the sValueName could not be created/set // CI_RegValueIncorrect if the dWordValue was not be set correctly Function WriteRegistryValue String sKey String sSubKey String sValueName String sValue Boolean bStringValue Returns Integer Boolean bOpened bExists bValueExists Integer iRetval iKeyDoesNotExist Handle hoRegistry DWord dWordValue dWordValueCheck String sValueCheck Move CI_Success to iRetval // Check for programming error If (sValueName = "") Begin Function_Return CI_ValueNameEmpty End If (bStringValue = False) Begin Move sValue to dWordValue End // Helper function to check if a key exists and can be opened. Get IsRegistryKey sKey to iRetval If (iRetval < 0) Begin Function_Return iRetval End // Helper function to check if a key exists and can be opened. // Check sSubKey Get IsRegistryKey (sKey + sSubKey) to iRetval If (iRetval < 0) Begin // Adjust the return value to represent a faulty sSubKey If (iRetval = CI_KeyDoesNotExist) Begin // We need to CREATE the key! Move iRetval to iKeyDoesNotExist //Function_Return CI_SubKeyDoesNotExist End If (iRetval = CI_KeyWasNotOpened) Function_Return CI_SubKeyWasNotOpened End // The RefClass syntax was introduced in VDF 15.1. To be able to use // this code with earlier VDF versions we use the older "U_" syntax. //Get Create (RefClass(cRegistry)) to hoRegistry Get Create U_cRegistry to hoRegistry If (hoRegistry = 0) Begin Send DoWriteLog CS_RegObjectNotCreated Function_Return CI_RegObjectNotCreated End // Set the default of phRootKey... Set phRootKey of hoRegistry to HKEY_LOCAL_MACHINE // Set access rights to read Set pfAccessRights of hoRegistry to KEY_ALL_ACCESS // If the key didn't exist when tested above, we need to create it: If (iKeyDoesNotExist = CI_KeyDoesNotExist) Begin Get CreateKey of hoRegistry (sKey + sSubKey) to iRetval // If the Key is not created, the return value will be the error code returned by Windows. // ToDo: How should this be handled?? If (iRetval <> 0) Begin Function_Return iRetval End End // Open the sSubKey... Get OpenKey of hoRegistry (sKey + sSubKey) to bOpened // This shouldn't be possible, we have already checked this above... If (bOpened = False) Begin Move CI_SubKeyWasNotOpened to iRetval End // The key was successfully opened Else Begin // Set access rights to create/write new value names & their values Send Ignore_Error of Error_Object_Id 4411 // "Windows registry error. Access is denied" // Write the value name and value If (bStringValue = True) Begin Send WriteString of hoRegistry sValueName sValue End // Dword value Else Begin Send WriteDword of hoRegistry sValueName dWordValue End // Check if all went well Get ValueExists of hoRegistry sValueName to bValueExists // Success writing sValueName If (bValueExists = True) Begin If (bStringValue = True) Begin Get ReadString of hoRegistry sValueName to sValueCheck // Check that the string value was set correctly If (sValue <> sValueCheck) Move CI_RegValueIncorrect to iRetval End // Dword value Else Begin Get ReadDword of hoRegistry sValueName to dWordValueCheck // Check that the dword value was set correctly If (dWordValue <> dWordValueCheck) Move CI_RegValueIncorrect to iRetval End End // Failure writing sValueName Else Begin Move CI_ValueNameNotCreated to iRetval End // Don't forget to close the registry key Send CloseKey of hoRegistry Send Trap_Error of Error_Object_Id 4411 // "Windows registry error. Access is denied" If (LastErr = 4411) Move Ci_RegistryAccessDenied to iRetval End Send Destroy of hoRegistry Function_Return iRetval End_Function // Helper function. // Used by the two main registry functions; IsRegistryValue & WriteRegistryValue // Parameters: // sKey is a registry key in HKEY_LOCAL_MACHINE // Returns: // CI_Success if operation was successful! // CI_RegObjectNotCreated if the registry object couldn't be created. // CI_KeyDoesNotExist if the sKey doesn't exist // CI_KeyWasNotOpened if the sKey couldn't be opened Function IsRegistryKey String sKey Returns Integer Integer iRetval Handle hoRegistry Boolean bExists bOpened Move CI_Success to iRetval // The RefClass syntax was introduced in VDF 15.1. To be able to use // this code with earlier VDF versions we use the older "U_" syntax. //Get Create (RefClass(cRegistry)) to hoRegistry Get Create U_cRegistry to hoRegistry If (hoRegistry = 0) Begin Send DoWriteLog CS_RegObjectNotCreated Function_Return CI_RegObjectNotCreated End // Set the default of phRootKey... Set phRootKey of hoRegistry to HKEY_LOCAL_MACHINE // Set access rights to read Set pfAccessRights of hoRegistry to Key_Read Get KeyExists of hoRegistry sKey to bExists // The sKey exists... If (bExists = True) Begin // Open the Key... Get OpenKey of hoRegistry sKey to bOpened // The key was opened successfully If (bOpened = True) Begin Send CloseKey of hoRegistry End // The sKey couldn't be opened Else Begin Move CI_KeyWasNotOpened to iRetval End End // The sKey is missing. Else Begin Move CI_KeyDoesNotExist to iRetval End Send Destroy of hoRegistry Function_Return iRetval End_Function // Converts a registry error to an error text string, that can be // written to the logfile. // Parameters: // Pass the iErrCode returned from the IsRegistryStringValue // and IsRegistryDwordValue functions. // Returns: // A corresponding error text to the passed iErrCode Function ConvertRegistryErrCodeToText Integer iErrCode Returns String String sRetval Case Begin Case (iErrCode = CI_RegObjectNotCreated) Move CS_RegObjectNotCreated to sRetval Case Break Case (iErrCode = CI_KeyDoesNotExist) Move CS_KeyDoesNotExist to sRetval Case Break Case (iErrCode = CI_KeyWasNotOpened) Move CS_KeyWasNotOpened to sRetval Case Break Case (iErrCode = CI_SubKeyDoesNotExist) Move CS_SubKeyDoesNotExist to sRetval Case Break Case (iErrCode = CI_SubKeyWasNotOpened) Move CS_SubKeyWasNotOpened to sRetval Case Break Case (iErrCode = CI_ValueNameEmpty) Move CS_ValueNameEmpty to sRetval Case Break Case (iErrCode = CI_ValueNameNotCreated) Move CS_ValueNameNotCreated to sRetval Case Break Case (iErrCode = CI_ValueNameDoesntExist) Move CS_ValueNameDoesntExist to sRetval Case Break Case (iErrCode = CI_RegValueIncorrect) Move CS_RegObjectNotCreated to sRetval Case Break Case Else Move CS_UndefinedErrorText to sRetval Case End Function_Return sRetval End_Function // Write changes to a log file, which is // either a .txt file or database file depending on the object // used at the bottom of this code. Default = Log to RegCheck2.txt Procedure DoWriteLog String sText String sUser sComputerName Integer ho bAnsiState Get phoLogObject to ho If (ho = 0) Procedure_Return // No log object defined. Do NOT log. Get pbAnsiState of ho to bAnsiState Get psComputerName to sComputerName // Public property of this class. Move ("Machine Name: " + sComputerName) to sComputerName Get psUserLoginName to sUser // Public property of this class. Move ("User Name:" * sUser * "- ") to sUser Move (Replaces("\n", sText, " ")) to sText Move (sComputerName * sUser * "Text:" * String(sText)) to sText If (bAnsiState = True) Begin Send Log_Status of ho (ToAnsi(sText)) End Else Begin Send Log_Status of ho sText End End_Procedure // Send message to log out the current user from Windows Procedure LoggOffUser Integer iIgnore Move (ExitWindowsEx(EWX_LOGOFF, SHTDN_REASON_MAJOR_SOFTWARE)) to iIgnore End_Procedure // Send a RebootMachine message to reboot computer. Procedure RebootMachine Integer iIgnore Move (ExitWindowsEx(EWX_RESTARTAPPS, SHTDN_REASON_MAJOR_APPLICATION ior SHTDN_REASON_MINOR_INSTALLATION ior SHTDN_REASON_FLAG_PLANNED)) to iIgnore End_Procedure // Function that returns the Computer Name. // Gets called by the End_Construct_Object and // sets the class psComputerName property. Function NetbiosComputerName Returns String Pointer lpComputerName lpNameSize String sComputerName sNameSize Integer iRetval Movestr (Repeat (Character (0), MAX$COMPUTERNAME$LENGTH + 1)) to sComputerName GetAddress of sComputerName to lpComputerName Movestr (DwordToBytes (MAX$COMPUTERNAME$LENGTH + 1)) to sNameSize GetAddress of sNameSize to lpNameSize Moveint (GetComputerName (lpComputerName, lpNameSize)) to iRetval Function_Return (CString(sComputerName)) End_Function // Function returns the User Name if user logged on to a network, else returns ''. // Gets called by the End_Construct_Object and // sets the class psUserLoginName property. Function GetUserLoginName Returns String String sName Pointer lpNameAddr Integer iRetval Move (Repeat(Character(0),255)) to sName GetAddress of sName to lpNameAddr Move (Win32_WNetGetUser(0, lpNameAddr, DWORDtoBytes(255))) to iRetval If (iRetval = 0) Begin Function_Return (ToOem(Cstring(sName))) End Else Begin Function_Return '' End End_Function Function CurrentMinServicePack Returns Integer Integer iRetval eOSVersion Get peOSVersion to eOSVersion Case Begin Case (eOSVersion = CE_OSVERSION_WXP) Get piMinXP_ServicePack to iRetval Case Break Case (eOSVersion = CE_OSVERSION_VISTA) Get piMinVista_ServicePack to iRetval Case Break Case (eOSVersion = CE_OSVERSION_W7) Get piMinWindows7_ServicePack to iRetval Case Break Case (eOSVersion = CE_OSVERSION_W8) Get piMinWindows8_ServicePack to iRetval Case Break Case ( eOSVersion = CE_OSVERSION_2K3 or eOSVersion = CE_OSVERSION_2K3R2) Get piMinServer2K3_ServicePack to iRetval Case Break Case ( eOSVersion = CE_OSVERSION_2K8) Get piMinServer2K8_ServicePack to iRetval Case Break Case ( eOSVersion = CE_OSVERSION_2K8R2) Get piMinServer2K8R2_ServicePack to iRetval Case Break Case ( eOSVersion = CE_OSVERSION_W2K12SRV) Get piMinServer2K12_ServicePack to iRetval Case Break Case Else Move -1 to iRetval Case End Function_Return iRetval End_Function // Message that sets all OS related properties. Procedure DoSetOSProperties Handle ho Variant vValue // The RefClass syntax was introduced in VDF 15.1. To be able to use // this code with earlier VDF versions we use the older "U_" syntax. //Get Create (RefClass(cOSVersionInfo)) to ho Get Create U_cOSVersionInfo to ho Get piMajorVersion of ho to vValue Set piMajorVersion to vValue Get piMinorVersion of ho to vValue Set piMinorVersion to vValue Get piBuildNumber of ho to vValue Set piBuildNumber to vValue Get piPlatFormId of ho to vValue Set piPlatFormId to vValue Get psCSDVersion of ho to vValue Set psCSDVersion to vValue Get piServicePackMajor of ho to vValue Set piServicePackMajor to vValue Get piSuiteMask of ho to vValue Set piSuiteMask to vValue Get psSuite of ho to vValue Set psSuite to vValue Get psEdition of ho to vValue Set psEdition to vValue Get piProductType of ho to vValue Set piProductType to vValue Get psOSVersion of ho to vValue Set psOSShortVersion to vValue Get peOSVersion of ho to vValue Set peOSVersion to vValue Get pbNTServer of ho to vValue Set pbNTServer to vValue Get pbWindowsHome of ho to vValue Set pbWindowsHome to vValue Get pbX64 of ho to vValue Set pbX64 to vValue Get OSVersion of ho to vValue Set psOSVersion to vValue Send Destroy of ho End_Procedure Procedure End_Construct_Object Forward Send End_Construct_Object Set psComputerName to (NetBiosComputerName(Self)) Set psUserLoginName to (GetUserLoginName(Self)) End_Procedure End_Class Use StatAlog.pkg // Standard package for logging to an Ascii file. // The log file will be created in the Data folder of the current workspace. Object oLogRegCheck is a StatusAsciiLog Property Integer pbAnsiState 1 Set Close_Always_State to True // ** 2002-11-26 WvA in the case of VDF8 or higher. #IF (FMAC_VERSION > 7) If (phoWorkspace(ghoApplication)) Begin Set Log_Name to ((Left((psDataPath(phoWorkspace(ghoApplication))), (Pos (";", (psDataPath(phoWorkspace(ghoApplication)))) - 1))) - "\" - CS_LogFileName) End Else Begin Set Log_Name to ("\" - CS_LogFileName) End #ELSE // ** 2002-11-26 WvA in the case of VDF7 If ghoWorkSpace Begin Set Log_Name to ((Left((CurrentDataPath(ghoWorkSpace)), (Pos (";", (CurrentDataPath(ghoWorkSpace))) - 1))) - "\" - CS_LogFileName) End Else Begin Set Log_Name to ("\" - CS_LogFileName) End #ENDIF End_Object // ...Alternatively you could use the following to log to a database table: // //Use StatFlog.pkg //Property Integer pbAnsiState 0 // Note: You need incorporate the Statlog.dat yourself. // (Hint: There is one in the RegCheck workspace, copied from the Contact Manager sample) // The description field length was increased to 250. //Object oLogRegCheck is a StatusdbLog // For Augmentation. This is called when a new record is about // to be saved. You can make any changes, set new field values, etc. // Procedure OnNewRecord // End_Procedure //End_Object // Global handle to the regcheck object. // You can use it in your program to check properties of the RegCheck2 object. #IFNDEF ghoRegCheck2 Global_Variable Integer ghoRegCheck2 #ENDIF // **** MAIN REGISTRY CHECKING OBJECT: **** // **** Set properties here that suits your needs: **** // Object oRegCheck2 is a cRegCheck2 Move Self to ghoRegCheck2 // Global handle to the object. Set phoLogObject to (oLogRegCheck(Self)) // Set this to the log object if you want to log to a file. // *** N.B. You DO want to change the pbReportOnlyState property // *** to FALSE in your program, else necessary changes // *** will be reported to the error log only and NO changes // *** will be made! Set pbReportOnlyState to False Set pbChangeHighDataIntegrity to False // Set True to set DF_HIGH_DATA_INTEGRITY to true Set pbSilentMode to False // Set True to NOT ask user before changing the registry. Set pbDoServerRegistryChanges to True // Set True to try making registry changes on Server machines. Set pbDriverBruteForce to True // Do perform checking even if other drivers than DataFlex are loaded. Set pbTestServicePack to True // Should we check for the service pack and if it is to low deny the running of the application? // Windows version settings: (We don't allow anything earlier than XP) Set piRunOnUnknownVersion to cx_Run_With_Warning // However, if older than XP we never allow program to run. Set piRunOnHomeVersion to cx_Run_With_Warning // ToDo: These aren't uses: // Set piRunOnWindowsXP to cx_Run_Normal // Set piRunOnWindowsVista to cx_Run_Normal // Set piRunOnWindows7 to cx_Run_Normal // Set piRunOnWindows8 to cx_Run_Normal // Client service packs: Set piMinXP_ServicePack to 3 Set piMinVista_ServicePack to 2 Set piMinWindows7_ServicePack to 1 Set piMinWindows8_ServicePack to 0 // Server service packs: Set piMinServer2K3_ServicePack to 3 Set piMinServer2K8_ServicePack to 3 Set piMinServer2K8R2_ServicePack to 1 Set piMinServer2K12_ServicePack to 0 End_Object //Send DoCheckRegistrySettings of ghoRegCheck2 // Start checking! (Main procedure)