<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-29648798</id><updated>2012-01-22T10:32:34.624-08:00</updated><title type='text'>Leonardo's blog</title><subtitle type='html'>FreePascal and Delphi programming.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>43</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-29648798.post-1470049491119502560</id><published>2011-06-07T10:54:00.000-07:00</published><updated>2011-06-07T11:14:41.627-07:00</updated><title type='text'>Using Lazarus as a GDB frontend</title><content type='html'>Did you know that you can use Lazarus to debug applications compiled with other languages than Pascal?. If not, I can tell you that yes, you can!.&lt;br /&gt;&lt;br /&gt;Some times, I need to connect Delphi/FPC applications to C or C++ shared libraries compiled with gcc or g++, but its a pain to debug using command line gdb.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Lazarus to the rescue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One very interesting way to debug C++ code I use is to open one or more C++ source files in Lazarus, set breakpoints, then configure the host application and run. Magically the execution stops in the breakpoints I defined, and I can step and inspect symbols as I do usually on my Lazarus applications.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;An example&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have a Delphi application that relies on a C++ Dll compiled with g++ (using the -g parameter to include debug information). Using Delphi, I can debug until the point an  external function is called, but nothing further than that. &lt;br /&gt;&lt;br /&gt;To be able to debug the dll source code, you can just follow this steps in Lazarus:&lt;br /&gt;&lt;br /&gt;1) Project -&gt; New -&gt; Application.&lt;br /&gt;2) File -&gt; Open -&gt; your_cpp_file&lt;br /&gt;3) Define as many breakpoints (F5) as you need.&lt;br /&gt;4) Run -&gt; Run parameters -&gt; Host application: c:\myapp\myapp.exe&lt;br /&gt;5) Run -&gt; Run parameters -&gt; Working directory: c:\myapp&lt;br /&gt;&lt;br /&gt;That's all, now just click on run (F9). This will open the host application, and when it calls the dll, the breakpoints you configured will be reached.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-1470049491119502560?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/1470049491119502560/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=1470049491119502560' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1470049491119502560'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1470049491119502560'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2011/06/using-lazarus-as-gdb-frontend.html' title='Using Lazarus as a GDB frontend'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-1562024798908852008</id><published>2011-03-16T11:28:00.000-07:00</published><updated>2011-03-16T11:32:08.505-07:00</updated><title type='text'>Technical analysis with FreePascal and TA-lib</title><content type='html'>As the &lt;a href="http://ta-lib.org/index.html"&gt;home page&lt;/a&gt; explains, TA-Lib is a C++ library widely used by trading software developers requiring to perform technical analysis of financial market data. It Includes 200 indicators such as ADX, MACD, RSI, Stochastic, Bollinger Bands, and  Candlestick pattern recognition.&lt;br /&gt;&lt;br /&gt;The library was written in C++, but its developers made a very intelligent decision by creating a C wrapper, to allow other languages interact with it. Here I'll show an example of how you can calculate &lt;a href="http://www.bollingerbands.com/"&gt;Bollinger Bands&lt;/a&gt; using this library and FreePascal.&lt;br /&gt;&lt;br /&gt;Let's download and uncompress the library.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cd ~/&lt;br /&gt;wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz&lt;br /&gt;tar xvfz ta-lib-0.4.0-src.tar.gz&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will create a ta-lib directory in our home. For example in /home/leonardo/ta-lib.&lt;br /&gt;&lt;br /&gt;Now, we need to create a shared library from its sources.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cd ~/ta-lib&lt;br /&gt;./configure&lt;br /&gt;make&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all, the files "src/libta_lib.la" and "src/.libs/libta_lib.so" were created.&lt;br /&gt;&lt;br /&gt;Now, let's create a new directory for our FreePascal program.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;mkdir ~/fpc-talib&lt;br /&gt;cd ~/fpc-talib&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and copy libta_lib.so to that new directory.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;cp ~/ta-lib/src/.libs/libta_lib.so ~/fpc-talib&lt;br /&gt;cd ~/fpc-talib&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Before creating our first FPC/ta_lib program, I recommend reading the &lt;a href="http://ta-lib.org/d_api/d_api.html"&gt;TA-Lib API Documentation&lt;/a&gt;, and also taking a look at the functions from the "src/ta_func" directory.&lt;br /&gt;&lt;br /&gt;Now, let's take a look at the function TA_BBANDS. It allows us to calculate the Bollinger Bands for a serie of closing prices.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;TA_RetCode TA_BBANDS( int    startIdx,&lt;br /&gt;                      int    endIdx,&lt;br /&gt;                      const double inReal[],&lt;br /&gt;                      int           optInTimePeriod,&lt;br /&gt;                      double        optInNbDevUp, &lt;br /&gt;                      double        optInNbDevDn, &lt;br /&gt;                      TA_MAType     optInMAType,&lt;br /&gt;                      int          *outBegIdx,&lt;br /&gt;                      int          *outNBElement,&lt;br /&gt;                      double        outRealUpperBand[],&lt;br /&gt;                      double        outRealMiddleBand[],&lt;br /&gt;                      double        outRealLowerBand[] )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The function's input is an array of double, containing for example the closing prices of XYZ stock for the last year. And its outputs are three arrays of double, of the same length as the input, containing the points that define the upper, middle and lower Bollinger bands.&lt;br /&gt;&lt;br /&gt;To be able to work with this function, we must create a Pascal version of its interface:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;  TTA_BBANDS = function(&lt;br /&gt;      startIdx, endIdx: Integer;&lt;br /&gt;      const inReal: Array of Double;&lt;br /&gt;      optInTimePeriod: Integer;&lt;br /&gt;      optInNbDevUp, optInNbDevDn: Double;&lt;br /&gt;      optInMAType: TTA_MAType;&lt;br /&gt;      out outBegIdx: Integer;&lt;br /&gt;      out outNBElement: Integer;&lt;br /&gt;      outRealUpperBand: Array of Double;&lt;br /&gt;      outRealMiddleBand: Array of Double;&lt;br /&gt;      outRealLowerBand: Array of Double&lt;br /&gt;    ): Integer; cdecl;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well, I must admit it took me a whole afternoon to figure out that the function needed those "out" params, but after that, everything was pretty smooth. &lt;br /&gt;&lt;br /&gt;Show me a complete example!&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: shell"&gt;&lt;br /&gt;unit formulas;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  Classes, SysUtils,&lt;br /&gt;  DynLibs;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;  TTA_MAType = (TA_MAType_SMA, TA_MAType_EMA, TA_MAType_WMA, TA_MAType_DEMA,&lt;br /&gt;     TA_MAType_TEMA, TA_MAType_TRIMA, TA_MAType_KAMA, TA_MAType_MAMA, TA_MAType_T3);&lt;br /&gt;&lt;br /&gt;  TTA_BBANDS = function(&lt;br /&gt;      startIdx, endIdx: Integer;&lt;br /&gt;      const inReal: Array of Double;&lt;br /&gt;      optInTimePeriod: Integer;&lt;br /&gt;      optInNbDevUp, optInNbDevDn: Double;&lt;br /&gt;      optInMAType: TTA_MAType;&lt;br /&gt;      out outBegIdx: Integer;&lt;br /&gt;      out outNBElement: Integer;&lt;br /&gt;      outRealUpperBand: Array of Double;&lt;br /&gt;      outRealMiddleBand: Array of Double;&lt;br /&gt;      outRealLowerBand: Array of Double&lt;br /&gt;    ): Integer; cdecl;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  lHandle: TLibHandle;&lt;br /&gt;  lBBands: TTA_BBANDS;&lt;br /&gt;  lInReal: Array of double;&lt;br /&gt;  lOutBegIdx: Integer;&lt;br /&gt;  lOutNBElement: Integer;&lt;br /&gt;  lOutRealUpper: Array of double;&lt;br /&gt;  lOutRealMiddle: Array of double;&lt;br /&gt;  lOutRealLower: Array of double;&lt;br /&gt;  lRes: Integer;&lt;br /&gt;  I: Integer;&lt;br /&gt;  lLowerVol: Double;&lt;br /&gt;  lSqueeze: Integer;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  lHandle := LoadLibrary('/home/leonardo/fpc-talib/libta_lib.so');&lt;br /&gt;  if lHandle &lt;&gt; 0 then&lt;br /&gt;  begin&lt;br /&gt;    Pointer(lBBands) := GetProcAddress(lHandle, 'TA_BBANDS');&lt;br /&gt;    if @lBBands &lt;&gt; nil then&lt;br /&gt;    begin&lt;br /&gt;      (* lInReal is the array of Closing prices containing &lt;br /&gt;         the last 100 days for XYZ stock *)&lt;br /&gt;      SetLength(lInReal, 100);&lt;br /&gt;      lInReal[0] := 10.50;&lt;br /&gt;      lInReal[1] := 10.75;&lt;br /&gt;      lInReal[2] := 11.25;&lt;br /&gt;      //..&lt;br /&gt;      //.. and so on to the 99nth element.&lt;br /&gt;      //..&lt;br /&gt;      lInReal[99] := 8.35;&lt;br /&gt;&lt;br /&gt;      SetLength(lOutRealUpper, Length(lInReal)); // the same length as lInReal&lt;br /&gt;      SetLength(lOutRealMiddle, Length(lInReal));// the same length as lInReal&lt;br /&gt;      SetLength(lOutRealLower, Length(lInReal));// the same length as lInReal&lt;br /&gt;&lt;br /&gt;      lRes := lBBands(&lt;br /&gt;        0, AQuotes.Count - 1, lInReal, APeriods, 2, 2, TA_MAType_SMA,&lt;br /&gt;        lOutBegIdx, lOutNBElement, lOutRealUpper, lOutRealMiddle, lOutRealLower);&lt;br /&gt;&lt;br /&gt;      lLowerVol := 0;&lt;br /&gt;      for I := 0 to Length(lInReal) - 1 do&lt;br /&gt;      begin&lt;br /&gt;        writeln(&lt;br /&gt;          Format('Close %f - Upper BBand %f - Middle BBand %f - Lower BBand %f', &lt;br /&gt;          [lInReal[I], lOutRealUpper[I], lOutRealMiddle[I], lOutRealLower[I]])&lt;br /&gt;        );&lt;br /&gt;     end;&lt;br /&gt;    end;&lt;br /&gt;    UnloadLibrary(lHandle);&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that's it.&lt;br /&gt;&lt;br /&gt;Following the same approach, I created a home-made trading system based on Bollinger Bands and Option Straddles that is performing pretty well!. I'm very seriously thinking of creating an investment fund, what about "The FreePascal Investment Fund" ? :). See you in my next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-1562024798908852008?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/1562024798908852008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=1562024798908852008' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1562024798908852008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1562024798908852008'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2011/03/technical-analysis-with-freepascal-and.html' title='Technical analysis with FreePascal and TA-lib'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-3424278860877154368</id><published>2011-02-16T06:15:00.000-08:00</published><updated>2011-02-16T17:30:13.776-08:00</updated><title type='text'>Working with C++ dll's from ObjectPascal</title><content type='html'>It's a well known fact that there are many excelent libraries written in C++, and we, as Object Pascal programmers can take advantage of them. &lt;br /&gt;&lt;br /&gt;Many times, I have found in forums or mailing lists, questions such as "How can I translate XYZ library to ObjectPascal?", or "How can I use a C++ from Object Pascal?". Well, it's not necessary to translate a library from C++, we can use it just by creating a C wrapper around it.&lt;br /&gt;&lt;br /&gt;Why I need to wrap the C++ library to C?&lt;br /&gt;&lt;br /&gt;Because of name mangling, if you look inside the library (using TDump included in Delphi, or by using "nm" on linux), you'll get something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;000DB314  794 0319 _ZN12DcmAgeStringC1ERKS_&lt;br /&gt;000DB274  795 031A _ZN12DcmAgeStringC2ERK6DcmTagm&lt;br /&gt;000DB2F0  796 031B _ZN12DcmAgeStringC2ERKS_&lt;br /&gt;000DB398  797 031C _ZN12DcmAgeStringD0Ev&lt;br /&gt;000DB368  798 031D _ZN12DcmAgeStringD1Ev&lt;br /&gt;000DB338  799 031E _ZN12DcmAgeStringD2Ev&lt;br /&gt;000DB3C8  800 031F _ZN12DcmAgeStringaSERKS_&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the snippet from above, you can see names like _ZN12DcmAgeStringaSERKS. Those names may reffer to functions, methods, constants or variables we can call from Object Pascal by using GetProcAddress. The name in the example, is the "mangled" version of DcmAgeStringa. &lt;br /&gt;&lt;br /&gt;If the C++ doesn't expose classes, but functions, you can call the functions just by passing the mangled name to GetProcAddress, but what about working with classes?.&lt;br /&gt;&lt;br /&gt;There's no way to directly use a C++ class from Object Pascal, before using it is mandatory to create a "C" wrapper around it.&lt;br /&gt;&lt;br /&gt;Let's start by defining a simple C++ class with only one method:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include &lt;iostream&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;class Test&lt;br /&gt;{&lt;br /&gt;  public:&lt;br /&gt;    Test();&lt;br /&gt;    void getHello(char * &amp;AString); &lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Test::Test()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void Test::getHello(char * &amp;AString)&lt;br /&gt;{&lt;br /&gt;  const char * str = "Hello World from C++";&lt;br /&gt;  strcpy(AString, str);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The class above, can be used with something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;int main(){&lt;br /&gt;  Test * t  = new Test();&lt;br /&gt;  char * str;&lt;br /&gt;  t-&gt;getHello(str);&lt;br /&gt;  cout &lt;&lt; str;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It just gets change the value of the string "str" and print its contents.&lt;br /&gt;&lt;br /&gt;As I mentioned earlier, there's no way to create an instance of a C++ class from Object Pascal, but there's a workaround, to create a "C" wrapper around it, and export it from the library.&lt;br /&gt;&lt;br /&gt;Creating our first "C" wrapper around a C++ class&lt;br /&gt;&lt;br /&gt;The idea is to create plain C functions in charge of creating an instance of the Test class, then return a pointer to that instance, then create another function that receive the pointer as param, and use its getHello function.&lt;br /&gt;&lt;br /&gt;Here's the code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: cpp"&gt;&lt;br /&gt;#include &lt;cstring&gt;&lt;br /&gt;#include &lt;iostream&gt;&lt;br /&gt;using namespace std;&lt;br /&gt;&lt;br /&gt;class Test&lt;br /&gt;{&lt;br /&gt;  public:&lt;br /&gt;    Test();&lt;br /&gt;    void getHello(char * &amp;AString); &lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Test::Test()&lt;br /&gt;{&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;void Test::getHello(char * &amp;AString)&lt;br /&gt;{&lt;br /&gt;  const char * str = "Hello World from C++";&lt;br /&gt;  strcpy(AString, str);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;extern "C"{&lt;br /&gt;  void createTestInstance(void * &amp;instance)&lt;br /&gt;  {&lt;br /&gt;    instance = new Test();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  void doHello(void * instance, char * &amp;AString)&lt;br /&gt;  {&lt;br /&gt;    Test * lInstance = (Test *) instance;&lt;br /&gt;    lInstance-&gt;getHello(AString);&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  void deleteTestInstance(void  * instance)&lt;br /&gt;  {&lt;br /&gt;    delete (Test *)instance;&lt;br /&gt;    instance = NULL;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you look at the snippet above, you can see I added code inside an extern "C" block, there will reside the exported functions. I added three functions to create an instance of our class, to use the method getHello, and one to delete the instance.&lt;br /&gt;&lt;br /&gt;To create a dll from this code, just do this (I'm using Linux here, but you can use MinGW from Windows using the same command):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;g++ -fPIC -shared test.cpp -o test.so&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That command compiles our code and creates a shared object file, in Windows you must replace "-o test.so" with "-o test.dll".&lt;br /&gt;&lt;br /&gt;Now you can check the contents of the library by using nm or tdump. The result should be something like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;...&lt;br /&gt;0000000000000c08 T _fini&lt;br /&gt;00000000000008b0 T _init&lt;br /&gt;0000000000000990 t call_gmon_start&lt;br /&gt;0000000000201068 b completed.7424&lt;br /&gt;0000000000000ac5 T createTestInstance&lt;br /&gt;0000000000000b4e T deleteTestInstance&lt;br /&gt;0000000000000b21 T doHello&lt;br /&gt;0000000000201070 b dtor_idx.7426&lt;br /&gt;0000000000000a30 t frame_dummy&lt;br /&gt;                 U strcpy@@GLIBC_2.2.5&lt;br /&gt;                 U strlen@@GLIBC_2.2.5&lt;br /&gt;...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can see, that createTestInstance, deleteTestInstance and doHello now aren't mangled as in the plain C++ library. &lt;br /&gt;&lt;br /&gt;Now the fun part!&lt;br /&gt;&lt;br /&gt;The last step of this journey, is to create an Object Pascal program that loads and use the shared library. The program should do this:&lt;br /&gt;&lt;br /&gt;1) Load the library and store a reference to its handler.&lt;br /&gt;2) Execute the method createTestInstance and store a reference to the pointer it returns.&lt;br /&gt;3) Execute the method doHello by passing the pointer stored in point 2 as parameter.&lt;br /&gt;4) Delete the instance by executing deleteTestInstance and passing the same pointer.&lt;br /&gt;&lt;br /&gt;Here's the code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;program Test;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  dynlibs;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;  TCreateInstance = procedure (var AInstance: Pointer); cdecl;&lt;br /&gt;  TdoHello = procedure (AInstance: Pointer; var AString: PAnsiChar); cdecl;&lt;br /&gt;  TDeleteInstance = procedure (AInstance: Pointer); cdecl;&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  lCreateInstance: TCreateInstance;&lt;br /&gt;  ldoHello: TdoHello;&lt;br /&gt;  lDeleteInstance: TDeleteInstance; &lt;br /&gt;  lHandle: TLibHandle;&lt;br /&gt;  lInstance: Pointer;&lt;br /&gt;  lStr: PAnsiChar;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  lHandle := LoadLibrary('./test.so');&lt;br /&gt;  if lHandle &lt;&gt; NILHandle then&lt;br /&gt;  begin&lt;br /&gt;    writeln('Library loaded successfully!.');&lt;br /&gt;    lInstance := nil;&lt;br /&gt;    // First, create the instance&lt;br /&gt;    Pointer(lCreateInstance) := GetProcAddress(lHandle, 'createTestInstance');&lt;br /&gt;    if @lCreateInstance &lt;&gt; nil then&lt;br /&gt;      lCreateInstance(lInstance);&lt;br /&gt;    // Second, use the instance&lt;br /&gt;    Pointer(ldoHello) := GetProcAddress(lHandle, 'doHello');&lt;br /&gt;    if @ldoHello &lt;&gt; nil then&lt;br /&gt;    begin&lt;br /&gt;      GetMem(lStr, 255);&lt;br /&gt;      ldoHello(lInstance, lStr);&lt;br /&gt;      writeln(lStr);&lt;br /&gt;      FreeMem(lStr);&lt;br /&gt;    end;&lt;br /&gt;    // Third, delete the instance and unload the library&lt;br /&gt;    Pointer(lDeleteInstance) := GetProcAddress(lHandle, 'deleteTestInstance');&lt;br /&gt;    if @lDeleteInstance &lt;&gt; nil then&lt;br /&gt;    begin&lt;br /&gt;      lDeleteInstance(lInstance);&lt;br /&gt;      UnloadLibrary(lHandle);&lt;br /&gt;    end;&lt;br /&gt;    writeln('Done.');&lt;br /&gt;  end&lt;br /&gt;  else&lt;br /&gt;    writeln('Cannot load library.');&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;That's all. Now you can use all your loved C++ libraries from Object Pascal!.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-3424278860877154368?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/3424278860877154368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=3424278860877154368' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3424278860877154368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3424278860877154368'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2011/02/working-with-c-dlls-from-objectpascal.html' title='Working with C++ dll&apos;s from ObjectPascal'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-877826242956854435</id><published>2010-10-21T11:57:00.000-07:00</published><updated>2010-10-21T12:03:20.720-07:00</updated><title type='text'>Web 2.0 programming with Object Pascal (Part 3)</title><content type='html'>&lt;span style="font-weight:bold;"&gt;Debugging CGI applications&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If you build web applications, surely often you'll face the need of a way to debug them. Remember that CGI or FastCGI applications run inside the Web Server, and you can't debug them by placing breakpoints in your Lazarus code.&lt;br /&gt;&lt;br /&gt;The easiest way I found to debug web applications is by using the "dbugintf" unit, provided by FreePascal. This unit, according to the FCL documentation can be used "..to add debug messages to your application. The messages are not sent to standard output, but are sent to a debug server process which collects messages from various clients and displays them somehow on screen", both, FreePascal and Lazarus include example debug servers to cath messages generated in apps.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;How to use dbugintf unit&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is easy, just add dbugintf to the "uses" of the units you want to debug, then use SendDebug to send messages to the debug server.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Example:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Using Lazarus, go to Project -&gt; New Project -&gt; CGI Application to create a new CGI application, then go to the FPWebModule1 automatically created by Lazarus, and then to the Object Inspector, select Events and double click on the "OnRequest" event handler to create the skeleton for this handler, then adapt it to look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;unit Unit1; &lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  dbugintf,&lt;br /&gt;  Classes, SysUtils, FileUtil, HTTPDefs, websession, fpHTTP, fpWeb; &lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;&lt;br /&gt;  { TFPWebModule1 }&lt;br /&gt;&lt;br /&gt;  TFPWebModule1 = class(TFPWebModule)&lt;br /&gt;    procedure DataModuleRequest(Sender: TObject; ARequest: TRequest;&lt;br /&gt;      AResponse: TResponse; var Handled: Boolean);&lt;br /&gt;  private&lt;br /&gt;    { private declarations }&lt;br /&gt;  public&lt;br /&gt;    { public declarations }&lt;br /&gt;  end; &lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  FPWebModule1: TFPWebModule1; &lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;{$R *.lfm}&lt;br /&gt;&lt;br /&gt;{ TFPWebModule1 }&lt;br /&gt;&lt;br /&gt;procedure TFPWebModule1.DataModuleRequest(Sender: TObject; ARequest: TRequest;&lt;br /&gt;  AResponse: TResponse; var Handled: Boolean);&lt;br /&gt;begin&lt;br /&gt;  AResponse.Content := 'Hello!';&lt;br /&gt;  SendDebug('---&gt;Debug this!&lt;---');&lt;br /&gt;  Handled := True;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;initialization&lt;br /&gt;  RegisterHTTPModule('TFPWebModule1', TFPWebModule1); &lt;br /&gt;end. &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then compile the program, and copy it to your web server's cgi-bin directory and test using your web browser. If everything is ok, you'll note just a web page containing the text "Hello!".&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Introducing debugserver&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As I explained at the beginning of this article, to catch the debugging messages you need to use a debug server. In the sources of Lazarus you can find a tool called "debugserver" placed in lazarus-sources/tools/debugserver, open it with Lazarus and compile, then Run.&lt;br /&gt;&lt;br /&gt;In Unix/Linux, After running the debugserver, you'll note it creates the file /tmp/debugserver. In my case, that file is created with 600 permissions, that is, read only. To let the cgi program write to that file, just do this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;sudo chmod 666 /tmp/debugserver&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now you can try again your program using the web browser, and look at the debugserver screen, it should add the message "---&gt;Debug this!&lt;---" to the messages list.&lt;br /&gt;&lt;br /&gt;That's all, hopefully you find this interesting.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-877826242956854435?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/877826242956854435/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=877826242956854435' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/877826242956854435'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/877826242956854435'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2010/10/web-20-programming-with-object-pascal.html' title='Web 2.0 programming with Object Pascal (Part 3)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-8125969019083079231</id><published>2010-08-19T04:56:00.000-07:00</published><updated>2010-08-19T05:01:42.134-07:00</updated><title type='text'>FreePascal 64bits Port for FreeBSD 8.x</title><content type='html'>Since a couple of days, the users of FreeBSD 8.x can enjoy developing with FPC 2.4.0 64bits!.&lt;br /&gt;&lt;br /&gt;Just update the ports tree and compile FPC.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;portsnap fetch update&lt;br /&gt;cd /usr/ports/lang/fpc&lt;br /&gt;make install clean&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you want to compile the bleeding edge 2.5.1, you can start with this compiler and follow the same instructions as &lt;a href="http://leonardorame.blogspot.com/2010/07/installing-freepascal-251-on-freebsd-8x.html"&gt;my last post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thats all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-8125969019083079231?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/8125969019083079231/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=8125969019083079231' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8125969019083079231'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8125969019083079231'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2010/08/freepascal-64bits-port-for-freebsd-8x.html' title='FreePascal 64bits Port for FreeBSD 8.x'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5822052095412781384</id><published>2010-07-18T15:43:00.000-07:00</published><updated>2011-03-02T07:22:37.995-08:00</updated><title type='text'>Installing FreePascal 2.5.1 on FreeBsd 8.x-i386</title><content type='html'>The other day my Ubuntu 9.10 home server broke my /boot partition and Grub2 wasn't able to boot, I don't know why this happened, I tried to reinstall Grub2 using &lt;a href="http://www.sysresccd.org"&gt;SystemRescueCD&lt;/a&gt; and lost one complete day trying, but it didn't work, so I thaught it whould be the time to try FreeBSD 8.1-RC2.&lt;br /&gt;&lt;br /&gt;This server's purpouse is mainly to act as a file server, it also has MySql and Firebird installed for my Delphi/FPC applications, Apache for testing my customer's web sites before deploying, and SVN.&lt;br /&gt;&lt;br /&gt;After installing the basics (Apache, database servers, SVN, SSH, VIM, etc.) I allways install FPC to compile my CGI apps inside the server. I'm sure I'm not the only one who needs to compile FPC from scratch, therefore, here is my step by step guide.&lt;br /&gt;&lt;br /&gt;The server is and AMD64, but as I didn't find an FPC 2.4.0 executable for FreeBSD x86_64 I had to settle on the i386 version for now :(.&lt;br /&gt;&lt;br /&gt;This guide assumes you are in /home/USER/dev, where USER is YOU!, in my case, USER=leonardo, so let's position in that place:&lt;br /&gt;&lt;pre&gt;cd /home/leonardo/dev&lt;/pre&gt;&lt;br /&gt;Before you can compile the compiler code, you'll need an executable FPC for your platform, this process is called bootstrapping, in my case I needed a 2.4.1 compiler for FreeBsd i386, so I downloaded it from sourceforge using this command:&lt;br /&gt;&lt;pre&gt;wget http://downloads.sourceforge.net/project/freepascal/Bootstrap/2.4.0/ppc386-freebsd-7.bz2&lt;/pre&gt;&lt;br /&gt;After donwloading it, I had to uncompress (it's bzipped), change its name to ppc386 and changed it mode to executable:&lt;br /&gt;&lt;pre&gt;bzip2 -d ppc386-freebsd-7.bz2&lt;br /&gt;mv ppc386-freebsd-7 ppc386&lt;br /&gt;chmod +x ppc386&lt;/pre&gt;&lt;br /&gt;If you don't have subversion installed you'll have to install it with this command:&lt;br /&gt;&lt;pre&gt;sudo pkg_add -r subversion&lt;/pre&gt;&lt;br /&gt;Now get the source code of FPC, in this case, the trunk version (2.5.1) in the "fpc" directory.&lt;br /&gt;&lt;pre&gt;svn checkout http://svn.freepascal.org/svn/fpc/trunk fpc&lt;/pre&gt;&lt;br /&gt;One difference between FreeBSD and Linux version of FPC is that you have to use "gmake" instead of "make" to build the compiler, that's because FreeBSD's make isn't GNU Make as in Linux. Ok, just use gmake and you'll be ok:&lt;br /&gt;&lt;pre&gt;gmake all PP=/home/leonardo/dev/ppc386&lt;br /&gt;sudo gmake install PP=/home/leonardo/dev/ppc386&lt;/pre&gt;&lt;br /&gt;After gmake install, the compiler files are in /usr/local/lib/fpc/2.5.1, then you have to move to that directory to create an fpc.cfg:&lt;br /&gt;&lt;pre&gt;cd /usr/local/lib/fpc/2.5.1/&lt;br /&gt;sudo ./samplecfg /usr/local/lib/fpc/2.5.1 /usr/local/etc&lt;/pre&gt;&lt;br /&gt;That's it, now you can create modern Object Pascal programs on FreeBSD!.&lt;br /&gt;&lt;br /&gt;P.S.: From Jul 24 up to Aug 13 I'll be in Europe!, in France, Italy and Spain. If you want to meet me to chat about Delphi/FPC, please drop me an email to arrange the meeting.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5822052095412781384?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5822052095412781384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5822052095412781384' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5822052095412781384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5822052095412781384'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2010/07/installing-freepascal-251-on-freebsd-8x.html' title='Installing FreePascal 2.5.1 on FreeBsd 8.x-i386'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-8489140635577821170</id><published>2010-03-21T13:04:00.000-07:00</published><updated>2010-03-21T13:49:31.633-07:00</updated><title type='text'>Web 2.0 programming with Object Pascal (Part 2)</title><content type='html'>As I promised in my last article, here I'll show you how to add CRUD (Create, Read, Update and Delete) operations to the sample application.&lt;br /&gt;&lt;br /&gt;The first step is to add a toolbar to the Grid, with three buttons, btnAdd, btnEdit and btnDelete in charge of Inserting, Updating and Deleting data. Also I'll create a new popup form where the user will work with that data.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The new Grid&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Instead of overwriting the files used in the last example, I recommend to create a new directory called samples2, containing all the files showed in this article.&lt;br /&gt;&lt;br /&gt;NOTE: the file grid2.html, has the same contents of grid1.html, so just copy it from the last example and paste in the new directory, and don't forget to rename to grid2.html. After that, you'll have to rename the div id "grid1" by "grid2".&lt;br /&gt;&lt;br /&gt;This is the code for grid2.js:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: javascript"&gt;&lt;br /&gt;Ext.onReady(function(){&lt;br /&gt;&lt;br /&gt;  var dataStore = new Ext.data.JsonStore({&lt;br /&gt;      //url: '/samples/customerslist.json',&lt;br /&gt;      url: '/cgi-bin/customerslist',&lt;br /&gt;      root: 'rows',&lt;br /&gt;      method: 'GET',&lt;br /&gt;      fields: [&lt;br /&gt;        {name: 'id', type: 'int'},&lt;br /&gt;        {name: 'firstname', type: 'string'},&lt;br /&gt;        {name: 'lastname', type: 'string'},&lt;br /&gt;        {name: 'age', type: 'int'},&lt;br /&gt;        {name: 'phone', type: 'string'}&lt;br /&gt;      ]&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;  var btnAdd = new Ext.Toolbar.Button({&lt;br /&gt;    text: 'Add',&lt;br /&gt;    handler: function(){&lt;br /&gt;      var win = new MyPeopleWindow();&lt;br /&gt;      // to refresh the grid after Insert&lt;br /&gt;      win.afterPost = function(){&lt;br /&gt;        dataStore.load();&lt;br /&gt;      };&lt;br /&gt;     win.show();&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  var btnEdit = new Ext.Toolbar.Button({&lt;br /&gt;    text: 'Edit',&lt;br /&gt;    handler: function(){&lt;br /&gt;      var win = new MyPeopleWindow(selRecordStore.id);&lt;br /&gt;      // to refresh the grid after Update&lt;br /&gt;      win.afterPost = function(){&lt;br /&gt;        dataStore.load();&lt;br /&gt;      };&lt;br /&gt;      win.show();&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  var btnDelete = new Ext.Toolbar.Button({&lt;br /&gt;    text: 'Delete',&lt;br /&gt;    handler: function(){&lt;br /&gt;      Ext.Msg.confirm(&lt;br /&gt;        'Delete customer?', &lt;br /&gt;        'Are you sure to delete this customer?',&lt;br /&gt;        function(btn){&lt;br /&gt;          if(btn == 'yes'){&lt;br /&gt;            var conn = new Ext.data.Connection();&lt;br /&gt;            conn.request({&lt;br /&gt;                url: '/cgi-bin/customerslist',&lt;br /&gt;                method: 'POST',&lt;br /&gt;                params: {"delete_person": selRecordStore.id},&lt;br /&gt;                success: function(response, options) {&lt;br /&gt;                    // refresh the grid after Delete&lt;br /&gt;                    JSonData = Ext.util.JSON.decode(response.responseText);&lt;br /&gt;                    if(JSonData.success)&lt;br /&gt;                      dataStore.load();&lt;br /&gt;                    else&lt;br /&gt;                      Ext.Msg.alert('Status', JSonData.failure);&lt;br /&gt;                },&lt;br /&gt;                failure: function(response, options) {&lt;br /&gt;                    Ext.Msg.alert('Status', 'An error ocurred while trying to delete this customer.');&lt;br /&gt;                }&lt;br /&gt;            });&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      );&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  var myGrid1 = new Ext.grid.GridPanel({&lt;br /&gt;    id: 'customerslist',&lt;br /&gt;    store: dataStore,&lt;br /&gt;    columns: [&lt;br /&gt;      {header: "First Name", width: 100, dataIndex: "firstname", sortable: true},&lt;br /&gt;      {header: "Last Name", width: 100, dataIndex: "lastname", sortable: true},&lt;br /&gt;      {header: "Age", width: 100, dataIndex: "age", sortable: true},&lt;br /&gt;      {header: "Phone", width: 100, dataIndex: "phone", sortable: true}&lt;br /&gt;    ],&lt;br /&gt;    sm: new Ext.grid.RowSelectionModel({&lt;br /&gt;      singleSelect: true,&lt;br /&gt;      listeners: {&lt;br /&gt;        rowselect: function(smObj, rowIndex, record){&lt;br /&gt;          selRecordStore = record;&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;    }),&lt;br /&gt;    tbar: [&lt;br /&gt;      btnAdd,&lt;br /&gt;      btnEdit,&lt;br /&gt;      btnDelete&lt;br /&gt;    ],&lt;br /&gt;    autoLoad: false,&lt;br /&gt;    stripeRows: true,&lt;br /&gt;    height: 200,&lt;br /&gt;    width: 500&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  dataStore.load();&lt;br /&gt;&lt;br /&gt;  myGrid1.render('grid2');&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, the editor form:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: javascript"&gt;&lt;br /&gt;MyPeopleForm = Ext.extend(Ext.FormPanel, {&lt;br /&gt;    initComponent: function(){&lt;br /&gt;      Ext.apply(this, {&lt;br /&gt;        border:false,&lt;br /&gt;        labelWidth: 80,&lt;br /&gt;        defaults: {&lt;br /&gt;          xtype:'textfield',&lt;br /&gt;          width: 150&lt;br /&gt;        },&lt;br /&gt;        items:[&lt;br /&gt;          {xtype:'numberfield',fieldLabel:'Id',name:'id'},&lt;br /&gt;          {fieldLabel:'First Name',name:'firstname'},&lt;br /&gt;          {fieldLabel:'Last Name',name:'lastname'},&lt;br /&gt;          {xtype:'numberfield',fieldLabel:'Age',name:'age'},&lt;br /&gt;          {fieldLabel:'Phone',name:'phone'}&lt;br /&gt;        ]&lt;br /&gt;      });&lt;br /&gt;      MyPeopleForm.superclass.initComponent.call(this, arguments);&lt;br /&gt;    },&lt;br /&gt;    setId: function(idPerson) {&lt;br /&gt;      this.load(&lt;br /&gt;        {&lt;br /&gt;          method: 'POST', &lt;br /&gt;          url: '/cgi-bin/customerslist', &lt;br /&gt;          params: {'idperson': idPerson}&lt;br /&gt;        }&lt;br /&gt;      );&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  MyPeopleWindow = Ext.extend(Ext.Window, {&lt;br /&gt;    constructor: function(idPerson){&lt;br /&gt;      MyPeopleWindow.superclass.constructor.call(this, this.config);&lt;br /&gt;      // if idPerson is not null, then edit record&lt;br /&gt;      // otherwise it's a new record&lt;br /&gt;      if(idPerson != null)&lt;br /&gt;        this.form.setId(idPerson);&lt;br /&gt;    },&lt;br /&gt;    afterPost: function(){&lt;br /&gt;      this.fireEvent('afterPost', this);&lt;br /&gt;    },&lt;br /&gt;    initComponent: function(){&lt;br /&gt;      Ext.apply(this, {&lt;br /&gt;        title: 'Loading data into a form',&lt;br /&gt;        bodyStyle: 'padding:10px;background-color:#fff;',&lt;br /&gt;        width:300,&lt;br /&gt;        height:270,&lt;br /&gt;        closeAction: 'close',&lt;br /&gt;        items: [ this.form = new MyPeopleForm() ],&lt;br /&gt;        buttons: [&lt;br /&gt;          {&lt;br /&gt;            text:'Save',&lt;br /&gt;            scope: this,&lt;br /&gt;            handler: function(){&lt;br /&gt;              this.form.getForm().submit({&lt;br /&gt;                scope: this,&lt;br /&gt;                url: '/cgi-bin/customerslist',&lt;br /&gt;                method: 'POST',&lt;br /&gt;                // here I add the param save_person&lt;br /&gt;                // to let the cgi program decide&lt;br /&gt;                // a course of action (save person data in this case).&lt;br /&gt;                params: {'save_person':'true'},&lt;br /&gt;                success: function(form, action){&lt;br /&gt;                  // on success I just close the form &lt;br /&gt;                  this.afterPost();&lt;br /&gt;                  this.close();&lt;br /&gt;                },&lt;br /&gt;                failure: function(form, action){&lt;br /&gt;                  Ext.Msg.alert("Error","There was an error processing your request\n" + action.result.message);&lt;br /&gt;                }&lt;br /&gt;              });&lt;br /&gt;            }&lt;br /&gt;          },&lt;br /&gt;          {&lt;br /&gt;            text:'Cancel',&lt;br /&gt;            handler: function(){this.close();},&lt;br /&gt;            // important!, without "scope: this" &lt;br /&gt;            // calling this.close() will try to close the Button!,&lt;br /&gt;            // and we need to close the Window, NOT the button.&lt;br /&gt;            scope: this           &lt;br /&gt;          }&lt;br /&gt;        ]&lt;br /&gt;      });&lt;br /&gt;      MyPeopleWindow.superclass.initComponent.call(this, arguments);&lt;br /&gt;    }&lt;br /&gt;  });&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all for the UI part. Now let's create our new customerslist.pp file, containing all the data required for the CGI application.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;program cgiproject1;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  Classes,SysUtils,&lt;br /&gt;  httpDefs,custcgi, // needed for creating CGI applications&lt;br /&gt;  fpjson, // needed for dealing with JSon data&lt;br /&gt;  Db, SqlDb, ibconnection; // needed for connecting to Firebird/Interbase;&lt;br /&gt;&lt;br /&gt;Type&lt;br /&gt;  TCGIApp = Class(TCustomCGIApplication)&lt;br /&gt;  Private&lt;br /&gt;    FConn: TSqlConnection;&lt;br /&gt;    FQuery: TSqlQuery;&lt;br /&gt;    FTransaction: TSqlTransaction;&lt;br /&gt;    procedure ConnectToDataBase;&lt;br /&gt;    function GetCustomersList: string;&lt;br /&gt;    function GetCustomer(AIdPerson: string): string;&lt;br /&gt;    procedure FillJSONObject(AJson: TJsonObject);&lt;br /&gt;    function SavePerson(ARequest: TRequest): string;&lt;br /&gt;    function DeletePerson(ARequest: TRequest): string;&lt;br /&gt;  Public&lt;br /&gt;    Procedure HandleRequest(ARequest : Trequest; AResponse : TResponse); override;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;procedure TCGIApp.ConnectToDataBase;&lt;br /&gt;begin&lt;br /&gt;  FConn := TIBConnection.Create(nil);&lt;br /&gt;  FQuery := TSqlQuery.Create(nil);&lt;br /&gt;  FTransaction := TSqlTransaction.Create(nil);&lt;br /&gt;  with FConn do&lt;br /&gt;  begin&lt;br /&gt;    DatabaseName := 'TARJETA';&lt;br /&gt;    UserName := 'SYSDBA';&lt;br /&gt;    Password := 'masterkey';&lt;br /&gt;    HostName := '192.168.1.254';&lt;br /&gt;    Connected := True;&lt;br /&gt;    Transaction := FTransaction;&lt;br /&gt;    FQuery.Database := FConn;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TCGIApp.FillJSONObject(AJson: TJsonObject);&lt;br /&gt;begin&lt;br /&gt;  AJson.Add('id', TJsonIntegerNumber.Create(FQuery.FieldByName('IdCliente').AsInteger));&lt;br /&gt;  AJson.Add('firstname', TJsonString.Create(FQuery.FieldByName('Apellido').AsString));&lt;br /&gt;  AJson.Add('lastname', TJsonString.Create(FQuery.FieldByName('Nombres').AsString));&lt;br /&gt;  AJson.Add('age', TJSONIntegerNumber.Create(FQuery.FieldByName('IdCliente').AsInteger));&lt;br /&gt;  AJson.Add('phone', TJsonString.Create(FQuery.FieldByName('TelFijo').AsString));&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TCGIApp.GetCustomersList: string;&lt;br /&gt;var&lt;br /&gt;  lPerson: TJSONObject;&lt;br /&gt;  lJson: TJSONObject;&lt;br /&gt;  lJsonArray: TJSONArray;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  (* Query the database *)&lt;br /&gt;  FQuery.Close;&lt;br /&gt;  FQuery.Sql.Text := 'select * from clientes';&lt;br /&gt;  FQuery.Open;&lt;br /&gt;  FQuery.First;&lt;br /&gt;&lt;br /&gt;  lJsonArray := TJSONArray.Create;&lt;br /&gt;  lJson := TJSONObject.Create;&lt;br /&gt;  try&lt;br /&gt;    while not FQuery.Eof do&lt;br /&gt;    begin&lt;br /&gt;      lPerson := TJSONObject.Create;&lt;br /&gt;      fillJsonObject(lPerson);&lt;br /&gt;      FQuery.Next;&lt;br /&gt;      (* Fill the array *)&lt;br /&gt;      lJsonArray.Add(lPerson);&lt;br /&gt;    end;&lt;br /&gt;    (* Add the array to rows property *)&lt;br /&gt;    lJson.Add('rows', lJsonArray);&lt;br /&gt;    Result := lJson.AsJSON;&lt;br /&gt;  finally&lt;br /&gt;    lJson.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TCGIApp.GetCustomer(AIdPerson: string): string;&lt;br /&gt;var&lt;br /&gt;  lPerson: TJSONObject;&lt;br /&gt;  lJson: TJSONObject;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  (* Query the database *)&lt;br /&gt;  FQuery.Close;&lt;br /&gt;  FQuery.Sql.Text := 'select * from clientes where IdCliente=' + AIdPerson;&lt;br /&gt;  FQuery.Open;&lt;br /&gt;  FQuery.First;&lt;br /&gt;&lt;br /&gt;  lJson := TJSONObject.Create;&lt;br /&gt;  try&lt;br /&gt;    lPerson := TJSONObject.Create;&lt;br /&gt;    fillJsonObject(lPerson);&lt;br /&gt;   (* Add the array to rows property *)&lt;br /&gt;    lJson.Add('success', 'true');&lt;br /&gt;    lJson.Add('data', lPerson);&lt;br /&gt;    Result := lJson.AsJSON;&lt;br /&gt;  finally&lt;br /&gt;    lJson.Free;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TCGIApp.SavePerson(ARequest: TRequest): string;&lt;br /&gt;var&lt;br /&gt;  lId: string;&lt;br /&gt;  lFirstName: string;&lt;br /&gt;  lLastName: string; &lt;br /&gt;  lPhone: string;&lt;br /&gt;  lSql: string;&lt;br /&gt;begin&lt;br /&gt;  lId := ARequest.ContentFields.Values['id'];&lt;br /&gt;  lFirstName := ARequest.ContentFields.Values['firstname'];&lt;br /&gt;  lLastName := ARequest.ContentFields.Values['lastname'];&lt;br /&gt;  lPhone := ARequest.ContentFields.Values['phone'];&lt;br /&gt;  if lId &lt;&gt; '' then&lt;br /&gt;    lSql := 'update clientes set ' + &lt;br /&gt;      'nombres = ''' + lLastName  + ''', ' + &lt;br /&gt;      'apellido = ''' + lFirstName + ''', ' + &lt;br /&gt;      'telfijo = ''' + lPhone + ''' where idcliente=' + lId&lt;br /&gt;  else&lt;br /&gt;  begin&lt;br /&gt;    &lt;br /&gt;    lSql := 'insert into clientes(IdCliente, Nombres, Apellido, TelFijo) ' + &lt;br /&gt;      'values(Gen_Id(SeqClientes, 1),''' + lFirstName + ''', ''' + lLastName + ''', ''' + lPhone + ''')';&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  try&lt;br /&gt;    FQuery.Sql.Text := lSql;&lt;br /&gt;    FConn.Transaction.StartTransaction;&lt;br /&gt;    FQuery.ExecSql;&lt;br /&gt;    FConn.Transaction.Commit;&lt;br /&gt;    Result := '{''success'': ''true''}';&lt;br /&gt;  except&lt;br /&gt;  on E: Exception do&lt;br /&gt;    Result := '{''message'': "' + E.message + '"}';&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TCGIApp.DeletePerson(ARequest: TRequest): string;&lt;br /&gt;var&lt;br /&gt;  lId: string;&lt;br /&gt;begin&lt;br /&gt;  lId := ARequest.ContentFields.Values['delete_person'];&lt;br /&gt;  try&lt;br /&gt;    FQuery.Sql.Text := 'delete from clientes where idcliente=' + lId;&lt;br /&gt;    FConn.Transaction.StartTransaction;&lt;br /&gt;    FQuery.ExecSql;&lt;br /&gt;    FConn.Transaction.Commit;&lt;br /&gt;    Result := '{''success'': ''true''}';&lt;br /&gt;  except&lt;br /&gt;  on E: Exception do&lt;br /&gt;    Result := '{''failure'': ''Error deleting person.''}';&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;Procedure TCGIApp.HandleRequest(ARequest : TRequest; AResponse : TResponse);&lt;br /&gt;var&lt;br /&gt;  lIdPerson: string;&lt;br /&gt;begin&lt;br /&gt;  if ARequest.ContentFields.Values['delete_person'] &lt;&gt; '' then&lt;br /&gt;  begin&lt;br /&gt;    AResponse.Content := DeletePerson(ARequest);&lt;br /&gt;  end&lt;br /&gt;  else&lt;br /&gt;  if ARequest.ContentFields.Values['save_person'] &lt;&gt; '' then&lt;br /&gt;  begin&lt;br /&gt;    AResponse.Content := SavePerson(ARequest);&lt;br /&gt;  end&lt;br /&gt;  else&lt;br /&gt;  begin&lt;br /&gt;    lIdPerson := ARequest.ContentFields.Values['idperson']; &lt;br /&gt;    if lIdPerson &lt;&gt; '' then&lt;br /&gt;      AResponse.Content := GetCustomer(lIdPerson)&lt;br /&gt;    else&lt;br /&gt;      AResponse.Content := GetCustomersList;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  With TCGIApp.Create(Nil) do&lt;br /&gt;    try&lt;br /&gt;      Initialize;&lt;br /&gt;      ConnectToDatabase;&lt;br /&gt;      Run;&lt;br /&gt;    finally&lt;br /&gt;      Free;&lt;br /&gt;    end;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To compile this file, I use a simple Bash script, that copies the compiled program to /var/www/cgi-bin directory, where my Apache2 is configured to host executable CGI programs.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: bash"&gt;&lt;br /&gt;#!/bin/bash&lt;br /&gt;fpc -XX -Xs -b -v ./customerslist.pp&lt;br /&gt;cp ./customerslist /var/www/cgi-bin&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In this article, I showed how to create an HTML page containing a grid, with a toolbar that allow CRUD operations. &lt;br /&gt;&lt;br /&gt;After I published my last article, some of you told me that this was too much work for showing just a simple grid on a web page, and I agree, but when you start adding complexity to the application, the effort needed to add features is marginal, and the separation of concerns used here allows to use better ways to speed up the code creation. I mean, instead of using a simple text editor to create the ExtJs code as I did (well, VIM is not a &lt;span style="font-style:italic;"&gt;simple&lt;/span&gt; editor), you could try the new &lt;a href="http://www.extjs.com/products/designer/?ref=family"&gt;ExtJs Designer&lt;/a&gt;, and for the Object Pascal part, it is very easy to replace it with higher level frameworks, like WebBroker or DataSnap.&lt;br /&gt;&lt;br /&gt;Some of you asked why I didn't use &lt;a href="http://code.google.com/p/extpascal/"&gt;ExtPascal&lt;/a&gt; in this article. I didn't use it because I wanted to show all the parts involved in an ExtJs application (HTML, JS, CGI App), and ExtPascal creates the JavaScript code automatically hiding those internals to the programmer, I think it is very powerfull for programmers who already know how to work with plain ExtJs, and after this article, you already know how it works, so now I can write about ExtPascal!.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://docs.google.com/uc?id=0BzAv2GennGjdOWI0Njk1NzctZTk2Mi00ZmEwLWIxZTctODc1OWUyY2NlMWNl&amp;export=download&amp;hl=en"&gt;Here are the files for this article.&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;What's next?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Before writing about ExtPascal, I'll show you how to replace the CGI part by a DataSnap Server application. See you in my next post!.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-8489140635577821170?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/8489140635577821170/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=8489140635577821170' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8489140635577821170'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8489140635577821170'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2010/03/web-20-programming-with-object-pascal.html' title='Web 2.0 programming with Object Pascal (Part 2)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5869672711906151294</id><published>2010-02-21T13:18:00.000-08:00</published><updated>2010-02-22T05:01:11.442-08:00</updated><title type='text'>Web 2.0 programming with Object Pascal (Part 1)</title><content type='html'>I'm sure you experience the same feeling as me, when&lt;br /&gt;a "web developer" looks at your computer screen&lt;br /&gt;saying "wow! you program in Delphi, I used to use it&lt;br /&gt;before programming web applications". When I hear that,&lt;br /&gt;I want to start telling them that Delphi (Object Pascal)&lt;br /&gt;can help you get better results than any other Web programming&lt;br /&gt;language.&lt;br /&gt;&lt;br /&gt;I hope this series of articles let you know what can&lt;br /&gt;be done in the web scene with Delphi and FreePascal,&lt;br /&gt;and, if you are a PHP-Python-Ruby-ASP-[place your language here] programmer,&lt;br /&gt;just take a look at what can be done with modern Object Pascal languages.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Part 1 - CGI + ExtJs&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Before continuing, please read &lt;a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface"&gt;what is CGI&lt;/a&gt;, and also take a look at &lt;a href="http://www.extjs.com/"&gt;ExtJs&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;From now on, I'll create a CGI program capable of creating&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/JSON"&gt;JSON&lt;/a&gt; responses to an ExtJs frontend.&lt;br /&gt;&lt;br /&gt;This example, will be compiled with FreePascal 2.5.1. The CGI&lt;br /&gt;framework I'll use will be fcl-web, but you can choose&lt;br /&gt;&lt;a href="http://powtils.googlecode.com/"&gt;PowTils&lt;/a&gt; and EzCGI as well. If you&lt;br /&gt;choose Delphi to create the CGI program, you can use WebBroker,&lt;br /&gt;an excelent web development framework.&lt;br /&gt;&lt;br /&gt;All the examples of this series will be hosted on an &lt;a href="http://httpd.apache.org/"&gt;Apache 2&lt;br /&gt;server&lt;/a&gt;, but of course, you can&lt;br /&gt;use any CGI capable server like IIS, LightHttpd (impressive!) and others.&lt;br /&gt;&lt;br /&gt;To test this example, I'll use Ubuntu 9.10 64bits and&lt;br /&gt;FreePascal 2.5.1 trunk version, and ExtJs 3.1.1.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The client&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As a starting point for this example, I'll create&lt;br /&gt;a simple ExtJs GridPanel placed on a static HTML page. The&lt;br /&gt;data shown on the grid, will be loaded from a JSon file,&lt;br /&gt;so no interaction with our CGI app here.&lt;br /&gt;&lt;br /&gt;Now lets create a simple HTML page containing an&lt;br /&gt;ExtJs grid, copy and paste the code below and name it&lt;br /&gt;grid1.html, and save it in /var/www/samples directory:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: html"&gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;    &amp;lt;head&amp;gt;&lt;br /&gt;      &amp;lt;meta equiv=&amp;quot;Content-Type&amp;quot; content=&amp;quot;text/html; charset=utf-8&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;title&amp;gt;Grid Example 1&amp;lt;/title&amp;gt;&lt;br /&gt;      &amp;lt;link rel=&amp;quot;stylesheet&amp;quot; type=&amp;quot;text/css&amp;quot; href=&amp;quot;/extjs/resources/css/ext-all.css&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;/extjs/adapter/ext/ext-base.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;      &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;/extjs/ext-all.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;    &amp;lt;/head&amp;gt;&lt;br /&gt;    &amp;lt;body&amp;gt;&lt;br /&gt;      &amp;lt;script type=&amp;quot;text/javascript&amp;quot; src=&amp;quot;grid1.js&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;      &amp;lt;h1&amp;gt;Grid Example 1&amp;lt;/h1&amp;gt;&lt;br /&gt;      &amp;lt;div id=&amp;quot;grid1&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/body&amp;gt;&lt;br /&gt;  &amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looking at the HTML code above, you'll note in many places there are&lt;br /&gt;references to "/extjs", that means I have uncompressed the ExtJs&lt;br /&gt;files into /var/www directory. My Apache2's document root directory&lt;br /&gt;is /var/www, and my ExtJs files are under /var/www/extjs, also&lt;br /&gt;in the line just below the  tag, there is a reference to&lt;br /&gt;"grid1.js", that reference points to that file placed in the same&lt;br /&gt;directory as "grid.html".&lt;br /&gt;&lt;br /&gt;This is the content of grid1.js (also saved in /samples directory):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: javascript"&gt;&lt;br /&gt;Ext.onReady(function(){&lt;br /&gt;&lt;br /&gt;var dataStore = new Ext.data.JsonStore({&lt;br /&gt;    url: '/samples/customerslist.json',&lt;br /&gt;    root: 'rows',&lt;br /&gt;    method: 'GET',&lt;br /&gt;    fields: [&lt;br /&gt;      {name: 'firstname', type: 'string'},&lt;br /&gt;      {name: 'lastname', type: 'string'},&lt;br /&gt;      {name: 'age', type: 'int'},&lt;br /&gt;      {name: 'phone', type: 'string'}&lt;br /&gt;    ]&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;var myGrid1 = new Ext.grid.GridPanel({&lt;br /&gt;  id: 'customerslist',&lt;br /&gt;  store: dataStore,&lt;br /&gt;  columns: [&lt;br /&gt;    {id: "firstname", header: "First Name", width: 100, dataIndex: "firstname", sortable: true},&lt;br /&gt;    {header: "Last Name", width: 100, dataIndex: "lastname", sortable: true},&lt;br /&gt;    {header: "Age", width: 100, dataIndex: "age", sortable: true},&lt;br /&gt;    {header: "Phone", width: 100, dataIndex: "phone", sortable: true}&lt;br /&gt;  ],&lt;br /&gt;  autoLoad: false,&lt;br /&gt;  stripeRows: true,&lt;br /&gt;  autoHeight: true,&lt;br /&gt;  width: 400&lt;br /&gt;});&lt;br /&gt;&lt;br /&gt;dataStore.load();&lt;br /&gt;&lt;br /&gt;myGrid1.render('grid1');&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This file creates an instance of Ext.data.JsonStore, the object&lt;br /&gt;in charge of loading data from an external JSon source, in this&lt;br /&gt;case the file "/samples/customerslist.json". Also, an&lt;br /&gt;instance of Ext.grid.GridPanel called myGrid1 is created, that&lt;br /&gt;is the Grid who'll contain the data loaded from the JsonStore,&lt;br /&gt;the last line of this file, tells where should render the grid,&lt;br /&gt;in this case on a Div with an ID named "grid1" (look at grid1.html).&lt;br /&gt;&lt;br /&gt;Please, play with this example by adding more data to the Json file,&lt;br /&gt;and change the properties of the grid before continuing.&lt;br /&gt;&lt;br /&gt;The result:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_jiP-KkurPP8/S4GwT7Rp-0I/AAAAAAAAA8I/OVYSh0pwccA/s1600-h/Pantallazo-Grid+Example+1+-+Chromium.png"&gt;&lt;img style="float:none; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 400px; height: 275px;" src="http://4.bp.blogspot.com/_jiP-KkurPP8/S4GwT7Rp-0I/AAAAAAAAA8I/OVYSh0pwccA/s400/Pantallazo-Grid+Example+1+-+Chromium.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5440823681216019266" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Dynamic JSon data&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The next step, is to create a CGI application in charge of&lt;br /&gt;responding requests from the JSonStore. This is a simple&lt;br /&gt;task for our CGI program, just create a string containing&lt;br /&gt;the same data as "customerslist.json" file and send to the client.&lt;br /&gt;&lt;br /&gt;The first FPC CGI application:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;program cgiproject1;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;Classes,SysUtils,httpDefs,custcgi;&lt;br /&gt;&lt;br /&gt;Type&lt;br /&gt;TCGIApp = Class(TCustomCGIApplication)&lt;br /&gt;Public&lt;br /&gt;  Procedure HandleRequest(ARequest : Trequest; AResponse : TResponse); override;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;Procedure TCGIApp.HandleRequest(ARequest : Trequest; AResponse : TResponse);&lt;br /&gt;begin&lt;br /&gt;AResponse.Content := 'Hello!';&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;With TCGIApp.Create(Nil) do&lt;br /&gt;  try&lt;br /&gt;    Initialize;&lt;br /&gt;    Run;&lt;br /&gt;  finally&lt;br /&gt;    Free;&lt;br /&gt;  end;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Save it as "customerslist.pp" then compile with "fpc customerslist.pp".&lt;br /&gt;Then, copy it to /var/www/cgi-bin and open a web browser, then point it to&lt;br /&gt;"http://localhost/cgi-bin/customerslist". It should show "Hello!". That's it.&lt;br /&gt;&lt;br /&gt;Now, to let this program respond with the needed JSon, just replace&lt;br /&gt;the line that AResponse.Content := 'Hello!'; with this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;AResponse.Content :=&lt;br /&gt;'{' +&lt;br /&gt;'  "rows": [ ' +&lt;br /&gt;'    {"firstname": "Leonardo", "lastname": "Ramé", "age": 35, "phone": "1234556"}, ' +&lt;br /&gt;'    {"firstname": "John", "lastname": "Williams", "age": 15, "phone": "435233"}, ' +&lt;br /&gt;'    {"firstname": "Cecilia", "lastname": "Strada", "age": 28, "phone": "423234"}, ' +&lt;br /&gt;'    {"firstname": "Gary", "lastname": "Thomson", "age": 43, "phone": "123"} ' +&lt;br /&gt;'  ] ' +&lt;br /&gt;'}';&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Compile, copy to /var/www/cgi-bin directoy and test.&lt;br /&gt;&lt;br /&gt;To call it from the JsonStore, go back to the grid1.js file,&lt;br /&gt;and replace the property url of the JsonStore by "/var/www/cgi-bin/customerslist".&lt;br /&gt;&lt;br /&gt;Note: if you are working on Windows, the file "customerslist" will be created&lt;br /&gt;as "customerslist.exe", so you must replace it in every reference to&lt;br /&gt;"/var/www/cgi-bin/customerslist".&lt;br /&gt;&lt;br /&gt;If you don't like to create JSON responses as simple strings,&lt;br /&gt;you can use the framework fcl-json, to create them using&lt;br /&gt;an object-oriented method.&lt;br /&gt;&lt;br /&gt;To adapt the example for usin fcl-json, you must add the&lt;br /&gt;unit fpjson to the uses clause, and replace&lt;br /&gt;the TCGIApp.HandleRequest by this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TCGIApp.HandleRequest(ARequest : Trequest; AResponse : TResponse);&lt;br /&gt;var&lt;br /&gt;lPerson1: TJSONObject;&lt;br /&gt;lPerson2: TJSONObject;&lt;br /&gt;lPerson3: TJSONObject;&lt;br /&gt;lPerson4: TJSONObject;&lt;br /&gt;lJson: TJSONObject;&lt;br /&gt;lJsonArray: TJSONArray;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;lPerson1 := TJSONObject.Create;&lt;br /&gt;lPerson2 := TJSONObject.Create;&lt;br /&gt;lPerson3 := TJSONObject.Create;&lt;br /&gt;lPerson4 := TJSONObject.Create;&lt;br /&gt;&lt;br /&gt;lJsonArray := TJSONArray.Create;&lt;br /&gt;lJson := TJSONObject.Create;&lt;br /&gt;try&lt;br /&gt;  (* populate lPerson1 *)&lt;br /&gt;  lPerson1.Add('firstname', TJsonString.Create('Leonardo'));&lt;br /&gt;  lPerson1.Add('lastname', TJsonString.Create('Ramé'));&lt;br /&gt;  lPerson1.Add('age', TJSONIntegerNumber.Create(35));&lt;br /&gt;  lPerson1.Add('phone', TJsonString.Create('1234567'));&lt;br /&gt;  (* populate lPerson2 *)&lt;br /&gt;  lPerson2.Add('firstname', TJsonString.Create('John'));&lt;br /&gt;  lPerson2.Add('lastname', TJsonString.Create('Williams'));&lt;br /&gt;  lPerson2.Add('age', TJSONIntegerNumber.Create(15));&lt;br /&gt;  lPerson2.Add('phone', TJsonString.Create('14567'));&lt;br /&gt;  (* populate lPerson3 *)&lt;br /&gt;  lPerson3.Add('firstname', TJsonString.Create('Cecilia'));&lt;br /&gt;  lPerson3.Add('lastname', TJsonString.Create('Strada'));&lt;br /&gt;  lPerson3.Add('age', TJSONIntegerNumber.Create(29));&lt;br /&gt;  lPerson3.Add('phone', TJsonString.Create('34567'));&lt;br /&gt;  (* populate lPerson4 *)&lt;br /&gt;  lPerson4.Add('firstname', TJsonString.Create('Gary'));&lt;br /&gt;  lPerson4.Add('lastname', TJsonString.Create('Thomson'));&lt;br /&gt;  lPerson4.Add('age', TJSONIntegerNumber.Create(43));&lt;br /&gt;  lPerson4.Add('phone', TJsonString.Create('344567'));&lt;br /&gt;&lt;br /&gt;  (* Fill the array *)&lt;br /&gt;  lJsonArray.Add(lPerson1);&lt;br /&gt;  lJsonArray.Add(lPerson2);&lt;br /&gt;  lJsonArray.Add(lPerson3);&lt;br /&gt;  lJsonArray.Add(lPerson4);&lt;br /&gt;&lt;br /&gt;  (* Add the array to rows property *)&lt;br /&gt;  lJson.Add('rows', lJsonArray);&lt;br /&gt;&lt;br /&gt;  AResponse.Content := lJson.AsJSON;&lt;br /&gt;finally&lt;br /&gt;  lJson.Free;&lt;br /&gt;end;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This looks like too much to type when comparing to the other method,&lt;br /&gt;but it's more readable, and easier to deal with complex JSon data.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Showing data from a database&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now, I'll replace the static JSon data with a dynamic one,&lt;br /&gt;resulting from a database query.&lt;br /&gt;&lt;br /&gt;In this example, I'll connect to a Firebird 2.1 database server,&lt;br /&gt;running on the same machine I'm creating the example. To connect&lt;br /&gt;to the database, I'll use fcl-db framework and its TIBConnection class.&lt;br /&gt;&lt;br /&gt;This is the final project of this part, a dynamic-driven&lt;br /&gt;web page containing an ExtJs Grid populated with data&lt;br /&gt;coming from a databse.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;program cgiproject1;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;Classes,SysUtils,&lt;br /&gt;httpDefs,custcgi, // needed for creating CGI applications&lt;br /&gt;fpjson, // needed for dealing with JSon data&lt;br /&gt;Db, SqlDb, ibconnection; // needed for connecting to Firebird/Interbase;&lt;br /&gt;&lt;br /&gt;Type&lt;br /&gt;TCGIApp = Class(TCustomCGIApplication)&lt;br /&gt;Private&lt;br /&gt;  FConn: TSqlConnection;&lt;br /&gt;  FQuery: TSqlQuery;&lt;br /&gt;  FTransaction: TSqlTransaction;&lt;br /&gt;  procedure ConnectToDataBase;&lt;br /&gt;Public&lt;br /&gt;  Procedure HandleRequest(ARequest : Trequest; AResponse : TResponse); override;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TCGIApp.ConnectToDataBase;&lt;br /&gt;begin&lt;br /&gt;FConn := TIBConnection.Create(nil);&lt;br /&gt;FQuery := TSqlQuery.Create(nil);&lt;br /&gt;FTransaction := TSqlTransaction.Create(nil);&lt;br /&gt;with FConn do&lt;br /&gt;begin&lt;br /&gt;  DatabaseName := 'TEST-DATABASE';&lt;br /&gt;  UserName := 'SYSDBA';&lt;br /&gt;  Password := 'masterkey';&lt;br /&gt;  HostName := 'localhost';&lt;br /&gt;  Connected := True;&lt;br /&gt;  Transaction := FTransaction;&lt;br /&gt;  FQuery.Database := FConn;&lt;br /&gt;end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;Procedure TCGIApp.HandleRequest(ARequest : Trequest; AResponse : TResponse);&lt;br /&gt;var&lt;br /&gt;lPerson: TJSONObject;&lt;br /&gt;lJson: TJSONObject;&lt;br /&gt;lJsonArray: TJSONArray;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;(* Query the database *)&lt;br /&gt;FQuery.Sql.Text := 'select * from people';&lt;br /&gt;FQuery.Open;&lt;br /&gt;FQuery.First;&lt;br /&gt;&lt;br /&gt;lJsonArray := TJSONArray.Create;&lt;br /&gt;lJson := TJSONObject.Create;&lt;br /&gt;try&lt;br /&gt;  while not FQuery.Eof do&lt;br /&gt;  begin&lt;br /&gt;    lPerson := TJSONObject.Create;&lt;br /&gt;    lPerson.Add('firstname', TJsonString.Create(FQuery.FieldByName('FirstName').AsString));&lt;br /&gt;    lPerson.Add('lastname', TJsonString.Create(FQuery.FieldByName('LastName').AsString));&lt;br /&gt;    lPerson.Add('age', TJSONIntegerNumber.Create(FQuery.FieldByName('Age').AsInteger));&lt;br /&gt;    lPerson.Add('phone', TJsonString.Create(FQuery.FieldByName('Phone').AsString));&lt;br /&gt;    FQuery.Next;&lt;br /&gt;    (* Fill the array *)&lt;br /&gt;    lJsonArray.Add(lPerson);&lt;br /&gt;  end;&lt;br /&gt;  (* Add the array to rows property *)&lt;br /&gt;  lJson.Add('rows', lJsonArray);&lt;br /&gt;  AResponse.Content := lJson.AsJSON;&lt;br /&gt;finally&lt;br /&gt;  lJson.Free;&lt;br /&gt;end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;With TCGIApp.Create(Nil) do&lt;br /&gt;  try&lt;br /&gt;    Initialize;&lt;br /&gt;    ConnectToDatabase;&lt;br /&gt;    Run;&lt;br /&gt;  finally&lt;br /&gt;    Free;&lt;br /&gt;  end;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The second part of this series, will focus on&lt;br /&gt;adding some CRUD (create, read, update and delete) functions&lt;br /&gt;to the application.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://docs.google.com/uc?id=0BzAv2GennGjdYmRkYjU5N2ItNjEzZC00ZDMxLTljZTktYzcwNmEzNzk2NThi&amp;export=download&amp;hl=en_US"&gt;Click here to download the source files&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;I hope you enjoy this article as much as I did writing it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5869672711906151294?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5869672711906151294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5869672711906151294' title='31 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5869672711906151294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5869672711906151294'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2010/02/web-20-programming-with-object-pascal.html' title='Web 2.0 programming with Object Pascal (Part 1)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_jiP-KkurPP8/S4GwT7Rp-0I/AAAAAAAAA8I/OVYSh0pwccA/s72-c/Pantallazo-Grid+Example+1+-+Chromium.png' height='72' width='72'/><thr:total>31</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-7109118950391167400</id><published>2010-01-29T09:56:00.000-08:00</published><updated>2010-01-29T10:01:03.844-08:00</updated><title type='text'>Synapse Based SSH Client</title><content type='html'>Many times, I needed a way to let my Delphi/FPC applications to connect to an SSH server, execute some commands, and get its results. Now I'm publishing a simple class based on Synapse's TTelnetSend class to do exactly what I needed.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Required ingredients&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First of all, you'll need to grab a copy of the latest version of &lt;a href="http://synapse.ararat.cz/doku.php/download"&gt;Synapse&lt;/a&gt;, now, to connect to an SSH server, the connection must be established by using the SSL protocol, and Synapse allows that kind of connections with plugins that allow filtering the data through OpenSSL, CryptLib and StreamSecII. Here, I'll use CryptLib, so you'll have to get &lt;a href="http://synapse.ararat.cz/files/crypt/cryptlib-3.2.2.zip"&gt;this compiled version of cl32.dll for Windows&lt;/a&gt;, if you need the library compiled for Linux search for it in your repository (or use Google).&lt;br /&gt;&lt;br /&gt;Now, configure the search paths of your compiler to find both, Synapse source and cryptlib.pas, the wrapper for cl32.dll.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Introducing TSSHClient class&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is a simple class to let you connect to an SSH server in an Object Oriented way, its internal parts where found in the Synapse's newsgroups, tested and arranged in a class.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;unit sshclient;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  tlntsend, ssl_openssl, ssl_openssl_lib, ssl_cryptlib;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;  TSSHClient = class&lt;br /&gt;  private&lt;br /&gt;    FTelnetSend: TTelnetSend;&lt;br /&gt;  public&lt;br /&gt;    constructor Create(AHost, APort, AUser, APass: string);&lt;br /&gt;    destructor Destroy; override;&lt;br /&gt;    procedure SendCommand(ACommand: string);&lt;br /&gt;    procedure LogOut;&lt;br /&gt;    function ReceiveData: string;&lt;br /&gt;    function LogIn: Boolean;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;{ TSSHClient }&lt;br /&gt;&lt;br /&gt;constructor TSSHClient.Create(AHost, APort, AUser, APass: string);&lt;br /&gt;begin&lt;br /&gt;  FTelnetSend := TTelnetSend.Create;&lt;br /&gt;  FTelnetSend.TargetHost := AHost;&lt;br /&gt;  FTelnetSend.TargetPort := APort;&lt;br /&gt;  FTelnetSend.UserName := AUser;&lt;br /&gt;  FTelnetSend.Password := APass;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;destructor TSSHClient.Destroy;&lt;br /&gt;begin&lt;br /&gt;  FTelnetSend.Free;&lt;br /&gt;  inherited;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TSSHClient.LogIn: Boolean;&lt;br /&gt;begin&lt;br /&gt;  Result := FTelnetSend.SSHLogin;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TSSHClient.LogOut;&lt;br /&gt;begin&lt;br /&gt;  FTelnetSend.Logout;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TSSHClient.ReceiveData: string;&lt;br /&gt;var&lt;br /&gt;  lPos: Integer;&lt;br /&gt;begin&lt;br /&gt;  Result := '';&lt;br /&gt;  lPos := 1;&lt;br /&gt;  while FTelnetSend.Sock.CanRead(1000) or (FTelnetSend.Sock.WaitingData&gt;0) do&lt;br /&gt;  begin&lt;br /&gt;    FTelnetSend.Sock.RecvPacket(1000);&lt;br /&gt;    Result := Result + Copy(FTelnetSend.SessionLog, lPos, Length(FTelnetSend.SessionLog));&lt;br /&gt;    lPos := Length(FTelnetSend.SessionLog)+1;&lt;br /&gt;  end;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TSSHClient.SendCommand(ACommand: string);&lt;br /&gt;begin&lt;br /&gt;  FTelnetSend.Send(ACommand + #13);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A sample application&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is an example of how to execute an "df -h" command in an external SSH server, inspirated by a &lt;a href="http://stackoverflow.com/questions/2161945/ssh-client-delphi-open-source-library-or-component-or-alternatives/2162326#2162326"&gt;question&lt;/a&gt; in StackOverflow.&lt;br /&gt;&lt;br /&gt;The example just connects to a server, execute the command and capture its output, just that.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;program TestSSHClient;&lt;br /&gt;&lt;br /&gt;{$APPTYPE CONSOLE}&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;  sshclient;&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  lSSh: TSSHClient;&lt;br /&gt;begin&lt;br /&gt;  lSSh := TSSHClient.Create('[TARGET_HOST_OR_IP_ADDRESS]', '[PORT]', '[USER]', '[PASSWORD]');&lt;br /&gt;  if lSSh.LogIn then&lt;br /&gt;  begin&lt;br /&gt;    Writeln('Connected!.');&lt;br /&gt;    (* Get welcome message *)&lt;br /&gt;    Writeln(lSSh.ReceiveData);&lt;br /&gt;    (* Send command *)&lt;br /&gt;    lSSh.SendCommand('df -h');&lt;br /&gt;    (* Receive results *)&lt;br /&gt;    Writeln(lSSh.ReceiveData);&lt;br /&gt;    lSSh.LogOut;&lt;br /&gt;    Writeln('Logged out.');&lt;br /&gt;  end&lt;br /&gt;  else&lt;br /&gt;    Writeln('Can''t connect.');&lt;br /&gt;  lSSh.Free;&lt;br /&gt;end.                            &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Replace the words between '[' and ']' by the real ones and test it.&lt;br /&gt;&lt;br /&gt;See you on my next post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-7109118950391167400?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/7109118950391167400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=7109118950391167400' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/7109118950391167400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/7109118950391167400'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2010/01/synapse-based-ssh-client.html' title='Synapse Based SSH Client'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-1072177742279569221</id><published>2009-12-03T11:05:00.001-08:00</published><updated>2009-12-03T11:23:41.457-08:00</updated><title type='text'>Apache 2.2 modules - FPC 64bits + Ubuntu 9.10</title><content type='html'>Yesterday I've received a inquiry from Harel Haruda about my example of Apache 2.2 modules for linux, he was having some trouble trying to let Apache load its modules.&lt;br /&gt;&lt;br /&gt;My former example was compiled using FPC 2.2.2 on an i386 PC with Debian Linux, now I'm using Ubuntu 9.10 for x86-64 with FPC 2.2.3.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;One very important change es in FPC 2.2.3, variables and functions have to be marked to be exportable, with the keyword "export". In previous versions, all functions and variables where exported automatically. I'd read this in the FPC Wiki.&lt;br /&gt;&lt;br /&gt;The new code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;{*******************************************&lt;br /&gt;*  Test library of the Apache Pascal Headers&lt;br /&gt;********************************************}&lt;br /&gt;library mod_helloworld;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}{$H+}&lt;br /&gt;&lt;br /&gt;uses SysUtils, httpd {$ifndef Apache1_3}, apr{$endif};&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt; test_module: module;  export;&lt;br /&gt; default_module_ptr: Pmodule;&lt;br /&gt;&lt;br /&gt;const&lt;br /&gt;  MODULE_NAME = 'mod_helloworld.so';&lt;br /&gt;  HANDLER = 'helloworld-handler';&lt;br /&gt;&lt;br /&gt;exports test_module;&lt;br /&gt; &lt;br /&gt;{****************************************************&lt;br /&gt;*  Handles apache requests&lt;br /&gt;*****************************************************}&lt;br /&gt;function DefaultHandler(r: Prequest_rec): Integer; cdecl;&lt;br /&gt;var&lt;br /&gt;  RequestedHandler: string;&lt;br /&gt;begin&lt;br /&gt;  RequestedHandler := r^.handler;&lt;br /&gt;&lt;br /&gt;  ap_log_error(MODULE_NAME, 54, APLOG_NOERRNO or APLOG_NOTICE,&lt;br /&gt;   0, r^.server, 'mod_hello: %s', [PChar('Before content is output')]);&lt;br /&gt;&lt;br /&gt;  if not SameText(RequestedHandler, 'helloworld-handler') then&lt;br /&gt;  begin&lt;br /&gt;    Result := DECLINED;&lt;br /&gt;    Exit;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;   ap_set_content_type(r, 'text/html');&lt;br /&gt; &lt;br /&gt;  { If the request is for a header only, and not a request for&lt;br /&gt;   the whole content, then return OK now. We don't have to do&lt;br /&gt;   anything else. }&lt;br /&gt;  if (r^.header_only &lt;&gt; 0) then&lt;br /&gt;  begin&lt;br /&gt;    Result := OK;&lt;br /&gt;    Exit;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  { Now we just print the contents of the document using the&lt;br /&gt;   ap_rputs and ap_rprintf functions. More information about&lt;br /&gt;   the use of these can be found in http_protocol.inc }&lt;br /&gt;  ap_rputs('&lt;html&gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&lt;head&gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&lt;title&gt;Hello There&lt;/title&gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&lt;/head&gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&lt;body bgcolor="#FFFFFF"&gt;' + LineEnding ,r);&lt;br /&gt;  ap_rputs('&lt;h1&gt;Hello world&lt;/h1&gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('This is the first Apache Module working with the new binding from Free Pascal' + LineEnding, r);&lt;br /&gt;  ap_rputs('&lt;/body&gt;&lt;/html&gt;' + LineEnding, r);&lt;br /&gt;&lt;br /&gt;  { We can either return OK or DECLINED at this point. If we return&lt;br /&gt;         * OK, then no other modules will attempt to process this request }&lt;br /&gt;  Result := OK;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{***************************************************&lt;br /&gt;*  Registers the hooks&lt;br /&gt;****************************************************}&lt;br /&gt;procedure RegisterHooks(p: Papr_pool_t); cdecl;&lt;br /&gt;begin&lt;br /&gt;  ap_hook_handler(@DefaultHandler, nil, nil, APR_HOOK_MIDDLE);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{***************************************************&lt;br /&gt;*  Library initialization code&lt;br /&gt;****************************************************}&lt;br /&gt;begin&lt;br /&gt;  default_module_ptr := @test_module;&lt;br /&gt;  FillChar(default_module_ptr^, SizeOf(default_module_ptr^), 0);&lt;br /&gt;&lt;br /&gt;   STANDARD20_MODULE_STUFF(test_module);&lt;br /&gt;&lt;br /&gt;    with test_module do&lt;br /&gt;    begin&lt;br /&gt;      name := MODULE_NAME;&lt;br /&gt;      register_hooks := @RegisterHooks;&lt;br /&gt;    end;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To compile this, I used this (long) command line:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;fpc -fPIC -B -WR -Xs -XX -Fu/usr/share/fpcsrc/2.2.4/packages/httpd22/src -Fu/usr/share/fpcsrc/2.2.4/packages/httpd22/src/apr -Fu/usr/share/fpcsrc/2.2.4/packages/httpd22/src/aprutil -Fu/usr/share/fpcsrc/2.2.4/src/apriconv mod_helloworld.pas&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This will create a "libmod_helloworld.so" library.&lt;br /&gt;&lt;br /&gt;Then, I had to copy it to /usr/lib64/apache2/modules directory changing its name to mod_helloworld.so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cp libmod_helloworld.so /usr/lib64/apache2/modules/mod_helloworld.so&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The next step, as usual, is to let Apache know from where to load the new module:&lt;br /&gt;&lt;br /&gt;File /etc/apache2/mods-available/helloworld.load&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;LoadModule test_module /usr/lib64/apache2/modules/mod_helloworld.so&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;File /etc/apache2/mods-available/helloworld.conf&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location /hello&amp;gt;&lt;br /&gt;  SetHandler helloworld-handler&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, I created a symlink to those files, from the directory /etc/apache2/mods-enabled:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ln -s /etc/apache2/mods-available/helloworld.conf /etc/apache2/mods-enabled/helloworld.conf&lt;br /&gt;ln -s /etc/apache2/mods-available/helloworld.load /etc/apache2/mods-enabled/helloworld.load&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The last step is to restart Apache and test the module:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sudo apache2ctl start&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To test the module go to http://localhost/hello&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-1072177742279569221?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/1072177742279569221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=1072177742279569221' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1072177742279569221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1072177742279569221'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/12/apache-22-modules-fpc-64bits-ubuntu-910.html' title='Apache 2.2 modules - FPC 64bits + Ubuntu 9.10'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-988245464904617078</id><published>2009-11-19T05:45:00.000-08:00</published><updated>2009-11-19T06:22:05.462-08:00</updated><title type='text'>Response to comments in "Inducing the Great Divide"</title><content type='html'>I've read the -nice- article &lt;a href="http://sourcecodeadventures.wordpress.com/2009/11/18/inducing-the-great-divide/"&gt;Inducing the great divide&lt;/a&gt; by Cobus Cruger, and was tempted to answer many comments about his approach to set object properties to visual controls.&lt;br /&gt;&lt;br /&gt;Many comments where related to the lack of scalability of this approach, specially when a form has many model objects pointed to views not contemplated in the framework Cobus created.&lt;br /&gt;&lt;br /&gt;I also am thinking about this problem since a long time, and started with my own framweork using the same approach of Cobus, and faced the same problems he is facing after the reader's comments.&lt;br /&gt;&lt;br /&gt;My conclussion was that it's not possible to create a simple, generic way to fill any control with data from any object, thus, I created a framework where the programmer has to configure the objects by using "associations", where a property of an object points to a TEdit, TComboBox, TListView and so on.&lt;br /&gt;&lt;br /&gt;For example, if the program needs to fill a listview with a TCollection of customers, in my framework, I have to configure this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;mvFramework.AddAssociation(TListViewAssociation.Create(&lt;br /&gt;  ListView1, // the list view associated&lt;br /&gt;  Customers, // Customers TCollection&lt;br /&gt;  ['FirstName','LastName'] // Properties pointed to the First and Second column of ListView1&lt;br /&gt;));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;On Each TForm of the application, the programmer has to configure each control the way I explained in the previous paragraph, then the framework provides two methods, the first one to copy the data from the model and the second does the reverse.&lt;br /&gt;&lt;br /&gt;To copy data from model to view:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;mvFramework.SetDataToView;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To copy data from view back to model:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;mvFramework.SetViewToData;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;br /&gt;What if I have a component not included in the framework?&lt;/span&gt;&lt;br /&gt;It's simple to create a custom association, like the example, that uses TListViewAssociation, one can create a TQuantumGridAssociation, for example.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;br /&gt;Sample code:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I uploaded not so trivial example to &lt;a href="http://cc.embarcadero.com/item/27468"&gt;Code Central&lt;/a&gt;, enjoy!.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-988245464904617078?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/988245464904617078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=988245464904617078' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/988245464904617078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/988245464904617078'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/11/response-to-comments-in-inducing-great.html' title='Response to comments in &quot;Inducing the Great Divide&quot;'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-4819689912814209947</id><published>2009-07-29T10:40:00.000-07:00</published><updated>2009-07-29T11:02:37.379-07:00</updated><title type='text'>Apache 2.2.x modules with FreePascal (Linux)</title><content type='html'>Finally I have a couple of minutes free to write about Apache 2.2.x Modules in Linux using FreePascal. The methodology is pretty much the same as &lt;a href="http://leonardorame.blogspot.com/2009/07/apache-22x-modules-with-freepascal.html"&gt;my previous article&lt;/a&gt; about Win32 modules.&lt;br /&gt;&lt;br /&gt;The code&lt;br /&gt;&lt;br /&gt;Open your favorite editor and type this code, then save it as "mod_helloworld.pp":&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;{*******************************************&lt;br /&gt;*  Test library of the Apache Pascal Headers&lt;br /&gt;********************************************}&lt;br /&gt;library mod_helloworld;&lt;br /&gt;&lt;br /&gt;{*******************************************&lt;br /&gt;* The mode must be objfpc on this unit because &lt;br /&gt;* the unix code uses some extensions &lt;br /&gt;* introduced on Free Pascal&lt;br /&gt;********************************************}&lt;br /&gt;{$ifdef fpc}&lt;br /&gt;  {$mode objfpc}{$H+}&lt;br /&gt;{$endif}&lt;br /&gt;&lt;br /&gt;{$IFDEF WIN32}&lt;br /&gt;  {$DEFINE WINDOWS}&lt;br /&gt;{$ENDIF}&lt;br /&gt;&lt;br /&gt;{$define Apache2_0}&lt;br /&gt;&lt;br /&gt;uses SysUtils, httpd {$ifndef Apache1_3}, apr{$endif};&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt; test_module: module; {$ifdef Unix} public name 'test_module'; {$endif}&lt;br /&gt; default_module_ptr: Pmodule;&lt;br /&gt;&lt;br /&gt;const&lt;br /&gt;  MODULE_NAME = 'mod_helloworld.so';&lt;br /&gt;  HANDLER = 'helloworld-handler';&lt;br /&gt; &lt;br /&gt;{****************************************************&lt;br /&gt;*  Free Pascal only supports exporting &lt;br /&gt;*  variables on Windows&lt;br /&gt;*****************************************************}&lt;br /&gt;{$ifdef WINDOWS}&lt;br /&gt;exports&lt;br /&gt; test_module name 'test_module';&lt;br /&gt;{$endif}&lt;br /&gt;&lt;br /&gt;{****************************************************&lt;br /&gt;*  Handles apache requests&lt;br /&gt;*****************************************************}&lt;br /&gt;function DefaultHandler(r: Prequest_rec): Integer; cdecl;&lt;br /&gt;var&lt;br /&gt;  RequestedHandler: string;&lt;br /&gt;begin&lt;br /&gt;  RequestedHandler := r^.handler;&lt;br /&gt;&lt;br /&gt;  { We decline to handle a request if hello-handler is not the value of r-&gt;handler }&lt;br /&gt;  if not SameText(RequestedHandler, 'testapache-handler') then&lt;br /&gt;  begin&lt;br /&gt;    Result := DECLINED;&lt;br /&gt;    Exit;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  { The following line just prints a message to the errorlog }&lt;br /&gt;  ap_log_error(MODULE_NAME, 54, APLOG_NOERRNO or APLOG_NOTICE,&lt;br /&gt;   {$ifndef Apache1_3}0,{$endif} r^.server,&lt;br /&gt;   'mod_hello: %s', [PChar('Before content is output')]);&lt;br /&gt;&lt;br /&gt;  { We set the content type before doing anything else }&lt;br /&gt;  {$ifdef Apache1_3}&lt;br /&gt;    r^.content_type := 'text/html';&lt;br /&gt;//    ap_send_http_header(r);&lt;br /&gt;  {$else}&lt;br /&gt;    ap_set_content_type(r, 'text/html');&lt;br /&gt;  {$endif}&lt;br /&gt; &lt;br /&gt;  { If the request is for a header only, and not a request for&lt;br /&gt;   the whole content, then return OK now. We don't have to do&lt;br /&gt;   anything else. }&lt;br /&gt;  if (r^.header_only &lt;&gt; 0) then&lt;br /&gt;  begin&lt;br /&gt;    Result := OK;&lt;br /&gt;    Exit;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  { Now we just print the contents of the document using the&lt;br /&gt;   ap_rputs and ap_rprintf functions. More information about&lt;br /&gt;   the use of these can be found in http_protocol.inc }&lt;br /&gt;  ap_rputs('&amp;lt;html&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;head&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;title&amp;gt;Hello There&amp;lt;/title&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;/head&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;body bgcolor="#FFFFFF"&amp;gt;' + LineEnding ,r);&lt;br /&gt;  ap_rputs('&amp;lt;h1&amp;gt;Hello world&amp;lt;/h1&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('This is the first Apache Module working with the new binding from Free Pascal' + LineEnding, r);&lt;br /&gt;//  ap_rprintf(r, '&amp;lt;br /&gt;A sample line generated by ap_rprintf&amp;lt;br /&amp;gt;' + LineEnding, []);&lt;br /&gt;  ap_rputs('&amp;lt;/body&gt;&amp;lt;/html&amp;gt;' + LineEnding, r);&lt;br /&gt;&lt;br /&gt;  { We can either return OK or DECLINED at this point. If we return&lt;br /&gt;         * OK, then no other modules will attempt to process this request }&lt;br /&gt;  Result := OK;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{***************************************************&lt;br /&gt;*  Registers the hooks&lt;br /&gt;****************************************************}&lt;br /&gt;{$ifdef apache1_3}&lt;br /&gt;&lt;br /&gt;procedure hw_init(s: PServer_rec; p: PPool); cdecl;&lt;br /&gt;begin&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  hw_handlers: array[0..0] of handler_rec =&lt;br /&gt;  (&lt;br /&gt;    (content_type: 'hw_app'; handler: @DefaultHandler)&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;{$else}&lt;br /&gt;&lt;br /&gt;procedure RegisterHooks(p: Papr_pool_t); cdecl;&lt;br /&gt;begin&lt;br /&gt;  ap_hook_handler(@DefaultHandler, nil, nil, APR_HOOK_MIDDLE);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{$endif}&lt;br /&gt;&lt;br /&gt;{***************************************************&lt;br /&gt;*  Library initialization code&lt;br /&gt;****************************************************}&lt;br /&gt;begin&lt;br /&gt;  default_module_ptr := @test_module;&lt;br /&gt;  FillChar(default_module_ptr^, SizeOf(default_module_ptr^), 0);&lt;br /&gt;&lt;br /&gt;  {$ifdef apache1_3}&lt;br /&gt;    STANDARD_MODULE_STUFF(test_module);&lt;br /&gt;&lt;br /&gt;    with test_module do&lt;br /&gt;    begin&lt;br /&gt;      name := MODULE_NAME;&lt;br /&gt;      init := @hw_init;&lt;br /&gt;      handlers := hw_handlers;&lt;br /&gt;    end;&lt;br /&gt;  {$else}&lt;br /&gt;    STANDARD20_MODULE_STUFF(test_module);&lt;br /&gt;&lt;br /&gt;    with test_module do&lt;br /&gt;    begin&lt;br /&gt;      name := MODULE_NAME;&lt;br /&gt;      register_hooks := @RegisterHooks;&lt;br /&gt;    end;&lt;br /&gt;  {$endif}&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then stop Apache 2 service. &lt;br /&gt;&lt;br /&gt;To compile the module I had to use this on my Debian 5 machine using FPC 2.2.4:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;fpc -WR -Xs -XX -Fu/usr/lib/fpc/2.2.4/units/i386-linux/httpd22 mod_helloworld.pp&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Pay attention I'm explicitly using the httpd22 path for loading the http.pp related stuff. Without this, the compiler could load the http unit for another version of Apache.&lt;br /&gt;&lt;br /&gt;Note: The -WR parameter tells the compiler to add relocatable code to the dll. Without this, you cannot load two dlls compiled with FPC with the same executable.&lt;br /&gt;&lt;br /&gt;After compiling the program you will get the file libmod_helloworld, now you have to rename the file to mod_helloworld.so and copy to your Apache/modules directory, usually in /usr/lib/apache2/modules/mod_helloworld.so.&lt;br /&gt;&lt;br /&gt;In my Debian 5, Apache2 config files are in /etc/apache2/mods-available. I created the files helloworld.load and helloworld.conf files containing this:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;helloworld.load&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;LoadModule test_module /usr/lib/apache2/modules/mod_helloworld.so&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;helloworld.conf&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location /hello&amp;gt;&lt;br /&gt;  SetHandler helloworld-handler&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;After this, do an "sudo apache2ctl restart" and go to http://&amp;lt;yourhost&amp;gt;/hello. It should open a page saying "Hello World".&lt;br /&gt;&lt;br /&gt;The last article of this series will be the same module for FreeBSD. I'd try to create one, bug got stuck in an issue...at the moment FPC can't create FreeBSD shared libraries, so I'll try to use a &lt;a href="http://bugs.freepascal.org/view.php?id=7833"&gt;workaround&lt;/a&gt; to at least, test a basic module.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-4819689912814209947?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/4819689912814209947/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=4819689912814209947' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/4819689912814209947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/4819689912814209947'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/07/apache-22x-modules-with-freepascal_29.html' title='Apache 2.2.x modules with FreePascal (Linux)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-6407318566413221321</id><published>2009-07-03T06:16:00.000-07:00</published><updated>2009-07-29T10:45:15.603-07:00</updated><title type='text'>Apache 2.2.x modules with FreePascal (Win32)</title><content type='html'>Continuing with this series of articles, I will start with the FreePascal version using plain command line FreePascal 2.2.4 for Win32, and later I'll try to create a module for Apache 2.2.x on Linux.&lt;br /&gt;&lt;br /&gt;Before copying-pasting this example, I recommend you to &lt;a href="http://wiki.freepascal.org/FPC_and_Apache_Modules"&gt;read this Wiki&lt;/a&gt; to get a detailed knowledge of the problem.&lt;br /&gt;&lt;br /&gt;The code&lt;br /&gt;&lt;br /&gt;Open your favorite editor and type this code, then save it as "mod_helloworld.pp":&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;{*******************************************&lt;br /&gt;*  Test library of the Apache Pascal Headers&lt;br /&gt;********************************************}&lt;br /&gt;library mod_helloworld;&lt;br /&gt;&lt;br /&gt;{*******************************************&lt;br /&gt;* The mode must be objfpc on this unit because &lt;br /&gt;* the unix code uses some extensions &lt;br /&gt;* introduced on Free Pascal&lt;br /&gt;********************************************}&lt;br /&gt;{$ifdef fpc}&lt;br /&gt;  {$mode objfpc}{$H+}&lt;br /&gt;{$endif}&lt;br /&gt;&lt;br /&gt;{$IFDEF WIN32}&lt;br /&gt;  {$DEFINE WINDOWS}&lt;br /&gt;{$ENDIF}&lt;br /&gt;&lt;br /&gt;{$define Apache2_0}&lt;br /&gt;&lt;br /&gt;uses SysUtils, httpd {$ifndef Apache1_3}, apr{$endif};&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt; test_module: module; {$ifdef Unix} public name 'test_module'; {$endif}&lt;br /&gt; default_module_ptr: Pmodule;&lt;br /&gt;&lt;br /&gt;const&lt;br /&gt;  MODULE_NAME = 'mod_helloworld.so';&lt;br /&gt;  HANDLER = 'helloworld-handler';&lt;br /&gt; &lt;br /&gt;{****************************************************&lt;br /&gt;*  Free Pascal only supports exporting &lt;br /&gt;*  variables on Windows&lt;br /&gt;*****************************************************}&lt;br /&gt;{$ifdef WINDOWS}&lt;br /&gt;exports&lt;br /&gt; test_module name 'test_module';&lt;br /&gt;{$endif}&lt;br /&gt;&lt;br /&gt;{****************************************************&lt;br /&gt;*  Handles apache requests&lt;br /&gt;*****************************************************}&lt;br /&gt;function DefaultHandler(r: Prequest_rec): Integer; cdecl;&lt;br /&gt;var&lt;br /&gt;  RequestedHandler: string;&lt;br /&gt;begin&lt;br /&gt;  RequestedHandler := r^.handler;&lt;br /&gt;&lt;br /&gt;  { We decline to handle a request if hello-handler is not the value of r-&gt;handler }&lt;br /&gt;  if not SameText(RequestedHandler, 'testapache-handler') then&lt;br /&gt;  begin&lt;br /&gt;    Result := DECLINED;&lt;br /&gt;    Exit;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  { The following line just prints a message to the errorlog }&lt;br /&gt;  ap_log_error(MODULE_NAME, 54, APLOG_NOERRNO or APLOG_NOTICE,&lt;br /&gt;   {$ifndef Apache1_3}0,{$endif} r^.server,&lt;br /&gt;   'mod_hello: %s', [PChar('Before content is output')]);&lt;br /&gt;&lt;br /&gt;  { We set the content type before doing anything else }&lt;br /&gt;  {$ifdef Apache1_3}&lt;br /&gt;    r^.content_type := 'text/html';&lt;br /&gt;//    ap_send_http_header(r);&lt;br /&gt;  {$else}&lt;br /&gt;    ap_set_content_type(r, 'text/html');&lt;br /&gt;  {$endif}&lt;br /&gt; &lt;br /&gt;  { If the request is for a header only, and not a request for&lt;br /&gt;   the whole content, then return OK now. We don't have to do&lt;br /&gt;   anything else. }&lt;br /&gt;  if (r^.header_only &lt;&gt; 0) then&lt;br /&gt;  begin&lt;br /&gt;    Result := OK;&lt;br /&gt;    Exit;&lt;br /&gt;  end;&lt;br /&gt;&lt;br /&gt;  { Now we just print the contents of the document using the&lt;br /&gt;   ap_rputs and ap_rprintf functions. More information about&lt;br /&gt;   the use of these can be found in http_protocol.inc }&lt;br /&gt;  ap_rputs('&amp;lt;html&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;head&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;title&amp;gt;Hello There&amp;lt;/title&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;/head&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('&amp;lt;body bgcolor="#FFFFFF"&amp;gt;' + LineEnding ,r);&lt;br /&gt;  ap_rputs('&amp;lt;h1&amp;gt;Hello world&amp;lt;/h1&amp;gt;' + LineEnding, r);&lt;br /&gt;  ap_rputs('This is the first Apache Module working with the new binding from Free Pascal' + LineEnding, r);&lt;br /&gt;//  ap_rprintf(r, '&amp;lt;br /&gt;A sample line generated by ap_rprintf&amp;lt;br /&amp;gt;' + LineEnding, []);&lt;br /&gt;  ap_rputs('&amp;lt;/body&gt;&amp;lt;/html&amp;gt;' + LineEnding, r);&lt;br /&gt;&lt;br /&gt;  { We can either return OK or DECLINED at this point. If we return&lt;br /&gt;         * OK, then no other modules will attempt to process this request }&lt;br /&gt;  Result := OK;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{***************************************************&lt;br /&gt;*  Registers the hooks&lt;br /&gt;****************************************************}&lt;br /&gt;{$ifdef apache1_3}&lt;br /&gt;&lt;br /&gt;procedure hw_init(s: PServer_rec; p: PPool); cdecl;&lt;br /&gt;begin&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;  hw_handlers: array[0..0] of handler_rec =&lt;br /&gt;  (&lt;br /&gt;    (content_type: 'hw_app'; handler: @DefaultHandler)&lt;br /&gt;  );&lt;br /&gt;&lt;br /&gt;{$else}&lt;br /&gt;&lt;br /&gt;procedure RegisterHooks(p: Papr_pool_t); cdecl;&lt;br /&gt;begin&lt;br /&gt;  ap_hook_handler(@DefaultHandler, nil, nil, APR_HOOK_MIDDLE);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;{$endif}&lt;br /&gt;&lt;br /&gt;{***************************************************&lt;br /&gt;*  Library initialization code&lt;br /&gt;****************************************************}&lt;br /&gt;begin&lt;br /&gt;  default_module_ptr := @test_module;&lt;br /&gt;  FillChar(default_module_ptr^, SizeOf(default_module_ptr^), 0);&lt;br /&gt;&lt;br /&gt;  {$ifdef apache1_3}&lt;br /&gt;    STANDARD_MODULE_STUFF(test_module);&lt;br /&gt;&lt;br /&gt;    with test_module do&lt;br /&gt;    begin&lt;br /&gt;      name := MODULE_NAME;&lt;br /&gt;      init := @hw_init;&lt;br /&gt;      handlers := hw_handlers;&lt;br /&gt;    end;&lt;br /&gt;  {$else}&lt;br /&gt;    STANDARD20_MODULE_STUFF(test_module);&lt;br /&gt;&lt;br /&gt;    with test_module do&lt;br /&gt;    begin&lt;br /&gt;      name := MODULE_NAME;&lt;br /&gt;      register_hooks := @RegisterHooks;&lt;br /&gt;    end;&lt;br /&gt;  {$endif}&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then stop Apache 2 service, and compile the file with this command:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;fpc -WR -XX -Xs  mod_helloworld.pp&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note: The -WR parameter tells the compiler to add relocatable code to the dll. Without this, you cannot load two dlls compiled with FPC with the same executable.&lt;br /&gt;&lt;br /&gt;After compiling the program you will get the file mod_helloworld.dll, now you have to rename the file to mod_helloworld.so and copy to your Apache/modules directory, usually in C:\Program files\Apache Software Foundation\Apache2.2\modules.&lt;br /&gt;&lt;br /&gt;Now add this to your httpd.conf file in C:\Program files\Apache Software Foundation\Apache2.2\conf:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;LoadModule test_module modules/mod_helloworld.so&lt;br /&gt;&amp;lt;Location /hello&amp;gt;&lt;br /&gt;SetHandler helloworld-handler&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I don't have a Linux box here to test this (shhh, I'm at work), I'll try to compile it this weekend at home in Linux and FreeBSD.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-6407318566413221321?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/6407318566413221321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=6407318566413221321' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6407318566413221321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6407318566413221321'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/07/apache-22x-modules-with-freepascal.html' title='Apache 2.2.x modules with FreePascal (Win32)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5370027617078425539</id><published>2009-07-01T10:56:00.000-07:00</published><updated>2009-07-01T11:22:39.291-07:00</updated><title type='text'>Apache 2.2.x modules with Delphi II</title><content type='html'>After my "Apache 2.2.x modules with Delphi" article, I received many requests for an example, and here it is.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;Creating the directory structure&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Create a directory where your Delphi project will reside, for example C:\myModule, then copy the files HTTPD2.pas, ApacheTwoApp.pas and ApacheTwoHTTP.pas from your Delphi/Source directory to the newly created directory.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Patching HTTPD2.pas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Open the file c:\myModule\HTTPD2.pas and replace/fix the lines as shown in &lt;a href="http://leonardorame.blogspot.com/2009/04/apache-22x-modules-with-delphi.html"&gt;my previous post&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is a very small HelloWorld WebBroker example, it is composed of only three files, mod_helloworld.dpr, main.pas and main.dfm&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;mod_helloworld.dpr&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;library mod_helloworld;&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;HTTPD2 in 'HTTPD2.pas',&lt;br /&gt;ApacheTwoApp,&lt;br /&gt;WebBroker,&lt;br /&gt;main in 'main.pas' {WebModule1: TWebModule};&lt;br /&gt;&lt;br /&gt;{$E so}&lt;br /&gt;&lt;br /&gt;{$R *.res}&lt;br /&gt;&lt;br /&gt;exports&lt;br /&gt;apache_module name 'helloworld_module';&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;Application.Initialize;&lt;br /&gt;Application.CreateForm(TWebModule1, WebModule1);&lt;br /&gt;Application.Run;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;main.pas&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;unit main;&lt;br /&gt;&lt;br /&gt;interface&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt;SysUtils, Classes, HTTPApp,&lt;br /&gt;HTTPProd;&lt;br /&gt;&lt;br /&gt;type&lt;br /&gt;TWebModule1 = class(TWebModule)&lt;br /&gt;procedure WebModule1WebActionItem1Action(Sender: TObject;&lt;br /&gt; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);&lt;br /&gt;private&lt;br /&gt;public&lt;br /&gt;{ Public declarations }&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt;WebModule1: TWebModule1;&lt;br /&gt;&lt;br /&gt;implementation&lt;br /&gt;&lt;br /&gt;{$R *.dfm}&lt;br /&gt;&lt;br /&gt;procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;&lt;br /&gt;Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);&lt;br /&gt;begin&lt;br /&gt;Response.Content := 'Hello from Apache Module!';&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;end.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;main.dfm&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;object WebModule1: TWebModule1&lt;br /&gt;OldCreateOrder = False&lt;br /&gt;Actions = &lt; default =" True" name =" 'WebActionItem1'" pathinfo =" '/test'" onaction =" WebModule1WebActionItem1Action"&gt;&lt;br /&gt;Left = 679&lt;br /&gt;Top = 385&lt;br /&gt;Height = 150&lt;br /&gt;Width = 215&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Compilling the module&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Save the three files in c:\myModule and open the .dpr with Delphi, then compile to your Apache2 modules directory, usually in C:\Program files\Apache Software Foundation\Apache2.2\modules.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Configuring Apache2&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If the Apache 2.2 service it's running, please stop it, then open your C:\Program files\Apache Software Foundation\Apache2.2\conf\httpd.conf file and look for the "LoadModule" entries, add this after the last entry:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;LoadModule helloworld_module modules/mod_helloworld.so&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;And below this line add this:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location /test&amp;gt;&lt;br /&gt;SetHandler mod_helloworld-handler&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Testing the module&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Restart your Apache 2.2 service and open a web browser, then type http://localhost:8080/test, it should show a page with the text "Hello from Apache Module!".&lt;br /&gt;&lt;br /&gt;That's it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5370027617078425539?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5370027617078425539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5370027617078425539' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5370027617078425539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5370027617078425539'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/07/apache-22x-modules-with-delphi-ii.html' title='Apache 2.2.x modules with Delphi II'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-1790494375873554745</id><published>2009-05-20T08:11:00.000-07:00</published><updated>2009-05-20T08:15:10.866-07:00</updated><title type='text'>FindFirst...FindClose in Linux</title><content type='html'>Last week a customer asked for one application we sell for sending a work list from a Radiology Information System to different equipment called Modalities in medical terminology, they are CT, CR, MR and so on. &lt;br /&gt;&lt;br /&gt;The application is called Modality WorkList Server, and works as a Windows Service. It's function is reading XML files from a shared directory, convert to Dicom format and place in a specific directory. Also it opens a socket port for listening incoming requests from modalities, matches the Dicom files with the request and send the files to the origin. The final product of this process is a list containing patient data a radiology technician can see in the modality screen.&lt;br /&gt;&lt;br /&gt;All went well, until our customer told us they use Linux!. Fortunately, the application was written taking care it should compile with FPC in the future, so, after a couple of IfDefs it compiled and started running on Linux.&lt;br /&gt;&lt;br /&gt;The sad news, were, after ten minutes it stopped working.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;What went wrong?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The first think I did was to add logs everywhere trying to catch the focus of the problem. And found it was stopping in a procedure similar to this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;  if FindFirst(Edit1.Text, FileAttrs, sr) = 0 then&lt;br /&gt;  begin&lt;br /&gt;    repeat&lt;br /&gt;     ...&lt;br /&gt;     ...&lt;br /&gt;    until FindNext(sr) &lt;&gt; 0;&lt;br /&gt;  FindClose(sr);&lt;br /&gt;  end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you look at the FindFirst function in Delphi help, it has an example very similar to this, if FindFirst finds files that matches what I'm seeking, then a loop traverses the file list, and at the end, a FindClose must be issued, but only if FindFirst returned 0.&lt;br /&gt;&lt;br /&gt;In Linux, on the other hand, FindClose must be called every time a FindFile is executed, it doesn't matter if it found anything or not. This is the code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;  if FindFirst(Edit1.Text, FileAttrs, sr) = 0 then&lt;br /&gt;  begin&lt;br /&gt;    repeat&lt;br /&gt;     ...&lt;br /&gt;     ...&lt;br /&gt;    until FindNext(sr) &lt;&gt; 0;&lt;br /&gt;  end;&lt;br /&gt;  (* In Linux, FindClose must be called always after FindFirst *)&lt;br /&gt;  FindClose(sr);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt; &lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Internals&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To those who want to know why the FreePascal Linux version is different than Delphi/FPC windows counterpart, I'll try to explain here. &lt;br /&gt;&lt;br /&gt;To interact with the Linux file system, FreePascal used to use an unit named OldLinux, which contains the function Glob(), in charge of finding files. This function returns a pointer to a glob structure (the file list buffer), it doesn't matter if there are found files or not, this pointer is always allocated. After calling Glob, always must be called GlobFree, to free the result of Glob call. &lt;br /&gt;&lt;br /&gt;The Glob function of FPC, is a wrapper to the Glob function of GLibC's Glob function, that's why it is implemented this way, and it's correct.&lt;br /&gt;&lt;br /&gt;In Linux, if you want to search for files, you use the "find" command, again, a wrapper to the GlibC Glob function. To implement FindFirst/FindNext/FindClose in FPC's RTL, the developers created them as wrappers to Glob, and again, this was a wise decision, at least to me. The only problem, is that it's usage is different in Linux than Windows, so, if you have to use this functions in a program that must run in both environments, just take care to write the call to FindClose in the right place for each operating system.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-1790494375873554745?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/1790494375873554745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=1790494375873554745' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1790494375873554745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1790494375873554745'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/05/findfirstfindclose-in-linux.html' title='FindFirst...FindClose in Linux'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5508239084038986223</id><published>2009-04-19T07:32:00.000-07:00</published><updated>2009-07-01T13:49:31.625-07:00</updated><title type='text'>Apache 2.2.x modules with Delphi</title><content type='html'>If you want to create Apache modules with Delphi, you can start reading DrBob's &lt;a href="http://www.drbob42.com/examines/examin80.htm"&gt;Delphi 2006 and Apache Web Apps&lt;/a&gt; and &lt;a href="http://www.drbob42.com/Delphi7/Apache2040.htm"&gt;this&lt;/a&gt; articles, they explain step by step how to create Apache modules with Dephi.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Apache 2.2.x&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;DrBob articles covers up to Apache 2.0.63, but the current version of the http server is 2.2.x, if you try to run them in this version, they won't work.&lt;br /&gt;&lt;br /&gt;Don't worry, to make it work with the new version, you have to change and add a couple of lines to the same HTTPD2.pas file as follows:&lt;br /&gt;&lt;br /&gt;Before start changing anything, copy the files HTTPD2.pas, ApacheTwoApp.pas and ApacheTwoHTTP.pas from the Delphi /source/win32/internet directory to the directory of your project, in this example c:\projects\apachemodule.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Changes&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Find the text in the left column and replace by the one in the right.&lt;br /&gt;&lt;br /&gt;&lt;table border="1" cellpadding="0" cellspacing="0"&gt;&lt;tbody&gt; &lt;tr&gt;  &lt;td style="text-align: center;" valign="top"&gt;Old HTTPD2.pas &lt;br /&gt; &lt;/td&gt;  &lt;td style="text-align: center;" valign="top"&gt;New HTTPD2.pas &lt;br /&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt;  &lt;td valign="top"&gt;&lt;pre&gt;LibAPR = 'libapr.dll';     {do not localize}&lt;/pre&gt;&lt;/td&gt;  &lt;td valign="top"&gt;&lt;pre&gt;LibAPR = 'libapr-1.dll';     {do not localize}&lt;/pre&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt;  &lt;td valign="top"&gt;&lt;pre&gt;MODULE_MAGIC_COOKIE = $041503230;  (* "AP20" *)&lt;br /&gt;{$EXTERNALSYM MODULE_MAGIC_COOKIE}&lt;br /&gt;MODULE_MAGIC_NUMBER_MAJOR = 20020903;   { Apache 2.0.42 }&lt;br /&gt;{$EXTERNALSYM MODULE_MAGIC_NUMBER_MAJOR}&lt;br /&gt;MODULE_MAGIC_NUMBER_MINOR = 0;          (* 0...n *)&lt;/pre&gt;&lt;/td&gt;  &lt;td valign="top"&gt;&lt;pre&gt;MODULE_MAGIC_COOKIE = $041503232; (* "AP22" *)&lt;br /&gt;{$EXTERNALSYM MODULE_MAGIC_COOKIE}&lt;br /&gt;MODULE_MAGIC_NUMBER_MAJOR = 20051115; { Apache 2.2.x }&lt;br /&gt;{$EXTERNALSYM MODULE_MAGIC_NUMBER_MAJOR}&lt;br /&gt;MODULE_MAGIC_NUMBER_MINOR = 0;  (* 0...n *)&lt;/pre&gt;&lt;/td&gt; &lt;/tr&gt;&lt;tr&gt;  &lt;td valign="top"&gt;&lt;pre&gt;{$EXTERNALSYM apr_bucket_alloc_t}&lt;/pre&gt;&lt;/td&gt;  &lt;td valign="top"&gt;&lt;pre&gt;{$EXTERNALSYM apr_bucket_alloc_t}&lt;br /&gt;ap_conn_keepalive_e = (AP_CONN_UNKNOWN, AP_CONN_CLOSE, AP_CONN_KEEPALIVE);&lt;/pre&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt;  &lt;td valign="top"&gt;&lt;pre&gt;(** Are we going to keep the connection alive for another request?&lt;br /&gt;* @see ap_conn_keepalive_e *)&lt;br /&gt;{keepalive: ap_conn_keepalive_e;}&lt;/pre&gt;&lt;/td&gt;  &lt;td valign="top"&gt;&lt;pre&gt;(** Are we going to keep the connection alive for another request?&lt;br /&gt;* @see ap_conn_keepalive_e *)&lt;br /&gt;keepalive: ap_conn_keepalive_e;&lt;/pre&gt;&lt;/td&gt; &lt;/tr&gt; &lt;tr&gt;  &lt;td valign="top"&gt;&lt;pre&gt;(** The bucket allocator to use&lt;br /&gt;for all bucket/brigade creations *)&lt;br /&gt; bucket_alloc: Papr_bucket_alloc_t;&lt;br /&gt; end;&lt;/pre&gt;&lt;/td&gt;  &lt;td valign="top"&gt;&lt;pre&gt;  (** The bucket allocator to use&lt;br /&gt;for all bucket/brigade creations *)&lt;br /&gt;   bucket_alloc: Papr_bucket_alloc_t;&lt;br /&gt; // New for Apache 2.2&lt;br /&gt; (** The current state of this connection *)&lt;br /&gt; cs: Pointer;&lt;br /&gt; (** Is there data pending in the input filters? *)&lt;br /&gt; data_in_input_filters: Integer;&lt;br /&gt;end;&lt;/pre&gt;&lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Delphi 2009&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To create an Apache module with Delphi 2009, go to File-&gt;New-&gt;Other-&gt;Delphi Projects-&gt;WebBroker-&gt;WebServer Application and Select CGI Stand-Alone executable, then click OK. This will create a new project, then go to the "uses" clause of this project and replace CGIApp by ApacheTwoApp. That's it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Update: I added an example to this article, please, &lt;/span&gt;&lt;a style="font-weight: bold;" href="http://leonardorame.blogspot.com/2009/07/apache-22x-modules-with-delphi-ii.html"&gt;read it here&lt;/a&gt;&lt;span style="font-weight: bold;"&gt;.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5508239084038986223?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5508239084038986223/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5508239084038986223' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5508239084038986223'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5508239084038986223'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/04/apache-22x-modules-with-delphi.html' title='Apache 2.2.x modules with Delphi'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-7292293481397280187</id><published>2009-04-18T17:08:00.000-07:00</published><updated>2009-04-18T17:10:49.870-07:00</updated><title type='text'>Paginating TListView - Part 3 of 3</title><content type='html'>To finish this series of paginating TListViews, I'll show a simple method of automatic assignment of data from a database query to properties of an object.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Introducing RTTI&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Delphi and FreePascal provides the ability to get and set the values of object's properties at run-time, this is called Run Time Type Information, RTTI for short. This allows a great deal of flexibility at the moment of reducing code size and automating repetitive tasks.&lt;br /&gt;&lt;br /&gt;In the last example, the function GetCurrentPage executes a database query, then iterates trough it's dataset assigning each property of each item the FCustomers collection. One problem here, is that this function only works for TCustomers, so if we want to use another collection, we need to create a GetCurrentPage_for_our_new_tcollection and so on.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Automating GetCurrentPage&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To generalize our GetCurrentPage method, we need to RTTI-enable the TCustomer class by replacing the "public" keyword by "published". This change tells the compiler that it's properties will be available to the RTTI functions like SetPropValue.&lt;br /&gt;&lt;br /&gt;The second step is to include the TypInfo unit in the "uses" clause. This unit contains all the RTTI functions. I recommend further reading about it.&lt;br /&gt;&lt;br /&gt;Now, the last step. Just replacing the lines 20 to 25 of GetCurrentPage function with this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;lCustomer := FCustomers.Add;&lt;br /&gt;for I := 0 to IbQuery.Fields.Count - 1 do&lt;br /&gt;  SetPropValue(lCustomer, &lt;br /&gt;    IbQuery.Fields[I].FieldName, &lt;br /&gt;    IbQuery.Fields[I].Value);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Warning: This code works because the properties of TCustomer class have the same name as the fields returned by the query I used in the example.&lt;br /&gt;&lt;br /&gt;Now, I'll remove the references to FCustomers in  the function:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.GetCurrentPage(ACurrentPage: Integer; &lt;br /&gt;  ACollection: TCollection);&lt;br /&gt;var&lt;br /&gt;  lFrom: Integer;&lt;br /&gt;  lTo: Integer;&lt;br /&gt;  I: Integer;&lt;br /&gt;  lItem: TCollectionItem;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  (* Do the query *)&lt;br /&gt;  lFrom := ((ACurrentPage * cPageSize) - cPageSize) + 1;&lt;br /&gt;  lTo := (ACurrentPage * cPageSize) + 1;&lt;br /&gt;  IbQuery1.Close;&lt;br /&gt;  IbQuery1.SQL.Text :=&lt;br /&gt;   'select CustId, FirstName, LastName from customers ' +&lt;br /&gt;   'rows ' + IntToStr(lFrom) + ' to ' + IntToStr(lTo);&lt;br /&gt;  IbQuery1.Open;&lt;br /&gt;  (* Fill the collection *)&lt;br /&gt;  ACollection.Clear;&lt;br /&gt;  while not IbQuery1.Eof do&lt;br /&gt;  begin&lt;br /&gt;    lItem := ACollection.Add;&lt;br /&gt;    for I := 0 to IbQuery1.Fields.Count - 1 do&lt;br /&gt;      SetPropValue(lItem, &lt;br /&gt;        IbQuery1.Fields[I].FieldName, &lt;br /&gt;        IbQuery1.Fields[I].Value);&lt;br /&gt;    IbQuery1.Next;&lt;br /&gt;  end;&lt;br /&gt;  IbQuery1.Close;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can improve this by adding a new parameter for the Query to be executed inside the function. Also you have to adapt the ListView1Data event by this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);&lt;br /&gt;var&lt;br /&gt; lCurrPage: Integer;&lt;br /&gt; lPos: Integer;&lt;br /&gt;begin&lt;br /&gt; (* Get current page index *)&lt;br /&gt; lCurrPage := Item.Index div cPageSize;&lt;br /&gt; (* Get the position in the current page *)&lt;br /&gt; lPos := Item.Index - (lCurrPage * cPageSize);&lt;br /&gt;&lt;br /&gt; (* Page changed? refresh the data *)&lt;br /&gt; if FCurrentPage - 1 &lt;&gt; lCurrPage then&lt;br /&gt; begin&lt;br /&gt;   FCurrentPage := lCurrPage + 1;&lt;br /&gt;   GetCurrentPage(FCurrentPage, FCustomers);&lt;br /&gt; end;&lt;br /&gt;&lt;br /&gt; (* Paint the ListView's item with our TCollection's items *)&lt;br /&gt; Item.Caption := FCustomers[lPos].LastName;&lt;br /&gt; Item.SubItems.Add(FCustomers[lPos].FirstName);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is the end of the series. This example could be improved even more, one way that comes to my mind is the creating a TListView descendant component.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-7292293481397280187?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/7292293481397280187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=7292293481397280187' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/7292293481397280187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/7292293481397280187'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/04/paginating-tlistview-part-3-of-3.html' title='Paginating TListView - Part 3 of 3'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-3669282752175935504</id><published>2009-04-06T17:31:00.000-07:00</published><updated>2009-04-13T06:24:50.933-07:00</updated><title type='text'>Binding TCollections to paginating ListViews</title><content type='html'>In my previous article, I wrote about a method to paginate data shown in a ListView. &lt;br /&gt;Now, I want to extend its reach by replacing TDataSets by TCollections.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;What is a TCollection?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A TCollection is a container, where objects of only one type can be stored in it. &lt;br /&gt;The difference with other containers like TList and TObjectList is they can&lt;br /&gt;contain any kind of pointer, in the case of TList, and heterogenous objects in&lt;br /&gt;a TObjectList, on the other hand, in a TCollection,&lt;br /&gt;only objects of a specific class can be used.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A collection of Customers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To create a collection of Customers, we have to derive a class from TCollectionItem,&lt;br /&gt;for example TCustomer, and another class from TCollection, for example &lt;br /&gt;TCustomers, as follows:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;TCustomer = class(TCollectionItem)&lt;br /&gt;private&lt;br /&gt; FCustId: Integer;&lt;br /&gt; FLastName: string;&lt;br /&gt; FFirstName: string;&lt;br /&gt;public&lt;br /&gt; property CustId: Integer read FCustId write FCustId;&lt;br /&gt; property FirstName: string read FFirstName write FFirstName;&lt;br /&gt; property LastName: string read FLastName write FLastName;&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;TCustomers = class(TCollection)&lt;br /&gt;private&lt;br /&gt; function GetItem(AIndex: Integer): TCustomer;&lt;br /&gt;public&lt;br /&gt; function Add: TCustomer;&lt;br /&gt; property Items[AIndex: Integer]: TCustomer read GetItem; default;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now lets define the methods Add and GetItem:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;function TCustomers.GetItem(AIndex: Integer): TCustomer;&lt;br /&gt;begin&lt;br /&gt;  result := TCustomer(inherited GetItem(AIndex));&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;function TCustomers.Add: TCustomer;&lt;br /&gt;begin&lt;br /&gt;  result := TCustomer(inherited Add);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Save the above code in a unit called customers.pas, then create a new application,&lt;br /&gt;and add the newly created unit to the "uses" clause of the main form, then add&lt;br /&gt;the attribute FCustomers: TCustomers; to the private section of the form:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;type&lt;br /&gt;  TForm1 = class(TForm)&lt;br /&gt;  private&lt;br /&gt;    FCustomers: TCustomers;&lt;br /&gt;  public&lt;br /&gt;    { Public declarations }&lt;br /&gt;  end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now, override the OnCreate and OnDestroy methods of the main form to &lt;br /&gt;instantiate and free the collection:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.FormCreate(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt;  FCustomers := TCustomers.Create(TCustomer);&lt;br /&gt;end;&lt;br /&gt;&lt;br /&gt;procedure TForm1.FormDestroy(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt;  FCustomers.Free;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;From database to collection&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ok, the next step is to fill the collection with data. As I explained in my last&lt;br /&gt;article, place the database connectivity components in a DataModule or just&lt;br /&gt;in the main form (this is just an example) and name the TQuery as IbQuery1.&lt;br /&gt;&lt;br /&gt;Good, the next step is to adapt the FormCreate method of the last article&lt;br /&gt;to this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.FormCreate(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt;  (* Create an instance of the customers collection *)&lt;br /&gt;  FCustomers := TCustomers.Create(TCustomer);&lt;br /&gt;  (* Get the total amount of customers *)&lt;br /&gt;  IbQuery1.Close;&lt;br /&gt;  IbQuery1.SQL.Text := 'select count(*) from customers';&lt;br /&gt;  IbQuery1.Open;&lt;br /&gt;  ListView1.Items.Count := IbQuery1.Fields[0].Value;&lt;br /&gt;  // query for the first page&lt;br /&gt;  FCurrentPage := 1;&lt;br /&gt;  GetCurrentPage(FCurrentPage);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, to fill our TCollection with data from the database, just modify&lt;br /&gt;our GetCurrentPage method as this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.GetCurrentPage(ACurrentPage: Integer);&lt;br /&gt;var&lt;br /&gt;  lFrom: Integer;&lt;br /&gt;  lTo: Integer;&lt;br /&gt;  I: Integer;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;  (* Do the query *)&lt;br /&gt;  lFrom := ((ACurrentPage * cPageSize) - cPageSize) + 1;&lt;br /&gt;  lTo := (ACurrentPage * cPageSize) + 1;&lt;br /&gt;  IbQuery1.Close;&lt;br /&gt;  IbQuery1.SQL.Text :=&lt;br /&gt;   'select CustId, FirstName, LastName from customers ' +&lt;br /&gt;   'rows ' + IntToStr(lFrom) + ' to ' + IntToStr(lTo);&lt;br /&gt;  IbQuery1.Open;&lt;br /&gt;  (* Fill the collection *)&lt;br /&gt;  FCustomers.Clear;&lt;br /&gt;  while not IbQuery1.Eof do&lt;br /&gt;  begin&lt;br /&gt;    with FCustomers.Add do&lt;br /&gt;    begin&lt;br /&gt;      CustId := IbQuery1.FieldByName('CustId').AsInteger;&lt;br /&gt;      FirstName := IbQuery1.FieldByName('FirstName').AsString;&lt;br /&gt;      LastName := IbQuery1.FieldByName('LastName').AsString;&lt;br /&gt;    end;&lt;br /&gt;    IbQuery1.Next;&lt;br /&gt;  end;&lt;br /&gt;  IbQuery1.Close;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Let me tell you that assigning data by querying with FieldByName is very slow,&lt;br /&gt;but simple enough for the purpouse of this article, in a future post, I'll &lt;br /&gt;show an improved version.&lt;br /&gt;&lt;br /&gt;The last step, is to place the TListView in the form, set its properties as&lt;br /&gt;shown in my last article and create the new OnData method, &lt;br /&gt;adapted to our TCollection:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);&lt;br /&gt;var&lt;br /&gt; lCurrPage: Integer;&lt;br /&gt; lPos: Integer;&lt;br /&gt;begin&lt;br /&gt; (* Get current page index *)&lt;br /&gt; lCurrPage := Item.Index div cPageSize;&lt;br /&gt; (* Get the position in the current page *)&lt;br /&gt; lPos := Item.Index - (lCurrPage * cPageSize);&lt;br /&gt;&lt;br /&gt; (* Page changed? refresh the data *)&lt;br /&gt; if FCurrentPage - 1 &lt;&gt; lCurrPage then&lt;br /&gt; begin&lt;br /&gt;   FCurrentPage := lCurrPage + 1;&lt;br /&gt;   GetCurrentPage(FCurrentPage);&lt;br /&gt; end;&lt;br /&gt;&lt;br /&gt; (* Paint the ListView's item with our TCollection's items *)&lt;br /&gt; Item.Caption := FCustomers[lPos].LastName;&lt;br /&gt; Item.SubItems.Add(FCustomers[lPos].FirstName);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it. &lt;br /&gt;&lt;br /&gt;My next article, will be focused on creating an automated object binding method&lt;br /&gt;using RTTI, to avoid repetitive assignments by hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-3669282752175935504?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/3669282752175935504/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=3669282752175935504' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3669282752175935504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3669282752175935504'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/04/binding-tcollections-to-paginating.html' title='Binding TCollections to paginating ListViews'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-6819147352130812324</id><published>2009-04-05T12:27:00.000-07:00</published><updated>2009-04-05T13:56:34.637-07:00</updated><title type='text'>ListView and Pagination</title><content type='html'>This is one of those things I needed for ages, but didn't bothered to implement&lt;br /&gt;because I thought it could require an unnecessary amount of work, and allways&lt;br /&gt;opted for a less perfectionist method.&lt;br /&gt;&lt;br /&gt;I'm talking about a method to retrieve data in Pages, then browse it in a ListView&lt;br /&gt;transparently for the user.&lt;br /&gt;&lt;br /&gt;I know TDbGrid allows a similar behavior when connected to *some* database connectors,&lt;br /&gt;such as ADO, but what about a general method of browsing paged data independently&lt;br /&gt;of the database engine?.&lt;br /&gt;&lt;br /&gt;Let's start by creating an application and a data module, containing a database&lt;br /&gt;connection, a dataset and a transaction. I'll assume you know how to create&lt;br /&gt;connections to databases, datasets and how to do queries.&lt;br /&gt;&lt;br /&gt;Now, supposse the TDataSet is a TIbQuery component that allows to query an Interbase&lt;br /&gt;or Firebird database, and we have a table called Customers, with one million records.&lt;br /&gt;The table has three fields, CustId, FirstName and LastName.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;ListView in Virtual Mode&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Place a TListView in the main form, then set this properties:&lt;br /&gt;&lt;br /&gt;ViewStyle = vsReport&lt;br /&gt;Columns = (3 columns Id, FirstName, LastName)&lt;br /&gt;OwnerData = True&lt;br /&gt;&lt;br /&gt;The OwnerData property setted to True, means the ListView will not be a data repository&lt;br /&gt;by itself, it won't contain any data. To show it on screen,&lt;br /&gt;it will rely on its OnData method, who will be in charge of getting data from&lt;br /&gt;the dataset and paint the rows of the ListView.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;First attempt&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Let's do a first essay by getting All the data from the database. TListView&lt;br /&gt;in Virtual Mode, must know in advance how much data it will show, so&lt;br /&gt;first of all, override the Form's OnCreate method with this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.FormCreate(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt; IbQuery1.Close;&lt;br /&gt; IbQuery1.SQL.Text := 'select count(*) from customers';&lt;br /&gt; IbQuery1.Open;&lt;br /&gt; ListView1.Items.Count := IbQuery1.Fields[0].Value;&lt;br /&gt; // Re-Set the query&lt;br /&gt; IbQuery1.Close;&lt;br /&gt; IbQuery1.SQL.Text := 'select CustId, FirstName, LastName from customers';&lt;br /&gt; IbQuery1.Open;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The second step is to override the OnData method of the TListView with&lt;br /&gt;this code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);&lt;br /&gt;begin&lt;br /&gt; (* Move the DataSet's pointer to Item.Index, and paint the ListView's item *)&lt;br /&gt; IbQuery1.RecNo := Item.Index + 1;&lt;br /&gt; Item.Caption := IbQuery1.Fields[0].Value;&lt;br /&gt; Item.SubItems.Add(IbQuery1.fields[1].Value);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In small datasets, this code will perform really god, but as the dataset&lt;br /&gt;becomes huge, it will start getting slower, and slower.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Pagination&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;What I want to accomplish, is to divide the data in pages of say 100 records,&lt;br /&gt;then show each page at a time. Querying only 100 records each time, is&lt;br /&gt;practically instantaneous, so let's show the first page, then, when the user&lt;br /&gt;tries to browse after the 100 nt record, re-query the database for the&lt;br /&gt;101 to 200 and so on.&lt;br /&gt;&lt;br /&gt;Almost every database engine has a method to query just a slice of the data,&lt;br /&gt;MySql has "limit nn to mm", Firebird has "Rows nn to mm", MsSql has "Top", etc.&lt;br /&gt;So, I'll add a new method to my program, to let query only a given page:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.GetCurrentPage(ACurrentPage: Integer);&lt;br /&gt;var&lt;br /&gt; lFrom: Integer;&lt;br /&gt; lTo: Integer;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt; lFrom := ((ACurrentPage * cPageSize) - cPageSize) + 1;&lt;br /&gt; lTo := (ACurrentPage * cPageSize) + 1;&lt;br /&gt; IbQuery1.Close;&lt;br /&gt; IbQuery1.SQL.Text :=&lt;br /&gt;  'select CustId, FirstName, LastName from customers ' +&lt;br /&gt;  'rows ' + IntToStr(lFrom) + ' to ' + IntToStr(lTo);&lt;br /&gt; IbQuery1.Open;&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add the constant "const cPageSize = 100;" just after the "implementation" section&lt;br /&gt;of the unit, or simply replace cPageSize by 100. Also add a the internal attribute&lt;br /&gt;FCurrentPage: Integer; in the private section of the form.&lt;br /&gt;&lt;br /&gt;Now, go back to the OnCreate method, and change it by this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.FormCreate(Sender: TObject);&lt;br /&gt;begin&lt;br /&gt; IbQuery1.Close;&lt;br /&gt; IbQuery1.SQL.Text := 'select count(*) from customers';&lt;br /&gt; IbQuery1.Open;&lt;br /&gt; ListView1.Items.Count := IbQuery1.Fields[0].Value;&lt;br /&gt; // query for the first page&lt;br /&gt; FCurrentPage := 1;&lt;br /&gt; GetCurrentPage(FCurrentPage);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The last step is to slightly modify the OnData method with this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: delphi"&gt;&lt;br /&gt;procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);&lt;br /&gt;var&lt;br /&gt; lCurrPage: Integer;&lt;br /&gt; lPos: Integer;&lt;br /&gt;begin&lt;br /&gt; (* Get current page index *)&lt;br /&gt; lCurrPage := Item.Index div cPageSize;&lt;br /&gt; (* Get the position in the current page *)&lt;br /&gt; lPos := Item.Index - (lCurrPage * cPageSize);&lt;br /&gt;&lt;br /&gt; (* Page changed? refresh the data *)&lt;br /&gt; if FCurrentPage - 1 &lt;&gt; lCurrPage then&lt;br /&gt; begin&lt;br /&gt;   FCurrentPage := lCurrPage + 1;&lt;br /&gt;   GetDataPage(FCurrentPage);&lt;br /&gt; end;&lt;br /&gt;&lt;br /&gt; (* Paint the ListView's item *)&lt;br /&gt; IbQuery1.RecNo := lPos + 1;&lt;br /&gt; Item.Caption := IbQuery1.Fields[0].Value;&lt;br /&gt; Item.SubItems.Add(IbQuery1.fields[1].Value);&lt;br /&gt;end;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I hope you enyoyed this as much as I did when I wrote it. When I'll find&lt;br /&gt;time, I'll post a modified version using TCollections instead of&lt;br /&gt;TDataSets, just as I do in my projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-6819147352130812324?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/6819147352130812324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=6819147352130812324' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6819147352130812324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6819147352130812324'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/04/listview-and-pagination.html' title='ListView and Pagination'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-4238075434451703029</id><published>2009-03-19T07:50:00.000-07:00</published><updated>2009-03-19T10:27:55.130-07:00</updated><title type='text'>Transferring objects over the network (Part II)</title><content type='html'>Here is the sample code and executables for Transferring Objects Over the Network.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.geocities.com/martinrame/TransfObj.zip"&gt;Click here to download&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Edit: If the above url doesn't work due to Geocities limitations. Please, download the file from &lt;a href="http://www.ceciliastrada.com.ar/TransfObj.zip"&gt;this link&lt;/a&gt;.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-4238075434451703029?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/4238075434451703029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=4238075434451703029' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/4238075434451703029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/4238075434451703029'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/03/transferring-objects-over-network-part_19.html' title='Transferring objects over the network (Part II)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-3683408414546490876</id><published>2009-03-18T07:49:00.000-07:00</published><updated>2009-03-18T07:52:56.424-07:00</updated><title type='text'>Transferring objects over the network (Part I)</title><content type='html'>&lt;div&gt;&lt;b&gt;Introduction&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A couple of weeks ago, I was talking with a friend about a software he uses to book bus trips, he told me his company needs something similar, also he thinks I could adapt one of my applications to work like this program. The software is installed on more than one hundred sites, and it's connected to a central server over the Internet. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What resulted interesting to my friend was its speed, it works like if it was connected to a database in a local network (LAN), but it isn't. Also he added the server's Internet access is a cheap cable connection with ~300kb/s upload speed, and the clients connects using standard DSL or Cable connections. I asked what database and programming language the developers used. It's connected to a Firebird database and was programmed in C#, he replied. Then, I decided to try to replicate the scenario with a simple Delphi program.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My first attempt was a Delphi client connected to a Firebird 2.1 database using IBX controls. In my LAN, the app screams, but when I connect it over Internet it is as slow as a turtle. It seems to be that Firebird sends too much information over the network and it slows down the connection.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In my second attempt, I changed my app to use RemObject's SDK, I created an application server connected to the same database, it improved a lot...but if I'd choose the RO approach, I'd must rewrite the whole application.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Persistence&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My application is based on classes derived from TPersistent, and uses TCollection/TCollection Item to store data, it doesn't uses any DbAware component and to persist it's data, it dynamically loads a Dll containing what I call a Database Access Driver who knows, thanks to RTTI and a (not so) complex mechanism, how to talk to the database. The Driver it's using currently, contains the IBX controls to talk to Interbase/Firebird, and I thought it could be great if I can create a similar Driver to use RemObjects SDK, but it turned out to be a real pain (and I hadn't time to investigate RO SDK in detail).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;The application server&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;After the failed attemt to use RemObjects, I decided to create a simple Application Server, based on Indy's TCP Server control. The server should receive a request from the client asking for an object, or collection of objects, and the params to filter the result, then the server dynamically creates an instance of the requested class, fill its data, then converts it to XML and sends the result to the client, who must un-serialize the XML to reconstruct the instance. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I created the server side of the application, and found that the XML serialization part was too slow, resulting in more than 5 seconds for a collection of 500 items, maybe the serializer I used isn't optimized, and I didn't have time to improve by myself, so I decided to find a new serialization method...Thanks god and Roland Beenhakker I found &lt;a href="http://beensoft.blogspot.com/2006/11/how-to-persist-tpersistent.html"&gt;this article&lt;/a&gt; on how to write and read the content of a TPersistent descendant to/from a stream. The first time I tested the trick Roland exposed in his article, I was shocked, it serialized my collection instantaneously, the de-serialization was also super fast. &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;How the server store the objects in the database?, it uses the same Data Access Driver the older Client Application used to connect to the database.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;The client's Data Access Driver&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Knowing the Serialization/Deserialization method I'll use for the application, I focused on writing a new Data Access Driver for connecting to the Application Server. The task was pretty easy, just serialize objects, then use the WriteStream and ReadStream method of Indy to send and receive the streamed objects to/from the server.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;The real test&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When I finished writing the new Data Access Driver, I decided to try it in a real environment, connecting the client from a remote machine to my local PC by using my home Internet connection. The results where amazing, the whole round trip only took 1.5 seconds!, impressive.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;A little improvement&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Knowing that the serialization/deserialization process is instantaneous, and the only possible bottleneck is the network. I decided to compress the streams sent over the network, and used InflateStream and DeflateStream from the great &lt;a href="http://sourceforge.net/projects/tpabbrevia/"&gt;TurboPower Abbrevia&lt;/a&gt; library. The reduced stream size improved even more the results.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Side effects&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As the data sent over the network is binary, it is also very (if not impossible) difficult to be intercepted and used by a unauthorized people. And if you are very concerned about security, after compressing the stream you can encrypt it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One negative side effect of this approach, is that it only works with clients and servers written in Delphi and C++ Builder. To me, this is not a problem, because I maintain both, the server and the client, but if the server must be accessed by some other language, you could improve the protocol to let the server determine in which format the data should be transferred.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the next post, I'll show a minimalistic version of the program.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-3683408414546490876?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/3683408414546490876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=3683408414546490876' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3683408414546490876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3683408414546490876'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/03/transferring-objects-over-network-part.html' title='Transferring objects over the network (Part I)'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-8472718798290553946</id><published>2009-01-13T08:37:00.000-08:00</published><updated>2009-01-13T09:20:22.380-08:00</updated><title type='text'>Auto Update application</title><content type='html'>A very common problem for desktop application programmers is the method of upgrading/installing new versions of our applications, when you have, say 10 customers with 5 PC's running your apps and you add a new feature, you have to upgrade 50 computers!, a real pain.&lt;br /&gt;&lt;br /&gt;Last week, I was thinking of a method to solve that problem, and taught the way should be to move my applications to the web with IntraWeb or something similar, but after a couple of tests I came to the conclusion that &lt;span style="font-style: italic;"&gt;my &lt;/span&gt;applications (specially the Medical applications) can't run on a web browser, as they use many technologies very tied to the underlying operating system (multimedia recording cames to my mind). Then I dreamed about creating something like a browser for desktop applications, who connects to a server via Http then the server responds to events requested by the client to draw the dialogs and forms, that way, the client is allways up to date, just like the web pages we look every day. I made a couple of examples, but it turned out to be a very complicated task.&lt;br /&gt;&lt;br /&gt;After my failed attempt, I started to look for someone's else's work and found a couple of similar projects for Object Pascal, they are &lt;a href="http://msedocumenting.svn.sourceforge.net/viewvc/msedocumenting/mse/trunk/help/tutorials/mseifi/ifipipedemo/"&gt;MseIfi&lt;/a&gt; (from the author of &lt;a href="http://homepage.bluewin.ch/msegui/"&gt;MseIde/MseGui&lt;/a&gt;) and &lt;a href="http://www.realthinclient.org"&gt;RTC WAC&lt;/a&gt; (from &lt;a href="http://www.realthinclient.org"&gt;RealThinClient&lt;/a&gt;), very promising, but both are in its early alpha stages of development.&lt;br /&gt;&lt;br /&gt;Then, I went for XUL (Mozilla's rendering engine) and Adobe AIR, but I didn't like them because they are very complicated to program (lots of hand made XML/JavaScript) and too slow to compile/run, at least tha't was my impression compared to native Delphi programs.&lt;br /&gt;&lt;br /&gt;Well, let's see...My desktop apps run very well, and my customers love them. Why should I start using a different technology just because I found a stone in my way?. The solution was ages simplier than re-programming the whole thing, just include an Auto Update mechanism!.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;TAutoUpdate was born&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style: italic;"&gt;AutoUpdate &lt;/span&gt;mechanism needed was easy to create, I made a class I called TAutoUpdate, now implemented it in my currently running systems, and It works really neat!. Because of that, I wanted to share with anyone interested and created a new &lt;a style="font-weight: bold;" href="http://code.google.com/p/tautoupdate/"&gt;Google Code project&lt;/a&gt; for that matter.&lt;br /&gt;&lt;br /&gt;If you like it, or have any suggestion, please don't hesitate to share with me and the &lt;a href="http://groups.google.com/group/tautoupdate"&gt;community around TAutoUpdate&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Again, the link is this: &lt;a href="http://code.google.com/p/tautoupdate/"&gt;http://code.google.com/p/tautoupdate/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-8472718798290553946?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/8472718798290553946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=8472718798290553946' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8472718798290553946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8472718798290553946'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2009/01/auto-update-application.html' title='Auto Update application'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5690581377162014552</id><published>2008-12-09T04:38:00.000-08:00</published><updated>2008-12-10T11:24:18.629-08:00</updated><title type='text'>Launch application as a service</title><content type='html'>Sometimes you need an application to start before the user logs in to Windows, just as Services do. If you have the source code, this is not a problem, but what if you don't have access to it?.&lt;br /&gt;&lt;br /&gt;With this small Delphi program you can launch a list of applications as windows services very easily.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How to use it?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After creating the service with the code below, you'll have to create the file AppAsService.conf containing the list of applications you want to start using this structure: &lt;span style="font-style: italic;"&gt;[program];[parameters]&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Sample AppAsService.conf file:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;c:\program files\filesharer\filesharer.exe;autostart&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;c:\myprograms\utils\clock.exe&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is the code:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;b&gt;program&lt;/b&gt; AppAsService&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;uses&lt;/b&gt;&lt;br /&gt;SvcMgr&lt;b&gt;,&lt;/b&gt;&lt;br /&gt;main &lt;b&gt;in&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'main.pas'&lt;/span&gt; &lt;span style="color: rgb(0, 0, 255);"&gt;{Service1: TService}&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;{$R *.RES}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;Application&lt;b&gt;.&lt;/b&gt;Initialize&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;Application&lt;b&gt;.&lt;/b&gt;CreateForm&lt;b&gt;(&lt;/b&gt;TService1&lt;b&gt;,&lt;/b&gt; Service1&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;Application&lt;b&gt;.&lt;/b&gt;Run&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;b&gt;unit&lt;/b&gt; main&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;interface&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;uses&lt;/b&gt;&lt;br /&gt;Windows&lt;b&gt;,&lt;/b&gt; Messages&lt;b&gt;,&lt;/b&gt; SysUtils&lt;b&gt;,&lt;/b&gt; Classes&lt;b&gt;,&lt;/b&gt; SvcMgr&lt;b&gt;,&lt;/b&gt; ShellApi&lt;b&gt;,&lt;/b&gt; TlHelp32&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;type&lt;/b&gt;&lt;br /&gt;TService1 &lt;b&gt;=&lt;/b&gt; &lt;b&gt;class&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;TService&lt;b&gt;)&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;procedure&lt;/b&gt; ServiceStart&lt;b&gt;(&lt;/b&gt;Sender&lt;b&gt;:&lt;/b&gt; TService&lt;b&gt;;&lt;/b&gt; &lt;b&gt;var&lt;/b&gt; Started&lt;b&gt;:&lt;/b&gt; Boolean&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;procedure&lt;/b&gt; ServiceStop&lt;b&gt;(&lt;/b&gt;Sender&lt;b&gt;:&lt;/b&gt; TService&lt;b&gt;;&lt;/b&gt; &lt;b&gt;var&lt;/b&gt; Stopped&lt;b&gt;:&lt;/b&gt; Boolean&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;  private&lt;/b&gt;&lt;br /&gt;  FAppList&lt;b&gt;:&lt;/b&gt; TStringList&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;function&lt;/b&gt; pKill&lt;b&gt;(&lt;/b&gt;ExeFileName&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; Boolean&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;  public&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;function&lt;/b&gt; GetServiceController&lt;b&gt;:&lt;/b&gt; TServiceController&lt;b&gt;;&lt;/b&gt; &lt;b&gt;override&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;  end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;Service1&lt;b&gt;:&lt;/b&gt; TService1&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;implementation&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;{$R *.DFM}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;procedure&lt;/b&gt; ServiceController&lt;b&gt;(&lt;/b&gt;CtrlCode&lt;b&gt;:&lt;/b&gt; DWord&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt; &lt;b&gt;stdcall&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;Service1&lt;b&gt;.&lt;/b&gt;Controller&lt;b&gt;(&lt;/b&gt;CtrlCode&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;function&lt;/b&gt; TService1&lt;b&gt;.&lt;/b&gt;GetServiceController&lt;b&gt;:&lt;/b&gt; TServiceController&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;Result &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; ServiceController&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;procedure&lt;/b&gt; TService1&lt;b&gt;.&lt;/b&gt;ServiceStart&lt;b&gt;(&lt;/b&gt;Sender&lt;b&gt;:&lt;/b&gt; TService&lt;b&gt;;&lt;/b&gt; &lt;b&gt;var&lt;/b&gt; Started&lt;b&gt;:&lt;/b&gt; Boolean&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;  (*&lt;br /&gt;AppAsService.conf example: &lt;program&gt;;&lt;parameters&gt;&lt;br /&gt;c:\program files\Mozilla Firefox\firefox.exe;http:\\www.google.com&lt;br /&gt;*)&lt;/parameters&gt;&lt;/program&gt;&lt;/span&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;lPath&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;lName&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;I&lt;b&gt;:&lt;/b&gt; Integer&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;lApp&lt;b&gt;:&lt;/b&gt; PChar&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;lParams&lt;b&gt;:&lt;/b&gt; PChar&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;lPath &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; ExtractFilePath&lt;b&gt;(&lt;/b&gt;ParamStr&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;lName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; ExtractFileName&lt;b&gt;(&lt;/b&gt;ParamStr&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;lName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Copy&lt;b&gt;(&lt;/b&gt;lName&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; Pos&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'.'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; lName&lt;b&gt;)&lt;/b&gt; &lt;b&gt;-&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;FAppList &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TStringList&lt;b&gt;.&lt;/b&gt;Create&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;FAppList&lt;b&gt;.&lt;/b&gt;LoadFromFile&lt;b&gt;(&lt;/b&gt;lPath &lt;b&gt;+&lt;/b&gt; lName &lt;b&gt;+&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'.conf'&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;for&lt;/b&gt; I &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; FAppList&lt;b&gt;.&lt;/b&gt;Count &lt;b&gt;-&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt; &lt;b&gt;downto&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt; &lt;b&gt;do&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt; lApp &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; PChar&lt;b&gt;(&lt;/b&gt;Copy&lt;b&gt;(&lt;/b&gt;FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; Pos&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;';'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; &lt;b&gt;-&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;if&lt;/b&gt; &lt;b&gt;not&lt;/b&gt; FileExists&lt;b&gt;(&lt;/b&gt;lApp&lt;b&gt;)&lt;/b&gt; &lt;b&gt;then&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;   FAppList&lt;b&gt;.&lt;/b&gt;Delete&lt;b&gt;(&lt;/b&gt;I&lt;b&gt;)&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;else&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;(* ShellExecute the app *)&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;lParams &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; PChar&lt;b&gt;(&lt;/b&gt;Copy&lt;b&gt;(&lt;/b&gt;FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; Pos&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;';'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; &lt;b&gt;+&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; &lt;/code&gt;&lt;code&gt; &lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;Length&lt;b&gt;(&lt;/b&gt;FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;   ShellExecute&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'open'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; lApp&lt;b&gt;,&lt;/b&gt; lParams&lt;b&gt;,&lt;/b&gt; PChar&lt;b&gt;(&lt;/b&gt;ExtractFilePath&lt;b&gt;(&lt;/b&gt;lApp&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; SW_NORMAL&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;(* Replace FApplist[I] by the AppName *)&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;   FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt; &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; ExtractFileName&lt;b&gt;(&lt;/b&gt;lApp&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;procedure&lt;/b&gt; TService1&lt;b&gt;.&lt;/b&gt;ServiceStop&lt;b&gt;(&lt;/b&gt;Sender&lt;b&gt;:&lt;/b&gt; TService&lt;b&gt;;&lt;/b&gt; &lt;b&gt;var&lt;/b&gt; Stopped&lt;b&gt;:&lt;/b&gt; Boolean&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;I&lt;b&gt;:&lt;/b&gt; Integer&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;lProcess&lt;b&gt;:&lt;/b&gt; Cardinal&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;lProcHandle&lt;b&gt;:&lt;/b&gt; Cardinal&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;lExitCode&lt;b&gt;:&lt;/b&gt; Cardinal&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;for&lt;/b&gt; I &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt; &lt;b&gt;to&lt;/b&gt; FAppList&lt;b&gt;.&lt;/b&gt;Count &lt;b&gt;-&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt; &lt;b&gt;do&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt; pKill&lt;b&gt;(&lt;/b&gt;FAppList&lt;b&gt;[&lt;/b&gt;I&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;FAppList&lt;b&gt;.&lt;/b&gt;Free&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;function&lt;/b&gt; TService1&lt;b&gt;.&lt;/b&gt;pKill&lt;b&gt;(&lt;/b&gt;ExeFileName&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; Boolean&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;const&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;PROCESS_TERMINATE&lt;b&gt;=&lt;/b&gt;&lt;b&gt;$&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0001&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;ContinueLoop&lt;b&gt;:&lt;/b&gt; BOOL&lt;b&gt;;&lt;/b&gt;FSnapshotHandle&lt;b&gt;:&lt;/b&gt; THandle&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;FProcessEntry32&lt;b&gt;:&lt;/b&gt; TProcessEntry32&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;result &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; False&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;FSnapshotHandle &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; CreateToolhelp32Snapshot&lt;b&gt;(&lt;/b&gt;TH32CS_SNAPPROCESS&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;FProcessEntry32&lt;b&gt;.&lt;/b&gt;dwSize &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Sizeof&lt;b&gt;(&lt;/b&gt;FProcessEntry32&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;ContinueLoop &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Process32First&lt;b&gt;(&lt;/b&gt;FSnapshotHandle&lt;b&gt;,&lt;/b&gt;FProcessEntry32&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;while&lt;/b&gt; integer&lt;b&gt;(&lt;/b&gt;ContinueLoop&lt;b&gt;)&lt;/b&gt; &lt;b&gt;&amp;lt;&lt;/b&gt;&lt;b&gt;&gt;&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt; &lt;b&gt;do&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;if&lt;/b&gt; &lt;b&gt;(&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;UpperCase&lt;b&gt;(&lt;/b&gt;ExtractFileName&lt;b&gt;(&lt;/b&gt;FProcessEntry32&lt;b&gt;.&lt;/b&gt;szExeFile&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; &lt;b&gt;=&lt;/b&gt; UpperCase&lt;b&gt;(&lt;/b&gt;ExeFileName&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;br /&gt;&lt;b&gt;or&lt;/b&gt; &lt;b&gt;(&lt;/b&gt;UpperCase&lt;b&gt;(&lt;/b&gt;FProcessEntry32&lt;b&gt;.&lt;/b&gt;szExeFile&lt;b&gt;)&lt;/b&gt; &lt;b&gt;=&lt;/b&gt; UpperCase&lt;b&gt;(&lt;/b&gt;ExeFileName&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; &lt;b&gt;then&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;Result &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TerminateProcess&lt;b&gt;(&lt;/b&gt;OpenProcess&lt;b&gt;(&lt;/b&gt;PROCESS_TERMINATE&lt;b&gt;,&lt;/b&gt; BOOL&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;,&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;   FProcessEntry32&lt;b&gt;.&lt;/b&gt;th32ProcessID&lt;b&gt;)&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt; ContinueLoop &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Process32Next&lt;b&gt;(&lt;/b&gt;FSnapshotHandle&lt;b&gt;,&lt;/b&gt;FProcessEntry32&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;code&gt;  &lt;/code&gt;&lt;code&gt;CloseHandle&lt;b&gt;(&lt;/b&gt;FSnapshotHandle&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;br /&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5690581377162014552?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5690581377162014552/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5690581377162014552' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5690581377162014552'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5690581377162014552'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2008/12/launch-application-as-service.html' title='Launch application as a service'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-1212096559827431483</id><published>2008-10-16T08:31:00.000-07:00</published><updated>2008-10-16T09:39:48.243-07:00</updated><title type='text'>Delphi plugin by example</title><content type='html'>One of the top asked questions in Delphi newsgroups is how to place a form contained in a Dll into our application forms. This method allows to create pluggable applications, easier to maintain and customize.&lt;br /&gt;&lt;br /&gt;To accomplish our task, first of all we'll create a Dll using File -&gt; New -&gt; Other -&gt; Dll Wizard.  Delete the comment aboud memory managment then Save the Dll as "dllform.dpr".&lt;br /&gt;&lt;br /&gt;Now create a form inside the Dll with File -&gt; New -&gt; Form.  Go to the Object Inspector and change the name of the new form to MyDllForm then add some controls like buttons, labels and everything you want, then remove the "var Form1: TForm1;" reference, you don't need it. Save the form as "myform".&lt;br /&gt;&lt;br /&gt;Go back to the Dll source by going to Project -&gt; View Source, and look at the uses clause, a reference to the recently created form unit must be there. Try to compile by hitting Ctrl + F9, if anything fails, re-check the previous paragraphs.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Adding exportable code&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As you can't export a Form directly, you must create an exportable function who can export your form class.&lt;br /&gt;&lt;br /&gt;Go to your form's source and add just above the "implementation" section, this code:&lt;br /&gt;&lt;pre&gt;&lt;span style="color:GREEN;"&gt;&lt;i&gt;  // To get a reference of your form's class&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;  TMyFormClass &lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;class&lt;/b&gt;&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;of&lt;/b&gt;&lt;/span&gt; TMyDllForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:GREEN;"&gt;&lt;i&gt;  // To be able to export the form class.&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;  function&lt;/b&gt;&lt;/span&gt; MyFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt; stdcall&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt; export&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;Then create the body of MyFormClass function, go below the "implementation" section and write this:&lt;br /&gt;&lt;pre&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;function&lt;/b&gt;&lt;/span&gt; MyFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt; stdcall&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;begin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;Result &lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; TMyDllForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;end&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Now, you must tell your library what functions to export. This is easy, just go to Project -&gt; View Source and add this before the "end.":&lt;br /&gt;&lt;pre&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;exports&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;MyFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;Before compiling be sure to activate "Build with Runtime Packages" in Project -&gt; Options -&gt; Packages. When you click the checkbox, a ton of packages separated by a comma appears just below, leave only the "vcl" package.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The main form&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Create a new application by going to File -&gt; New -&gt; VCL Forms Application. This creates a new application with a main form called Form1. Go to the form and add a TButton and a TPanel.&lt;br /&gt;&lt;br /&gt;Why a TPanel?, whell, the TPanel will contain the form we'll load from the Dll. You can use a TPageControl with a TTabSheet instead, or any other container.&lt;br /&gt;&lt;br /&gt;Now we'll add a couple of private fields in the TForm1 class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;private&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;FLoadedForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;FLibHandle&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; Cardinal&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Now we'll implement a method to dynamically load the Dll. Go to the Form1, then to the Object Inspector -&gt; Events and double click on "OnCreate" and "OnDestroy" to create these two events, then write this code inside them:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;procedure&lt;/b&gt;&lt;/span&gt; TForm5&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;FormCreate&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;Sender&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TObject&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;begin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;FLibHandle &lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; LoadLibrary&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color:PURPLE;"&gt;'dllform.dll'&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;end&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;procedure&lt;/b&gt;&lt;/span&gt; TForm5&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;FormDestroy&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;Sender&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TObject&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;begin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;FLoadedForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;Free&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;FreeLibrary&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;FLibHandle&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;end&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;After that, double click on the TButton to add the code needed to call the exported function in the Dll and create an instance of TMyForm:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;procedure&lt;/b&gt;&lt;/span&gt; TForm1&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;Button1Click&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;Sender&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TObject&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;type&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;TMyFormClass &lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;function&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt; stdcall&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;var&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;lMyFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt; TMyFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;begin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;not&lt;/b&gt;&lt;/span&gt; Assigned&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;FLoadedForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;then&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;begin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;lMyFormClass &lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; GetProcAddress&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;FLibHandle&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;,&lt;/b&gt;&lt;/span&gt; &lt;span style="color:PURPLE;"&gt;'MyFormClass'&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;if&lt;/b&gt;&lt;/span&gt; &lt;span style=";font-size:78%;color:BLUE;"  &gt;@&lt;/span&gt;lMyFormClass &lt;span style=";font-size:78%;color:BLUE;"  &gt;&amp;lt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&amp;gt;&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;nil&lt;/b&gt;&lt;/span&gt; &lt;span style="color:RED;"&gt;&lt;b&gt;then&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;begin&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:GREEN;"&gt;&lt;i&gt;  // Create a TMyForm instance (not visible)&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;      FLoadedForm &lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; lMyFormClass&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;Create&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;(&lt;/b&gt;&lt;/span&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;nil&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;)&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span style="color:GREEN;"&gt;&lt;i&gt;// Place the Panel from TMyForm in Panel1&lt;br /&gt;&lt;/i&gt;&lt;/span&gt;      FLoadedForm&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;Controls&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;[&lt;/b&gt;&lt;/span&gt;&lt;span style="color:BROWN;"&gt;0&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;]&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;.&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;Parent &lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;:&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;=&lt;/span&gt; Panel1&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;end&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;end&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color:RED;"&gt;&lt;b&gt;end&lt;/b&gt;&lt;/span&gt;&lt;span style=";font-size:78%;color:BLUE;"  &gt;&lt;b&gt;;&lt;/b&gt;&lt;/span&gt;&lt;/pre&gt;Again, before compiling remember to enable "Build with Runtime Packages", as you did when compiling the Dll. If you don't do this, an "Cannot assign a TFont to a TFont" error will be raised when you click Button1.&lt;br /&gt;&lt;br /&gt;You can download a sample project from &lt;a style="font-weight: bold;" href="http://geocities.com/martinrame/TestPlugin.zip"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-1212096559827431483?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/1212096559827431483/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=1212096559827431483' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1212096559827431483'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1212096559827431483'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2008/10/delphi-plugin-by-example.html' title='Delphi plugin by example'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-6877204827619928172</id><published>2008-06-25T05:18:00.000-07:00</published><updated>2008-06-25T05:23:26.338-07:00</updated><title type='text'>FileSharer on GoogleCode</title><content type='html'>After receiving a long list of emails from FileSharer users asking for the source code, I decided to Open Source a Lite version of it.&lt;br /&gt;&lt;br /&gt;If you want to add/improve something, please contact me by mail.&lt;br /&gt;&lt;br /&gt;The code is here (currently only SVN access): &lt;a href="http://filesharer.googlecode.com/"&gt;http://filesharer.googlecode.com&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-6877204827619928172?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/6877204827619928172/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=6877204827619928172' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6877204827619928172'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6877204827619928172'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2008/06/filesharer-on-googlecode.html' title='FileSharer on GoogleCode'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-6728913023457051678</id><published>2008-06-15T12:19:00.000-07:00</published><updated>2008-06-15T12:48:36.517-07:00</updated><title type='text'>Ajax from ObjectPascal!</title><content type='html'>If you dreamed of creating Ajax/Web 2.0 applications using your favorite ObjectPascal compiler, and thought it's a difficult task because of JavaScript/CSS/DOM, and the lack of wrappers similar to those found in another languages. Now your dream come true thanks to &lt;a href="http://extpascal.googlecode.com/"&gt;ExtPascal&lt;/a&gt;, an Object Pascal wrapper to &lt;a href="http://www.extjs.com/"&gt;ExtJs&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;ExtPascal allows to build Ajax applications pretty much the same way you program GUI based applications using Delphi, Lazarus or &lt;a href="http://homepage.bluewin.ch/msegui/"&gt;MseIde/MseGUI&lt;/a&gt;...not visually, but coding (trust me, it's very easy).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Some examples&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To begin internalizing this concept, you can see some examples from the &lt;a href="http://pitinnu.com/cgi-bin/extpascalsamples"&gt;ExtPascal site&lt;/a&gt;, or going to &lt;a href="http://www.extjs.com/products/extjs/"&gt;ExtJs's samples site&lt;/a&gt;. So, if you liked what you saw, then start by reading this &lt;a href="http://code.google.com/p/extpascal/wiki/GettingStarted"&gt;Getting Started&lt;/a&gt; tutorial and have fun!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-6728913023457051678?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/6728913023457051678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=6728913023457051678' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6728913023457051678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6728913023457051678'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2008/06/ajax-from-objectpascal.html' title='Ajax from ObjectPascal!'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-6133501118317593359</id><published>2008-04-01T07:58:00.000-07:00</published><updated>2008-04-01T08:06:54.384-07:00</updated><title type='text'>New version of FileSharer!</title><content type='html'>&lt;span&gt;Hi, I improved FileSharer by adding two new exciting features:&lt;/span&gt;  &lt;ul&gt;&lt;li&gt;Connections tab to be informed on who's connected/uploading/downloading.&lt;/li&gt;&lt;li&gt;Simple authentication plugin. To add security to your published files/folders.&lt;/li&gt;&lt;/ul&gt;&lt;span&gt;What are you waiting for? &lt;/span&gt;&lt;a href="http://www.geocities.com/martinrame/FileSharer.zip"&gt;download it now!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A couple of screenshots:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_jiP-KkurPP8/R_JPI873siI/AAAAAAAAACQ/meXheuHgKyU/s1600-h/fs1.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_jiP-KkurPP8/R_JPI873siI/AAAAAAAAACQ/meXheuHgKyU/s320/fs1.jpg" alt="" id="BLOGGER_PHOTO_ID_5184293136271651362" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_jiP-KkurPP8/R_JPJM73sjI/AAAAAAAAACY/oECmKYjrnzA/s1600-h/fs2.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_jiP-KkurPP8/R_JPJM73sjI/AAAAAAAAACY/oECmKYjrnzA/s320/fs2.jpg" alt="" id="BLOGGER_PHOTO_ID_5184293140566618674" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-6133501118317593359?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/6133501118317593359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=6133501118317593359' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6133501118317593359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6133501118317593359'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2008/04/new-version-of-filesharer.html' title='New version of FileSharer!'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_jiP-KkurPP8/R_JPI873siI/AAAAAAAAACQ/meXheuHgKyU/s72-c/fs1.jpg' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-1461271849166390712</id><published>2008-03-20T13:15:00.000-07:00</published><updated>2008-03-20T13:41:46.837-07:00</updated><title type='text'>Vonage  calling from ObjectPascal</title><content type='html'>Last week, a customer asked if he could call using his &lt;a href="http://www.vonage.com"&gt;Vonage&lt;/a&gt; phone to call from an application I developed some time ago. After searching for a way to do this, I stumbled upon this &lt;a href="https://secure.click2callu.com/"&gt;tutorial&lt;/a&gt;, then adapted the examples to ObjectPascal. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The results&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The first problem I found was how to connect to an HTTPS site. Of course the answer was &lt;a href="http://synapse.ararat.cz"&gt;Synapse&lt;/a&gt; with the help of &lt;a href="www.openssl.org"&gt;OpenSSL&lt;/a&gt; libraries.&lt;br /&gt;&lt;br /&gt;I created couple of small functions that first log in to a Vonage account, then place the call. The functions are contained in this unit:&lt;br /&gt;&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;unit&lt;/span&gt; vonagecall;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;interface&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;(* Calls to Vonage phone *)&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; CallToVonage(AUserName, APassword, AFromNumber, AToNumber: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;): &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt;  Classes,&lt;br /&gt;  ssl_openssl,&lt;br /&gt;  httpsend;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetVonageNumbers(AUserName, APassword: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;): &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-comment"&gt;(* This function must be allways called from CallToVonage *)&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  lHttp: THTTPSend;&lt;br /&gt;  lParams: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;  lResponse: TStringStream;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := &lt;span class="pas-str"&gt;''&lt;/span&gt;;&lt;br /&gt;  lHttp := THTTPSend.Create;&lt;br /&gt;  lResponse := TStringStream.Create(&lt;span class="pas-str"&gt;''&lt;/span&gt;);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;    lParams := &lt;span class="pas-str"&gt;'username='&lt;/span&gt; + AUserName;&lt;br /&gt;    lParams := lParams + &lt;span class="pas-str"&gt;'&amp;amp;password='&lt;/span&gt; + APassword;&lt;br /&gt;    &lt;span class="pas-comment"&gt;(* Get Phone numbers *)&lt;/span&gt;&lt;br /&gt;    lHttp.HTTPMethod(&lt;span class="pas-str"&gt;'GET'&lt;/span&gt;, &lt;span class="pas-str"&gt;'https://secure.click2callu.com/tpcc/getnumbers?'&lt;/span&gt; + lParams);&lt;br /&gt;    lHttp.Document.SaveToStream(lResponse);&lt;br /&gt;    Result := lResponse.DataString;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;if&lt;/span&gt; Pos(&lt;span class="pas-str"&gt;':'&lt;/span&gt;, Result) &amp;gt; &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;      Result := &lt;span class="pas-str"&gt;'Failed to retrieve Vonage phone numbers. Check your username and password.'&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;finally&lt;/span&gt;&lt;br /&gt;    lResponse.Free;&lt;br /&gt;    lHttp.Free;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; CallToVonage(AUserName, APassword, AFromNumber, AToNumber: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;): &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-comment"&gt;(* This makes the real call *)&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  lHttp: THTTPSend;&lt;br /&gt;  lParams: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;  lResponse: TStringStream;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := &lt;span class="pas-str"&gt;''&lt;/span&gt;;&lt;br /&gt;  lHttp := THTTPSend.Create;&lt;br /&gt;  lResponse := TStringStream.Create(&lt;span class="pas-str"&gt;''&lt;/span&gt;);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;    lParams := &lt;span class="pas-str"&gt;'username='&lt;/span&gt; + AUserName;&lt;br /&gt;    lParams := lParams + &lt;span class="pas-str"&gt;'&amp;amp;password='&lt;/span&gt; + APassword;&lt;br /&gt;    lParams := lParams + &lt;span class="pas-str"&gt;'&amp;amp;fromnumber='&lt;/span&gt; + AFromNumber;&lt;br /&gt;    lParams := lParams + &lt;span class="pas-str"&gt;'&amp;amp;tonumber='&lt;/span&gt; + AToNumber;&lt;br /&gt;    GetVonageNumbers(AUserName, APassword);&lt;br /&gt;    lHttp.HTTPMethod(&lt;span class="pas-str"&gt;'GET'&lt;/span&gt;, &lt;span class="pas-str"&gt;'https://secure.click2callu.com/tpcc/makecall?'&lt;/span&gt; + lParams);&lt;br /&gt;    lHttp.Document.SaveToStream(lResponse);&lt;br /&gt;    Result := lResponse.DataString;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;finally&lt;/span&gt;&lt;br /&gt;    lResponse.Free;&lt;br /&gt;    lHttp.Free;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The unit requires you to deploy libeay32.dll and ssleay32.dll, you can download these files from OpenSSL.org project. There are Windows and Linux versions.&lt;br /&gt;&lt;br /&gt;To use it, just include the &lt;span style="font-weight:bold;"&gt;vonagecall&lt;/span&gt; unit in the &lt;span style="font-weight:bold;"&gt;uses&lt;/span&gt; of your program and call the function CallToVonage passing UserName, Password, FromNumber and ToNumber parameters.&lt;br /&gt;&lt;br /&gt;In this &lt;a href="http://www.geocities.com/martinrame/vonage.zip"&gt;zip file&lt;/a&gt;, I packaged everything you need to test the function.&lt;br /&gt;&lt;br /&gt;Hasta la próxima!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-1461271849166390712?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/1461271849166390712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=1461271849166390712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1461271849166390712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/1461271849166390712'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2008/03/vonage-calling-from-objectpascal.html' title='Vonage  calling from ObjectPascal'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-8295223707918084387</id><published>2007-12-02T16:36:00.000-08:00</published><updated>2007-12-02T17:25:24.711-08:00</updated><title type='text'>Powerful CGI  applications</title><content type='html'>In the article &lt;a href="http://z505.com/cgi-bin/qkcont/qkcont.cgi?p=Myths%20About%20CGI%20Scalability"&gt;Myths About CGI Scalability&lt;/a&gt; published some time ago by Lars from &lt;a href="http://www.z505.com"&gt;z505&lt;/a&gt;, one of the authors of &lt;a href="http://z505.com/powtils/idx.shtml"&gt;Powerful Web Utilities&lt;/a&gt;, he exposes a problem every CGI programmer faces with database applications. As you can see in that document, every time a CGI is called by a web browser, the operating system execute it and wait until its end showing the results to the client's browser. This operation is cached by the operating system after the first execution of the CGI, resulting in a faster execution of following calls.&lt;br /&gt;&lt;br /&gt;If a web application uses a database, it's very common to create a CGI that connects to the database, perform a query then format the results and return them to the client. Using this approach, every time a user try to execute the CGI program a new connection to the database is created, resulting in slow and inefficient response times.&lt;br /&gt;&lt;br /&gt;To resolve this problem, I created a simple CGI that sends socket commands to a program (I'll call it AppServer) wich takes care of database connections and heavy processes and returns its results to the CGI to be shown in the client's browser, using only one database connection to handle every CGI request. If the CGI finds the AppServer isn't running, it starts an instance and send the commands.&lt;br /&gt;&lt;br /&gt;One interesting part of this approach is it runs in cheap hosting accounts, using Apache, IIS or any CGI capable web server. Also it can be compiled using FreePascal or Delphi.&lt;br /&gt;&lt;br /&gt;The example uses &lt;a href="http://z505.com/powtils/idx.shtml"&gt;PWU&lt;/a&gt; and &lt;a href="http://synapse.ararat.cz"&gt;Synapse&lt;/a&gt; for the AppServer, but you can use WebBroker or any other CGI technology.&lt;br /&gt;&lt;br /&gt;You can &lt;a href="http://www.geocities.com/martinrame/cgiserver.zip"&gt;&lt;b&gt;download&lt;/b&gt;&lt;/a&gt; the CGI client and server to give it a try.&lt;br /&gt;&lt;br /&gt;The CGI side is this:&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt;  classes,&lt;br /&gt;  PwuMain,&lt;br /&gt;  HttpSend,&lt;br /&gt;  Windows;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetHtml: Boolean;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  lHttp: THTTPSend;&lt;br /&gt;  lHtml:  TStringList;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := False;&lt;br /&gt;  lHtml := TStringList.Create;&lt;br /&gt;  lHttp := THTTPSend.Create;&lt;br /&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;with&lt;/span&gt; lHttp &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;    HTTPMethod(&lt;span class="pas-str"&gt;'GET'&lt;/span&gt;, &lt;span class="pas-str"&gt;'localhost:85/'&lt;/span&gt;);&lt;br /&gt;    lHtml.LoadFromStream(Document);&lt;br /&gt;    Result := lHtml.Text &amp;lt;&amp;gt; &lt;span class="pas-str"&gt;''&lt;/span&gt;;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;out&lt;/span&gt;(lHtml.Text);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  lHttp.Free;&lt;br /&gt;  lHtml.Free;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  &lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  lHandle: longint;&lt;br /&gt;  &lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;if&lt;/span&gt; &lt;span class="pas-kwd"&gt;not&lt;/span&gt; GetHtml &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-comment"&gt;(* If not connected, start the process *)&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-preproc"&gt;{$IFDEF WIN32}&lt;/span&gt;&lt;br /&gt;    lHandle := ShellExecute(&lt;span class="pas-num"&gt;0&lt;/span&gt; , &lt;span class="pas-str"&gt;'open'&lt;/span&gt;, &lt;span class="pas-str"&gt;'.\httpserver.exe'&lt;/span&gt;,&lt;br /&gt;      &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;, &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;, SW_HIDE);&lt;br /&gt;    &lt;span class="pas-preproc"&gt;{$ELSE}&lt;/span&gt;&lt;br /&gt;    lHandle := Shell(&lt;span class="pas-str"&gt;'./httpserver'&lt;/span&gt;);&lt;br /&gt;    &lt;span class="pas-preproc"&gt;{$ENDIF}&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;while&lt;/span&gt; True &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; GetHtml &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        Break;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-8295223707918084387?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/8295223707918084387/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=8295223707918084387' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8295223707918084387'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/8295223707918084387'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2007/12/powerful-cgi-applications.html' title='Powerful CGI  applications'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5418414720061356379</id><published>2007-11-12T11:55:00.000-08:00</published><updated>2011-03-09T04:06:58.281-08:00</updated><title type='text'>Using Sqlite3 with Fcl-Db</title><content type='html'>In this tutorial, I'll try to explain how to access Sqlite3 databases using&lt;br /&gt;&lt;span style="font-style: italic;"&gt;FreePascal&lt;/span&gt; and Fcl-Db. Fcl-Db is the &lt;span style="font-style: italic;"&gt;standard&lt;/span&gt; way of accessing databases using FreePascal, but you can find some other methods to connect to them, such as &lt;a href="http://sourceforge.net/project/showfiles.php?group_id=103463"&gt;&lt;span style="text-decoration: underline;"&gt;LibSql&lt;/span&gt;&lt;/a&gt; and &lt;span style="text-decoration: underline;"&gt;&lt;a href="http://zeos.firmos.at/"&gt;Zeos&lt;/a&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The first step is to install Sqlite versión 3.x.x and its development library (in Ubuntu is libsqlite3-dev), please go to &lt;a href="http://www.blogger.com/www.sqlite.org"&gt;www.sqlite.org&lt;/a&gt; to download a stable version and install in your computer. I'll don't explain how to install it, you are a programmer, you should know how to install a program.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Creating the "tutorial" database&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Creating a database using the command line SQLite Client is as easy as:&lt;br /&gt;&lt;pre&gt;&gt;sqlite3 tutorial.db&lt;/pre&gt;With that command, you created a file named "tutorial.db" and started the command line SQLite Client. Note I'm using sqlite3, not sqlite, the older is a client for 2.8.xx version.&lt;br /&gt;&lt;br /&gt;Now, create the customers table using this command:&lt;br /&gt;&lt;pre&gt;sqlite&gt; create table customers(id integer not null primary key,&lt;br /&gt;...&gt; firstname varchar(100),&lt;br /&gt;...&gt; lastname varchar(100),&lt;br /&gt;...&gt; borndate date);&lt;/pre&gt;After creating the table, return to the shell:&lt;br /&gt;&lt;pre&gt;sqlite&gt; .quit&lt;/pre&gt;You can do a 'ls' in Linux to check if the file tutorial.db exists.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Sample program&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The following sample program assumes you have created the &lt;b&gt;tutorial.db&lt;/b&gt; database in the same directory as the compiled program, just type it in any text editor, then save as &lt;b&gt;sqlitetest.pp&lt;/b&gt; and call FreePascal compiler from the command line using this command:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;fpc -Sd -B -CX -Xs sqlitetest.pp&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The program&lt;/span&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;b&gt;program&lt;/b&gt; SqliteTest;&lt;br /&gt;&lt;br /&gt;uses&lt;br /&gt; db&lt;b&gt;,&lt;/b&gt; sqlite3ds&lt;b&gt;,&lt;/b&gt; SysUtils&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;{$mode objfpc}&lt;br /&gt;&lt;br /&gt;var&lt;br /&gt; dsTutorial&lt;b&gt;:&lt;/b&gt; TSQLite3Dataset&lt;b&gt;;&lt;/b&gt;&lt;br /&gt; mSql&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;  (* TFields declarations&lt;br /&gt;    You can use Field[n].AsString but its slower *)&lt;/span&gt;&lt;br /&gt; mId&lt;b&gt;:&lt;/b&gt; TIntegerField&lt;b&gt;;&lt;/b&gt;&lt;br /&gt; mFirstName&lt;b&gt;:&lt;/b&gt; TStringField&lt;b&gt;;&lt;/b&gt;&lt;br /&gt; mLastName&lt;b&gt;:&lt;/b&gt; TStringField&lt;b&gt;;&lt;/b&gt;&lt;br /&gt; mBornDate&lt;b&gt;:&lt;/b&gt; TDateTimeField&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt; dsTutorial &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TSqlite3Dataset&lt;b&gt;.&lt;/b&gt;Create&lt;b&gt;(&lt;/b&gt;&lt;b&gt;nil&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;  try&lt;br /&gt; &lt;b&gt;with&lt;/b&gt; dsTutorial do&lt;br /&gt; begin&lt;br /&gt;   FileName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'tutorial.db'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   TableName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'customers'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   PrimaryKey &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Id'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Define the Insert skleton -uptate and delete works the same way-*)&lt;/span&gt;&lt;br /&gt;   mSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'insert into customers(firstname, lastname, borndate) '&lt;/span&gt; &lt;b&gt;+&lt;/b&gt;&lt;br /&gt;     &lt;span style="color: rgb(0, 160, 0);"&gt;'values('&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'%s'&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;', '&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'%s'&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;', %f)'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Non transactional method *)&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Insert first customer *)&lt;/span&gt;&lt;br /&gt;   Sql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Format&lt;b&gt;(&lt;/b&gt;mSql&lt;b&gt;,&lt;/b&gt; &lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'Leonardo'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Ramé'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; Now&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   ExecSql&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Insert second customer *)&lt;/span&gt;&lt;br /&gt;   Sql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Format&lt;b&gt;(&lt;/b&gt;mSql&lt;b&gt;,&lt;/b&gt; &lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'Michael'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Stratten'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; Now&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   ExecSql&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Transactional method *)&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* I don't really know why I must populate the TFields using a Select,&lt;br /&gt;&lt;/span&gt;       &lt;span style="color: rgb(0, 0, 255);"&gt;if you know an elegant way to accomplish this, please tell me. *)&lt;/span&gt;&lt;br /&gt;   Sql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'select Id, FirstName, LastName, BornDate from customers limit 1'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Open&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Assign TFields *)&lt;/span&gt;&lt;br /&gt;   mId &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TIntegerField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mFirstName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TStringField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mLastName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TStringField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;2&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mBornDate &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TDateTimeField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;3&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Append, Edit or Insert for the first field *)&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Append&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mFirstName&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Juan'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mLastName&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Pérez'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mBornDate&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Now&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Post&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Append, Edit or Insert for the second field *)&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Insert&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mFirstName&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Johan'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mLastName&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'Arndth'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   mBornDate&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Now&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Post&lt;b&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;    &lt;span style="color: rgb(0, 0, 255);"&gt;(* This commits the data to the db. *)&lt;/span&gt;&lt;br /&gt;   ApplyUpdates&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* Now, select all fields *)&lt;/span&gt;&lt;br /&gt;   Close&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Sql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'select Id, FirstName, LastName, BornDate from customers'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   Open&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: rgb(0, 0, 255);"&gt;(* To go to the first record of the DataSet, use First.&lt;br /&gt;&lt;/span&gt;       &lt;span style="color: rgb(0, 0, 255);"&gt;This isn't usefull here since Open points to the first record,&lt;br /&gt;&lt;/span&gt;       &lt;span style="color: rgb(0, 0, 255);"&gt;but you'll need in your projects so I keep it in the example. *)&lt;/span&gt;&lt;br /&gt;   First&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   &lt;b&gt;while&lt;/b&gt; &lt;b&gt;not&lt;/b&gt; Eof do&lt;br /&gt;   begin&lt;br /&gt;     mId &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TIntegerField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;     mFirstName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TStringField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;     mLastName &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TStringField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;2&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;     mBornDate &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TDateTimeField&lt;b&gt;(&lt;/b&gt;Fields&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;3&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;     writeln &lt;b&gt;(&lt;/b&gt;IntToStr&lt;b&gt;(&lt;/b&gt;mId&lt;b&gt;.&lt;/b&gt;Value&lt;b&gt;)&lt;/b&gt; &lt;b&gt;+&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;' - '&lt;/span&gt; &lt;b&gt;+&lt;/b&gt; mFirstName&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;+&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;', '&lt;/span&gt; &lt;b&gt;+&lt;/b&gt;&lt;br /&gt;       mLastName&lt;b&gt;.&lt;/b&gt;Value &lt;b&gt;+&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;' - '&lt;/span&gt; &lt;b&gt;+&lt;/b&gt; DateToStr&lt;b&gt;(&lt;/b&gt;mBornDate&lt;b&gt;.&lt;/b&gt;Value&lt;b&gt;)&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;     &lt;span style="color: rgb(0, 0, 255);"&gt;(* Move to the next record *)&lt;/span&gt;&lt;br /&gt;     Next&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;   &lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt; &lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt; finally&lt;br /&gt;   dsTutorial&lt;b&gt;.&lt;/b&gt;Free&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;  end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;end&lt;b&gt;.&lt;/b&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5418414720061356379?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5418414720061356379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5418414720061356379' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5418414720061356379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5418414720061356379'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2007/11/using-sqlite3-with-fcl-db.html' title='Using Sqlite3 with Fcl-Db'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-2767275149083266248</id><published>2007-10-03T10:57:00.000-07:00</published><updated>2007-10-03T11:10:51.026-07:00</updated><title type='text'>Session class for SQLite</title><content type='html'>I needed Session management for a web project I made using Delphi with WebBroker, and found a unit called MISessionClass in newsgroups created by Tony Caduto. The problem with that unit was it worked with Interbase and my web hosting provider doesn't inlcude Interbase/Firebird in the package I purchased.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;SQLite to the rescue&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;My hosting provider doesn't allow me to use Interbase/Firebird, but I can use SQLite to store my session data. To access SQLite databases, I use the exelent TLiteDb connector by Renè for DuBaron.com and adapted the unit mentioned in the previous paragraph to this database.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;This is the result:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;b&gt;unit&lt;/b&gt; SqliteSessionClass&lt;b&gt;;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;{**********************************************************************&lt;br /&gt;Open Source WebBroker Session Management Class For SQLite&lt;br /&gt;&lt;br /&gt;All database management is done by TLiteDb object by Rene from dubaron.com&lt;br /&gt;&lt;br /&gt;Modified version of Tony Caduto's MISessionClass.&lt;br /&gt;Programmed by Leonardo M. Ramé (martinrame at yahoo dot com)&lt;br /&gt;Sep 22th, 2007&lt;br /&gt;&lt;br /&gt;Some Code based loosely on the MDweb Components by Mark Brittingham&lt;br /&gt;Programming by Tony Caduto (tcad...@amsoftwaredesign.com)&lt;br /&gt;May 24th, 2001&lt;br /&gt;This class uses a IBexpress database,transaction and query which are created in the&lt;br /&gt;constructor.&lt;br /&gt;To use you must create a table on some interbase server (it could be on the&lt;br /&gt;same pc as the webserver or on a remote server) called SESSIONS which&lt;br /&gt;contains three fields:&lt;br /&gt;&lt;br /&gt;SESSIONID      (integer)&lt;br /&gt;SESSTIMESTAMP  (Timestamp)&lt;br /&gt;SESSDATA       (blob)&lt;br /&gt;&lt;br /&gt;In the constructor set your database name IE www.myserver.com:c:\yourpath\yourdatabase.gdb&lt;br /&gt;and the SQL dialect&lt;br /&gt;&lt;br /&gt;Add a public declaration for the class IE&lt;br /&gt;PUBLIC&lt;br /&gt; SessionMgr:TMISession;&lt;br /&gt;&lt;br /&gt;In your webmodule add the following:&lt;br /&gt;in the oncreate event&lt;br /&gt;SessionMgr:=TMISession.create;&lt;br /&gt;in the ondestroy event add:&lt;br /&gt;SessionMgr.free;&lt;br /&gt;&lt;br /&gt;Use is very similar to MDweb without all the extra stuff IE special tags etc&lt;br /&gt;This class works with the standard Webbroker pageproducer.&lt;br /&gt;&lt;br /&gt;In the before dispatch add something like this:&lt;br /&gt; if request.queryFields.Values['islogon'] &lt;&gt; 'true' then&lt;br /&gt;      begin&lt;br /&gt;           SessionMgr.SessionID :=&lt;br /&gt;             StrToIntDef(Request.CookieFields.Values['SID'], 0);&lt;br /&gt;           if (Not SessionMgr.FindSessionRecord) then&lt;br /&gt;           Response.SendRedirect('http://www.yourserver.com/timeout.htm');&lt;br /&gt;      end;&lt;br /&gt;&lt;br /&gt;In your logon or other action add code like this:&lt;br /&gt; SessionMgr.CreateSessionRecord;&lt;br /&gt;     cookiestrings:=tstringlist.create;&lt;br /&gt;     cookiestrings.add('SID='+intTostr(sessionmgr.SessionID));&lt;br /&gt;     response.SetCookieField(cookiestrings,'','',now+1,false);&lt;br /&gt;     SessionMgr.Values['USERID']        := userid;&lt;br /&gt;     SessionMgr.Values['PINNUMBER']     := pinnumber;&lt;br /&gt;     SessionMgr.Values['COMPANYPATH']   := companypath ;&lt;br /&gt;     SessionMgr.Values['TOPATH']        := topath ;&lt;br /&gt;     SessionMgr.Values['FROMPATH']      := frompath ;&lt;br /&gt;     SessionMgr.Values['COMPANYNAME']   := companyname;&lt;br /&gt;     SessionMgr.Values['COMPANYID']     := CompanyID;&lt;br /&gt;     SessionMgr.Values['FIRSTNAME']     := firstname;&lt;br /&gt;     SessionMgr.Values['LASTNAME']      := Lastname;&lt;br /&gt;     SessionMgr.Values['MI']            := MidIntial;&lt;br /&gt;     SessionMgr.Values['EMAIL']         := UserEmail;&lt;br /&gt;&lt;br /&gt;     To read or set values do this:&lt;br /&gt;&lt;br /&gt;     SET A value&lt;br /&gt;     SessionMgr.values['Firstname'] := 'Tony';&lt;br /&gt;&lt;br /&gt;     Get a Value&lt;br /&gt;     thelastname:=  SessionMgr.values['Lastname']&lt;br /&gt;&lt;br /&gt;     Because this is not based on a shared btree this class should work on&lt;br /&gt;     linux as well as with&lt;br /&gt;     CGI.  You are not limited to ISAPI&lt;br /&gt;&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;interface&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;uses&lt;/b&gt;&lt;br /&gt;sysutils&lt;b&gt;,&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;classes&lt;b&gt;,&lt;/b&gt;&lt;br /&gt;passqlite&lt;b&gt;,&lt;/b&gt;&lt;br /&gt;passql&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;type&lt;/b&gt;&lt;br /&gt;  TLiteSession &lt;b&gt;=&lt;/b&gt; &lt;b&gt;class&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;Tobject&lt;b&gt;)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;private&lt;/b&gt;&lt;br /&gt;    SessionDatabase&lt;b&gt;:&lt;/b&gt; TLiteDb&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;protected&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;public&lt;/b&gt;&lt;br /&gt;    ExpireInterval &lt;b&gt;:&lt;/b&gt; TDateTime&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;    SessionID&lt;b&gt;:&lt;/b&gt; Integer&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;    SessionTimeStamp&lt;b&gt;:&lt;/b&gt; TDateTime&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;    SessionData&lt;b&gt;:&lt;/b&gt; TStringList&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;procedure&lt;/b&gt; SetValue&lt;b&gt;(&lt;/b&gt;&lt;b&gt;const&lt;/b&gt; &lt;b&gt;Name&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; Value&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;function&lt;/b&gt; GetValue&lt;b&gt;(&lt;/b&gt;&lt;b&gt;const&lt;/b&gt; &lt;b&gt;Name&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;function&lt;/b&gt;  FindSessionRecord&lt;b&gt;:&lt;/b&gt;boolean&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;procedure&lt;/b&gt; DeleteExpiredSessions&lt;b&gt;(&lt;/b&gt;thetime&lt;b&gt;:&lt;/b&gt;tdatetime&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;procedure&lt;/b&gt; CreateSessionRecord&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;property&lt;/b&gt;  Values&lt;b&gt;[&lt;/b&gt;&lt;b&gt;const&lt;/b&gt; &lt;b&gt;Name&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt; read GetValue write SetValue&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;constructor&lt;/b&gt; Create&lt;b&gt;(&lt;/b&gt;ADataBase&lt;b&gt;:&lt;/b&gt; TLiteDb&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;destructor&lt;/b&gt; Destroy&lt;b&gt;;&lt;/b&gt; &lt;b&gt;override&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;published&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;implementation&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;{ TLiteSession }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;constructor&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;Create&lt;b&gt;(&lt;/b&gt;ADataBase&lt;b&gt;:&lt;/b&gt; TLiteDb&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;b&gt;inherited&lt;/b&gt; Create&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;SessionDatabase &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; ADataBase&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;SessionData &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; TStringList&lt;b&gt;.&lt;/b&gt;Create&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;procedure&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;CreateSessionRecord&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;lSql&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;//Delete any expired sessions before createing a new one&lt;/span&gt;&lt;br /&gt;DeleteExpiredSessions&lt;b&gt;(&lt;/b&gt;Now &lt;b&gt;-&lt;/b&gt; ExpireInterval&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;//********************create random ID number ***************************************&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;randomize&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;SessionID &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Random&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;2000000000&lt;/span&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;//*******************Add new session to the database*********************************&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt;&lt;br /&gt;  &lt;span style="color: rgb(0, 160, 0);"&gt;'INSERT INTO sessions(SESSIONID,SESSIONTIMESTAMP,SESSIONDATA)'&lt;/span&gt; &lt;b&gt;+&lt;/b&gt;&lt;br /&gt;  &lt;span style="color: rgb(0, 160, 0);"&gt;'VALUES (%d, '&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'%s'&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;', '&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'%s'&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;')'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Format&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;,&lt;/b&gt; &lt;b&gt;[&lt;/b&gt;sessionID&lt;b&gt;,&lt;/b&gt; FormatDateTime&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'yyyy-mm-dd hh:mm:ss'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; now&lt;b&gt;)&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;''&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;SessionDatabase&lt;b&gt;.&lt;/b&gt;Query&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;procedure&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;DeleteExpiredSessions&lt;b&gt;(&lt;/b&gt;thetime&lt;b&gt;:&lt;/b&gt; tdatetime&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;lSql&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: rgb(0, 160, 0);"&gt;'DELETE From SESSIONS WHERE (SESSIONTIMESTAMP &amp;lt; %f)'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Format&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;,&lt;/b&gt; &lt;b&gt;[&lt;/b&gt;thetime&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;SessionDatabase&lt;b&gt;.&lt;/b&gt;Query&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;destructor&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;Destroy&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;SessionData&lt;b&gt;.&lt;/b&gt;Free&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;inherited&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;function&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;FindSessionRecord&lt;b&gt;:&lt;/b&gt; boolean&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;lSql&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;Result &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; False&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;//**************First Check for Expired Sessions********************************&lt;/span&gt;&lt;br /&gt;DeleteExpiredSessions&lt;b&gt;(&lt;/b&gt;Now &lt;b&gt;-&lt;/b&gt; ExpireInterval&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;//******************************************************************************&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Format&lt;b&gt;(&lt;/b&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'select SESSIONID, SESSIONTIMESTAMP, SESSIONDATA from SESSIONS Where SESSIONID = %d'&lt;/span&gt;&lt;b&gt;,&lt;/b&gt; &lt;b&gt;[&lt;/b&gt;SessionID&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;if&lt;/b&gt; SessionDatabase&lt;b&gt;.&lt;/b&gt;Query&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;)&lt;/b&gt; &lt;b&gt;then&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;  &lt;b&gt;if&lt;/b&gt; SessionDatabase&lt;b&gt;.&lt;/b&gt;RowCount &lt;b&gt;&gt;&lt;/b&gt; &lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt; &lt;b&gt;then&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;    SessionId &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; StrToInt&lt;b&gt;(&lt;/b&gt;SessionDatabase&lt;b&gt;.&lt;/b&gt;Results&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;Strings&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;    ShortDateFormat &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'yyyy-mm-dd'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;    DateSeparator &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; &lt;span style="color: rgb(0, 160, 0);"&gt;'-'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;    SessionTimeStamp&lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; StrToDateTime&lt;b&gt;(&lt;/b&gt;SessionDatabase&lt;b&gt;.&lt;/b&gt;Results&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;Strings&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;1&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;    SessionData&lt;b&gt;.&lt;/b&gt;Text &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; SessionDatabase&lt;b&gt;.&lt;/b&gt;Results&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;0&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;Strings&lt;b&gt;[&lt;/b&gt;&lt;span style="color: rgb(224, 0, 0);"&gt;2&lt;/span&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;    Result &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; True&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;  &lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;function&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;GetValue&lt;b&gt;(&lt;/b&gt;&lt;b&gt;const&lt;/b&gt; &lt;b&gt;Name&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;result &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; SessionData&lt;b&gt;.&lt;/b&gt;Values&lt;b&gt;[&lt;/b&gt;&lt;b&gt;Name&lt;/b&gt;&lt;b&gt;]&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;procedure&lt;/b&gt; TLiteSession&lt;b&gt;.&lt;/b&gt;SetValue&lt;b&gt;(&lt;/b&gt;&lt;b&gt;const&lt;/b&gt; &lt;b&gt;Name&lt;/b&gt;&lt;b&gt;,&lt;/b&gt; Value&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;var&lt;/b&gt;&lt;br /&gt;lSql&lt;b&gt;:&lt;/b&gt; &lt;b&gt;string&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;begin&lt;/b&gt;&lt;br /&gt;SessionData&lt;b&gt;.&lt;/b&gt;Values&lt;b&gt;[&lt;/b&gt;&lt;b&gt;Name&lt;/b&gt;&lt;b&gt;]&lt;/b&gt; &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Value&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 0, 255);"&gt;//Save session string list to the database&lt;/span&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt;&lt;br /&gt;  &lt;span style="color: rgb(0, 160, 0);"&gt;'UPDATE SESSIONS SET SESSIONDATA = '&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;'%s'&lt;/span&gt;&lt;span style="color: rgb(0, 160, 0);"&gt;' WHERE SESSIONID = %d'&lt;/span&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;lSql &lt;b&gt;:&lt;/b&gt;&lt;b&gt;=&lt;/b&gt; Format&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;,&lt;/b&gt; &lt;b&gt;[&lt;/b&gt;sessiondata&lt;b&gt;.&lt;/b&gt;text&lt;b&gt;,&lt;/b&gt; SessionID&lt;b&gt;]&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;SessionDatabase&lt;b&gt;.&lt;/b&gt;QueryOne&lt;b&gt;(&lt;/b&gt;lSql&lt;b&gt;)&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;end&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-2767275149083266248?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/2767275149083266248/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=2767275149083266248' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/2767275149083266248'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/2767275149083266248'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2007/10/session-class-for-sqlite.html' title='Session class for SQLite'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-5557709268363169703</id><published>2007-05-13T21:41:00.000-07:00</published><updated>2007-05-13T21:55:05.348-07:00</updated><title type='text'>Sending big files to friends</title><content type='html'>A couple of weeks ago, my sister was working on an &lt;a href="http://autodesk.com/"&gt;AutoCad&lt;/a&gt; design for an university assignment and wanted to send it to his teammate by email. She zipped it and tryed without any luck to send using her HotMail account, she can't send the file because HotMail imposes a size limitation of attachment files, and she was trying to send a 30mb file.&lt;br /&gt;&lt;br /&gt;She called me and asked how to send big files to her friends, and I answered she can install an FTP server in her machine and a client on her teammate side...or an Apache web server and copy the files to the &lt;b&gt;htdocs&lt;/b&gt; directory to allow others to download the file. Fortunately, I remembered her current computer is an Athlon 1100 I used for a long time with an Apache installed and working, and told her how to &lt;i&gt;publish&lt;/i&gt; the file to allow her friend's access and download.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;File Sharer&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;After that &lt;i&gt;incident&lt;/i&gt;, I started to figure out a real use for the &lt;a href="http://leonardorame.blogspot.com/2007_04_01_archive.html"&gt;Embedded Web Server&lt;/a&gt; project I mentioned in my last post, and &lt;a href="http://www.download.com/File-Sharer/3000-7240_4-10666767.html?tag=lst-0-1"&gt;&lt;b&gt;File sharer&lt;/b&gt;&lt;/a&gt; was born.&lt;br /&gt;&lt;br /&gt;File Sharer is a small utility that allows navigation and downloading of files from a friend's computer just using a web browser. Tecnically, is a web server embedded into a simple Win32 application, once the server is started, one can access to the server's IP address and access shared files using the browser.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;ScreenShots&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.geocities.com/martinrame/filesharer1.jpg"&gt;&lt;br&gt;&lt;br /&gt;&lt;img src="http://www.geocities.com/martinrame/filesharer2.jpg"&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-5557709268363169703?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/5557709268363169703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=5557709268363169703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5557709268363169703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/5557709268363169703'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2007/05/sending-big-files-to-friends.html' title='Sending big files to friends'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-3599155869732082501</id><published>2007-04-15T13:18:00.000-07:00</published><updated>2007-04-27T06:06:44.854-07:00</updated><title type='text'>Your own zero-configuration web server</title><content type='html'>One month ago, a customer who's using a client/server application I've created for his office asked me if it's possible to make a simple web form where customers can add orders to his system using a web browser. Of course it's possible, one way is to contract with a web hosting provider and upload some CGI or PHP programs, another way is by installing a web server like Apache or IIS  into his office and connect it to Internet, but both methods involves some degree of configuration.&lt;br /&gt;&lt;br /&gt;A third method is the creation of a stand alone web/application server installed on one of his computers just like any other program, without any configuration. I suggested him to use &lt;a href="http://www.atozed.com"&gt;IntraWeb&lt;/a&gt; or WebBroker, and the system was developed using IntraWeb.&lt;br /&gt;&lt;br /&gt;After that small requirement I'd keep dreaming of a very small, fast and multiplatform (Win32/Linux/Bsd) stand alone web server and endded up with a working version of the project.&lt;br /&gt;&lt;br /&gt;A first usable version of the server can be &lt;a href="http://www.geocities.com/martinrame/httpserv.zip"&gt;downloaded from here&lt;/a&gt;. It's based on the HttpServ example of &lt;a href="http://synapse.ararat.cz"&gt;Synapse&lt;/a&gt;, It's multithreaded, 100% Object Pascal and compiles without modifications with Delphi and FreePascal.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Looking for voluntieers&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I know this can be the starting point of a very interesting project, and I'll be very happy to find some enthusiasts who can contribute with code and Ideas, so if you are one of them feel free to contact me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-3599155869732082501?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/3599155869732082501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=3599155869732082501' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3599155869732082501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/3599155869732082501'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2007/04/your-own-zero-configuration-web-server.html' title='Your own zero-configuration web server'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-6253964796890493933</id><published>2007-02-25T12:02:00.000-08:00</published><updated>2007-02-25T12:49:23.492-08:00</updated><title type='text'>Object Pascal &amp; LDAP</title><content type='html'>Object Pascal &amp; LDAP&lt;br /&gt;&lt;br /&gt;One common feature of enterprise software is security, applications must be prepared to allow/deny access to its resources (modules or dialogs) to different usesers. The vast majority of development teams, every time a project involves user authentication develops its own methods based on a repository (database) of user accounts. This approach isn't bad at all, but implies in some cases the duplication of users, one user account for the app, another with the same name for the operating system session, anoter one for email.&lt;br /&gt;&lt;br /&gt;To resolve the problem described above, &lt;a href="http://en.wikipedia.org/wiki/Directory_service"&gt;Directory Services&lt;/a&gt; were created, basically they are a common database of users and groups shared across a computer network allowing its access from different applications. Windows servers (2000 and 2003) call it &lt;a href="http://en.wikipedia.org/wiki/Active_Directory"&gt;Active Directory&lt;/a&gt;, UNIX and Linux uses LDAP and &lt;a href="http://en.wikipedia.org/wiki/OpenLDAP"&gt;OpenLdap&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Personally, I preffer open standards, and the fact that Ms Active Directory can cooperate transparently with an LDAP server favors the later because it can be installed and accesed by Windows, Linux, and other Oses.&lt;br /&gt;&lt;br /&gt;In this article, I'll show how to set up a simple &lt;a href="www.openldap.org"&gt;OpenLDAP&lt;/a&gt; server and a client that works on Delphi or FreePascal with the help of the great &lt;a href="synapse.ararat.cz"&gt;Synapse&lt;/a&gt; library.&lt;br /&gt;&lt;br /&gt;Installing OpenLdap&lt;br /&gt;&lt;br /&gt;First, download the OpenLdap binaries from &lt;a href="http://lucas.bergmans.us/hacks/openldap/download"&gt;Win32 port&lt;/a&gt; or you can look at &lt;a href="http://www.openldap.org/faq/data/cache/108.html"&gt;this link&lt;/a&gt; for other operating systems. Of course you can download the &lt;a href="http://www.openldap.org/software/download/"&gt;sources&lt;/a&gt; and build an OpenLdap server from scratch.&lt;br /&gt;&lt;br /&gt;From here, all examples will be based on the Win32 version of the server, but I'm sure you'll understand how to adapt the examples and configuration to a Linux based server.&lt;br /&gt;&lt;br /&gt;After installing and configuring &lt;b&gt;slapd&lt;/b&gt;, the OpenLDAP daemon, I'll change the default settings. Stop the OpenLDAP Directory Service going to Control Panel -&gt; Administrative Tools -&gt; Services in windows and edit the slapd.conf file replacing the default entries with:&lt;br /&gt;&lt;pre&gt;ucdata-path ./ucdata&lt;br /&gt;include  ./schema/core.schema&lt;br /&gt;include  ./schema/cosine.schema&lt;br /&gt;include  ./schema/inetorgperson.schema&lt;br /&gt;&lt;br /&gt;suffix  "dc=ldapserver,dc=com"&lt;br /&gt;rootdn  "dc=ldapserver,dc=com&lt;br /&gt;rootpw          secret&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Save and restart the service.&lt;br /&gt;&lt;br /&gt;The next step you must do is the creation of a root entry in the LDAP database, this can be done by creating an ldif file like this:&lt;br /&gt;&lt;pre&gt;dn: dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: dcObject&lt;br /&gt;objectClass: domain&lt;br /&gt;dc: ldapserver&lt;br /&gt;o: MyLdapServer, Inc.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;save the file as root.ldif and add to the database using this command from the command prompt:&lt;br /&gt;&lt;pre&gt;ldapadd -D "dc=ldapserver,dc=com" -W -f root.ldif&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next step is the creation of a group of users. To accomplish this, I'll create the users.ldif file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;dn: ou=users,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: organizationalUnit&lt;br /&gt;ou: users&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To add this group use this statement:&lt;br /&gt;&lt;pre&gt;ldapadd -D "dc=ldapserver,dc=com" -W -users.ldif&lt;/pre&gt;&lt;br /&gt;Now, I must populate the database with user and group entries using an ldif file like this one:&lt;br /&gt;&lt;pre&gt;# User leonardo&lt;br /&gt;dn: cn=Leonardo M. Rame,ou=users,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: person&lt;br /&gt;objectClass: organizationalPerson&lt;br /&gt;objectClass: inetOrgPerson&lt;br /&gt;uid: leonardorame&lt;br /&gt;cn: Leonardo M. Rame&lt;br /&gt;gn: Leonardo M.&lt;br /&gt;sn: Rame&lt;br /&gt;ou: users&lt;br /&gt;mail: martinrame@yahoo.com&lt;br /&gt;userPassword: lrame123&lt;br /&gt;&lt;br /&gt;# User Peter&lt;br /&gt;dn: cn=Peter Jones,ou=users,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: person&lt;br /&gt;objectClass: organizationalPerson&lt;br /&gt;objectClass: inetOrgPerson&lt;br /&gt;uid: peterjones&lt;br /&gt;cn: Peter Jones&lt;br /&gt;gn: Peter&lt;br /&gt;sn: Jones&lt;br /&gt;ou: users&lt;br /&gt;mail: peterjones@yahoo.com&lt;br /&gt;userPassword: peter111&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Save the file as leonardo.ldif and add the user using this command:&lt;br /&gt;&lt;pre&gt;ldapadd -D "dc=ldapserver,dc=com" -W -f leonardo.ldif&lt;/pre&gt;&lt;br /&gt;If you whant to check if the entries where added correctly, you can try this:&lt;br /&gt;&lt;pre&gt;ldapsearch -b "dc=ldapserver,dc=com" "objectclass=*" -x&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, I'll create two groups of users, &lt;I&gt;Full Access&lt;/I&gt; and &lt;I&gt;Limited&lt;/I&gt;. The first, has access to all features of the system, the second one, has a limited vision. Let's create this groups.ldif file:&lt;br /&gt;&lt;pre&gt;# Groups&lt;br /&gt;dn: ou=groups,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: organizationalUnit&lt;br /&gt;ou: groups&lt;br /&gt;&lt;br /&gt;# Full Access group&lt;br /&gt;dn: cn=Full Access,ou=groups,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: groupOfNames&lt;br /&gt;cn=Full Access&lt;br /&gt;memberuid: leonardorame&lt;br /&gt;&lt;br /&gt;# Limited Access group&lt;br /&gt;dn: cn=Limited,ou=groups,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: groupOfNames&lt;br /&gt;cn=Limited&lt;br /&gt;memberuid: peterjones&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Save the file as leonardo.ldif and add the user using this command:&lt;br /&gt;&lt;pre&gt;ldapadd -D "dc=ldapserver,dc=com" -W -f leonardo.ldif&lt;/pre&gt;&lt;br /&gt;If you whant to check if the entries where added correctly, you can try this:&lt;br /&gt;&lt;pre&gt;ldapsearch -b "dc=ldapserver,dc=com" "objectclass=*" -x&lt;/pre&gt;&lt;br /&gt;Now, I'll create two groups of users, &lt;i&gt;Full Access&lt;/i&gt; and &lt;i&gt;Limited&lt;/i&gt;. The first, has access to all features of the system, the second one, has a limited vision. Let's create this groups.ldif file:&lt;br /&gt;&lt;pre&gt;# Groups&lt;br /&gt;dn: ou=groups,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: organizationalUnit&lt;br /&gt;ou: groups&lt;br /&gt;&lt;br /&gt;# Full Access group&lt;br /&gt;dn: cn=Full Access,ou=groups,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: groupOfNames&lt;br /&gt;cn=Full Access&lt;br /&gt;memberuid: leonardorame&lt;br /&gt;&lt;br /&gt;# Limited Access group&lt;br /&gt;dn: cn=Limited,ou=groups,dc=ldapserver,dc=com&lt;br /&gt;objectClass: top&lt;br /&gt;objectClass: groupOfNames&lt;br /&gt;cn=Limited&lt;br /&gt;memberuid: peterjones&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;of course, you must type:&lt;br /&gt;&lt;pre&gt;ldapadd -D "dc=ldapserver,dc=com" -W -f grouops.ldif&lt;/pre&gt;&lt;br /&gt;With that, I finished the server configuration part. Now, I must code the program to access the Ldap server:&lt;br /&gt;&lt;/span&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; ldapexample;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;{$APPTYPE CONSOLE}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt; SysUtils,&lt;br /&gt; classes,&lt;br /&gt; lDapSend; &lt;span class="pas-comment"&gt;// &amp;lt;-- Synapse Ldap unit&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; CheckAccess(ALevel, AUserName, APassword: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;): Boolean;&lt;br /&gt;&lt;span class="pas-comment"&gt;(* This function checks if a user has access to a group&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;   in the ldap server *)&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt; ldap: TLDAPsend;&lt;br /&gt; lAttribs: TStringList;&lt;br /&gt; I: Integer;&lt;br /&gt; A: Integer;&lt;br /&gt; lMember: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; Result := False;&lt;br /&gt; ldap:= TLDAPsend.Create;&lt;br /&gt; lAttribs := TStringList.Create;&lt;br /&gt; &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;   ldap.TargetHost := &lt;span class="pas-str"&gt;'localhost'&lt;/span&gt;;&lt;br /&gt;   &lt;span class="pas-comment"&gt;(* Anonimous access *)&lt;/span&gt;&lt;br /&gt;   ldap.Login;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ldap.Bind &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;     lAttribs.Add(&lt;span class="pas-str"&gt;'member'&lt;/span&gt;);&lt;br /&gt;     ldap.Search(&lt;span class="pas-str"&gt;'cn='&lt;/span&gt; + ALevel + &lt;span class="pas-str"&gt;',ou=groups,dc=ldapserver,dc=com'&lt;/span&gt;,&lt;br /&gt;       False, &lt;span class="pas-str"&gt;''&lt;/span&gt;, lAttribs);&lt;br /&gt;     &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ldap.SearchResult.Count = &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;     &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;       &lt;span class="pas-comment"&gt;(* Iterate through members of this group *)&lt;/span&gt;&lt;br /&gt;       &lt;span class="pas-kwd"&gt;for&lt;/span&gt; I := &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; ldap.SearchResult[&lt;span class="pas-num"&gt;0&lt;/span&gt;].Attributes.Count - &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;       &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;         &lt;span class="pas-comment"&gt;(* search for this member attributes *)&lt;/span&gt;&lt;br /&gt;         lAttribs.Clear;&lt;br /&gt;         lAttribs.Add(&lt;span class="pas-str"&gt;'uid'&lt;/span&gt;);&lt;br /&gt;         lAttribs.Add(&lt;span class="pas-str"&gt;'userPassword'&lt;/span&gt;);&lt;br /&gt;         lMember := ldap.SearchResult[&lt;span class="pas-num"&gt;0&lt;/span&gt;].Attributes[I][&lt;span class="pas-num"&gt;0&lt;/span&gt;];&lt;br /&gt;         ldap.Search(lMember, False, &lt;span class="pas-str"&gt;'uid='&lt;/span&gt; + AUserName, lAttribs);&lt;br /&gt;         &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ldap.SearchResult.Count = &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;         &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;           &lt;span class="pas-comment"&gt;(* if found, iterate through its attributes to find uid and userPassword *)&lt;/span&gt;&lt;br /&gt;           &lt;span class="pas-kwd"&gt;for&lt;/span&gt; A := &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; ldap.SearchResult[&lt;span class="pas-num"&gt;0&lt;/span&gt;].Attributes.Count - &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;             &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ldap.SearchResult[&lt;span class="pas-num"&gt;0&lt;/span&gt;].Attributes[A].AttributeName = &lt;span class="pas-str"&gt;'userPassword'&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;               &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ldap.SearchResult[&lt;span class="pas-num"&gt;0&lt;/span&gt;].Attributes[A][&lt;span class="pas-num"&gt;0&lt;/span&gt;] = APassword &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;               &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;                 Result := True;&lt;br /&gt;                 break; &lt;span class="pas-comment"&gt;// for A&lt;/span&gt;&lt;br /&gt;               &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;           break; &lt;span class="pas-comment"&gt;// for I&lt;/span&gt;&lt;br /&gt;         &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;       &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;     &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;end&lt;/span&gt;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;else&lt;/span&gt;&lt;br /&gt;     &lt;span class="pas-comment"&gt;(* Display ldap error message *)&lt;/span&gt;&lt;br /&gt;     Writeln(ldap.ResultString);&lt;br /&gt;   &lt;span class="pas-comment"&gt;(* Disconnect from ldap server *)&lt;/span&gt;&lt;br /&gt;   ldap.Logout;&lt;br /&gt; &lt;span class="pas-kwd"&gt;finally&lt;/span&gt;&lt;br /&gt;   ldap.Free;&lt;br /&gt;   lAttribs.Free;&lt;br /&gt; &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;if&lt;/span&gt; CheckAccess(&lt;span class="pas-str"&gt;'Full Access'&lt;/span&gt;, &lt;span class="pas-str"&gt;'leonardorame'&lt;/span&gt;, &lt;span class="pas-str"&gt;'lrame123'&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;   writeln(&lt;span class="pas-str"&gt;'User authenticated Ok.'&lt;/span&gt;)&lt;br /&gt; &lt;span class="pas-kwd"&gt;else&lt;/span&gt;&lt;br /&gt;   writeln(&lt;span class="pas-str"&gt;'Wrong user.'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;if&lt;/span&gt; CheckAccess(&lt;span class="pas-str"&gt;'Limited'&lt;/span&gt;, &lt;span class="pas-str"&gt;'leonardorame'&lt;/span&gt;, &lt;span class="pas-str"&gt;'lrame123'&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;   writeln(&lt;span class="pas-str"&gt;'User authenticated Ok.'&lt;/span&gt;)&lt;br /&gt; &lt;span class="pas-kwd"&gt;else&lt;/span&gt;&lt;br /&gt;   writeln(&lt;span class="pas-str"&gt;'Wrong user.'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;if&lt;/span&gt; CheckAccess(&lt;span class="pas-str"&gt;'Limited'&lt;/span&gt;, &lt;span class="pas-str"&gt;'peterjones'&lt;/span&gt;, &lt;span class="pas-str"&gt;'peter111'&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;   writeln(&lt;span class="pas-str"&gt;'User authenticated Ok.'&lt;/span&gt;)&lt;br /&gt; &lt;span class="pas-kwd"&gt;else&lt;/span&gt;&lt;br /&gt;   writeln(&lt;span class="pas-str"&gt;'Wrong user.'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-6253964796890493933?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/6253964796890493933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=6253964796890493933' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6253964796890493933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/6253964796890493933'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2007/02/object-pascal-ldap.html' title='Object Pascal &amp; LDAP'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-116458628224965136</id><published>2006-11-26T16:09:00.000-08:00</published><updated>2006-11-26T16:11:22.693-08:00</updated><title type='text'>Is Delphi a memory predator?</title><content type='html'>In this article, i want to show why the Delphi way of development of visual applications is not as cleaner as you should want. When I say clean, i mean in memory terms, it mantains an instance of every form in memory, even if you don't want it.&lt;br /&gt;&lt;br /&gt;To show it in an example, just create a new application and add two forms. The code in your Project1.dpr file is similar to this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; Project1;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt;  Forms,&lt;br /&gt;  form1 &lt;span class="pas-kwd"&gt;in&lt;/span&gt; &lt;span class="pas-str"&gt;'form1.pas'&lt;/span&gt;,&lt;br /&gt;  form2 &lt;span class="pas-kwd"&gt;in&lt;/span&gt; &lt;span class="pas-str"&gt;'form2.pas'&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;{$R *.res}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Application.Initialize;&lt;br /&gt;  Application.CreateForm(TForm1, Form1);&lt;br /&gt;  Application.CreateForm(TForm2, Form2);&lt;br /&gt;  Application.Run;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The Application.CreateForm method creates an instance of a form and mantains it in memory until Application object is destroyed. That means the form is in memory even if the program doesn't requires it.&lt;br /&gt;I remember some candidates for the &lt;a href="http://www.codinghorror.com"&gt;Coding Horror&lt;/a&gt; blog, applications with tens of forms created in this way. That's not a programmer's fault, it's the fault of the Delphi IDE, and its Help, and many books that teach how to create elegant and scalable Delphi applications.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Keeping control of the forms&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Every time you create a form in Delphi, it adds its unit to the uses of the project (.dpr file) and an Application.Create(...) between Application.Initialize and Application.Run methods. To keep memory usage low, you must delete those instructions from the project file, and also the variable (like var Form1: TForm1;) declared just below "implementation" declaration in the form's unit.&lt;br /&gt;&lt;br /&gt;If you want to use the form, for example when an event of the main form is called, just create it and free inmediately after you use it:&lt;br /&gt;&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;procedure&lt;/span&gt; TForm1.Button1Click(Sender: TObject);&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  Form2: TForm2;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Form2 := TForm2.Create(&lt;span class="pas-kwd"&gt;nil&lt;/span&gt;);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;    Form2.ShowModal;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;finally&lt;/span&gt;&lt;br /&gt;    Form2.Free;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Just be careful to include form2 unit in the uses clause of form1 and remove the Form2 variable that Delphi automatically creates when you design the form. &lt;br /&gt;The example above creates a "modal" form, if you want to create a "modeless" form, you must create it using the "standard" Delphi way.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-116458628224965136?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/116458628224965136/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=116458628224965136' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/116458628224965136'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/116458628224965136'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/11/is-delphi-memory-predator.html' title='Is Delphi a memory predator?'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-116006257030727876</id><published>2006-10-05T08:31:00.000-07:00</published><updated>2006-10-06T11:29:24.196-07:00</updated><title type='text'>Web software in Pascal?</title><content type='html'>In 1997, i and a partner where the owners of a small graphic design and editorial studio working with 133mhz - Win95 PCs and a Mac Performa 6200 (90 mhz). In that time, the usual type of job a customer could request was the design of a leaflet or printed magazine, eventually a customer could call asking for the design of a static (html only) web page.&lt;br /&gt;&lt;br /&gt;Later that year, the web started to get traction between small business here in Argentina and an idea came to our mind, bring people the possibility to check the price of any product before buy, all using the web. What a great idea!, the only problem was that we didn't know how to create dynamic web pages such those of (in that time)  &lt;a href="http://web.archive.org/web/19961120231442/http://www.viaweb.com/"&gt;Viaweb&lt;/a&gt; (now Yahoo Store). Since then, i started to learn everything i could with the objective to generate dynamic web pages.&lt;br /&gt;&lt;br /&gt;The first tool i used was IDC (Internet Database Connector), a small utility created by Microsoft for the IIS (Internet Information Server) that allows users to embed a database query into an HTML page. Later came ASP with a lot more features, i remember used it to create the firtst  &lt;a href="version%20of%20http://web.archive.org/web/20010519093837/http://elshopping.com/"&gt;elshopping.com&lt;/a&gt;, now defunct (it makes me sad). Then cames PHP, way faster than ASP and multiplatform and Java with JSP. Now we must add Python and frameworks.&lt;br /&gt;&lt;br /&gt;With that background i thought web development meant scripting languages, in fact, i'd repeatedly read that CGI isn't a good development choice because after each request, a new fork is created turning the proccess very inefficient. Well,  &lt;a href="http://www.wrensoft.com/zoom/benchmarks.html"&gt;this site&lt;/a&gt; shows that the inneficient CGI is more efficient than scripting languages (many times faster).&lt;br /&gt;&lt;br /&gt;After that introduction, i'll show you an open source library that allows CGI creation using FreePascal. It's name is &lt;a href="http://www.psp.furtopia.org"&gt;Pascal Server Pages&lt;/a&gt; (PSP for short).&lt;br /&gt;&lt;br /&gt;Many times i wanted to create dynamic web pages in FreePascal, and must admit, i tought Pascal Server Pages was an emulation of ASP, but for Pascal language, and did't pay much attention to it. But last week, i found PSP fits very well into the kind of software i like to create, fast-efficient and easy to improve.&lt;br /&gt;&lt;br /&gt;You can start reading Vladimirs Sibirov's &lt;a href="http://www.psp.furtopia.org/doc/overview-1.5.0.html"&gt;introduction to PSP&lt;/a&gt; to learn more.&lt;br /&gt;&lt;br /&gt;Well, we have arrived to the end of this post, and i know you want to see some code. The example below is a neat trick that allows you to embed images into the HTML generated by PSP.&lt;br /&gt;&lt;br /&gt;To compile this program, you must download PSP from &lt;a href="http://sourceforge.net/projects/pascal-webdev/"&gt;http://sourceforge.net/projects/pascal-webdev/&lt;/a&gt; and make sure your compiler can find the base64enc unit included in the PSP source tree. Also you'll need a web server capable of executing CGI files (i preffer Apache) and copy the resulting file (embedimage.exe) to it.&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; embedimage;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;{$mode objfpc}&lt;/span&gt;&lt;span class="pas-preproc"&gt;{$H+}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt; SysUtils,&lt;br /&gt; Classes,&lt;br /&gt; base64enc,&lt;br /&gt; pwu;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt; lImage:    TMemoryStream;&lt;br /&gt; lString:   PChar;&lt;br /&gt; lStrImage: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; WebWriteLn(&lt;span class="pas-str"&gt;'&amp;lt;html&amp;gt;'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt; WebWriteLn(&lt;span class="pas-str"&gt;'Hello, this is an embedded image&amp;lt;hr&amp;gt;'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt; &lt;span class="pas-comment"&gt;(* Load the image from disk as a stream *)&lt;/span&gt;&lt;br /&gt; lImage := TMemoryStream.Create;&lt;br /&gt; lImage.LoadFromFile(&lt;span class="pas-str"&gt;'image1.jpg'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt; &lt;span class="pas-comment"&gt;(* Convert the stream to a Base64 encoded string *)&lt;/span&gt;&lt;br /&gt; GetMem(lString, lImage.Size);&lt;br /&gt; lImage.&lt;span class="pas-kwd"&gt;Read&lt;/span&gt;(lString^, lImage.Size);&lt;br /&gt; SetString(lStrImage, lString, lImage.Size);&lt;br /&gt;&lt;br /&gt; WebWriteLn(&lt;span class="pas-str"&gt;'&amp;lt;/html&amp;gt;'&lt;/span&gt;);&lt;br /&gt; WebWriteLn(&lt;span class="pas-str"&gt;'&amp;lt;img src="data:image/jpg;base64,'&lt;/span&gt;&lt;br /&gt;   + base64_encode(lStrImage) + '&lt;span class="pas-str"&gt;"&amp;gt;'&lt;/span&gt;);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-116006257030727876?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/116006257030727876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=116006257030727876' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/116006257030727876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/116006257030727876'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/10/web-software-in-pascal.html' title='Web software in Pascal?'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115877198097559688</id><published>2006-09-20T10:03:00.000-07:00</published><updated>2006-09-20T10:06:22.026-07:00</updated><title type='text'>Why i can't capture exceptions from Dlls?</title><content type='html'>A very common topic in newsgroups is Dll Exception handling. If your dll raises an exception, the exception handler in the host app should not be able to recognize the class of the exception it trapped. That's because the host application doesn't recognizes the exception class raised by the dll.&lt;br /&gt;&lt;br /&gt;The recommended aproach is to capture any exception within the Dll and create functions that return values of success or failure, then the host application can evaluate such results and raise an exception.&lt;br /&gt;&lt;br /&gt;This is an example produces different results when compiled with Delphi or FreePascal, you shouldn't do this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;library&lt;/span&gt; failedlib;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt; SysUtils;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; divide(a, b: integer): Boolean; &lt;span class="pas-kwd"&gt;cdecl&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;if&lt;/span&gt; b = &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;raise&lt;/span&gt; Exception.Create(&lt;span class="pas-str"&gt;'Can''t divide by zero'&lt;/span&gt;);&lt;br /&gt; Result := True;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;exports&lt;/span&gt;&lt;br /&gt; divide;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; sample;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;{$APPTYPE CONSOLE}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;type&lt;/span&gt;&lt;br /&gt; TDivide = &lt;span class="pas-kwd"&gt;function&lt;/span&gt; (a, b: integer): Boolean; &lt;span class="pas-kwd"&gt;cdecl&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt; FuncDivide: TDivide;&lt;br /&gt; lHandle:    Cardinal;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;lHandle := LoadLibrary(&lt;span class="pas-str"&gt;'failedlib.dll'&lt;/span&gt;);&lt;br /&gt;&lt;span class="pas-kwd"&gt;if&lt;/span&gt; lHanlde &amp;lt;&amp;gt; &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  FuncDivide = GetProcAddress(lHandle, &lt;span class="pas-str"&gt;'divide'&lt;/span&gt;);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;if&lt;/span&gt; pointer(@FuncDivide) &amp;lt;&amp;gt; &lt;span class="pas-kwd"&gt;nil&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; FuncDivide(&lt;span class="pas-num"&gt;10&lt;/span&gt;, &lt;span class="pas-num"&gt;0&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        writeln(&lt;span class="pas-str"&gt;'Success!'&lt;/span&gt;);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;except&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      writeln(&lt;span class="pas-str"&gt;'Failure!'&lt;/span&gt;);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  UnloadLibrary(lHandle);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The correct method is this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;library&lt;/span&gt; correctlib;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt; SysUtils;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; divide(a, b: integer): Boolean; &lt;span class="pas-kwd"&gt;cdecl&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt; c: Integer;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-comment"&gt;(* Catch any exception before return *)&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;   c := a &lt;span class="pas-kwd"&gt;div&lt;/span&gt; b;&lt;br /&gt;   Result := True;&lt;br /&gt; &lt;span class="pas-kwd"&gt;except&lt;/span&gt;&lt;br /&gt;   Result := False;&lt;br /&gt; &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;exports&lt;/span&gt;&lt;br /&gt; divide;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; sample;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;{$APPTYPE CONSOLE}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;type&lt;/span&gt;&lt;br /&gt; TDivide = &lt;span class="pas-kwd"&gt;function&lt;/span&gt; (a, b: integer): Boolean; &lt;span class="pas-kwd"&gt;cdecl&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt; FuncDivide: TDivide;&lt;br /&gt; lHandle:    Cardinal;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;lHandle := LoadLibrary(&lt;span class="pas-str"&gt;'correctlib.dll'&lt;/span&gt;);&lt;br /&gt;&lt;span class="pas-kwd"&gt;if&lt;/span&gt; lHanlde &amp;lt;&amp;gt; &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  FuncDivide = GetProcAddress(lHandle, &lt;span class="pas-str"&gt;'divide'&lt;/span&gt;);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;if&lt;/span&gt; pointer(@FuncDivide) &amp;lt;&amp;gt; &lt;span class="pas-kwd"&gt;nil&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;if&lt;/span&gt; FuncDivide(&lt;span class="pas-num"&gt;10&lt;/span&gt;, &lt;span class="pas-num"&gt;0&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;      writeln(&lt;span class="pas-str"&gt;'Success!'&lt;/span&gt;)&lt;br /&gt;    &lt;span class="pas-kwd"&gt;else&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-comment"&gt;(* The exception is raised in the host app, not in the DLL *)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;raise&lt;/span&gt; &lt;span class="pas-str"&gt;'Failure!'&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  UnloadLibrary(lHandle);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115877198097559688?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115877198097559688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115877198097559688' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115877198097559688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115877198097559688'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/09/why-i-cant-capture-exceptions-from.html' title='Why i can&apos;t capture exceptions from Dlls?'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115755719848242788</id><published>2006-09-06T08:35:00.000-07:00</published><updated>2006-09-15T17:46:55.510-07:00</updated><title type='text'>Cross compiler Data Access</title><content type='html'>Object Pascal compilers offers a wide range of classes and components that allows you to connect to different databases, ranging from BDE to open source components. But many of them only works in Delphi or only in FreePascal, turning very dificult to develop cross platform database applications.&lt;br /&gt;&lt;br /&gt;One of the most known data access components is Zeos Lib, a heavy weight component library that allows you to acces a lot of database backends like MySql, PostGreSQL, Oracle, MsSql Server, Interbase and others. It's free, open source and works on Delphi, Kylix and FreePascal. You can get more information and download from&lt;a href="http://sourceforge.net/projects/zeoslib"&gt; http://sourceforge.net/projects/zeoslib&lt;/a&gt;, also, if you want to install it in Lazarus must read this &lt;a href="http://wiki.lazarus.freepascal.org/index.php/Zeos_tutorial"&gt;http://wiki.lazarus.freepascal.org/index.php/Zeos_tutorial&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you (like me) like smaller and faster programs, i'll recommend a small library called LibSQL. Just like Zeos, this library allows access to different databases, but allows you to use driver you want, making the executable many times smaller than Zeos. If you want to download it, go to &lt;a href="http://sourceforge.net/projects/libsql"&gt;http://sourceforge.net/projects/libsql&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you know different cross platform libraries or components like Zeos or LibSQL, please comment on them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115755719848242788?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115755719848242788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115755719848242788' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115755719848242788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115755719848242788'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/09/cross-compiler-data-access.html' title='Cross compiler Data Access'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115567005534054291</id><published>2006-08-15T12:21:00.000-07:00</published><updated>2006-08-15T12:27:35.806-07:00</updated><title type='text'>Designing for the future</title><content type='html'>Most programmers doesn't take care of the future of the software they're writing, they just try to make it work and send it to the market. But many times, when a customer asks for a change, very large portions of the code must be rewritten. This is specially known in applications that share Database, Business Rules and User Interface.&lt;br /&gt;&lt;br /&gt;A large portion of Object Pascal developers came from Delphi, where one tend to think in a Delphish (RAD) way, placing data aware components inside their UI forms, then adding another form and placing more data components inside it and so on (with forms i mean TForms and TDataModules). This method of working, can be very rapid in small applications, but in enterprise-class software this approach can cause more headaches than applauses.&lt;br /&gt;&lt;br /&gt;Must be a better way&lt;br /&gt;&lt;br /&gt;The first step to architect your software for the future is to identify what parts can be classified into Database, Business Rules and User Interface, i'll call them packages (don't confuse with Borland Package Libraries) so you'll end writing Database package, BR package and UI package. Each package must have an interface to communicate with the world (other packages), so if you whant to execute a function (or method) that retrieves information from a database, just call the Database package and access to the required method.&lt;br /&gt;&lt;br /&gt;Supouse your application uses a Database package that connects to MySql, and your customer uses an Oracle server. It's easier to create a new package for Oracle than rewriting the whole program.&lt;br /&gt;&lt;br /&gt;The same rule applies to user interfaces. Using modules like i propouse, your application's Business Rules can be shared by Win32 UI, GTK UI, Web UI or something else without re-coding busines rules and database access for every UI client.&lt;br /&gt;&lt;br /&gt;It's not rocket science&lt;br /&gt;&lt;br /&gt;This method of working receives the name of MVC (&lt;a href="http://en.wikipedia.org/wiki/Model-view-controller"&gt;&lt;span style="text-decoration: underline;"&gt;Model-View-Controller&lt;/span&gt;&lt;/a&gt;), a Design Pattern used mostly in modern web environments (in Java and .NET especially), but applies to any object oriented language and fills very well in Object Pascal.&lt;br /&gt;&lt;br /&gt;To communicate different packages, i recommend not to use any platform nor language native feature. With simple shared libraries (like i explain in &lt;a href="http://leonardorame.blogspot.com/2006/06/how-to-import-object-from-dll.html"&gt;this post&lt;/a&gt;), you can create portable, cross-platform applications that works for the future.&lt;br /&gt;&lt;br /&gt;Feel free to express your opinions about this approach.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115567005534054291?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115567005534054291/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115567005534054291' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115567005534054291'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115567005534054291'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/08/designing-for-future.html' title='Designing for the future'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115106851088529110</id><published>2006-06-23T06:09:00.000-07:00</published><updated>2006-06-23T06:15:10.896-07:00</updated><title type='text'>Example added</title><content type='html'>In order to finalize  this series of articles related to &lt;span style="font-style: italic;"&gt;interfacing objects with Dll's&lt;/span&gt; i've added an example (as promised on my last post) you can download from &lt;a href="http://www.geocities.com/martinrame/intfs.zip"&gt;this url&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Feel free to post comments about the example and/or propose alternatives.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115106851088529110?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115106851088529110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115106851088529110' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115106851088529110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115106851088529110'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/06/example-added.html' title='Example added'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115083338340240022</id><published>2006-06-20T12:43:00.000-07:00</published><updated>2006-06-20T12:56:23.420-07:00</updated><title type='text'>Virtual-Abstract objects as Interfaces</title><content type='html'>In my last post i exposed a method to share objects between Dll's and applications. As i wrote in that post, the compiler doesn't know the type of received object (it assumes it's a pointer), so you must cast it to the correct type. In the code below, i casted it to TObject only to be sure it compiles correctly:&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-comment"&gt;(* Initialize exported class *)&lt;/span&gt;&lt;br /&gt;myObj := TObject(_EC).create;&lt;br /&gt;&lt;span class="pas-comment"&gt;(* work with exported class &lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;       :&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;       :&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;       ... and free exported class *)&lt;/span&gt;&lt;br /&gt;myObj.free;&lt;/pre&gt;This aproach allows you to create objects inside Dlls (in some cases it's all you need), but compiler can't access public methods and properties of the object because it doesn't know the object's type.&lt;br /&gt;&lt;br /&gt;Delphi &amp; Kylix developers can argue (with reason) that if you use Packages (bpl's) instead of Dll's you can share objects and the compiler will intelligently know wich is the type of shared objects. But if applications must compile in FreePascal in addition to that compilers you have to devise a workaround.&lt;br /&gt;&lt;br /&gt;Well, the workaround is to create an interface who'll allow the compiler to access objects methods and properties. Object Pascal knows two Interface types, COM Interfaces and CORBA interfaces wich i'm not going to explain here, and a third one that isn't really an Interface type but works the same way is called Virtual-Abstract Objects, it will help you with code portability.&lt;br /&gt;&lt;br /&gt;To define a Virtual-Abstract Object you have to create a class with all of it's methods virtual and abstract as this code shows:&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;Type&lt;/span&gt;&lt;br /&gt; TAgeCalculator = &lt;span class="pas-kwd"&gt;Class&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetAge: Integer; &lt;span class="pas-kwd"&gt;virtual&lt;/span&gt;; &lt;span class="pas-kwd"&gt;abstract&lt;/span&gt;;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;procedure&lt;/span&gt; SetAge; &lt;span class="pas-kwd"&gt;virtual&lt;/span&gt;; &lt;span class="pas-kwd"&gt;abstract&lt;/span&gt;;&lt;br /&gt; &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;Abstract means that the implementation of the object doesn't have to be in the same level of inheritance of the interface, but inherited to be implemented. Virtual means methods can be oberriden. &lt;br /&gt;&lt;br /&gt;As an example, if you need to share objects between Dll's and must access to methods and properties, you have to include the class definition (like TAgeCalculator) in both units, the one wich implements the methods and the one wich access to the instance.&lt;br /&gt;&lt;br /&gt;In my next post i'll include a complete example of this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115083338340240022?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115083338340240022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115083338340240022' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115083338340240022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115083338340240022'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/06/virtual-abstract-objects-as-interfaces.html' title='Virtual-Abstract objects as Interfaces'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115020988952899948</id><published>2006-06-13T07:34:00.000-07:00</published><updated>2006-06-13T08:11:10.323-07:00</updated><title type='text'>How to import an Object from a dll?</title><content type='html'>Some time ago, a boss of a company that i used to work gave a course of COM &amp; DCOM technologies because he thought packages (bpl's) wasn't working as one might expect (actually the problem wasn't packages at all, but that's another story). He was trying to change packages to COM interfaces as a way to share application's objects with Dll's (dynamic link libraries). Thanks god we solved the problem without changing the more than 50 packages the application uses.&lt;br /&gt;&lt;br /&gt;Well, there's a way to export objects (and classes) from dll's. The first thing you have to do is to code a Unit containing the classes you want to export:&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-comment"&gt;{ MyClasses.pas }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;unit&lt;/span&gt; MyClasses;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;interface&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt; Classes;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;type&lt;/span&gt;&lt;br /&gt; TMyClass = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;  public&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;    constructor&lt;/span&gt; create;&lt;br /&gt;&lt;span class="pas-comment"&gt;    (* Here you can create the methods you want *)&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;  end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; TMyClass.create;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; writeln(&lt;span class="pas-str"&gt;'Create!!!'&lt;/span&gt;);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;The second thing to do is to create the the library project and include the recent unit:&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-comment"&gt;{ interfaces.dpr }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;library&lt;/span&gt; interfaces;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt; Classes,&lt;br /&gt; MyClasses;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetMyExportedClass: Pointer; &lt;span class="pas-kwd"&gt;cdecl&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; Result := GetClass(&lt;span class="pas-str"&gt;'TMyClass'&lt;/span&gt;);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;exports&lt;/span&gt;&lt;br /&gt; GetMyExportedClass;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;After this you have to create a program that loads the dll and execute the exported function (GetMyExportedClass) to create TMyClass objects:&lt;br /&gt;&lt;br /&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-comment"&gt;{ test.dpr }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; test;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;{$APPTYPE CONSOLE}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;  {$ifdef fpc}&lt;/span&gt;&lt;br /&gt;dynlibs,&lt;br /&gt;&lt;span class="pas-preproc"&gt;  {$endif}&lt;/span&gt;&lt;br /&gt;SysUtils,&lt;br /&gt;Classes;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-preproc"&gt;  {$ifdef fpc}&lt;/span&gt;&lt;br /&gt;mHandle: TLibHandle;&lt;br /&gt;&lt;span class="pas-preproc"&gt;  {$else}&lt;/span&gt;&lt;br /&gt;mHandle: THandle;&lt;br /&gt;&lt;span class="pas-preproc"&gt;  {$endif}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;myObj: TObject;&lt;br /&gt;_EC: &lt;span class="pas-kwd"&gt;function&lt;/span&gt; : Pointer; &lt;span class="pas-kwd"&gt;cdecl&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;  (* load shared library *)&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;  if&lt;/span&gt; (pointer(mHandle) = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;  begin&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;  (* Try to load shared library &lt;/span&gt;&lt;span class="pas-comment"&gt;*)&lt;/span&gt;&lt;br /&gt;  mHandle := LoadLibrary(&lt;span class="pas-str"&gt;'lib.dll'&lt;/span&gt;);&lt;br /&gt;&lt;span class="pas-kwd"&gt;    if&lt;/span&gt; (pointer(mHandle) &amp;lt;&amp;gt; &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;    begin&lt;/span&gt;&lt;br /&gt;    writeln(&lt;span class="pas-str"&gt;'Loaded!'&lt;/span&gt;);&lt;br /&gt;   &lt;span class="pas-comment"&gt;(* instantiate a TMyExportedClass object *)&lt;/span&gt;&lt;br /&gt;   _EC := GetProcAddress(mHandle, &lt;span class="pas-str"&gt;'GetMyExportedClass'&lt;/span&gt;);&lt;br /&gt;   &lt;span class="pas-kwd"&gt;if&lt;/span&gt; (pointer(@_EC) &amp;lt;&amp;gt; &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;   &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-comment"&gt;(* Initialize exported class *)&lt;/span&gt;&lt;br /&gt;     myObj := TObject(_EC).create;&lt;br /&gt;    &lt;span class="pas-comment"&gt;(* work with exported class &lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;           :&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;           :&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;           ... and free exported class *)&lt;/span&gt;&lt;br /&gt;    myObj.free;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt; &lt;span class="pas-comment"&gt;(* unload shared library *)&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-preproc"&gt;{$ifdef fpc}&lt;/span&gt;&lt;br /&gt; UnLoadLibrary(mHandle);&lt;br /&gt; &lt;span class="pas-preproc"&gt;{$else}&lt;/span&gt;&lt;br /&gt; FreeLibrary(mHandle);&lt;br /&gt; &lt;span class="pas-preproc"&gt;{$endif}&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;The code above exports an object from a dll, but as the object must be type casted to TObject, you can't view it's methods. In the next post, i'll show you how to create &lt;span style="font-style: italic;"&gt;interfaces&lt;/span&gt; that allows to view the object's metods.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115020988952899948?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115020988952899948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115020988952899948' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115020988952899948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115020988952899948'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/06/how-to-import-object-from-dll.html' title='How to import an Object from a dll?'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-29648798.post-115020060536963189</id><published>2006-06-13T05:06:00.000-07:00</published><updated>2006-06-15T07:38:33.740-07:00</updated><title type='text'>First blog!</title><content type='html'>Hi everybody, my name is Leonardo M. Ramé, i'm software developer from Córdoba, Argentina and started this blog to help the Object Pascal community with some hacks useful to me (and you).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/29648798-115020060536963189?l=leonardorame.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://leonardorame.blogspot.com/feeds/115020060536963189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29648798&amp;postID=115020060536963189' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115020060536963189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29648798/posts/default/115020060536963189'/><link rel='alternate' type='text/html' href='http://leonardorame.blogspot.com/2006/06/first-blog.html' title='First blog!'/><author><name>Leonardo M. Ramé</name><uri>http://www.blogger.com/profile/01545328313711727680</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/-iRF1dvBi78E/TdfJuL-OldI/AAAAAAAAEjI/6CouS2xMINA/s220/yo-blog.png'/></author><thr:total>2</thr:total></entry></feed>
