Jump to content

Leaderboard


Popular Content

Showing content with the highest reputation on 09/05/19 in all areas

  1. Markus Kinzler

    Firebird Encryption options

    Firebird 3 support over-the-wire encryption out of the box. https://firebirdsql.org/file/documentation/release_notes/html/en/3_0/rnfb30-security-wirecrypt.html
  2. Colleagues! We are pleased to present an updated version of the grid component for Delphi FireMonkey - FireDataGrid 1.7. The current version contains many pieces of code that have been completely rewritten to support the current version of FireMonkey RAD Rio 10.3.2. The current version of the component is fully adapted to work on High DPI monitors. The component scaling is processed very carefully (by the pixels) and does not contain artifacts at different screen resolutions. The rendering speed has been increased, and other grid operation bugs in different modes have also been fixed. Redesigned support for FMX themes and styles, absolutely any FireMonkey themes are now supported. Improved the grid zooming feature (activated by the Cntrol +"+", Control+"-" or Control + mouse scroll wheel). Zooming now works much faster. Reworked Filter panel and Field Chooser panel. Added visual effect to them. In addition, bugs have been fixed in the implementation of the TDataSet and the Grid pairing mechanism for read-only, edit modes and in adding new entries mode. You can download the evaluation version at this link. Please send us your feedback and suggestions. All your wishes will be taken into account in further development! Below some screenshots are available: FireDataGrid 1.7 uses Transparent FireMonkey Style FireDataGrid 1.7 uses Windows 10 Modern Blue Style Sincerely, Developer Machines https://www.devmachines.com
  3. Arnaud Bouchez

    Good practices with nested methods

    Yes, @Bill Meyer is right: do not put the nested functions part of the main class. It would break SOLID principles for sure - at least the single responsibility. Always favor composition in OOP. About refactoring and breaking huge functions, you could take a look at those slides: Following these proposals, for code refactoring, I would move the code into dedicated records with methods (or classes).
  4. Bill Meyer

    Good practices with nested methods

    A point not yet mentioned is the use of nested methods in the course of refactoring. In legacy code, it is not uncommon to find huge routines which have been thoughtlessly coded, and often with useless naming. I have found it helpful to remove to nested methods the chunks of code which might better be placed in a private class, as Arnaud suggests. Making them methods of the class in which you are working often makes no sense because these routines may have no practical value out of the context in which they were found. In new design, their use is harder to justify, I think.
  5. Mark Williams

    ISAPI DLL concurrent requests

    Spot on! That was the problem. As for WebGarden, I have check and you create a WebGarden by increasing the ApplicationPool's Maximum Worker Processes above 1. I am now running as non-web garden. I have done a lot of research based on your helpful points above and eventually the mist began to clear. I now have a working ISAPI DLL which appears to be thread safe. Unfortunately, I couldn't find any good examples of how to do what I was trying to do. My DLL uses global variables stored in an inifile. I don't want to load the inifile each time a new thread is fired to avoid repeated loading of the ini file.. So I need to load the inifile once when the DLL is first loaded. Also, I need to have a threadsafe logging system and finally I use a MimeTable in the DLL and I don't want to load that for each request. Below is my relevant code (with the detail removed) to show I have gone about the above. It is working as far as I can see, but I would appreciate any tips for improvement or re potential pitfalls. Hopefully, this will then help others who are new ISAPI dll. It took me many hours of head scratching to get this far. The dpr library WebServer; uses Winapi.ActiveX, System.Win.ComObj, System.SysUtils, Web.WebBroker, Web.Win.ISAPIApp, Web.Win.ISAPIThreadPool, WinAPI.Isapi2, WinAPI.Windows, System.SyncObjs, WebModule in 'WebModule.pas' {WebModule1: TWebModule}; {$R *.res} //Read online somewhere that it is best to initialize/Finalize your globals in the following function //rather than in Initialization/Finalization sections of the webmodule} function GetExtensionVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall; begin Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver); CriticalSection := TCriticalSection.Create; StartServer; end; function TerminateVersion(var Ver: THSE_VERSION_INFO): BOOL; stdcall; begin Result := Web.Win.ISAPIApp.GetExtensionVersion(Ver); CloseServer; CriticalSection.Free; end; exports GetExtensionVersion, HttpExtensionProc, TerminateExtension; begin CoInitFlags := COINIT_MULTITHREADED; Application.Initialize; Application.WebModuleClass := WebModuleClass; {Application.MaxConnections:=100;} //if you want to increase beyond the default 32 for example NumberOfThreads:=Application.MaxConnections; //see comments following code samples. This is necessary even if you do not want to change the MaxConnections Application.Run; end. Stripped down WebModule unit WebModule; interface uses WinApi.Windows, System.SysUtils, System.Classes, Web.CGIHTTP, Web.HTTPApp, Web.ReqMulti, System.SyncObjs, ThreadFileLog, IDGlobalProtocols {, etc...}; type TLogType = (leNone, leCriticalError, leMajorError, leMinorError, leMajorEvents, leDevelopment); type TWebModule1 = class(TWebModule) FDQuery: TFDQuery; FDPhysPgDriverLink: TFDPhysPgDriverLink; FDConnection: TFDConnection; procedure WebModule1HeadAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); procedure WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); procedure WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); private { Private declarations } public { Public declarations } end; var WebModuleClass: TComponentClass = TWebModule1; CriticalSection: TCriticalSection; const EncryptionKey='Q9__M_f_qc1EX8ce98HsQrYG_s4aRGQj5G_Ixu4XTQs___esfxdH_kf_lg5_8lnk'; Procedure StartServer; Procedure CloseServer; implementation Uses web.WebBroker; var //these will be accessible to all threads ServerStarted:boolean; LogType:TLogType; LogFile, PGVendorLib:string; FThreadFileLog : TThreadFileLog; MimeTable:TIDMimeTable; {%CLASSGROUP 'System.Classes.TPersistent'} {$R *.dfm} procedure AddToLog(str: string; whichLogType:TLogType); var Log:string; begin try if (LogType=leNone) or (ord(whichLogType)>ord(LogType)) then //we are not logging these types of events or errors exit; case whichLogType of leCriticalError: Log:='CRITICAL ERROR: '; leMajorError: Log:='MAJOR ERROR: '; leMinorError: Log:='MINOR ERROR: '; leMajorEvents: Log:='MAJOR EVENT: '; leDevelopment: Log:='DEVELOPMENT: '; end; Log:=Log+str; FThreadFileLog.Log(LogFile, Log); Except end; end; Procedure CreateMimeTable; begin try if not assigned(MimeTable) then begin MimeTable:=TIDMimeTable.create; AddToLog('Mime Table Create', leDevelopment); end; except on E: Exception do AddToLog('Error CreateMimeTable: '+e.Message, leMajorError) end; end; Procedure StartServer; var appPath:String; iniFile:TIniFile; begin if serverStarted then //Probably overkill as should only be called once exit; CriticalSection.Enter; try try FThreadFileLog := TThreadFileLog.Create(); appPath:=GetModuleName(HInstance); if pos('\\?\', appPath)>0 then //this is something to do with the Windows function GetModuleFileName and uncpaths appPath:=copy(appPath, 5, MaxInt); iniFile:=TIniFile.Create(changeFileExt(appPath, '.ini')); try if not FileExists(iniFile.FileName) then begin LogFile:='c:\ProgramData\Log.txt'; AddToLog('IniFile does not exist at location: "'+iniFile.FileName, leCriticalError); Exit; end; AddToLog('Server Started', leMajorEvents); LogType:=TLogType(iniFile.ReadInteger('GENERAL', 'log_type', 1)); LogFile:=inifile.ReadString('GENERAL', 'log_file', ''); if (LogType<>leNone) and (LogFile<>'') then begin if not DirectoryExists(ExtractFilePath(LogFile)) and not ForceDirectories(ExtractFilePath(LogFile)) then LogFile:='c:\ProgramData\Log.txt'; end; PGVendorLib:=inifile.ReadString('LOCATIONS', 'pg_vendor_lib', ''); finally iniFile.Free; end; AddToLog('LogType:'+inttostr(ord(logType)), leDevelopment); AddToLog('LogFile: '+LogFile, leDevelopment); AddToLog('FDPhysPGDriverLink.VendorLib: '+PGVendorLib, leDevelopment); AddToLog('Server Started', leMajorEvents); ServerStarted:=true; except try AddToLog('Server could not be loaded: '+SysErrorMessage(GetLastError), leCriticalError); except end; end; finally CriticalSection.Leave; end; end; Procedure CloseServer; begin CriticalSection.Enter; try if assigned(MimeTable) then try MimeTable.Free; except end; if assigned(FThreadFileLog) then try FThreadFileLog.Free; except end; try AddToLog('Server Closed', leMajorEvents); except end; finally CriticalSection.Leave; end; end; procedure TWebModule1.WebModule1HeadAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); var fileName:string; begin Handled:=true; try //If this action needed the mimetable it would call CreateMimeTable at this point FileName:=trim(Request.GetFieldByName('Location')); if FileName='' then begin SetResponse(Response, 400, 'Bad Request', 'Head'); exit; end; if fileExists(FileName) then SetResponse(Response, 200, 'OK') else SetResponse(Response, 404, 'NotFound'); except end; end; procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin FDPhysPgDriverLink.VendorLib:=PGVendorLib; AddToLog('Before Dispatch - PathInfo: '+Request.PathInfo, leDevelopment); AddToLog('Before Dispatch - Method: '+Request.Method, leDevelopment); AddToLog('MaxConnections: : '+Application.MaxConnections.ToString, leDevelopment); AddToLog('ActiveConnections: : '+Application.ActiveCount.ToString, leDevelopment); AddToLog('InactiveConnections: : '+Application.InActiveCount.ToString, leDevelopment); AddToLog('CachedConnections: : '+Ord(Application.CacheConnections).ToString, leDevelopment); end; procedure TWebModule1.WebModuleAfterDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin AddToLog('After Dispatch - Method: '+Request.Method, leDevelopment); end; end. ThreadFileLog unit unit ThreadFileLog; interface uses Windows, ThreadUtilities, System.Classes; type PLogRequest = ^TLogRequest; TLogRequest = record LogText : String; FileName : String; end; TThreadFileLog = class(TObject) private FThreadPool: TThreadPool; procedure HandleLogRequest(Data: Pointer; AThread: TThread); public constructor Create(); destructor Destroy; override; procedure Log(const FileName, LogText: string); end; implementation uses System.SysUtils; procedure LogToFile(const FileName, LogString: String); var c1, c2 : dword; i:integer; s:string; begin try c1 := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0); i:=0; while c1=INVALID_HANDLE_VALUE do begin if i>200 then //2 secs elapsed and haven't been able to access log exit else begin inc(i); Sleep(10); c1 := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0); end; end; if c1 <> INVALID_HANDLE_VALUE then begin SetFilePointer(c1, 0, nil, FILE_END); S:=DateTimeToStr(Now)+': '+LogString+sLineBreak+SLineBreak; WriteFile(c1, s[1], length(s)*SizeOf(Char), c2, nil); CloseHandle(c1); end except end; end; constructor TThreadFileLog.Create(); begin FThreadPool := TThreadPool.Create(HandleLogRequest, 1); end; destructor TThreadFileLog.Destroy; begin FThreadPool.Free; inherited; end; procedure TThreadFileLog.HandleLogRequest(Data: Pointer; AThread: TThread); var Request: PLogRequest; begin Request := Data; try LogToFile(Request^.FileName, Request^.LogText); finally Dispose(Request); end; end; procedure TThreadFileLog.Log(const FileName, LogText: string); var Request: PLogRequest; begin New(Request); Request^.LogText := LogText; Request^.FileName := FileName; FThreadPool.Add(Request); end; end. Other possible useful information: The Application The application created in the project file is a TWebApplication not a TApplication. It exposes MaxConnections, ActiveConnections and InActiveConnections, CachedConnection. MaxConnections The maximum no of connections to your DLL. By default this is 32. So up to 32 new threads will be spawned (but note comments re NumberOfThreads below). You can increase or decrease this number as requi ActiveConnections This reports the number of connections currently in use. InactiveConnections If CachedConnections is true new threads are not disposed of after use, but cached for future use. whilst cached they are recorded as InactiveConnections. ISAPIThreadPool unit This is added by default and deals with threading. NumberOfThreads Global variable declared in the ISAPIThreadPool unit. It is set by default to 25. So whilst the default MaxConnections is 32 (so you can theoretically have 32 connections) unless you increase the NumberOfThreads you will in effect be allowed only 25 connections! The example project code deals with this.
  6. Arnaud Bouchez

    Good practices with nested methods

    A quick and simple rule would be to define only mandatory variables as shared for the nested procedures, i.e. above them. That is, try to keep all variables of the main block in the lower part of the code, just before the main block. Just as you did. From my experiment, nested functions are fine if there is only 1 (or 2) of them. Once I reach the number of 2/3 nested functions, I usually define a private record type, with proper methods. It is kept private in the implementation section of the unit. The trick of using a record with methods, and not a class, allows to allocate it on the stack in the main method, with no try..finally Free block. It is much cleaner (more SOLID), and also much more extendable. The main benefit is that it is re-usable: you could reuse some code, in another function. Last but not least, you have the possibility to add a unitary test method, and browse easier the code in your IDE.
  7. File a bug report, giving them as much info of your OS and graphics hardware setup as possible. And don't forget to include screenshots like the one above and also one which does not show these controls at all.
  8. I have just adjusted the title of this topic. Just „very strange issue“ could also apply to nearly everything. 😉
  9. I am not good at writing and debugging experts, so I could be quite wrong in this question .. Nevertheless, I think that it is possible to function GetPackageName(Index: Integer): string; or function GetPackage(Index: Integer): IOTAPackageInfo; suit you. In any case, you can familiarize yourself with them in more detail on the website: Browsing Package Information
  10. There is a not obvious button to the left of the desktop selection dropdown in the title bar, which shows a menu:
  11. Jacek Laskowski

    IDE Background Work

    I've noticed a very big difference in the speed of generating hints/suggestion in Code Insight Plus and in MMX between Delphi 10.2 and 10.3. Sometimes I have to wait more than a minute to show the type or variable selection list! The same applies to MMX and editing by ctrl + E ... sometimes I waiting 2-3 minutes. Since two independent experts have the same problem, I guess the problem is in the new Delphi, specifically in OpenToolAPI. Something was broken and performance slowed down a lot. Probably only Andreas Hausladen @jbg can improve anything 😞
  12. Attila Kovacs

    ISAPI DLL concurrent requests

    Btw. the fact that your broker works in garden mode, where the dll will be loaded every time you have a new request (as I understand it), indicates that you are using globals in non thread-safe mode.
  13. Daniel

    Do we have a spam problem?

    The mods do a great job here, but we definitely will have some more protection against these spammers.
  14. Remy Lebeau

    JSON string value

    Have you tried toJSON() instead of toString()? procedure TForm1.Button1Click(Sender: TObject); const KEK = '"value\with' + sLineBreak + '{strange}chars/"'; Var s: TJSONString; begin s := TJSONString.Create(KEK); Try Edit1.Text := s.ToJSON; Finally s.Free; End; end; Also, which version of Delphi are you using? Recent versions have made updates to the JSON framework to make it more compliant with the JSON spec.
  15. dummzeuch

    Fixed two more formatter bugs

    Two more formatter bugs just got axed: #133 Code formatter does not support the "Final" keyword #134: Formatter does not handle variant records with methods correctly Also found a problem with the Uses Expert, but that was not in the released code yet.
  16. Dalija Prasnikar

    With's the deal with "With"?

    We are talking about TRect here. Very basic type used all over. It is not very likely that one will create his own types for just about everything. And this is where using with can easily break your code without you even realizing it is broken. This is especially problematic when used in combination with UI where testing is hard and you can have a lot of code that is poorly covered with tests that could reveal such issues. 'with' is relic of another time and another coding practices. And, yes you could shoot yourself in the foot even back then, but now it is extremely easy to do so. Not using 'with' is the best advice one could offer. It is almost like using goto and absolute. You may have some very limited use case where using it is justified, but you will not find such constructs being used at large.
  17. Sherlock

    With's the deal with "With"?

    I beg to differ. Naming a property/function more explicitly for the sake of with is...how do I put this politely?...not helpful. Say I have a class TCar with properties Wheel and Engine and functions Drive and Park. If I understand you correctly you would expect me to call those properties and functions CarWheel, CarEngine, CarDrive and CarPark, to be explicit enough for the rare case when someone uses with on my TCar...
  18. Stefan Glienke

    With's the deal with "With"?

    It's not about proper naming - its about newly added members creeping into the with scope - as happened when TRect got Height and Width properties! It was perfectly clear that Top, Left, Right, Bottom referred inside the with belonged to the "withed" rect variable and Height and Width to something outside - now those got added to TRect and boom.
  19. Fritzew

    With's the deal with "With"?

    Yes, please! proper namespacing like in Remobjects.Elements would be ...........
  20. Stefan Glienke

    With's the deal with "With"?

    +1000 And then also introduce proper namespacing and ways to alias things (not only non generic types) at the consuming side (for example)
  21. David Heffernan

    With's the deal with "With"?

    Regarding a fix, this is only one of a number of problems with scope resolution in the language. Same issue arises when you use units, where more recently imported symbols hide those from units imported earlier. Same for local variables and methods. Same for local variables and global symbols. Anywhere where you refer to a symbol and there are multiple matches then the compiler chooses the most recent. In princple the resolution is simple. Introduce a new compiler warning whenever the compiler uses this most recent principle.
  22. David Heffernan

    With's the deal with "With"?

    Debugging was never the issue. That was just annoying. Refactoring was never the issue either. That also is annoying but one of many broken aspects of the refactoring. The issue is the silent bugs that arise when you add a field to a type and that one hides local variables. This has been discussed hundreds of times. Try some more websearch. Then, stop using with.
  23. dummzeuch

    With's the deal with "With"?

    My recommendation is simple: Don't use the WITH keyword. There are not just the problems you mention. It can also be difficult to determine the scope of an identifier within a WITH block. Example: var Caption: char; procedure TMyForm.Button1Click(Sender: TObject); type TSomeRec = record Caption: integer; end; var Caption: double; SomeRec: TSomeRec; begin // [lots of code] with SomeRec do begin // [lots of code] Caption := 'bla'; // <- which Caption variable/property/field is accessed here? And why does it not compile? end; end; We have had this discussion so many times that it is really pointless to start it afresh. Just google it.
  24. Remy Lebeau

    Firebird, change length of Varchar Primary key column?

    You can create the new column with NULL as its default value, then populate the column with data as needed, and then alter the column to drop the default value, and update the system tables to mark the column as "not null". However, none of this is necessary if you don't drop the original column to begin with.
  25. Arnaud Bouchez

    With's the deal with "With"?

    As everything powerful can be armful when misused, I like "with", when it is not over-abused. For instance, I like "with", when it has only several lines of scopes. It does make sense to me, when used for a single line. Otherwise, I switch to an implicit local variable: asm generation would be the very same. If you think "with" should be avoided due to its implicit context switch, I guess next step would be to force using self.foo for class properties... just like the $this->foo of Php... what a progress! 🙂 I am against any new warning at compiler level. And if you are confused by with someclass do x := a * b and don't know to which class a, b, or x belongs to, it is time renaming the properties/functions to something more explicit!
×