Mr. Hater is a well known Delphi-related FUD spreader. From times to times some colleague or friend send me a link pointing to one of his infamous posts.
Mr. "dumbass" Hater pretends to be some kind of Delphi user defender, some kind of Batman of the Delphi community, but he turned out to be more like the Jokerman. His correspondents from the north pole, Atlantis and Bermuda triangle always send him "hot" information that he "generously" shares with us poor mortals. Unfortunately, he don't have a clue about most of the things that he intends to review or analyze. I think that positive criticisms are necessary, but what he does has nothing to do with it.
The last post that I read was this: http://delphihaters.blogspot.de/2013/01/delphi-hidden-settings.html
Some options are really funny, I must admit. The funnier part though is that Mr. Dumbass didn't publish my genuine question/comment about IntraWeb. All that I asked him was:
1) Provide me a test case where an IntraWeb application uses 2 Gb of memory without any explanation and without memory leaks caused by user code.
2) Provide me a test case where an IntraWeb application can't handle Unicode by default (HTML rendering/uploaded and downloaded file names/whatever)
3) Provide me a test case where an IntraWeb application takes forever to render a page (no need to take forever though. Just more than expected and I would be satisfied)
I'm not asking much from such a programming guru, right? But the comment wasn't approved by Mr. D. Can't you find/create such examples, Mr. D?
Another genuine question, Mr. D:
Why someone that keeps spreading FUD in the dark has a MODERATED blog??? You are blogging anonymously, so why do you want me to identify myself when adding comments to your blog?
My psychologist would say that Mr. Dumbass has some kind of complex with strange name. His crusade against IntraWeb, for instance, is more than a personal point of view. Not satisfied spreading FUD about Delphi and related products he also attacks the companies and the people behind it, what I find to be unacceptable. Probably someone really hurt Mr. Dumbass and he just can't live with that... Why don't you accept Delphi podcast invitation and tell us who did it to you, Mr. D?
PS: This is my personal point of view about the subject and is not endorsed or has nothing to do with Embarcadero, Atozed or whatever.
Pseudo-random tech thoughts
About technology, software development and Delphi Programming
Tuesday, February 26, 2013
Wednesday, December 19, 2012
ShellExecute as administrator + Drag and Drop in Windows 7
This is a little tip that took me sometime to figure it out:
This kind of issue happened when I was debugging a IntraWeb application created with Delphi XE3 in Windows 7. I often run Delphi XE3 as local administrator. I know, I know... but sometimes it is required for proper ISAPI debugging.
This application renders a control that supports drag and drop from Windows Explorer, when the browser also supports it (Firefox, Chorme, etc.). The issue then is: When you start the browser using ShellExecute (or CreateProcess for that matter), from Delphi XE3 (or any other version or software) running with administrator credentials, the browser is also running as administrator, right? But Windows Explorer IS NOT. So, drag and drop from Windows Explorer to Firefox, for instance, won't work. Applications running with elevated privileges and applications running as regular user just won't do drag and drop... Simple, yes?
This kind of issue happened when I was debugging a IntraWeb application created with Delphi XE3 in Windows 7. I often run Delphi XE3 as local administrator. I know, I know... but sometimes it is required for proper ISAPI debugging.
This application renders a control that supports drag and drop from Windows Explorer, when the browser also supports it (Firefox, Chorme, etc.). The issue then is: When you start the browser using ShellExecute (or CreateProcess for that matter), from Delphi XE3 (or any other version or software) running with administrator credentials, the browser is also running as administrator, right? But Windows Explorer IS NOT. So, drag and drop from Windows Explorer to Firefox, for instance, won't work. Applications running with elevated privileges and applications running as regular user just won't do drag and drop... Simple, yes?
Thursday, July 12, 2012
My IntraWeb optimizations are not required anymore
Yes! If you are using the latest 12.2.6 IntraWeb version you won't need my "speed patches" anymore because IntraWeb 12.2.6 are using these same faster routines, and more! :-)
You may download the new IntraWeb 12.2.6 version from here (follow the Test Releases link)
http://www.atozed.com/IntraWeb/Download/Download.EN.aspx
Enjoy!
You may download the new IntraWeb 12.2.6 version from here (follow the Test Releases link)
http://www.atozed.com/IntraWeb/Download/Download.EN.aspx
Enjoy!
Sunday, April 15, 2012
Debug ISAPI extensions in Windows 7 and IIS 7.5 using Delphi XE2
Many things have changed in IIS from version 6 to 7 (and 7.5 that comes with Windows 7). If you are not familiarized with IIS 7 (and 7.5) debugging, maybe this post may help you.
In fact, debugging ISAPI in Winodws 7 is easy if you follow a few steps:
1) In IIS, create the web application under Default Web Site.
2) Every application under Default Web Site should be using the same application pool, DefaultAppPool.
If you ignore these two rules, you will have additional configuration steps to run w3wp later.
2) In Delphi XE2 Ide, choose Run -> Parameters. Inform Host application and Parameters as you can see in the following picture:
W3WP.exe is the IIS Worker Process executable, and we will run it interactively to debug the ISAPI app. If you didn´t follow rules (1) and (2) of IIS Setup, then you will have to do additional configuration:
- Inform the web site using the parameter: -s "TheSiteId"
-Inform the application pool using the parameter: -ap "TheAppPoolName"
(This doesn't work with IIS 7 and above. The -ap parameter is not recognized by w3wp, so it's better to stay with tip (2) above)
Setting up IIS
First, you have to create a new application in IIS and set it up as usual. Two things are important during IIS and application setup:1) In IIS, create the web application under Default Web Site.
2) Every application under Default Web Site should be using the same application pool, DefaultAppPool.
If you ignore these two rules, you will have additional configuration steps to run w3wp later.
Setting up the application in Delphi XE2
1) First and more important: Run Delphi XE2 as administrator! If you don't, Delphi may not be able to start the IIS Worker process (W3WP.exe).2) In Delphi XE2 Ide, choose Run -> Parameters. Inform Host application and Parameters as you can see in the following picture:
W3WP.exe is the IIS Worker Process executable, and we will run it interactively to debug the ISAPI app. If you didn´t follow rules (1) and (2) of IIS Setup, then you will have to do additional configuration:
- Inform the web site using the parameter: -s "TheSiteId"
-
Ready to go
Now that everything is ready, you have to stop the WWW publication service (W3SVC). You may use "net stop W3SVC" from an elevated command prompt, or use the Windows services console. Once the W3SVC is stopped, just run the application from Delphi XE2 IDE and call it from your browser. When the application is loaded all your breakpoints will be activated and you can easily debug the application.Additional notes
I´ve found some references saying that you have to set the linking options "Map file -> Detailed" and "Include remote symbols -> On", in your project options. Well, in fact, the debuggin of ISAPI apps as explained here works perfeclty without the map file and the remote symbols.Monday, April 9, 2012
Faster IntToStr functions for Delphi 32 bits
Last week I was trying to use the excellent "Enhanced Run time library" from A. Bouchez, in a Delphi 2006 project. Unfortunately, due copyright restrictions, the modifications are released as a code patch and for Delphi 7 only.
Use all the modifications in a Delphi 2006 project is not an easy task. So I decided to use some of its code in another unit, patching the RTL original functions with new code, as I did a lot lately ;-)
The first two functions ported are faster IntToStr functions written 100% in assembly. The functions are 500 up to 1000% faster than original RTL functions, depending on the compiler (almost 1000% faster in Delphi 2006, almost 500% faster in Delphi XE2 - 32 bits). The code cannot be used in XE2 64 bit projects, because ASM 64 is a different beast. Maybe I will port other functions as well (things that don't violate copyright material), but many of them are already addressed in RtlVclOptimize (from Andreas Hausladen).
I created an unit called uCBRTLPatch.pas, containing two functions:
These functions will replace (patch) original IntToStr functions in runtime (for Integer and Int64 parameters).
To use it, you just have to add uCBRTLPatch.pas to your .DPR uses clause and you are done. You can donwload uCBRTLPatch.pas here.
Enjoy!
The first two functions ported are faster IntToStr functions written 100% in assembly. The functions are 500 up to 1000% faster than original RTL functions, depending on the compiler (almost 1000% faster in Delphi 2006, almost 500% faster in Delphi XE2 - 32 bits). The code cannot be used in XE2 64 bit projects, because ASM 64 is a different beast. Maybe I will port other functions as well (things that don't violate copyright material), but many of them are already addressed in RtlVclOptimize (from Andreas Hausladen).
I created an unit called uCBRTLPatch.pas, containing two functions:
function FastIntToStr(Value: Integer): string; function FastInt64ToStr(Value: Int64): string;
These functions will replace (patch) original IntToStr functions in runtime (for Integer and Int64 parameters).
To use it, you just have to add uCBRTLPatch.pas to your .DPR uses clause and you are done. You can donwload uCBRTLPatch.pas here.
Enjoy!
TCodeRedirect rewrite
A few weeks ago I wrote a post about TCodeRedirect. After that, I had a few problems using that code in Delphi 2006, specifically internal compiler errors (Internal Compiler Errors = useless error message!). The errors were occurring in some projects, not in all...
After a few tests I thought that could be something related to naming conflicts with original CodeRedirect function in Andreas Hausladen's RtlVclOptimize.pas (also used in my projects). So I decided to rename everything to avoid naming conflicts and it worked! No more internal compiler errors! Now my unit is named CodePatch.pas. I've updated my IntrawebPatch to use this new unit, and I'm publishing my new code here.
Enjoy!
After a few tests I thought that could be something related to naming conflicts with original CodeRedirect function in Andreas Hausladen's RtlVclOptimize.pas (also used in my projects). So I decided to rename everything to avoid naming conflicts and it worked! No more internal compiler errors! Now my unit is named CodePatch.pas. I've updated my IntrawebPatch to use this new unit, and I'm publishing my new code here.
Enjoy!
Monday, March 12, 2012
Show "loading animation" during long Intraweb AJAX requests
Yesterday I was talking to another Intraweb developer and I've created a demo application to show him how I lock the screen, showing the loading AJAX animation (IWLocker), during an AJAX request that takes long time to return. After that, I've improved my demo using 3 different techniques do do that. In this post I will show you all the 3 techniques.
Note that to show the IWLocker, I'm calling ShowBusy function. ShowBusy is declared in IWPreScript.js, a JavaScript library that is available to all your IWForms. ShowBusy expects a single boolean parameter. If you pass true the IWLocker will be shown, and if you pass false the IWLocker will be hidden. Also note that executeAjaxEvent will call IWFORM1.DoMyAjaxFunc method of your form. This method must be registered so IW core can call it. To register that method, first you have to create the method DoMyAjaxFunc in your IWForm1:
Then we must register DoMyAjaxFunc callback, using:
That's it! If you run your application and click on the IWButton1, you will see that IWLocker will become visible for 5 seconds (the time that DoMyAjaxFunc() method takes to run) and then will be hidden again.
When the event OnAsyncClick is fired the second time, there will be a SecondCall parameter in EventParams list and then we know that this time we must do the "real" work. When the method finishes, it will then call ShowBusy(false); hiding the IWLocker again.
Enjoy!
The problem
Suppose you have a IWButton in a form and want to trigger an async (AJAX) request that will take a long time to return. For instance, you will create a complex report, or generate a complex file to be downloaded later. You want your user to click the button and have some kind of feedback that your application is working, yes? At the same time you don't want your user to keep clicking all over the place... The best option, IMO, is tho show the IWLocker (the IW AJAX "loading animation") during the request. I will show you three different techniques to do that. Very similar but you may prefer one over the other.First solution
For this solution, I will create a JavaScript function that will be called by your button directly, without any OnAsyncClick event handler. First, you have to create the JavaScript function. Let's say:
function myAjaxFunc() {
var myData = "Nicole Scherzinger,Scarlett Johansson,Jennifer Lawrence,Doutzen Kroes,Charlize Theron";
ShowBusy(true);
executeAjaxEvent("&data="+myData, null,"IWFORM1.DoMyAjaxFunc", false, null, false);
return true;
}
This is a simple js function: There is a variable myData that holds the data that I want to send to the server, in this case, a comma separated list of names, but it can be whatever you want. Then I call ShowBusy, and after that I call the real AJAX request, executeAjaxEvent. Note that to show the IWLocker, I'm calling ShowBusy function. ShowBusy is declared in IWPreScript.js, a JavaScript library that is available to all your IWForms. ShowBusy expects a single boolean parameter. If you pass true the IWLocker will be shown, and if you pass false the IWLocker will be hidden. Also note that executeAjaxEvent will call IWFORM1.DoMyAjaxFunc method of your form. This method must be registered so IW core can call it. To register that method, first you have to create the method DoMyAjaxFunc in your IWForm1:
const
CDATATag = '< ! [CDATA[%s] ] >'; // spaces added so blogger won't mess the CDATA string
procedure TIWForm1.DoMyAjaxFunc(EventParams: TStringList);
var
ResponseFunc: string;
sl: TStrings;
s: string;
begin
sl := TStringList.Create;
try
sl.StrictDelimiter := True;
sl.CommaText := EventParams.Values['data'];
s := sl.Strings[Random(sl.Count)];
finally
sl.Free;
end;
Sleep(5000); // simulate a long operation
IWLabel1.Caption := 'The hot chick chosen is: ' + s;
ResponseFunc := Format(CDATATag, ['ShowBusy(false);']);
WebApplication.CallBackResponse.AddJavaScriptToExecute(ResponseFunc);
end;
The code above shows the DoMyAjaxFunc. It retrieves the "data" parameter sent from myAjaxFunc() and simulates a long operation (in this case, 5 seconds). When this method finishes, it sends ShowBusy(false) to the browser using WebApplication.CallBackResponse.AddJavaScriptToExecute method. Then we must register DoMyAjaxFunc callback, using:
WebApplication.RegisterCallBack(UpperCase(Self.Name) + '.DoMyAjaxFunc', DoMyAjaxFunc); // Self.Name -> IWForm1Finally you must call the JavaScript function myAjaxFunc from some JavaScript event directly, let's say in the onClick button event handler:
procedure TIWForm1.IWAppFormCreate(Sender: TObject);
begin
IWButton1.ScriptEvents.HookEvent('onClick', 'myAjaxFunc();');
end;
I hooked myAjaxFunc() to the onClick event handler of IWButton1 in runtime. It can be done in design time, using Object Inspector as well.That's it! If you run your application and click on the IWButton1, you will see that IWLocker will become visible for 5 seconds (the time that DoMyAjaxFunc() method takes to run) and then will be hidden again.
Second solution
The second solution is very similar to the first, but instead of calling your myAjaxFunc() directly from your onClick event handler, you can use IW async events to call it, in this case, IWButton2 (another button in IWForm1) onAsyncClick event:const CDATATag = '< ! [CDATA[%s] ] >'; // spaces added so blogger won't mess the CDATA string procedure TIWForm1.IWButton2AsyncClick(Sender: TObject; EventParams: TStringList); var ResponseFunc: string; begin ResponseFunc := Format(CDATATag, ['ShowBusy(true);myAjaxFunc();']); WebApplication.CallBackResponse.AddJavaScriptToExecute(ResponseFunc); end;Inside IWButton2 OnAsyncClick event handler I add JavaScript to execute, again using AddJavaScriptToExecute method. The JavaScript code that will be executed is a call to ShowBusy (showing the IWLocker) and then a call to myAjaxFunc();.
Third solution
The third solution is a little different from the others. It uses the IWButton3 OnAsyncClick twice. Let's see how:
procedure TIWForm1.IWButton3AsyncClick(Sender: TObject;
EventParams: TStringList);
var
ResponseFunc: string;
FirstCall: boolean;
begin
FirstCall := (EventParams.Values['SecondCall'] <> 'true');
if FirstCall then // this code will run in the first IWButton3AsyncClick call
begin
ResponseFunc := Format(CDATATag, ['ShowBusy(true);executeAjaxEvent("&SecondCall=true", null,"' + IWButton3.HTMLName + '.DoOnAsyncClick", false, null, false);']);
end else
begin // this code will run in the second IWButton3AsyncClick call
Sleep(5000); // simulate a long operation
ResponseFunc := Format(CDATATag, ['ShowBusy(false);']);
IWLabel1.Caption := 'Result returned';
end;
WebApplication.CallBackResponse.AddJavaScriptToExecute(ResponseFunc);
end;
When you click the IWButton3, the OnAsyncClick event is fired. You then search for some parameter, in this case "SecondCall" in the EventParams list. If SecondCall is not there, then this is the first OnAsyncClick call. In the first call we add some JavaScript code to be executed. This code shows the IWLocker (calling ShowBusy(true)), and then calls the OnAsyncClick event again, this time passing the SecondCall parameter to the method.When the event OnAsyncClick is fired the second time, there will be a SecondCall parameter in EventParams list and then we know that this time we must do the "real" work. When the method finishes, it will then call ShowBusy(false); hiding the IWLocker again.
Conclusions
The three techniques have the same effect: Show IWLocker during a long AJAX async request. Using the first method, your application will use a single AJAX request to your server, but you have to write some JavaScript code. Using the third method you won't have to write a single line of JavaScript but there are two AJAX requests involved. If you are not comfortable writing JavaScript you may prefer this solution. The second has the worst of both worlds: Two AJAX requests involved and some JavaScript coding... ;-)Download the sample project
You can download a complete project showing all the three methods here.Enjoy!
Subscribe to:
Posts (Atom)
