Using Delphi, it is very easy to debug a DLL having it called from a host application. This host application can be written using Delphi or any other language.
To debug your DLL in the context for a host application, just add the host application into the corresponding field in Delphi / Menu / Run / Parameters. Then run your project as usual. Delphi will not load the DLL but the host application and intercept the DLL when it is loaded by the host application. It will then honor any breakpoint you've set.
Easy? Yes, it is easy but sometimes, there is a glitch. For some reasons, Delphi won't honor your breakpoints. Among the reasons is that you forgot to use the debug build, or you forgot to enable debugging in the DLL project options.
Sometimes, there is another reason. It made me lose a lot of time playing with all options. Sometimes, I thought that I had found the right option. But later the breakpoints were still not honored.
Finally I found the correct solution. Why it works, I don't know. But so far it worked for me. The solution is: Set option "Include remote debug symbols" to true (Delphi compiler / Linking).
This was not obvious since I didn't used the remote debugger. Anyway, it works!
When option "Include remote debug symbols" is used, Delphi creates one more file with rsm extension in the same directory as the executable. It looks like the debugger always find the symbols there.
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
July 29, 2013
July 27, 2013
Using RTTI to convert record to/from string
Delphi RTTI can be easily used to convert a record (Or a class by the way) to a string representation without taking care of how the record is changed during software maintenance.
RTTI has a set of methods to handle metadata collected by the compiler at compile time. For example, you can iterate thru all fields of a record to find out his name, data type and get or set his value. I used those methods as a simple way to marshal record values between two different processes communicating using sockets.
To simplify my original problem without removing features, I designed a very simple sample program demonstrating the RTTI usage. This simple program simply adds two methods “ToString” and “FromString” to a record. Instead of hardcoding each field in the conversion, I used RTTI to iterate all fields.
Basically, to use RTTI with records, you need the following data types: TRttiContext, TRttiRecordType and TRttiField.
Assuming we handle a record type name “THdr”, the simplest code looks like this:
AContext := TRttiContext.Create; try ARecord := AContext.GetType(TypeInfo(THdr)).AsRecord; for AField in ARecord.GetFields do begin AFldName := AField.Name; end; finally AContext.Free; end;
As you can see, we have to create a TRttiContext instance. We then use his method “GetType” to get an instance of TRttiRecord related to the record we are handling. Finally we can iterate thru all fields using a for..in construct. Each field has a corresponding TRttiField instance which can be used to get field name, set or get his value and so on.
My sample code use a loop similar to the above twice: one for converting all fields to a string and one for extracting values from a string and assigning the values to the corresponding fields.
To achieve the goal, I used two support functions: “ParamByNameAsString” and “EscapeQuotes”. They are required to be able to store any fieldname=”fieldvalue” pair into a simple string. There is a simple escape mechanism so that the value can contain embedded double quote.
Here is the full source code:
unit RttiTestForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Rtti, TypInfo, Vcl.StdCtrls; type THdr = record Name : String; Phrase : String; ActionDate : TDateTime; procedure FromString(const FromValue: String); function ToString : String; end; TForm1 = class(TForm) Memo1: TMemo; Button1: TButton; procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} // This will take an inut string having the format: // name1=value1;name2=value2;... // Value may also be placed between double quotes if it contain spaces. // Double quotes inside the value is escaped with a backslash // Backslashes are double. See EscapeQuotes below. function ParamByNameAsString( const Params : String; const ParamName : String; var Status : Boolean; const DefValue : String) : String; var I, J : Integer; Ch : Char; begin Status := FALSE; I := 1; while I <= Length(Params) do begin J := I; while (I <= Length(Params)) and (Params[I] <> '=') do Inc(I); if I > Length(Params) then begin Result := DefValue; Exit; // Not found end; if SameText(ParamName, Trim(Copy(Params, J, I - J))) then begin // Found parameter name, extract value Inc(I); // Skip '=' if (I <= Length(Params)) and (Params[I] = '"') then begin // Value is between double quotes // Embedded double quotes and backslashes are prefixed // by backslash Status := TRUE; Result := ''; Inc(I); // Skip starting delimiter while I <= Length(Params) do begin Ch := Params[I]; if Ch = '\' then begin Inc(I); // Skip escape character if I > Length(Params) then break; Ch := Params[I]; end else if Ch = '"' then break; Result := Result + Ch; Inc(I); end; end else begin // Value is up to semicolon or end of string J := I; while (I <= Length(Params)) and (Params[I] <> ';') do Inc(I); Result := Copy(Params, J, I - J); Status := TRUE; end; Exit; end; // Not good parameter name, skip to next Inc(I); // Skip '=' if (I <= Length(Params)) and (Params[I] = '"') then begin Inc(I); // Skip starting delimiter while I <= Length(Params) do begin Ch := Params[I]; if Ch = '\' then begin Inc(I); // Skip escape character if I > Length(Params) then break; end else if Ch = '"' then break; Inc(I); end; Inc(I); // Skip ending delimiter end; // Param ends with ';' while (I <= Length(Params)) and (Params[I] <> ';') do Inc(I); Inc(I); // Skip semicolon end; Result := DefValue; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function EscapeQuotes(const S: String) : String; begin // Easy but not best performance Result := StringReplace(S, '\', '\\', [rfReplaceAll]); Result := StringReplace(Result, '"', '\"', [rfReplaceAll]); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure THdr.FromString(const FromValue: String); var Status : Boolean; Value : String; AValue : TValue; AContext : TRttiContext; ARecord : TRttiRecordType; AField : TRttiField; AFldName : String; begin if FromValue = '' then Exit; // We use RTTI to iterate thru all fields of THdr and use each field name // to get field value from metadata string and then set value into Hdr. AContext := TRttiContext.Create; try ARecord := AContext.GetType(TypeInfo(THdr)).AsRecord; for AField in ARecord.GetFields do begin AFldName := AField.Name; Value := ParamByNameAsString(FromValue, AFldName, Status, '0'); if Status then begin try case AField.FieldType.TypeKind of tkFloat: // Also for TDateTime ! begin if Pos('/', Value) >= 1 then AValue := StrToDateTime(Value) else AValue := StrToFloat(Value); AField.SetValue(@Self, AValue); end; tkInteger: begin AValue := StrToInt(Value); AField.SetValue(@Self, AValue); end; tkUString: begin AValue := Value; AField.SetValue(@Self, AValue); end; // You should add other types as well end; except // Ignore any exception here. Likely to be caused by // invalid value format end; end else begin // Do whatever you need if the string lacks a field // For example clear the field, or just do nothing end; end; finally AContext.Free; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} function THdr.ToString: String; var AContext : TRttiContext; AField : TRttiField; ARecord : TRttiRecordType; AFldName : String; AValue : TValue; begin Result := ''; AContext := TRttiContext.Create; try ARecord := AContext.GetType(TypeInfo(THDR)).AsRecord; for AField in ARecord.GetFields do begin AFldName := AField.Name; AValue := AField.GetValue(@Self); Result := Result + AFldName + '="' + EscapeQuotes(AValue.ToString) + '";'; end; finally AContext.Free; end; end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} procedure TForm1.Button1Click(Sender: TObject); var Hdr1 : THdr; Hdr2 : THdr; Buf : String; begin Hdr1.Name := 'Francois Piette'; Hdr1.Phrase := 'I said "\Hello\"'; Hdr1.ActionDate := Now; Memo1.Lines.Add(Hdr1.Phrase); Buf := Hdr1.ToString; Memo1.Lines.Add(Buf); Hdr2.FromString(Buf); Memo1.Lines.Add(Hdr2.Phrase); Memo1.Lines.Add(Hdr2.ToString); end; {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *} end.
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
July 20, 2013
Creating an AVI file from bitmaps using Delphi
It is quite easy to create an AVI file from a number of individual bitmaps. The AVI file maybe compressed on the fly using any installed codec. An example application would be creating a video from computer generated images such as 3D view or discrete bitmaps such as those generated from a Mandelbrot fractal zoom in. Each zoom step produces a new bitmap. Taking all bitmap into an AVI makes a nice movie showing the zoom into the fractal deep space!
Windows has two API to handle AVI files: an old API named “Video For Windows” (VfW) and a new API named “DirectShow”. The old API is the easiest to use for the task.
A guy named Gopalakrishna Palemat wrote a Visual Studio C++ application to demonstrate that use of VfW API. I have ported his code to Delphi. I have slightly updated his code to fix shortcomings and make it “the Delphi way”.
The original article can be found at http://gopalakrishna.palem.in/createmovie.html
My Delphi source code can be found at http://www.overbyte.be/eng/blog_source_Code.html
I will speak only about my source code from now.
All the AVI creation code has been moved into a component named “TAviFromBitmaps”. This class handle everything required with VfW API to create an AVI movie, compressed or not. You just has to create an instance of the class and call his AppendNewFrame method to add a bitmap at the end of the already added bitmaps. The result is an AVI file.
As a simple sample, I wrote a procedure which generate an AVI file made of 25 bitmaps generated on the fly. Each bitmap contain the text “Delphi rocks!” and the number of the frame. This is for simple demo. As I said above, you could as well produce bitmap with a 3D scene viewed from a moving point of view, or a zoom into a Mandelbrot fractal.
Compression of the AVI is very important. Without compression, your AVI file will be quickly very large. VfW API is able to compress with about any algorithm. It is enough to install a “codec” into Windows. I like the XVID codec because it produces MPEG-4 video with high quality and high compression. Xvid is an open-source research project focusing on video compression and is a collaborative development effort (http://www.xvid.org). All code is released under the terms of GNU GPL license.
To use XVID compressor, download the setup from http://www.xvid.org and install it. Microsoft Media Player is able to decode XVID compressed movie without installing xvid software because the result is a standard MPEG-4 file.
Each codec is identified by a four character code name “FourCC”. VfW API has a function to transform those four characters codes into an integer value suitable for the rest of the API. This is why you see “MKFOURCC(‘x’, ‘v’, ‘I’, ‘d’)” in the above code.
Of course compression comes with a price: image quality is not the same as the original. So in some applications, you may want to avoid compression. This is done by selecting a compressor of type “MKFOURCC(‘D’, ‘I’, ‘B’, ‘ ’)” (Fourth character is a space).
Download source code from: http://www.overbyte.be/eng/blog_source_code.html
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
Windows has two API to handle AVI files: an old API named “Video For Windows” (VfW) and a new API named “DirectShow”. The old API is the easiest to use for the task.
A guy named Gopalakrishna Palemat wrote a Visual Studio C++ application to demonstrate that use of VfW API. I have ported his code to Delphi. I have slightly updated his code to fix shortcomings and make it “the Delphi way”.
The original article can be found at http://gopalakrishna.palem.in/createmovie.html
My Delphi source code can be found at http://www.overbyte.be/eng/blog_source_Code.html
I will speak only about my source code from now.
All the AVI creation code has been moved into a component named “TAviFromBitmaps”. This class handle everything required with VfW API to create an AVI movie, compressed or not. You just has to create an instance of the class and call his AppendNewFrame method to add a bitmap at the end of the already added bitmaps. The result is an AVI file.
As a simple sample, I wrote a procedure which generate an AVI file made of 25 bitmaps generated on the fly. Each bitmap contain the text “Delphi rocks!” and the number of the frame. This is for simple demo. As I said above, you could as well produce bitmap with a 3D scene viewed from a moving point of view, or a zoom into a Mandelbrot fractal.
procedure TCAviFileForm.GenerateAvi var I : Integer; Y : Integer; H : Integer; BackBitmap : Graphics.TBitMap; Avi : TAviFromBitmaps; begin BackBitmap := Graphics.TBitMap.Create; BackBitmap.Width := 320; BackBitmap.Height := 240; BackBitmap.PixelFormat := TPixelFormat.pf32bit; Avi := TAviFromBitmaps.CreateAviFile( nil, 'output.avi', //MKFOURCC('x', 'v', 'i', 'd'),// XVID (MPEG-4) compression MKFOURCC('D', 'I', 'B', ' '), // No compression 2, 1); // 2 frames per second // First, add a blank frame Avi.AppendNewFrame(BackBitmap.Handle); // Then add frames with text BackBitmap.Canvas.Font.Size := 20; H := (BackBitmap.Canvas.TextHeight('I') * 15) div 10; Y := (BackBitmap.Height div 2) - H; BackBitmap.Canvas.TextOut(10, Y, 'Delphi rocks!'); Y := (BackBitmap.Height div 2); for I := 1 to 25 do begin BackBitmap.Canvas.TextOut(10, Y, IntToStr(I)); Avi.AppendNewFrame(BackBitmap.Handle); end; // Finally, add two blank frame // (MediaPlayer doesn't show the last two frames) BackBitmap.Canvas.FillRect(Rect(0, 0, BackBitmap.Width, BackBitmap.Height)); Avi.AppendNewFrame(BackBitmap.Handle); Avi.AppendNewFrame(BackBitmap.Handle); FreeAndNil(Avi); FreeAndNil(BackBitmap); end;
Compression of the AVI is very important. Without compression, your AVI file will be quickly very large. VfW API is able to compress with about any algorithm. It is enough to install a “codec” into Windows. I like the XVID codec because it produces MPEG-4 video with high quality and high compression. Xvid is an open-source research project focusing on video compression and is a collaborative development effort (http://www.xvid.org). All code is released under the terms of GNU GPL license.
To use XVID compressor, download the setup from http://www.xvid.org and install it. Microsoft Media Player is able to decode XVID compressed movie without installing xvid software because the result is a standard MPEG-4 file.
Each codec is identified by a four character code name “FourCC”. VfW API has a function to transform those four characters codes into an integer value suitable for the rest of the API. This is why you see “MKFOURCC(‘x’, ‘v’, ‘I’, ‘d’)” in the above code.
Of course compression comes with a price: image quality is not the same as the original. So in some applications, you may want to avoid compression. This is done by selecting a compressor of type “MKFOURCC(‘D’, ‘I’, ‘B’, ‘ ’)” (Fourth character is a space).
Download source code from: http://www.overbyte.be/eng/blog_source_code.html
Follow me on Twitter
Follow me on LinkedIn
Follow me on Google+
Visit my website: http://www.overbyte.be
Labels:
API,
bitmap,
borland,
codegear,
delphi,
DirectShow,
DIVX,
embarcadero,
Movie,
Mutlimedia,
VFW,
XVID
July 8, 2013
Upgrade from ANY version to XE4
Users of ANY earlier version of RAD Studio, Delphi, C++ Builder or Borland Developer Studio can upgrade to XE4.
From now until 31 July 2013. RAD Studio XE4 gives you the tools you need to create multi-device, true native apps for iOS, Windows and Mac.
NEW! Completely new Delphi iOS visual development solution and mobile compiler
NEW! Create apps for PCs, tablets and smartphones from a single codebase
NEW! Access more databases on more devices with FireDAC
NEW! InterBase IBLite embeddable database for iOS with free unlimited distribution
…and much more.
Visit Embarcadero shop
From now until 31 July 2013. RAD Studio XE4 gives you the tools you need to create multi-device, true native apps for iOS, Windows and Mac.
NEW! Completely new Delphi iOS visual development solution and mobile compiler
NEW! Create apps for PCs, tablets and smartphones from a single codebase
NEW! Access more databases on more devices with FireDAC
NEW! InterBase IBLite embeddable database for iOS with free unlimited distribution
…and much more.
Visit Embarcadero shop
Subscribe to:
Posts (Atom)