2006. január 31., kedd
Calling conventions and DLLs
Problem/Question/Abstract:
How do I call a method in a DLL written in either C or C++? What do you mean by calling conventions?
Answer:
In Delphi, when we declare a procedure or function, we can specify a convention using one of the directives register, pascal, cdecl, stdcall, and safecall. All these conventions determine the order in which parameters are passed to the procedure/function.
Let me briefly explain what these conventions mean. All the calling conventions make use of stack to pass parameters back and forth except the Register convention.
Register/Pascal
These conventions pass parameters (of procedure/function) from left to right. The leftmost parameter is evaluated and passed first and the rightmost parameter is evaluated and passed last.
And the "Register" calling convention makes use of CPU registers and hence it’s faster than other conventions. When you use this convention, there will not be any stack creation at runtime if the parameters are less than or equal to three. If the parameters are more than three, then the remaining parameters will use the stack.
This "Register" convention is the default in Delphi and it’s the efficient of all because it does not create/use the stack at runtime.
The "Pascal" convention is used for backward compatibility.
The stack cleanup process will be done automatically for all the conventions when the call returns except "Cdecl".
Cdecl/stdcall/safecall
These conventions pass parameters from right to left. The rightmost parameter is evaluated and passed first
and the leftmost parameter is evaluated and passed last. With this convention, the caller has to remove the parameters from the stack when the call returns. So it's the responsibility of the caller.
Why am I writing these simple things in detail?
Yes. This will be very helpful when you write DLL either in Delphi and access it in Delphi Or accessing a DLL written in other languages.
When you write a function/procedure in a DLL in Delphi, you will be specifying the calling convention for each. Also when you call those functions/procedures from an application through either the static loading or dynamic loading, you need to specify the type of calling convention.
When you call a DLL written in either C or C++, you have to use the "cdecl" convention. Otherwise, you
will end up in "Access violation" problems and sometimes the application may crash. Also the DLL, you are calling, should be on the search path.
I faced a problem in my project just because of this calling convention. In my application, I need to call a method in a C DLL. I copied the DLL into my machine. Then I declared all the methods in the DLL in Delphi and tried to call one of them. When I try to access a method, I got "Access violation"; sometimes the application hung and sometimes the entire application crashed. Finally I looked into the Delphi help and got the solution with the calling convention. So I declared each method in the DLL in Delphi with the "cdecl" directive. It worked fine. So don’t forget to add this directive in each method call from a DLL written in C/C++.
2006. január 30., hétfő
Detect UNIX textfiles
Problem/Question/Abstract:
How to detect if an ASCII textfile uses UNIX or Windows linebreaks?
This function will detect if a textfile is a Windows or UNIX textfile, and while we're at it, let's show two versions of the same function, one beautful and one less beautful.
Answer:
First of all, the reason is because Windows uses CRLF ($0D $0A or #13 #10) and UNIX/Linux uses just LF ($0A or #10) as linebreaks in textfiles.
The need to do it is because when using the Readln procedure it will not work on UNIX files because it cannot detect the linebreak. Instead of seeing your application go crazy it might be a nice thing to detect if it's a UNIX file or not in advance, and then provide the option to convert it if necessary.
The way to detect if it's a UNIX or Windows file is to spot the difference, i.e. to see if a CR char precedes the LF char.
Here is a go at it:
function IsFileUNIX(Filename: string): boolean;
var
StopRead: boolean;
F: file of Byte;
CurB, PrevB: Byte;
begin
StopRead := False;
PrevB := 0;
Result := True;
AssignFile(F, Filename);
FileMode := 0; // read only
Reset(F);
while (not Eof(F)) and (StopRead = False) do
begin
Read(F, CurB);
// check if $0D precedes $0A
if CurB = $0A then
begin
Result := PrevB <> $0D;
StopRead := True;
end;
PrevB := CurB;
end;
end;
Well, this function did what I wanted, however, I thought it looked kind of ugly so I began to think a little bit how I may use the same principle, but execute it with fewer statements and make the function a little bit more beautiful.
Simply replacing the while loop with a repeat loop did miracles, here's the second go at it:
function IsFileUNIX2(Filename: string): boolean;
var
F: file of Byte;
CurB, PrevB: Byte;
begin
AssignFile(F, Filename);
FileMode := 0; // read only
Reset(F);
repeat
PrevB := CurB;
Read(F, CurB);
until (CurB = $0A) or (Eof(F));
// check if $0D precedes $0A
Result := PrevB <> $0D;
end;
2006. január 29., vasárnap
Divide a file into 1.44 mb volumes
Problem/Question/Abstract:
How can i divide a file into 1.44 mb volumes if file size is longer than floppy capacity?
Answer:
const
MaxSize: Longint = 1440000; //byte
function ExtractFileNames(FileNames: string): string;
var
S: string;
begin
S := '';
while Pos('.', FileNames) > 0 do
begin
S := S + Copy(FileNames, 1, Pos('.', FileNames) - 1);
Delete(FileNames, 1, Pos('.', FileNames));
end;
result := S;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
InFile, OutFile: file;
CopyBuffer: POINTER; { buffer for copying }
iRecsOK, iRecsWr, index: Integer;
sFileName, sFileExt, sFileFullName: string;
fFileSize: file of Byte;
Size: LongInt;
begin
sFileFullName := 'C:\1\1.mp3';
sFileName := ExtractFileName(sFileFullName);
sFileExt := ExtractFileExt(sFileName);
sFileName := ExtractFileNames(sFileName);
ShowMessage(sFileFullName + #13 + sFilename + #13 + sFileExt);
if FileExists(sFileFullName) then
begin
AssignFile(fFileSize, sFileFullName);
FileMode := 0; {Set file access to read only }
Reset(fFileSize);
Size := FileSize(fFileSize); {Get File Size}
CloseFile(fFileSize);
ShowMessage(IntToStr(Size));
if Size > MaxSize then
begin {Divide}
Getmem(CopyBuffer, MaxSize); { allocate the buffer }
Assignfile(inFile, sFileFullName); //+ '.ZIP');
Reset(inFile, 1);
index := 1;
repeat
AssignFile(outFile, sFileName + '-' + IntToStr(index) + sFileExt);
Rewrite(OutFile, 1);
inc(index);
BlockRead(InFile, CopyBuffer^, MaxSize, iRecsOK);
BlockWrite(OutFile, CopyBuffer^, iRecsOK, iRecsWr);
CloseFile(OutFile);
until (iRecsOK < MaxSize);
CloseFile(InFile);
FreeMem(CopyBuffer, MaxSize); { free the buffer }
ShowMessage('Done..!');
end
else
begin
ShowMessage('Do nothing..!');
end;
end
else
ShowMessage('File: ' + sFileFullName + ' not found');
end;
If you can put it back, you can use DOS copy function;
copy /b file-1.xxx+file-2.xxx+file-3.xxx file.mp3
2006. január 28., szombat
An Overview of UDP
Problem/Question/Abstract:
What is UDP? How can we use it in Delphi?
Answer:
UDP is an abbreviation for User Datagram Protocol. It’s nothing but a connectionless transport protocol that runs on TCP/IP’s IP.
The advantage of this protocol is that it is connectionless. It doesn’t need any connection before sending data packets to another computer.
The disadvantage is that it provides an unreliable datagram service. That is, the data packets may be duplicated, lost or received in a different order than the one in which they were sent. So the application must handle all those situations robustly.
The receiving program requests a number of bytes (the maximum will be the total number of bytes in the received packet). If less than the full packet is read, then the remainder is discarded. Then the next read is from the next packet. That means the boundaries of the original packet are preserved. For that the application must handle error correction while reading packets.
This UDP is best suited for small, independent requests like requesting a value of a variable etc., If the data is too large to send (i.e many packets of data) and valuable, then UDP is not the preferred protocol to use.
There is a component in Delphi 5 edition for the UDP from NetMasters called NMUDP. That component is similar to use as TclientSocket component.
The comparison of TClientSocket component with NMUDP:
As far as the properties are concerned, here in NMUDP we need to set the LocalPort(it could be any integer greater than zero; but should not be zero) to receive the data sent from the server in addition to the RemoteHost and RemotePort. But actually, the host may not be a remote one. It could be a local machine. (i.e) we can send data packets to the client machine itself and get response back for the testing purposes.
Also we can set the Report Level property to get the status during the transmission.
And as far as the methods are concerned, there is no major difference; you have the ReadStream, ReadBuffer methods as in TClientSocket component.
Regarding the events:
As the event Onclientsocketread in Tclientsocket component, here we can use the key event OnDataReceived to get the data back from the server.
Regarding the boundaries of the data packets, we need to identify the boundaries of the data packets while using either the TclientSocket or the NMUDP component (Which I didn’t discuss in my previous article ‘Making an application a TCP/IP client(with sample code)…’) to get the exact data sent from the server.
For that (irrespective of which component you use), we can use the concept of message header tag and end tag like HTML tags. By that we can identify the starting and ending of a data packet. Also we can send many information in a data packet with different message heading/ending tag.
(This paragraph will answer a question a person asked sometime back thro’ e-mail)
In my application (Using TClientSocket...Please refer my previous articles), I’m sending a whole bunch of bytes to another computer and getting the response back using the message header/end tag only. With this approach, there is very less possibility of losing data. If we didn’t get the whole tag contents between the header/end tag, we can throw an error to the user so that the user can try resending the same data again or take some other steps robustly.
2006. január 27., péntek
Remove extra spaces from a string
Problem/Question/Abstract:
I would like to strip all extra/ unnecessary spaces from a string. Meaning, if there are two or more space characters next to each other, I want to strip all but one. How can I do this?
Answer:
Solve 1:
{ ... }
st := 'This is a test';
p: pos(' ', st);
while p <> 0 do
begin
delete(st, p, 1);
p: pos(' ', st);
end;
{ ... }
Solve 2:
{ ... }
while pos(' ', st) > 0 do
st := StringReplace(st, ' ', ' ', [rfReplaceAll]);
{ ... }
2006. január 26., csütörtök
Enumerating Network Connections
Problem/Question/Abstract:
How to detecting current network connections?
Answer:
From the MS-DOS prompt, you can enumerate the network connections (drives) by using the following command:
net use
Programmatically, you would call WNetOpenEnum() to start the enumeration of connected resources and WNetEnumResources() to continue the enumeration.
The following sample code enumerates the network connections:
Sample Code
procedure TForm1.Button1Click(Sender: TObject);
var
i, dwResult: DWORD;
hEnum: THANDLE;
lpnrDrv,
lpnrDrvLoc: PNETRESOURCE;
s: string;
const
cbBuffer: DWORD = 16384;
cEntries: DWORD = $FFFFFFFF;
begin
dwResult := WNetOpenEnum(RESOURCE_CONNECTED,
RESOURCETYPE_ANY,
0,
nil,
hEnum);
if (dwResult <> NO_ERROR) then
begin
ShowMessage('Cannot enumerate network drives.');
Exit;
end;
s := '';
repeat
lpnrDrv := PNETRESOURCE(GlobalAlloc(GPTR, cbBuffer));
dwResult := WNetEnumResource(hEnum, cEntries, lpnrDrv, cbBuffer);
if (dwResult = NO_ERROR) then
begin
s := 'Network drives:'#13#10;
lpnrDrvLoc := lpnrDrv;
for i := 0 to cEntries - 1 do
begin
if lpnrDrvLoc^.lpLocalName <> nil then
s := s + lpnrDrvLoc^.lpLocalName + #9 + lpnrDrvLoc^.lpRemoteName + #13#10;
Inc(lpnrDrvLoc);
end;
end
else if dwResult <> ERROR_NO_MORE_ITEMS then
begin
s := s + 'Cannot complete network drive enumeration';
GlobalFree(HGLOBAL(lpnrDrv));
break;
end;
GlobalFree(HGLOBAL(lpnrDrv));
until (dwResult = ERROR_NO_MORE_ITEMS);
WNetCloseEnum(hEnum);
if s = '' then
s := 'No network connections.';
ShowMessage(s);
end;
2006. január 25., szerda
Improving your Object classes reliability
Problem/Question/Abstract:
One of the worst things you can do is not call a destructor for an object. I found this the hard way with my article on Compound Volumes. The destructor call ensured that any new additions to the file were properly recorded. So forgetting it caused corruption if new files were added.
Answer:
So what we want is a way to call the destructor automatically if you forget to do it. Now I could be accused of encouraging lazy programming. So what you should do is put a ShowMessage call saying something like “*Oi dipstick, you haven’t called a destructor”. That way you avoid corrupting data and your mistakes are found a bit easier.
Heres the main code to be added after the implementation section:
Note that calling TObject(Pointer).Free works for all objects. (Unless you know better...)
var
cvList: Tlist;
const
InTidy: boolean = false;
procedure Remove(V: TCompoundVolume);
var
Index: integer;
begin
if InTidy then
exit;
for Index := cvlist.count - 1 downto 0 do
if cvlist[Index] = v then
cvlist.Delete(Index);
end;
procedure Tidylist;
var
Index: integer;
begin
if InTidy then
exit;
InTidy := true;
for Index := cvlist.count - 1 downto 0 do
if assigned(Cvlist[Index]) then
begin
TObject(Cvlist[index]).Free;
cvlist.Delete(Index);
end;
InTidy := false;
end;
In the class creator add this line
cvList.Add(Self);
and in the destructor add this
Remove(Self);
And in your unit, add the lines or modify the Initialization/finalization sections
initialization
cvlist := tlist.Create;
finalization
TidyList;
cvlist.free;
If your destructor is called by you, the call to Remove will remove it from the list. This needs a recursion check in case you forgot to call it and it tries to call Remove while the destructor is called from TidyList. That is what the flag InTidy guards against.
*Dipstick is a mild English term of abuse, about the same as tosspot or tosser, but not as bad as say wanker.
2006. január 24., kedd
Detecting simultaneous left and right mouse clicks
Problem/Question/Abstract:
How to known if the user has pressed simultaneously the left and right mouse buttons?
Answer:
The OnMouse event is declared as follows:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
If you find in the Classes unit the declaration of TShiftState is:
TShiftState = set of (ssShift, ssAlt, ssCtrl,
ssLeft, ssRight, ssMiddle, ssDouble);
So you need to test if sLeft and ssRight are present in the Shift parameter, now your code must be like this:
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (ssRight in Shift) and (ssLeft in Shift) then
ShowMessage('The user has pressed Left and Right buttons');
end;
2006. január 23., hétfő
From resources to TWebBrowser
Problem/Question/Abstract:
Ever wanted to fast do your own exe containing HTML pages. This way of doing, lets you easy manage HTML files included into your EXE in a TWebBrowser.
Answer:
First of all you need to include those two units:
uses mshtml, activex;
Next, you must insert a TWebBrowser (called "WB" in this article) into your form (called frmMain in this article).
You must add 2 public procedures, called "InternalPage" and "ResourcePage", to your form. After that, the declaration should look like this:
{...}
type
TfrmMain = class(TForm)
wb: TWebBrowser;
{...}
public
procedure InternalPage(const HTMLString: string);
procedure ResourcePage(const Name: string);
{...}
end;
The implementation of that procedures is this:
procedure tfrmmain.InternalPage(const HTMLString: string);
var
pagesource: OleVariant;
HTMLDocument: IHTMLDocument2;
begin
if not (Assigned(WB.Document)) then
WB.Navigate('about:blank', EmptyParam, EmptyParam, EmptyParam, EmptyParam);
HTMLDocument := WB.Document as IHTMLDocument2;
pagesource := VarArrayCreate([0, 0], varVariant);
pagesource[0] := HTMLString;
HTMLDocument.Write(PSafeArray(TVarData(pagesource).VArray));
HTMLDocument.Close;
end;
procedure TfrmMain.ResourcePage(const Name: string);
var
RS: TResourceStream;
SL: TStringList;
begin
try
RS := TResourceStream.create(HInstance, uppercase(trim(Name)), RT_RCDATA);
try
SL := TStringList.create;
try
SL.LoadFromStream(RS);
InternalPage(SL.Text);
finally
SL.Destroy;
end;
finally
RS.Destroy;
end;
except
on e: exception do
;
end;
end;
The next move is to manage a little the BeforeNavigate2 event of our TWebBrowser. You only need to make this:
procedure TfrmMain.wbBeforeNavigate2(Sender: TObject;
const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
Headers: OleVariant; var Cancel: WordBool);
var
pagename: string;
begin
if lowercase(trim(url)) = 'about:blank' then
exit;
if pos('internal://', lowercase(URL)) = 1 then
begin
cancel := true;
pagename := copy(URL, (pos('://', URL) + 3), maxint);
if length(pagename) > 0 then
if pagename[length(pagename)] = '/' then
delete(pagename, length(pagename), 1);
ResourcePage(pagename);
end;
end;
Now, add, for example tro pages as RT_RCDATA into project's resources (Project\Resources menu into Delphi IDE, then right click on the toolwindow, and select New\User Data), called for example "FIRSTHTMLPAGE" and "SECONDHTMLPAGE".
On the Create event of your form you need to load the first page:
procedure TfrmMain.FormCreate(Sender: TObject);
begin
ResourcePage('FIRSTHTMLPAGE');
end;
That's all.
By the way: you'll need to refer all links in your page to "internal://" + name of the resource containing the page for it to work.
Here's a way to use this source code but without having to put "internal://" for every link inside to html files:
declare a TStringList:
private
{ Private declarations }
MyPages: TStringList;
...on the oncreate event add all your pages in the form:
MyPages := TStringList.Create;
MyPages.Add('about:blankdd.htm=HTMLFILE1');
MyPages.Add('about:blankddamigos.htm=HTMLFILE2');
MyPages.Add('about:blankddamigos2.htm=HTMLFILE3');
MyPages.Add('about:blankddamigos3.htm=HTMLFILE4')
...all your pages, note that I added 'about:blank' before the name of each html file... I don't know why, but it doesn't work without it, so... just put it...
then the BeforeNavigate2 event would look like:
procedure TForm1.wbBeforeNavigate2(Sender: TObject;
const pDisp: IDispatch; var URL, Flags, TargetFrameName, PostData,
Headers: OleVariant; var Cancel: WordBool);
var
pagename: string;
begin
if lowercase(trim(url)) = 'about:blank' then
exit;
pagename := MyPages.Values[url];
if (PageName <> '') then
begin
Cancel := True;
ResourcePage(pagename)
end;
end;
That's it!
Of course both approaches have their advantages and disadvantages... I find this approach useful because you leave your html alone and you only worry about your Delphi Source code, like if there's more than one link to the same page (like from pages 2, 3, 4 to page 1) you don't need to make each of those links "internal://htmlfile1", this does it automatically for you.
2006. január 22., vasárnap
Invert someones desktop for fun (has usefull code)
Problem/Question/Abstract:
Bored? Like playing tricks on your coworkers? I tested it out on my bosses secretary and it was fun, so I'll share it with you. BUT its just not fun, it also contains usefull classes.
Answer:
Fun program to trick your friends, secretary or anyone with a computer :-). The program flips your desktop upside down until you click on it.
BUT, this does have some interesting code.
It contains TDesktopCanvas where you can access your desktop through a TCanvas object.
It contains TQuickPixel which gives you high speed pixel access, btw - it caches the scan lines for even faster performance.
Download the source, it is fairly easy to follow. Compile it and stick it in your friends startup folder :-) or just run it and walk away.
To end the program just click the inverted screen.
Now for the usefull part as far as coding:
A class I made so I could have fast pixel access without fumbling with scan lines. This class caches the scan lines for faster perfomance. One drawback of this class is that it sets your Bitmap to 24bit. If you want me to build a class that supports all bit formats then please make a comment to do so and I can build one without causing a performance hit (use method pointers so there is no testing of bit format). I will also speed up the pixel setting to work without the shifts if anyone asks for the multiple format thing. As a side note I think it would be possible to include Line, arc and circle methods... but only if there is enough interest. Windows is really slow about drawing.
Here is the code for TQuickPixel. You can also go to my website for working EXE and download full source.
unit QuickPixel;
interface
uses
Windows, Graphics;
type
TQuickPixel = class
private
FBitmap: TBitmap;
FScanLines: array of PRGBTriple;
function GetPixel(X, Y: Integer): TColor;
procedure SetPixel(X, Y: Integer; const Value: TColor);
function GetHeight: Integer;
function GetWidth: Integer;
public
constructor Create(const ABitmap: TBitmap);
property Pixel[X, Y: Integer]: TColor read GetPixel write SetPixel;
property Width: Integer read GetWidth;
property Height: Integer read GetHeight;
end;
implementation
{ TQuickPixel }
constructor TQuickPixel.Create(const ABitmap: TBitmap);
var
I: Integer;
begin
inherited Create;
FBitmap := ABitmap;
FBitmap.PixelFormat := pf24bit;
SetLength(FScanLines, FBitmap.Height);
for I := 0 to FBitmap.Height - 1 do
FScanLines[I] := FBitmap.ScanLine[I];
end;
function TQuickPixel.GetHeight: Integer;
begin
Result := FBitmap.Height;
end;
function TQuickPixel.GetPixel(X, Y: Integer): TColor;
var
P: PRGBTriple;
begin
P := FScanLines[Y];
Inc(P, X);
Result := (P^.rgbtBlue shl 16) or (P^.rgbtGreen shl 8) or P^.rgbtRed;
end;
function TQuickPixel.GetWidth: Integer;
begin
Result := FBitmap.Width;
end;
procedure TQuickPixel.SetPixel(X, Y: Integer; const Value: TColor);
var
P: PRGBTriple;
begin
P := FScanLines[Y];
Inc(P, X);
P^.rgbtBlue := (Value and $FF0000) shr 16;
P^.rgbtGreen := (Value and $00FF00) shr 8;
P^.rgbtRed := Value and $0000FF;
end;
end.
unit DesktopCanvas;
// original aurthor is Erwin Molendijk
interface
uses
Graphics, Windows;
type
TDesktopCanvas = class(TCanvas)
private
FDC: HDC;
function GetWidth: Integer;
function GetHeight: Integer;
public
constructor Create;
destructor Destroy; override;
published
property Width: Integer read GetWidth;
property Height: Integer read GetHeight;
end;
implementation
{ TDesktopCanvas }
function TDesktopCanvas.GetWidth: Integer;
begin
Result := GetDeviceCaps(Handle, HORZRES);
end;
function TDesktopCanvas.GetHeight: Integer;
begin
Result := GetDeviceCaps(Handle, VERTRES);
end;
constructor TDesktopCanvas.Create;
begin
inherited Create;
FDC := GetDC(0);
Handle := FDC;
end;
destructor TDesktopCanvas.Destroy;
begin
Handle := 0;
ReleaseDC(0, FDC);
inherited Destroy;
end;
end.
2006. január 21., szombat
Disable Keyboard and Mouse
Problem/Question/Abstract:
How to disable mouse and keyboard for n seconds
Answer:
This Function detect is Function exists in Library (dll)
function FuncAvail(VLibraryname, VFunctionname: string; var VPointer: pointer):
boolean;
var
Vlib: tHandle;
begin
Result := false;
VPointer := nil;
if LoadLibrary(PChar(VLibraryname)) = 0 then
exit;
VPointer := GetModuleHandle(PChar(VLibraryname));
if Vlib <> 0 then
begin
VPointer := GetProcAddress(Vlib, PChar(VFunctionname));
if VPointer <> nil then
Result := true;
end;
end;
Source code in Button1 on Form1
procedure TForm1.Button1Click(Sender: TObject);
var
xBlockInput: function(Block: BOOL): BOOL; stdcall;
begin
if FuncAvail('USER32.DLL', 'BlockInput', @xBlockInput) then
begin
xBlockInput(true);
Sleep(15000); // 15 secounds
xBlockInput(false);
end;
end;
2006. január 20., péntek
Towards a more accurate sort order
Problem/Question/Abstract:
Sorting Addresses is a pain at the best of times, especially when a client supplies bad data (You may define clear fields in your DB, but when the data comes in, does it fit easily??)
This attempts to resolve this issue
Answer:
unit AddrSortOrder;
{The custom sort order is used to deal with the fact that the
house and flat numbers are sorted as strings. They are stored as
strings to allow things like '150-175' as a house number, or '3a',
or perhaps even simply a flat 'A'.
The need for a custom sort order is caused by the fact that with an
ordinary ASCII sort order '4' will appear after '30'. This is not
desirable behaviour.
This approach to fix this problem is to look for the first number
in the string (if there is one) and then use this as some kind of
primary sort order. The rest of the sorting will then be done on
the remaining characters (with preceding and trailing spaces
stripped out), based on the ASCII value of their upper-
case varients. Potential problems caused by this approach include
(but are not limited to) the use of accented characters will
possibly cause strange orderings and furthermore, if there is a block
of flats with three floors A, B, C for example then supposing the
flats on those floors are A1, A2, A3, B1, B2, B3 then the ordering
of records will not be ideal - this approach will sort them as
A1, B1, A2, B2, A3, B3. This behaviour is regrettable, but
acceptable - we cannot tell that it is not flat A on floor 1 for
example. It's unlikely that we will be able to find a sort order
that always produces ideal results.
Some examples of sorted lists (not all ideal):
EXAMPLE 1 EXAMPLE 2 EXAMPLE 3
Flat 1 1 A
Flat 2 -2 B
3 2-4 C
3B 3a 1
Flat 3A 5 2
}
interface
uses SysUtils;
function CalcSortIndex(NumStr: string): double;
implementation
function CalcSortIndex(NumStr: string): double;
var
strlength, i, j, tmp: integer;
found: boolean;
numpart, strpart, divisor: double;
choppedstr: string;
begin
//This function will return the sort index value for the string passed
strlength := length(NumStr);
if strlength = 0 then
begin
result := 0;
exit;
end;
found := false;
//split the string into a 'number' and a 'string' part..
//initialise
choppedstr := numstr;
numpart := 0;
//Locate the first digit (if there)
for i := 1 to strlength do
begin
if numstr[i] in ['0'..'9'] then
begin
found := true; //First digit found!!
break;
end;
end; //for i..
if found then
begin
//now get the to the end of the digits..
found := false;
for j := i to strlength do
begin
if not (numstr[j] in ['0'..'9']) then
begin
found := true; //end of digits found
break;
end;
end; //for j..
//Separate out the string parts
if found then
begin
//Number was embedded..
val(copy(numstr, i, j - i), numpart, tmp);
Delete(choppedstr, i, j - i);
end
else
begin
//Number went to the end of the string
val(copy(numstr, i, strlength), numpart, tmp);
Delete(choppedstr, i, strlength);
end;
end;
choppedstr := Uppercase(trim(choppedstr));
strlength := length(choppedstr);
//evaluate a number for the remaining part of the string
strpart := 0;
divisor := 1;
for i := 1 to strlength do
begin
divisor := divisor / 256;
//convert from Char to single using a variant conversion
strpart := strpart + (ord(choppedstr[i]) * divisor);
end;
//All done, return the value
result := numpart + strpart;
end;
end.
NB a version of this Algorithm for MSSQL7 is also posted (Title "Towards a more accurate sort order in MSSQL7")
2006. január 19., csütörtök
Drawing components' bitmaps appeared in Delphi palette
Problem/Question/Abstract:
How to draw components' bitmaps appeared in Delphi palette?
Answer:
Example below demonstrates drawing components images in combo box items. Combo box is filled with names of all components placed on form. See comments in source for description of each action.
Source:
// Add LibIntf unit into uses clause of your unit.
// Add this definition for Delphi version detection, just for better reading
{$IFDEF VER90}
{$DEFINE DELPHI2}
{$ENDIF}
{$IFDEF VER100}
{$DEFINE DELPHI3}
{$ENDIF}
{$IFDEF VER120}
{$DEFINE DELPHI3}
{$DEFINE DELPHI4}
{$ENDIF}
{$IFDEF VER130}
{$DEFINE DELPHI3}
{$DEFINE DELPHI4}
{$ENDIF}
procedure TDlgForm.cbComponentsDrawItem(Control: TWinControl;
Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
ARect: TRect;
{$IFDEF DELPHI3}
PIt: LibIntf.TIPaletteItem;
{$ENDIF}
{$IFDEF DELPHI4}
PIt: LibIntf.IPaletteItem;
{$ENDIF}
B, tmpBmp: TBitmap;
{$IFDEF DELPHI2 }
tmpImList: TImageList;
{$ENDIF}
SName: string;
begin
with cbComponents.Canvas do
begin
Brush.Color := cbComponents.Color;
Brush.Style := bsSolid;
FillRect(Rect);
tmpBmp := TBitmap.Create;
B := TBitmap.Create;
try
SName := cbComponents.Items[Index];
B.Width := 24;
B.Height := 24;
{$IFDEF DELPHI3}
//Under Delphi 3 or later we should use routines from LibIntf unit
PIt := LibIntf.DelphiIDE.GetPaletteItem(TComponentClass(GetClass(Comp.Owner.FindComponent(SName).ClassName)));
// Drawing component image on our bitmap canvas
PIt.paint(B.Canvas, 0, 0);
tmpBmp.Assign(B);
{$ENDIF}
ARect := Bounds(0, 0, 24, 24);
{$IFDEF DELPHI2}
// Detecting the class name of component
SName := TComponent(Comp.Owner.FindComponent(SName)).ClassName;
// Loading bitmap image from resources since it is linked to cmplib32.dcl
B.LoadFromResourceName(hInstance, UpperCase(PChar(SName)));
tmpBmp.Width := 22;
tmpBmp.Height := 22;
BitBlt(tmpBmp.Canvas.Handle, 0, 0, 22, 22, B.Canvas.Handle, 2, 2, SRCCOPY);
tmpImList := TImageList.CreateSize(22, 22);
try
tmpImList.AddMasked(tmpBmp, tmpBmp.TransparentColor);
tmpBmp.Canvas.Brush.Color := cbComponents.Color;
tmpBmp.Canvas.Brush.Style := bsSolid;
tmpBmp.Canvas.FillRect(ARect);
tmpImList.Draw(tmpBmp.Canvas, 0, 0, 0);
finally
tmpImList.Free;
end;
{$ENDIF}
{$IFDEF DELPHI3}
tmpBmp.Canvas.Brush.Color := cbComponents.Color;
tmpBmp.Canvas.FillRect(ARect);
tmpBmp.Width := 24;
tmpBmp.Height := 24;
BitBlt(tmpBmp.Canvas.Handle, 0, 0, 24, 24, B.Canvas.Handle, 4, 4, SRCCOPY);
{$ENDIF}
// Drawing component image on the combo box canvas
Draw(Rect.Left + 3, Rect.Top + 2, tmpBmp);
finally
tmpBmp.Free;
B.Free;
end;
// Drawing raised rectangle if item is selected
if odSelected in State then
begin
ARect := Bounds(Rect.Left + 1, Rect.Top + 1, 23, 23);
Frame3D(cbComponents.Canvas, ARect, clBtnHighlight, clBtnShadow, 1);
Font.Color := clHighlightText;
Brush.Color := clHighlight;
ARect := Bounds(26, Rect.Top, Rect.Right - Rect.Left - 26, Rect.Bottom -
Rect.Top);
FillRect(ARect);
end
else
Font.Color := clBlack;
Brush.Style := bsClear;
TextOut(Rect.Left + 27, Rect.Top + 4, cbComponents.Items[Index]);
end;
end;
2006. január 18., szerda
Communicating between your applications
Problem/Question/Abstract:
I want to perform communication between two my applications or between two instances of my application.
Answer:
You can perform communication between your application using Windows messages exchange mechanism. We can use HWND_BROADCAST value for first parameter for SendMessage function for suppressing finding of forms' in other applications HANDLE.
For using HWND_BROADCAST we should register our messages in Windows.
For performing this you could make the following:
(In example below we will inform about our form's top position)
1. Define type of your message structure, it could be something like this:
type
TWMMYMessage = record
Msg: Cardinal; // ( first is the message ID )
Handle: HWND; // ( this is the wParam, Handle of sender)
Info: LongInt; // ( this is lParam, pointer to our data)
Result: LongInt;
end;
2. Override your form's DefaultHandler method and add method for handling your message, like this
TForm1 = class(TForm)
{... }
public
{ Public declarations }
{... }
procedure DefaultHandler(var Message); override;
procedure WMMYMessage(var Msg: TWMMYMessage);
{... }
end;
3. Declare message variable:
var
WM_OURMESSAGE: DWORD;
4. Insert realisation of DefaultHandler and our message handler methods:
procedure TForm1.DefaultHandler(var Message);
var
ee: TWMMYMessage;
begin
with TMessage(Message) do
begin
if (Msg = WM_OURMESSAGE) then
begin
ee.Msg := Msg;
ee.Handle := wParam;
ee.Info := lParam;
// Checking if this message is not from us
if ee.Handle <> Handle then
WMMYMessage(ee);
end
else
inherited DefaultHandler(Message);
end;
end;
procedure TForm1.WMMYMessage(var Msg: TWMMYMessage);
begin
Label1.Caption := Format('Our another form handle :%d', [Msg.Handle]);
Label2.Caption := Format('Our another form top :%d', [Msg.Info]);
end;
5. Add registration of your message that you could handle the HWND_BROADCAST messages:
initialization
WM_OURMESSAGE := RegisterWindowMessage('Our broadcast message');
6. Add the message sending somewhere:
procedure TForm1.Button1Click(Sender: TObject);
begin
SendMessage(HWND_BROADCAST, WM_OURMESSAGE, Handle, Top);
end;
7. Compile and run two copies of your application and test it functionality.
2006. január 17., kedd
Gradient fill procedure
Problem/Question/Abstract:
Gradient fill
Answer:
Paste this on your form + 1 button and 2 panels and a paintbox and link the panels onclick event and you will see it work it is fairly fast. When using this code in your application it is suggested to first draw to a tbitmap this will when the screen area neads to be redrawn speed it up considerably.
function getnewcolor(M1, M2: TColor; Location: Integer): TColor;
var
V: array[0..2] of Byte; //BeginRGBValue
D: array[0..2] of integer; //RGBDifference
R, G, B: Byte;
K1, K2: Longint;
begin
K1 := ColorToRGB(M1);
K2 := ColorToRGB(M2);
V[0] := GetRValue(K1);
V[1] := GetGValue(K1);
V[2] := GetBValue(K1);
D[0] := GetRValue(K2) - V[0];
D[1] := GetGValue(K2) - V[1];
D[2] := GetBValue(K2) - V[2];
R := V[0] + MulDiv(Location, D[0], Form1.PaintBox1.Width - 1);
G := V[1] + MulDiv(Location, D[1], Form1.PaintBox1.Width - 1);
B := V[2] + MulDiv(Location, D[2], Form1.PaintBox1.Width - 1);
Result := RGB(R, G, B);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
for i := 1 to PaintBox1.Width do
begin
PaintBox1.Canvas.Pen.Color := GetNewColor(panel1.color, panel2.color, i);
PaintBox1.Canvas.MoveTo(i, 1);
PaintBox1.Canvas.LineTo(i, Panel2.Height);
end;
end;
procedure TForm1.Panel1Click(Sender: TObject);
begin
if ColorDialog1.Execute then
if Sender = Panel1 then
Panel1.Color := ColorDialog1.Color
else
Panel2.Color := ColorDialog1.Color;
end;
2006. január 16., hétfő
Object Pooling in COM+
Problem/Question/Abstract:
How is Object Pooling Implemented in COM+?
Answer:
What is COM?
It’s a technology that defines a standard way for a client module and server module to communicate through a set of interfaces. The module could be an application or a DLL. Also the client and server can be in the same box or in different boxes
What is COM+?
It’s just an extended version of COM coming with Windows 2000. Microsoft has redefined and added some concepts/features with COM+.
What is Object Pooling and Why do we need Object Pooling?
To better illustrate the need for Object Pooling, let us consider a sample web application using COM objects. The basic functions of such an application using a COM object would be the following:
Creates a COM object
Uses the COM object by calling various methods/properties.
Destroys the COM object
Let us assume that we have an ASP page in that web application and got some 30,000 requests for a day/at peak times. Let us suppose that the ASP page creates three COM objects in that page. So as per the calculation, there would be 90,000 objects created and destroyed. No need to say, it will definitely consume a lot of resources and an overhead. What could be done to avoid this? Recycle. Yes. We can recycle the objects used at one transaction in the next transaction.
Here comes the Object Pool. It’s a place where the application return the COM objects after using it instead of destroying it. So every time the application needs a COM object, it needs to perform the following steps:
Checks the object pool if one exists. If exists, then use it or creates a new COM object.
Use the COM object by calling its methods/properties.
Return it to the same object pool after using it.
This looks like a good concept in reducing/conserving the usage of resources in a web application. But the next question comes to our mind is that can we implement all of those in an web application? We can; but we need to think of the following things in mind.
Who is responsible for managing the Object Pool?
When that Object Pool will be created/destroyed?
When will the objects be created/destroyed in that pool?
When those objects will be destroyed in that pool?
How will the web application know to use the objects in the pool? Does the application need to write a separate code to call an object in the object pool?
How can we manage multiple clients accessing an object in the object pool?
But implementing these in every web application is really a difficult task and it’s an overhead on the programmer’s side; Also it’s error prone.
Here comes the COM+
COM+ helps us in doing all those processes without changing a single line of code in our COM object. That’s the beauty of COM+. A COM+ application is typically an MTS application in the earlier Windows versions(NT,95); but in Windows 2000, the name has been changed to COM+. That’s it.
COM+ provides us with a lot of services. One of its services is this Object Pooling. All we have to do is to set the component properties in Component Services Editor to use Object Pooling. The rest will be taken care of by the COM+ runtime.
But in COM+ 1.0, there are some restrictions on using its services by COM objects developed by various languages. This is because of the incompatibility between the COM+ and the languages.
As of now, COM objects developed using Visual Basic cannot use this Object Pooling service provided by the COM+. Is that right? Any ideas? What about COM objects developed using Delphi? Is Delphi 5.0/6.0 COM+ compatible? Can Delphi 5.0/6.0 use all of the services provided by COM+? Because, I didn’t get a chance to try these out. Discussions are welcome!!!
When COM Objects are destroyed?
As every COM programmer knows, COM implements some basic methods.
QueryInterface
AddRef
Release
Out of these methods, Release is responsible for destroying the object. After the COM Object is created, every time a method of the COM Object is called, it’ll call AddRef method to increase the reference count for that component. And after the method call has been over, the reference count will be reduced by one and once it reaches zero, the COM object will be destroyed.
Reference count is a number that indicates the number of active clients using the COM object.
How Object Pooling is implemented in COM+
This is implemented by intercepting the calls to the Release method of IUnknown. As you know already, every COM object maintains a reference count and once it reaches zero, then that COM object is destroyed. But this is not good if we would like to reuse/recycle the object. In turn, it's not good to implement the Object Pooling service. So the following are implemented by COM+:
COM+ maintains an additional reference count for COM objects to be pooled when the object is created.
COM+ intercepts the calls to Release method for pooled COM objects.
The above two things are implemented by a technique called Interception. The interception is implemented by a light-weight proxy. It's also called an Interceptor. It contains a small amount of code that acts between the client and the real object. This code is invoked for components which are marked as pooled components. So by this technique, inteception, COM+ runtime implements the Object Pooling.
All these are happening behind the scenes by COM+ runtime. All we have to do to make use of this Object Pooling service is to make sure that we set the right 2option in the Component Services Editor. Component Services is available in Win 2000.
This article is just a beginning to COM+ and I would like to explore more on this COM+ later. If you have any views,comments or have any experience with that, please feel free to share.
2006. január 15., vasárnap
Write Components which handle ENTER-Key like TAB-Key
Problem/Question/Abstract:
Standard-behaviour of controls when pressing ENTER-KEY is a BEEP-Sound. But how to write Components which handle ENTER-Key like TAB-Key?
Answer:
This Article will show how to write Components which will handle the ENTER-Key in the same behaviour like the TAB-Key. As Sample I will take a TEdit-Component but it should work on oll other components with OnKeyPress-Event.
Create a new Component from TEdit with a new property:
EnterNextCtrl: Boolean
If this is TRUE (standard), the Focus will jump to next Control if the user press ENTER. If it is FALSE it will show standard-behaviour (beep). This functionality is included in OnKeyPress-Event. There the component sends the Message "WM_NextDLGCTL" to the parent Form. It's important to get the ParentForm, because the TMyEdit need not included on TForm directly but maybe on a TPanel. So you have to send the message directly to the parent-FORM (and not to PARENT, which may be a TPanel).
It should be possible to use this way for other components which have a OnKeyPress-Event.
type
TMyEdit = class(TEdit)
private
FEnterNextCtrl: Boolean;
protected
procedure KeyPress(var Key: Char); override;
published
property EnterNextCtrl: Boolean read FEnterNextCtrl write FEnterNextCtrl;
constructor Create(AOwner: TComponent); override;
end;
constructor TMyEdit.Create(AOwner: TComponent);
begin
inherited;
FEnterNextCtrl := TRUE;
end;
procedure TMyEdit.KeyPress(var Key: Char);
var
ParentForm: TCustomForm;
begin
inherited;
if key = #13 then
begin
Key := #0;
//Get the parent-Form.
ParentForm := GetParentForm(self);
if FEnterNextCtrl = TRUE {//Jump to next Control or beep } then
parentform.Perform(WM_NextDLGCTL, 0, 0)
else
messageBeep(0);
end;
end;
2006. január 14., szombat
Designer instance for DataModules
Problem/Question/Abstract:
I can retreive instance of Designer for forms as their property "Designer", but how to retreive it for DataModule like objects?
Answer:
For receiving instance of Designer for forms you should just get form's property "Designer". But how to receive the Designer instance if you have only DataModule's instance?
It is very simply: the DataModule is an usual VCL component and its owner is hidden form, so for retreiving Designer instance of DataModule just retreive "Designer" property of DataModule's owner.
The function below demonstrates retreiving Designer instance for any form in your project by its name. This function is useful when Designer instance is necessary for your experts.
Add these definitions:
{$IFDEF VER120}
TFormDesigner = IFormDesigner;
{$ENDIF}
{$IFDEF VER125}
TFormDesigner = IFormDesigner;
{$ENDIF}
{$IFDEF VER130}
TFormDesigner = IFormDesigner;
{$ENDIF}
function GetDesigner(FToolServices: TIToolServices; FormName: string): TFormDesigner;
var
tmpC: TComponent;
begin
Result := nil;
with FToolServices.GetFormModuleInterface(FormName).GetFormInterface.GetFormComponent
do
begin
tmpC := GetComponentHandle;
if (tmpC is TCustomForm) then
begin
// We have the usual form
Result := TFormDesigner(TCustomForm(tmpC).Designer);
Exit;
end
else
// We have the DataModule or WebModule or something else
Result := TFormDesigner(TCustomForm(tmpc.Owner).Designer);
end;
end;
For receiving Designer without using ToolServices you can use following function:
finction GetDesigner(AComp: TComponent): TFormDesigner;
var
tmpC: TComponent;
begin
Result = nil;
if not Assigned(AComp) then
Exit;
if AComp is TCustomForm then
Result := TFormDesigner(TCustomForm(AComp).Designer)
else
begin
tmpC := AComp;
while true do
begin
if Assigned(tmpC.Owner) then
tmpC := tmpC.Owner
else
break;
if tmpC is TCustomForm then
begin
Result := TFormDesigner(TCustomForm(AComp).Designer);
break;
end;
end;
end;
end;
2006. január 13., péntek
Retrieve data from a URL
Problem/Question/Abstract:
How do I fetch text from a URL?
Answer:
This is a follow up to article "Checking if a URL is valid" which returns the data at the web page in a string. If it fails you should get a Status:xxx where xxx is the status or nothing at all if there isn't a web server at the url you try.
It uses the InternetReadfile function to read the data in 4kb chunks. The actual size of the buffer is irrelevant unless (as in this case) it is declared local to the function. This takes up stack space and a large buffer could potentially lead to a stack overflow if there were many functions nested in the call stack. Either move it somewhere non stack based or keep it small.
One thing to watch is the string conversion. If you are using buffer data, make sure you add a #0 on the end and use pchars to convert before ending up with strings.
uses wininet...
function FetchHTML(url: string): string;
var
databuffer: array[0..4095] of char;
ResStr: string;
hSession, hfile, hRequest: hInternet;
dwindex, dwcodelen, datalen, dwread, dwNumber: cardinal;
dwcode: array[1..20] of char;
res: pchar;
Str: pchar;
begin
ResStr := '';
if pos('http://', lowercase(url)) = 0 then
url := 'http://' + url;
hSession := InternetOpen('InetURL:/1.0',
INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
if assigned(hsession) then
begin
hfile := InternetOpenUrl(
hsession,
pchar(url),
nil,
0,
INTERNET_FLAG_RELOAD,
0);
dwIndex := 0;
dwCodeLen := 10;
HttpQueryInfo(hfile, HTTP_QUERY_STATUS_CODE,
@dwcode, dwcodeLen, dwIndex);
res := pchar(@dwcode);
dwNumber := sizeof(databuffer) - 1;
if (res = '200') or (res = '302') then
begin
while (InternetReadfile(hfile, @databuffer, dwNumber, DwRead)) do
begin
if dwRead = 0 then
break;
databuffer[dwread] := #0;
Str := pchar(@databuffer);
resStr := resStr + Str;
end;
end
else
ResStr := 'Status:' + res;
if assigned(hfile) then
InternetCloseHandle(hfile);
end;
InternetCloseHandle(hsession);
Result := resStr;
end;
This code was written with the help of 'Essential WinInet' by Aaron Skonnard and if you are interested in this subject I strongly suggest you buy the book. ISBN 0-201-37936-8. The code in the book is in C/C++ but isn't too difficult to convert to delphi.
2006. január 12., csütörtök
Delphi .NET: "Hello, world!"
Problem/Question/Abstract:
How to make a simple application using Delphi .NET compiler
Answer:
WinForms is a programming model used to create Windows Applications. .NET framework offers base classes for building Windows applications. Most of the functionality for these classes is in the System.Windows.Forms.
Lets look at the simplest traditional “hello world” example which will help us create our first Windows application in Delphi .NET
The form class is derived form the System.Windows.Forms.Form class. In the constructor of this class private procedure called InitializeComponents is called. If we want to add a new controls to the form we can do it in this procedure.
program newform;
uses
System.Drawing,
System.Collections,
System.ComponentModel,
System.Windows.Forms,
System.Data;
type
TMyform = class(Form)
private
components: IContainer;
procedure InitializeComponent;
public
constructor Create;
destructor Destroy; override;
end;
constructor TMyform.Create;
begin
inherited Create;
InitializeComponent;
end;
destructor TMyform.Destroy;
begin
inherited;
end;
procedure TMyform.InitializeComponent;
begin
Self.components := System.ComponentModel.Container.Create as IContainer;
Self.AutoScaleBaseSize := System.Drawing.Size.Create(5, 13);
Self.ClientSize := System.Drawing.Size.Create(160, 85);
Self.Name := 'Myform';
Self.Text := 'Hello World'; //change the form title
end;
var
MyForm: TMyForm;
begin
MyForm := TMyForm.Create;
Application.Run(MyForm);
end.
2006. január 11., szerda
Utility to Generate the Stored procedures and views of a SQL Database
Problem/Question/Abstract:
How can I create Stored Procedures and Views with out Knowing the Scripts ?
Answer:
For the persons who does not have the knowledge of Databases creating the stored procedures and views in the SQL Database was always a problem.
This utility will allow you to create the Stored procedures for Insert, Update and delete of a table and also will create the views. You have to just connect to the Database. All the Tables in the Database will be listed . Click on the table for which you need to create the stored procedures. The Script will be generated depending on the default templete. You can modify the templetes. Check or uncheck the fields you want to include in the Stored procedure. By default the need fields based upon the key fields will be included. Then just click, to create the stored procedures. For views you can include the fields in the views or cange the display names of the fields.
Copy the following codes to their respective files. Compile it and enjoy the ease of creating stored procedures.
GenerateSp.dpr file
program GenerateSp;
uses
Forms,
Main in 'Main.pas' {fmMain};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TfmMain, fmMain);
Application.Run;
end.
Main.dfm file
object fmMain: TfmMain
Left = 37
Top = 103
Width = 1225
Height = 759
ActiveControl = edtsrv
Caption = 'fmMain'
Color = clBtnFace
Constraints.MinHeight = 759
Constraints.MinWidth = 1225
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnClose = FormClose
OnCreate = FormCreate
OnDestroy = FormDestroy
OnShow = FormShow
PixelsPerInch = 120
TextHeight = 16
object Label1: TLabel
Left = 44
Top = 12
Width = 46
Height = 16
Caption = 'Server :'
end
object Label2: TLabel
Left = 24
Top = 38
Width = 66
Height = 16
Caption = 'Database :'
end
object Label3: TLabel
Left = 15
Top = 64
Width = 75
Height = 16
Caption = 'User Name :'
end
object Label4: TLabel
Left = 24
Top = 91
Width = 66
Height = 16
Caption = 'Password :'
end
object lblConn: TLabel
Left = 98
Top = 140
Width = 3
Height = 16
end
object Label5: TLabel
Left = 3
Top = 138
Width = 89
Height = 16
Caption = 'Table Names :'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = [fsUnderline]
ParentFont = False
end
object edtsrv: TEdit
Left = 96
Top = 8
Width = 137
Height = 24
TabOrder = 0
end
object edtdb: TEdit
Left = 96
Top = 34
Width = 137
Height = 24
TabOrder = 1
end
object edtUn: TEdit
Left = 96
Top = 60
Width = 137
Height = 24
TabOrder = 2
end
object edtPw: TEdit
Left = 96
Top = 87
Width = 137
Height = 24
PasswordChar = '@'
TabOrder = 3
end
object btnConnect: TButton
Left = 96
Top = 112
Width = 75
Height = 25
Caption = 'Connect'
TabOrder = 4
OnClick = btnConnectClick
end
object pcMain: TPageControl
Left = 240
Top = 0
Width = 977
Height = 726
ActivePage = tsFields
Align = alRight
TabIndex = 0
TabOrder = 5
object tsFields: TTabSheet
Caption = 'Select Fields'
object Bevel1: TBevel
Left = 0
Top = 221
Width = 976
Height = 9
Shape = bsTopLine
end
object Bevel3: TBevel
Left = -19
Top = 440
Width = 994
Height = 9
Shape = bsTopLine
end
object Bevel4: TBevel
Left = -11
Top = 656
Width = 992
Height = 9
Shape = bsTopLine
end
object Label6: TLabel
Left = 8
Top = 0
Width = 92
Height = 16
Caption = 'Fields To Insert'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = [fsUnderline]
ParentFont = False
end
object Label7: TLabel
Left = 3
Top = 226
Width = 129
Height = 16
Caption = 'Key Fields for Update'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = [fsUnderline]
ParentFont = False
end
object Label8: TLabel
Left = 3
Top = 444
Width = 134
Height = 16
Caption = 'Key Fields for Deletion'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = [fsUnderline]
ParentFont = False
end
object lblStatus: TLabel
Left = 280
Top = 664
Width = 3
Height = 16
Font.Charset = DEFAULT_CHARSET
Font.Color = clBlue
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object clbInsert: TCheckListBox
Left = 1
Top = 18
Width = 185
Height = 198
ItemHeight = 16
TabOrder = 0
end
object clbUpdate: TCheckListBox
Left = 1
Top = 244
Width = 185
Height = 193
ItemHeight = 16
TabOrder = 1
end
object clbDelete: TCheckListBox
Left = 1
Top = 461
Width = 185
Height = 193
ItemHeight = 16
TabOrder = 2
end
object btnOk: TBitBtn
Left = 809
Top = 664
Width = 75
Height = 25
Caption = 'Ok'
TabOrder = 3
OnClick = btnOkClick
end
object btnClose: TBitBtn
Left = 889
Top = 664
Width = 75
Height = 25
Caption = 'Close'
TabOrder = 4
OnClick = btnCloseClick
end
object memScrInsert: TMemo
Left = 194
Top = 18
Width = 769
Height = 201
ScrollBars = ssBoth
TabOrder = 5
end
object memscrUpdate: TMemo
Left = 194
Top = 244
Width = 769
Height = 193
ScrollBars = ssBoth
TabOrder = 6
end
object memScrDelete: TMemo
Left = 194
Top = 461
Width = 769
Height = 193
ScrollBars = ssBoth
TabOrder = 7
end
object chbInsert: TCheckBox
Left = 0
Top = 668
Width = 81
Height = 17
Caption = 'Sp Insert'
Checked = True
State = cbChecked
TabOrder = 8
end
object chbUpdate: TCheckBox
Left = 80
Top = 668
Width = 88
Height = 17
Caption = 'Sp UpDate'
Checked = True
State = cbChecked
TabOrder = 9
end
object chbDelete: TCheckBox
Left = 179
Top = 668
Width = 81
Height = 17
Caption = 'Sp Delete'
Checked = True
State = cbChecked
TabOrder = 10
end
end
object tsTemplate: TTabSheet
Caption = 'Templates'
ImageIndex = 1
object Bevel2: TBevel
Left = -6
Top = 218
Width = 984
Height = 9
Shape = bsTopLine
end
object Bevel5: TBevel
Left = -24
Top = 440
Width = 1002
Height = 9
Shape = bsTopLine
end
object Bevel6: TBevel
Left = -22
Top = 665
Width = 1000
Height = 9
Shape = bsTopLine
end
object Label9: TLabel
Left = 16
Top = -2
Width = 32
Height = 16
Caption = 'Insert'
end
object Label10: TLabel
Left = 16
Top = 221
Width = 45
Height = 16
Caption = 'Update'
end
object Label11: TLabel
Left = 16
Top = 444
Width = 43
Height = 16
Caption = 'Delete '
end
object btnok1: TBitBtn
Left = 809
Top = 669
Width = 75
Height = 25
Caption = 'Ok'
TabOrder = 0
OnClick = btnok1Click
end
object btnCancel: TBitBtn
Left = 889
Top = 669
Width = 75
Height = 25
Caption = 'Cancel'
TabOrder = 1
end
object memInsert: TMemo
Left = 16
Top = 13
Width = 946
Height = 201
ScrollBars = ssBoth
TabOrder = 2
end
object memUpdate: TMemo
Left = 16
Top = 237
Width = 946
Height = 201
ScrollBars = ssBoth
TabOrder = 3
end
object memDelete: TMemo
Left = 16
Top = 461
Width = 946
Height = 201
ScrollBars = ssBoth
TabOrder = 4
end
end
object tbPrefix: TTabSheet
Caption = 'Prefixes'
ImageIndex = 2
object Label12: TLabel
Left = 24
Top = 32
Width = 38
Height = 16
Caption = 'Insert :'
end
object Label13: TLabel
Left = 16
Top = 112
Width = 46
Height = 16
Caption = 'Delete :'
end
object Label14: TLabel
Left = 11
Top = 72
Width = 51
Height = 16
Caption = 'Update :'
end
object Label15: TLabel
Left = 27
Top = 148
Width = 35
Height = 16
Caption = 'View :'
end
object edtInsert: TEdit
Left = 66
Top = 28
Width = 121
Height = 24
TabOrder = 0
end
object edtUpdate: TEdit
Left = 66
Top = 68
Width = 121
Height = 24
TabOrder = 1
end
object edtDelete: TEdit
Left = 66
Top = 108
Width = 121
Height = 24
TabOrder = 2
end
object btnOk2: TBitBtn
Left = 67
Top = 183
Width = 75
Height = 23
Caption = 'Ok'
TabOrder = 3
OnClick = btnOk2Click
end
object edtView: TEdit
Left = 66
Top = 144
Width = 121
Height = 24
TabOrder = 4
end
end
object tbViews: TTabSheet
Caption = 'Views'
ImageIndex = 3
object Label16: TLabel
Left = 4
Top = 5
Width = 151
Height = 16
Caption = 'Fields To Include in View'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = [fsUnderline]
ParentFont = False
end
object Label17: TLabel
Left = 233
Top = 5
Width = 86
Height = 16
Caption = 'Display Name'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = [fsUnderline]
ParentFont = False
end
object lblStatusView: TLabel
Left = 604
Top = 340
Width = 36
Height = 16
Caption = 'wwww'
Font.Charset = DEFAULT_CHARSET
Font.Color = clBlue
Font.Height = -13
Font.Name = 'MS Sans Serif'
Font.Style = []
ParentFont = False
end
object sgView: TStringGrid
Left = 232
Top = 24
Width = 249
Height = 665
ColCount = 2
DefaultRowHeight = 19
FixedCols = 0
RowCount = 1
FixedRows = 0
Options = [goFixedVertLine, goFixedHorzLine, goVertLine, goHorzLine,
goRangeSelect, goEditing]
TabOrder = 0
OnSetEditText = sgViewSetEditText
ColWidths = (
243
64)
RowHeights = (
20)
end
object memView: TMemo
Left = 483
Top = 24
Width = 481
Height = 305
TabOrder = 1
end
object clbView: TCheckListBox
Left = 1
Top = 24
Width = 230
Height = 665
OnClickCheck = clbViewClickCheck
Columns = 1
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -17
Font.Name = 'MS Sans Serif'
Font.Style = []
ItemHeight = 20
ParentFont = False
TabOrder = 2
end
object btnView: TButton
Left = 488
Top = 336
Width = 97
Height = 25
Caption = 'Create View'
TabOrder = 3
OnClick = btnViewClick
end
end
end
object lbTables: TListBox
Left = 0
Top = 160
Width = 233
Height = 559
ItemHeight = 16
TabOrder = 6
OnMouseUp = lbTablesMouseUp
end
object adoConn: TADOConnection
ConnectionString =
'Provider=SQLOLEDB.1;Password=Robotech!;Persist Security Info=Tru' +
'e;User ID=sa;Initial Catalog=Dependency;Data Source=devrequest'
Provider = 'SQLOLEDB.1'
Left = 504
Top = 72
end
object adoQry: TADOQuery
Connection = adoConn
Parameters = <>
Left = 472
Top = 72
end
end
Main.pas file
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, DB, ADODB, Menus, Buttons, ExtCtrls, CheckLst,
ComCtrls, IniFiles, StrUtils, QDialogs, Grids;
type
TfmMain = class(TForm)
adoConn: TADOConnection;
adoQry: TADOQuery;
Label1: TLabel;
edtsrv: TEdit;
Label2: TLabel;
edtdb: TEdit;
Label3: TLabel;
Label4: TLabel;
edtUn: TEdit;
edtPw: TEdit;
btnConnect: TButton;
lblConn: TLabel;
Label5: TLabel;
pcMain: TPageControl;
tsFields: TTabSheet;
tsTemplate: TTabSheet;
clbInsert: TCheckListBox;
clbUpdate: TCheckListBox;
clbDelete: TCheckListBox;
Bevel1: TBevel;
Bevel3: TBevel;
Bevel4: TBevel;
btnOk: TBitBtn;
btnClose: TBitBtn;
Label6: TLabel;
Label7: TLabel;
Label8: TLabel;
lbTables: TListBox;
Bevel2: TBevel;
Bevel5: TBevel;
Bevel6: TBevel;
btnok1: TBitBtn;
btnCancel: TBitBtn;
memInsert: TMemo;
memUpdate: TMemo;
memDelete: TMemo;
Label9: TLabel;
Label10: TLabel;
Label11: TLabel;
memScrInsert: TMemo;
memscrUpdate: TMemo;
memScrDelete: TMemo;
tbPrefix: TTabSheet;
Label12: TLabel;
Label13: TLabel;
Label14: TLabel;
edtInsert: TEdit;
edtUpdate: TEdit;
edtDelete: TEdit;
btnOk2: TBitBtn;
lblStatus: TLabel;
chbInsert: TCheckBox;
chbUpdate: TCheckBox;
chbDelete: TCheckBox;
Label15: TLabel;
edtView: TEdit;
tbViews: TTabSheet;
sgView: TStringGrid;
memView: TMemo;
clbView: TCheckListBox;
Label16: TLabel;
Label17: TLabel;
btnView: TButton;
lblStatusView: TLabel;
procedure btnConnectClick(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure lbTablesMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure btnCloseClick(Sender: TObject);
procedure btnOkClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnOk2Click(Sender: TObject);
procedure btnok1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure clbViewClickCheck(Sender: TObject);
procedure sgViewSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
procedure btnViewClick(Sender: TObject);
private
{ Private declarations }
Fini: TIniFile;
FTblDisplayName, FSelectedTable: string;
procedure GetTables;
procedure GetColumns;
procedure ScriptInsert;
procedure ScriptUpdate;
procedure ScriptDelete;
procedure ScriptView;
procedure UpDateDatabase;
procedure GenScriptView;
public
{ Public declarations }
end;
const
LengthFields = '173,175,106,62,239,108,231,165,167';
var
fmMain: TfmMain;
implementation
{$R *.dfm}
procedure TfmMain.btnConnectClick(Sender: TObject);
var
S: string;
begin
S := 'Provider=SQLOLEDB.1;Password=' + edtPw.Text + ';User ID=' + edtUn.Text +
';Initial Catalog=' + edtdb.Text + ';Data Source=' + edtsrv.Text;
adoConn.Close;
adoConn.ConnectionString := S;
lblConn.Font.Color := clGreen;
try
adoConn.Open;
lblConn.Caption := 'Connection Succeded';
except
lblConn.Font.Color := clRed;
lblConn.Caption := 'Connection Failed';
end;
GetTables;
end;
procedure TfmMain.GetTables;
begin
adoQry.SQL.Clear;
adoQry.SQL.Text := 'Select name from sysobjects where xtype = ' +
#39 + 'U' + #39 + ' order by name ';
try
adoQry.Open;
lbTables.Clear;
while (not adoQry.Eof) do
begin
if (adoQry.fieldbyname('name').AsString <> 'dtproperties') then
begin
lbTables.Items.Add(adoQry.fieldbyname('name').AsString);
end;
adoQry.Next;
end;
adoQry.Close;
except
end;
end;
procedure TfmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
adoQry.Close;
adoConn.Close;
end;
procedure TfmMain.lbTablesMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
tp: TPoint;
begin
tp.X := X;
tp.Y := y;
FSelectedTable := lbTables.Items[lbTables.ItemAtPos(tp, true)];
FTblDisplayName := AnsiReplaceStr(FSelectedTable, 'tb_', '');
GetColumns;
ScriptInsert;
ScriptUpdate;
ScriptDelete;
ScriptView;
lblStatus.Caption := '';
lblStatusView.Caption := '';
end;
procedure TfmMain.btnCloseClick(Sender: TObject);
begin
Close;
end;
procedure TfmMain.GetColumns;
var
vIdCol: string;
procedure FillClb(var clb: TCheckListBox);
var
I: word;
begin
adoQry.First;
clb.Clear;
while (not adoQry.Eof) do
begin
clb.Items.Add(adoQry.fieldbyname('name').AsString);
if (clb.Name = 'clbInsert') then
begin
clb.Checked[clb.Items.Count - 1] := True;
end
else
begin
end;
adoQry.Next;
end;
if (clb.Name <> 'clbInsert') then
begin
for I := 0 to (clb.Items.Count - 1) do
begin
if (pos(clb.Items[I], vIdCol) > 0) then
begin
clb.Checked[I] := True;
end;
end;
end;
end;
begin
vIdCol := '';
adoQry.Close;
adoQry.SQL.Clear;
adoQry.SQL.Text := 'select A.NAME from SYSCOLUMNS A, sysINDEXKEYS B where A.id = ' +
'( select id from sysobjects where name = ' + #39 + FSelectedTable + #39 + ' )' +
' and (a.Id = b.Id ) and ( a.ColId = b.ColId ) order by a.colid';
try
adoQry.Open;
while (not adoQry.Eof) do
begin
vIdCol := vIdCol + adoQry.fieldbyname('name').AsString + '#';
adoQry.Next;
end;
except
end;
adoQry.Close;
adoQry.SQL.Clear;
adoQry.SQL.Text := 'select name from syscolumns where id = ' +
'( select id from sysobjects where name = ' +
#39 + FSelectedTable + #39 + ' ) order by colid';
try
adoQry.Open;
FillClb(clbInsert);
FillClb(clbUpdate);
FillClb(clbDelete);
adoQry.Close;
except
end;
end;
procedure TfmMain.ScriptInsert;
var
vFields: string;
vParamsType: string;
vParams: string;
vReplace: string;
I: Integer;
vSpName: string;
begin
adoQry.Close;
adoQry.SQL.Text := 'Select a.name, b.name dt, a.xtype, a.length FROM SYSCOLUMNS a,'
+
'systypes b where a.id = ( select id from sysobjects where name = ' +
#39 + FSelectedTable + #39 + ' ) and ( b.xtype = a.xtype )';
try
adoQry.Open;
except
end;
vFields := '';
vParams := '';
vParamsType := '';
for I := 0 to (clbInsert.Items.Count - 1) do
begin
if (clbInsert.Checked[I]) then
begin
if (vFields <> '') then
vFields := vFields + ', ';
vFields := vFields + clbInsert.Items[I];
if (vParamsType <> '') then
vParamsType := vParamsType + ', ';
vParamsType := vParamsType + '@' + clbInsert.Items[I] + ' ';
if (vParams <> '') then
vParams := vParams + ', ';
vParams := vParams + '@' + clbInsert.Items[I] + ' ';
if adoQry.Locate('name', clbInsert.Items[I], [locaseinsensitive]) then
begin
vParamsType := vParamsType + adoQry.fieldbyname('dt').AsString + ' ';
if (pos(adoQry.fieldbyname('xtype').AsString, LengthFields) > 0) then
begin
vParamsType := vParamsType + '( ' + adoQry.fieldbyname('length').AsString +
' )';
end
else
begin
end;
end;
end;
end;
vSpName := Fini.ReadString('Insert', 'Prefix', '');
vReplace := memInsert.Lines.Text;
vReplace := AnsiReplaceStr(vReplace, '', FSelectedTable);
vReplace := AnsiReplaceStr(vReplace, '', vSpName + FTblDisplayName);
vReplace := AnsiReplaceStr(vReplace, '', FTblDisplayName);
vReplace := AnsiReplaceStr(vReplace, '', vFields);
vReplace := AnsiReplaceStr(vReplace, '', vParamsType);
vReplace := AnsiReplaceStr(vReplace, '', vParams);
memScrInsert.Lines.Text := vReplace;
end;
procedure TfmMain.btnOkClick(Sender: TObject);
begin
UpDateDatabase;
end;
procedure TfmMain.FormCreate(Sender: TObject);
begin
Fini := TIniFile.Create(ExtractFileDir(Application.ExeName) + '\SpSettings.Ini');
if (not Fini.SectionExists('Insert')) then
begin
Fini.WriteString('Insert', 'Prefix', '');
end;
if (not Fini.SectionExists('Update')) then
begin
Fini.WriteString('Update', 'Prefix', '');
end;
if (not Fini.SectionExists('Delete')) then
begin
Fini.WriteString('Delete', 'Prefix', '');
end;
Fini.UpdateFile;
end;
procedure TfmMain.FormDestroy(Sender: TObject);
begin
Fini.Free;
Fini := nil;
end;
procedure TfmMain.btnOk2Click(Sender: TObject);
begin
Fini.WriteString('Insert', 'Prefix', edtInsert.Text);
Fini.WriteString('Update', 'Prefix', edtUpdate.Text);
Fini.WriteString('delete', 'Prefix', edtDelete.Text);
Fini.WriteString('View', 'Prefix', edtView.Text);
Fini.UpdateFile;
end;
procedure TfmMain.btnok1Click(Sender: TObject);
var
I: Integer;
begin
Fini.WriteInteger('Insert', 'Lines', memInsert.Lines.Count - 1);
for I := 0 to (memInsert.Lines.Count - 1) do
begin
Fini.WriteString('Insert', 'Script' + Inttostr(I), memInsert.Lines[I]);
end;
Fini.WriteInteger('Update', 'Lines', memUpdate.Lines.Count - 1);
for I := 0 to (memUpdate.Lines.Count - 1) do
begin
Fini.WriteString('Update', 'Script' + Inttostr(I), memUpdate.Lines[I]);
end;
Fini.WriteInteger('Delete', 'Lines', memDelete.Lines.Count - 1);
for I := 0 to (memUpdate.Lines.Count - 1) do
begin
Fini.WriteString('delete', 'Script' + Inttostr(I), memDelete.Lines[I]);
end;
Fini.UpdateFile;
end;
procedure TfmMain.FormShow(Sender: TObject);
var
I: Integer;
begin
edtInsert.Text := Fini.ReadString('Insert', 'Prefix', '');
edtUpdate.Text := Fini.ReadString('Update', 'Prefix', '');
edtDelete.Text := Fini.ReadString('delete', 'Prefix', '');
edtView.Text := Fini.ReadString('View', 'Prefix', '');
memInsert.Clear;
for I := 0 to (Fini.ReadInteger('Insert', 'Lines', 0)) do
begin
memInsert.Lines.Add(Fini.ReadString('Insert', 'Script' + intTostr(I), ''));
end;
memUpdate.Clear;
for I := 0 to (Fini.ReadInteger('Update', 'Lines', 0)) do
begin
memUpdate.Lines.Add(Fini.ReadString('Update', 'Script' + intTostr(I), ''));
end;
memDelete.Clear;
for I := 0 to (Fini.ReadInteger('delete', 'Lines', 0)) do
begin
memDelete.Lines.Add(Fini.ReadString('Delete', 'Script' + intTostr(I), ''));
end;
sgView.Cells[0, 0] := 'Table Fields';
sgView.Cells[1, 0] := 'Display Name';
end;
procedure TfmMain.ScriptDelete;
var
vDeleteKey: string;
vParamsType: string;
vReplace: string;
I: Integer;
vSpName: string;
begin
vDeleteKey := '';
for I := 0 to (clbDelete.Items.Count - 1) do
begin
if (clbDelete.Checked[I]) then
begin
if (vDeleteKey <> '') then
vDeleteKey := vDeleteKey + ' and ';
vDeleteKey := vDeleteKey + ' (' + clbDelete.Items[I] + ' = @' +
clbDelete.Items[I] + ') ';
if (vParamsType <> '') then
vParamsType := vParamsType + ', ';
vParamsType := vParamsType + '@' + clbUpdate.Items[I] + ' ';
if adoQry.Locate('name', clbDelete.Items[I], [locaseinsensitive]) then
begin
vParamsType := vParamsType + adoQry.fieldbyname('dt').AsString + ' ';
if (pos(adoQry.fieldbyname('xtype').AsString, LengthFields) > 0) then
begin
vParamsType := vParamsType + '( ' + adoQry.fieldbyname('length').AsString +
' )';
end
else
begin
end;
end;
end
else
begin
end;
end;
vSpName := Fini.ReadString('delete', 'Prefix', '');
vReplace := memDelete.Lines.Text;
vReplace := AnsiReplaceStr(vReplace, '', FSelectedTable);
vReplace := AnsiReplaceStr(vReplace, '', vSpName + FTblDisplayName);
vReplace := AnsiReplaceStr(vReplace, '', vDeleteKey);
vReplace := AnsiReplaceStr(vReplace, '', FTblDisplayName);
vReplace := AnsiReplaceStr(vReplace, '', vParamsType);
memScrDelete.Lines.Text := vReplace;
end;
procedure TfmMain.ScriptUpdate;
var
vUpdateFields: string;
vUpDateKey: string;
vFields: string;
vParamsType: string;
vParams: string;
vReplace: string;
I: Integer;
vSpName: string;
begin
vUpdateFields := '';
vUpDateKey := '';
vFields := '';
vParams := '';
vParamsType := '';
for I := 0 to (clbUpdate.Items.Count - 1) do
begin
if (clbUpdate.Checked[I]) then
begin
if (vUpDateKey <> '') then
vUpDateKey := vUpDateKey + ' and ';
vUpDateKey := vUpDateKey + ' (' + clbUpdate.Items[I] + ' = @' +
clbUpdate.Items[I] + ') ';
end
else
begin
if (vFields <> '') then
vFields := vFields + ', ';
vFields := vFields + ' ' + clbUpdate.Items[I] + ' = ' + '@' + clbUpdate.Items[I]
+ ' ';
end;
if (vParamsType <> '') then
vParamsType := vParamsType + ', ';
vParamsType := vParamsType + '@' + clbUpdate.Items[I] + ' ';
if (vParams <> '') then
vParams := vParams + ', ';
vParams := vParams + '@' + clbInsert.Items[I] + ' ';
if adoQry.Locate('name', clbInsert.Items[I], [locaseinsensitive]) then
begin
vParamsType := vParamsType + adoQry.fieldbyname('dt').AsString + ' ';
if (pos(adoQry.fieldbyname('xtype').AsString, LengthFields) > 0) then
begin
vParamsType := vParamsType + '( ' + adoQry.fieldbyname('length').AsString +
' )';
end
else
begin
end;
end;
end;
vSpName := Fini.ReadString('Update', 'Prefix', '');
vReplace := memUpdate.Lines.Text;
vReplace := AnsiReplaceStr(vReplace, '', FSelectedTable);
vReplace := AnsiReplaceStr(vReplace, '', vSpName + FTblDisplayName);
vReplace := AnsiReplaceStr(vReplace, '', vFields);
vReplace := AnsiReplaceStr(vReplace, '', vParamsType);
vReplace := AnsiReplaceStr(vReplace, '', FTblDisplayName);
vReplace := AnsiReplaceStr(vReplace, '', vUpDateKey);
memscrUpdate.Lines.Text := vReplace;
end;
procedure TfmMain.UpDateDatabase;
var
vSpName: string;
procedure Insert;
begin
try
adoQry.Close;
adoQry.SQL.Text := memScrInsert.Lines.Text;
adoQry.ExecSQL;
lblStatus.Caption := 'Insert Done';
except
lblStatus.Caption := 'Insert Failed';
end;
end;
procedure Update;
begin
try
adoQry.Close;
adoQry.SQL.Text := memscrUpdate.Lines.Text;
adoQry.ExecSQL;
lblStatus.Caption := lblStatus.Caption + 'Update - Done'
except
lblStatus.Caption := lblStatus.Caption + 'Update - Failed'
end;
end;
procedure Delete;
begin
try
adoQry.Close;
adoQry.SQL.Text := memScrDelete.Lines.Text;
adoQry.ExecSQL;
lblStatus.Caption := lblStatus.Caption + ', Delete - Done'
except
lblStatus.Caption := lblStatus.Caption + ', Delete - Failed'
end;
end;
begin
vSpName := Fini.ReadString('Insert', 'Prefix', '') + FTblDisplayName;
try
adoQry.Close;
adoQry.SQL.Text := 'Select count(1) obj from sysobjects where name = ' +
#39 + vSpName + #39;
adoQry.Open;
if (adoQry.FieldByName('obj').AsInteger > 0) then
begin
if (MessageDlg('Insert', 'Stored Procedure ' + vSpName +
' already Exists, Over Write it ?', mtconfirmation, [mbYes, mbNo], 0) = mrYes)
then
begin
adoQry.Close;
adoQry.SQL.Text := 'drop procedure ' + vSpName;
try
adoQry.ExecSQL;
Insert;
except
ShowMessage('Could not delete ' + vSpName);
end;
end;
end
else
Insert;
except
end;
if (lblStatus.Caption <> '') then
lblStatus.Caption := lblStatus.Caption + ', ';
vSpName := Fini.ReadString('Update', 'Prefix', '') + FTblDisplayName;
try
adoQry.Close;
adoQry.SQL.Text := 'Select count(1) obj from sysobjects where name = ' +
#39 + vSpName + #39;
adoQry.Open;
if (adoQry.FieldByName('obj').AsInteger > 0) then
begin
if (MessageDlg('Update', 'Stored Procedure ' + vSpName +
' already Exists, Over Write it ?', mtConfirmation, [mbYes, mbNo], 0) = mrYes)
then
begin
adoQry.Close;
adoQry.SQL.Text := 'drop procedure ' + vSpName;
try
adoQry.ExecSQL;
Update;
except
ShowMessage('Could not delete ' + vSpName);
end;
end;
end
else
Update;
except
end;
if (lblStatus.Caption <> '') then
lblStatus.Caption := lblStatus.Caption + ', ';
vSpName := Fini.ReadString('Delete', 'Prefix', '') + FTblDisplayName;
try
adoQry.Close;
adoQry.SQL.Text := 'Select count(1) obj from sysobjects where name = ' +
#39 + vSpName + #39;
adoQry.Open;
if (adoQry.FieldByName('obj').AsInteger > 0) then
begin
if (MessageDlg('Delete', 'Stored Procedure ' + vSpName +
' already Exists, Over Write it ?', mtConfirmation, [mbYes, mbNo], 0) = mrYes)
then
begin
adoQry.Close;
adoQry.SQL.Text := 'drop procedure ' + vSpName;
try
adoQry.ExecSQL;
Delete;
except
ShowMessage('Could not delete ' + vSpName);
end;
end;
end
else
Delete;
except
end;
end;
procedure TfmMain.ScriptView;
var
I: Integer;
vScr: string;
begin
vScr := '';
sgView.RowCount := 1;
sgView.Cells[0, 0] := '';
clbView.Items := clbInsert.Items;
// sgView.RowCount := ( clbInsert.Items.Count - 1 );
for I := 0 to (clbInsert.Items.Count - 1) do
begin
if (I > 0) then
sgView.RowCount := (I + 1);
sgView.Cells[0, I] := clbInsert.Items[I];
clbView.Checked[I] := true;
end;
GenScriptView;
end;
procedure TfmMain.GenScriptView;
var
I: Integer;
vScr: string;
begin
vScr := 'Create View ' + Fini.ReadString('View', 'Prefix', 'vw_') + FTblDisplayName +
' As ' + #13 +
' Select ';
for I := 0 to (clbView.Items.Count - 1) do
begin
if clbView.Checked[I] then
begin
if (I > 0) then
vScr := vScr + ', ' + #13;
if (I > 0) then
vScr := vScr + ' ';
vScr := vScr + clbView.Items[I];
if (sgView.Cells[0, I] <> clbView.Items[I]) then
begin
vScr := vScr + ' [' + sgView.Cells[0, I] + ']';
end
else
begin
end;
end;
end;
vScr := vScr + #13 + ' from ' + FSelectedTable;
memView.Lines.Text := vScr;
end;
procedure TfmMain.clbViewClickCheck(Sender: TObject);
begin
GenScriptView;
end;
procedure TfmMain.sgViewSetEditText(Sender: TObject; ACol, ARow: Integer;
const Value: string);
begin
GenScriptView;
end;
procedure TfmMain.btnViewClick(Sender: TObject);
var
vSpName: string;
procedure ViewScript;
begin
try
adoQry.Close;
adoQry.SQL.Text := memView.Text;
adoQry.ExecSQL;
lblStatusView.Caption := 'View Created.';
except
lblStatusView.Caption := 'View Creation Failed';
end;
end;
begin
vSpName := Fini.ReadString('View', 'Prefix', '') + FTblDisplayName;
try
adoQry.Close;
adoQry.SQL.Text := 'Select count(1) obj from sysobjects where name = ' +
#39 + vSpName + #39;
adoQry.Open;
if (adoQry.FieldByName('obj').AsInteger > 0) then
begin
if (Application.MessageBox(pchar('View ' + vSpName +
' already Exists, Over Write it ?'), pchar('View'), MB_YESNO) = 6) then
begin
// if ( MessageDlg( 'View', 'View ' + vSpName + ' already Exists, Over Write it ?', mtconfirmation, [mbYes, mbNo],0 ) = mrYes ) then begin
adoQry.Close;
adoQry.SQL.Text := 'drop view ' + vSpName;
try
adoQry.ExecSQL;
ViewScript;
except
ShowMessage('Could not delete ' + vSpName);
end;
end;
end
else
ViewScript;
except
end;
end;
end.
SpSettings.ini
[Insert]
Prefix=spIns_
Lines=16
Script0=CREATE PROCEDURE
Script1=AS
Script2=DECLARE @Err int, @RowC int
Script3=BEGIN TRAN
Script4=SET NOCOUNT ON
Script5=Insert into () values ( )
Script6=
Script7=Select @Err=@@Error,@RowC=@@RowCount
Script8=IF @Err <> 0
Script9=BEGIN
Script10=ROLLBACK TRAN
Script11=RAISERROR('Could not Add Information into ',16,-1)
Script12=RETURN
Script13=END
Script14=SET NOCOUNT OFF
Script15=COMMIT TRAN
Script16=GO
[Update]
Prefix=spUpd_
Lines=25
Script0=CREATE PROCEDURE
Script1=AS
Script2=DECLARE @Err int, @RowC int
Script3=BEGIN TRAN
Script4=SET NOCOUNT ON
Script5=Update set
Script6=where
Script7=
Script8=Select @Err=@@Error,@RowC=@@RowCount
Script9=
Script10=IF @RowC = 0
Script11=BEGIN
Script12=ROLLBACK TRAN
Script13=RAISERROR(' Information does not exist in ',16,-1)
Script14=RETURN
Script15=END
Script16=
Script17=IF @Err <> 0
Script18=BEGIN
Script19=ROLLBACK TRAN
Script20=RAISERROR('Could not Update Information in ',16,-1)
Script21=RETURN
Script22=END
Script23=SET NOCOUNT OFF
Script24=COMMIT TRAN
Script25=GO
Script26=GO
[Delete]
Prefix=spDel_
Lines=24
Script0=CREATE PROCEDURE
Script1=AS
Script2=DECLARE @Err int, @RowC int
Script3=BEGIN TRAN
Script4=SET NOCOUNT ON
Script5=Delete from where
Script6=
Script7=Select @Err=@@Error,@RowC=@@RowCount
Script8=
Script9=IF @RowC = 0
Script10=BEGIN
Script11=ROLLBACK TRAN
Script12=RAISERROR('Information does not exist in ',16,-1)
Script13=RETURN
Script14=END
Script15=
Script16=IF @Err <> 0
Script17=BEGIN
Script18=ROLLBACK TRAN
Script19=RAISERROR('Could not Delete Information from ',16,-1)
Script20=RETURN
Script21=END
Script22=SET NOCOUNT OFF
Script23=COMMIT TRAN
Script24=GO
Script25=
Script26=
[View]
Prefix=vw_
2006. január 10., kedd
Simple Implementation of LZW Compression/Decompression Algorithm
Problem/Question/Abstract:
How do I Compress and Decompress files using LZW Algorithm.
Answer:
Here is a simple implemntation of LZW compression/Decompression algorithm. It is not fast and compression ratio is very small. Here is the code.
unit RevLZW;
interface
uses
sysutils, classes, dialogs, windows;
const
tabsize: integer = 4095;
copybyte: integer = 0;
compbyte: integer = 1;
endlist: integer = -1;
nochar: integer = -2;
empty: integer = -3;
eofchar: integer = -4;
bufsize: integer = 32768;
maxstack: integer = 4096;
type
TStringObject = record
prevchar: integer;
nextchar: integer;
next: integer;
used: boolean;
nused: integer;
flocked: boolean;
end;
procedure Initialize;
procedure Terminate;
function OpenInputFile(fname: string): boolean;
function OpenOutputFile(fname: string): boolean;
function getbyte: integer;
procedure putbyte(c: integer);
procedure compress;
procedure decompress;
procedure putcode(code: integer; lbyte: boolean = false);
function getcode: integer;
function GetHashCode(prevc, nextc: integer): integer;
function findstring(prevc, nextc: integer): integer;
function MakeTableEntry(prevc: integer; nextc: integer): boolean;
procedure push(c: integer);
procedure pop(var c: integer);
procedure InitializeStringTable;
var
fsize: integer;
fread, fwrote: integer;
ihandle, ohandle: integer;
inbufpos, outbufpos: integer;
objectid: integer;
stringtable: array[0..4095] of TstringObject;
inblock: array[0..65535 {32767}] of char;
outblock: array[0..65535 {32767}] of char;
stack: array[0..4095] of char;
stackpointer: integer;
rembits: integer;
lastbyte: boolean;
rembitcount: integer;
lzwerr: boolean;
imap, omap: integer;
implementation
function OpenInputFile(fname: string): boolean;
begin
result := true;
ihandle := fileopen(fname, fmShareExclusive or fmOpenRead);
fsize := getfilesize(ihandle, nil);
if fsize < 32768 then
fileread(ihandle, inblock, fsize)
else
fileread(ihandle, inblock, 32768);
if ihandle = -1 then
result := false;
end;
function OpenOutputFile(fname: string): boolean;
begin
result := true;
ohandle := filecreate(fname);
if ohandle = -1 then
result := false;
end;
function getbyte: integer;
begin
if inbufpos = 32768 then
begin
inbufpos := 0;
fileread(ihandle, inblock, 32768);
end;
if fread = fsize then
result := eofchar
else
result := integer(inblock[inbufpos]);
inc(inbufpos);
inc(fread);
end;
procedure putbyte(c: integer);
begin
if outbufpos = 32768 then
begin
outbufpos := 0;
filewrite(ohandle, outblock, 32768);
end;
outblock[outbufpos] := char(c);
inc(outbufpos);
inc(fwrote);
end;
procedure Initialize;
begin
inbufpos := 0;
outbufpos := 0;
fread := 0;
fwrote := 0;
objectid := 0;
stackpointer := 0;
lastbyte := false;
rembits := empty;
rembitcount := 0;
lzwerr := false;
InitializeStringtable;
end;
procedure InitializeStringTable;
var
i: integer;
begin
objectid := 0;
for i := 0 to 4095 do
begin
with stringtable[i] do
begin
if not flocked then
begin
prevchar := nochar;
nextchar := nochar;
next := endlist;
used := false;
nused := 0;
flocked := false;
end;
end;
if i <= 255 then
begin
stringtable[i].nextchar := i;
stringtable[i].used := true;
inc(objectid);
end;
end;
end;
procedure Terminate;
begin
if outbufpos > 0 then
filewrite(ohandle, outblock, outbufpos);
setendoffile(ohandle);
fileclose(ihandle);
fileclose(ohandle);
end;
function GetHashCode(prevc, nextc: integer): integer;
var
index, newindex: integer;
begin
index := ((prevc shl 5) xor nextc) and tabsize;
if not stringtable[index].used then
result := index
else
begin
while stringtable[index].next <> endlist do
index := stringtable[index].next;
newindex := index and tabsize;
while stringtable[newindex].used do
newindex := succ(newindex) and tabsize;
stringtable[index].next := newindex;
result := newindex;
end;
end;
function findstring(prevc, nextc: integer): integer;
var
index: integer;
found: boolean;
begin
result := endlist;
if (prevc = nochar) and (nextc <= 255) then
result := nextc
else
begin
index := ((prevc shl 5) xor nextc) and tabsize;
repeat
found := (stringtable[index].prevchar = prevc) and (stringtable[index].nextchar
= nextc);
if not found then
index := stringtable[index].next;
until found or (index = endlist);
if found then
begin
result := index;
inc(stringtable[index].nused);
end;
end;
end;
function MakeTableEntry(prevc: integer; nextc: integer): boolean;
var
index: integer;
begin
result := true;
if objectid <= tabsize then
begin
index := gethashcode(prevc, nextc);
with stringtable[index] do
begin
prevchar := prevc;
nextchar := nextc;
used := true;
end;
inc(objectid);
if objectid = tabsize + 1 then
result := false;
end;
end;
procedure putcode(code: integer; lbyte: boolean);
var
tmpcode: integer;
begin
if stringtable[code].prevchar = nochar then
begin
if rembitcount < 7 then
begin
tmpcode := (rembits shl (8 - rembitcount)) or (copybyte shl (7 - rembitcount))
or ((code shr (rembitcount + 1)) and ($7F shr rembitcount));
putbyte(tmpcode);
inc(fwrote);
rembits := code and ($FF shr (7 - rembitcount));
inc(rembitcount);
end
else if rembitcount = 7 then
begin
tmpcode := (rembits shl 1) or copybyte;
putbyte(tmpcode);
inc(fwrote, 2);
putbyte(code);
rembits := empty;
rembitcount := 0;
end;
end
else
begin
tmpcode := (rembits shl (8 - rembitcount)) or (compbyte shl (7 - rembitcount)) or
(code shr (5 + rembitcount) and ($7F shr rembitcount));
putbyte(tmpcode);
inc(fwrote);
rembitcount := rembitcount + 5;
if rembitcount < 8 then
rembits := code and ($FF shr (8 - rembitcount));
if rembitcount >= 8 then
begin
rembits := (code shr (rembitcount - 8)) and $FF;
inc(fwrote);
putbyte(rembits);
rembitcount := rembitcount - 8;
rembits := code and ($FF shr (8 - rembitcount));
end;
end;
if lbyte and (rembitcount > 0) then
begin
tmpcode := ((rembits and ($FF shr (8 - rembitcount))) shl (8 - rembitcount));
putbyte(tmpcode);
inc(fwrote);
end;
end;
function getcode: integer;
var
part1, part2: integer;
iscomp: integer;
c1, c2: integer;
begin
result := eofchar;
if (fread = fsize) and (rembitcount = 0) then
begin
result := eofchar;
exit;
end;
if rembitcount = 0 then
begin
part1 := getbyte;
part2 := getbyte;
iscomp := (part1 shr 7) and 1;
if iscomp = 1 then
begin
c1 := part1 and $7F;
c2 := (part2 shr 3) and $1F;
rembits := part2 and $7;
rembitcount := 3;
result := (c1 shl 5) or c2;
end
else if iscomp = 0 then
begin
c1 := part1 and $7F;
c2 := (part2 shr 7) and $1;
result := (c1 shl 1) or c2;
rembits := part2 and $7F;
rembitcount := 7;
end;
end
else if rembitcount = 1 then
begin
part1 := getbyte;
iscomp := rembits;
if iscomp = 1 then
begin
part2 := getbyte;
c1 := part1 and $FF;
c2 := (part2 shr 4) and $F;
rembits := part2 and $F;
rembitcount := 4;
result := (c1 shl 4) or c2;
end
else if iscomp = 0 then
begin
c1 := part1 and $FF;
result := c1;
rembits := empty;
rembitcount := 0;
end;
end
else if rembitcount = 2 then
begin
part1 := getbyte;
iscomp := (rembits shr 1) and 1;
if iscomp = 1 then
begin
part2 := getbyte;
c1 := ((rembits and 1) shl 7) or ((part1 shr 1) and $7F);
c2 := ((part1 and 1) shl 3) or ((part2 shr 5) and $7);
rembits := part2 and $1F;
rembitcount := 5;
result := (c1 shl 4) or (c2 and $F);
end
else if iscomp = 0 then
begin
c1 := ((rembits and 1) shl 7) or ((part1 shr 1) and $7F);
result := c1;
rembits := part1 and 1;
rembitcount := 1;
end;
end
else if rembitcount = 3 then
begin
part1 := getbyte;
iscomp := (rembits shr 2) and 1;
if iscomp = 1 then
begin
part2 := getbyte;
c1 := ((rembits and $3) shl 6) or ((part1 shr 2) and $3F);
c2 := ((part1 and $3) shl 2) or ((part2 shr 6) and $3);
rembits := part2 and $3F;
rembitcount := 6;
result := (c1 shl 4) or (c2 and $F);
end
else if iscomp = 0 then
begin
c1 := ((rembits and $3) shl 6) or ((part1 shr 2) and $3F);
result := c1;
rembits := part1 and $3;
rembitcount := 2;
end;
end
else if rembitcount = 4 then
begin
part1 := getbyte;
iscomp := (rembits shr 3) and 1;
if iscomp = 1 then
begin
part2 := getbyte;
c1 := ((rembits and $7) shl 5) or ((part1 shr 3) and $1F);
c2 := ((part1 and $7) shl 1) or ((part2 shr 7) and $1);
rembits := part2 and $7F;
rembitcount := 7;
result := (c1 shl 4) or (c2 and $F);
end
else if iscomp = 0 then
begin
c1 := ((rembits and $7) shl 5) or ((part1 shr 3) and $1F);
result := c1;
rembits := part1 and $7;
rembitcount := 3;
end;
end
else if rembitcount = 5 then
begin
part1 := getbyte;
iscomp := (rembits shr 4) and 1;
if iscomp = 1 then
begin
c1 := ((rembits and $F) shl 4) or ((part1 shr 4) and $F);
c2 := part1 and $F;
rembits := empty;
rembitcount := 0;
result := (c1 shl 4) or (c2 and $F);
end
else if iscomp = 0 then
begin
c1 := ((rembits and $F) shl 4) or ((part1 shr 4) and $F);
result := c1;
rembits := part1 and $F;
rembitcount := 4;
end;
end
else if rembitcount = 6 then
begin
part1 := getbyte;
iscomp := (rembits shr 5) and 1;
if iscomp = 1 then
begin
c1 := ((rembits and $1F) shl 3) or ((part1 shr 5) and $7);
c2 := (part1 shr 1) and $F;
rembits := part1 and 1;
rembitcount := 1;
result := (c1 shl 4) or (c2 and $F);
end
else if iscomp = 0 then
begin
c1 := ((rembits and $1F) shl 3) or ((part1 shr 5) and $7);
result := c1;
rembits := part1 and $1F;
rembitcount := 5;
end;
end
else if rembitcount = 7 then
begin
part1 := getbyte;
iscomp := (rembits shr 6) and 1;
if iscomp = 1 then
begin
c1 := ((rembits and $3F) shl 2) or ((part1 shr 6) and $3);
c2 := (part1 shr 2) and $F;
rembits := part1 and $3;
rembitcount := 2;
result := (c1 shl 4) or (c2 and $F);
end
else if iscomp = 0 then
begin
c1 := ((rembits and $3F) shl 2) or ((part1 shr 6) and $3);
result := c1;
rembits := part1 and $3F;
rembitcount := 6;
end;
end;
end;
procedure compress;
var
c, wc, w: integer;
begin
initialize;
c := getbyte;
w := findstring(nochar, c);
c := getbyte;
while fread <= fsize - 1 do
begin
if lastbyte then
begin
putcode(w);
lastbyte := false;
InitializeStringtable;
c := getbyte;
w := findstring(nochar, c);
c := getbyte;
end;
wc := findstring(w, c);
if wc = endlist then
begin
lastbyte := not (MakeTableEntry(w, c));
putcode(w);
w := findstring(nochar, c);
end
else
w := wc;
if not lastbyte then
c := getbyte;
end;
putcode(w, true);
end;
procedure decompress;
var
unknown: boolean;
finchar, lastchar: integer;
code, oldcode, incode: integer;
c, tempc: integer;
begin
initialize;
unknown := false;
lastchar := empty;
oldcode := getcode;
code := oldcode;
c := stringtable[code].nextchar;
putbyte(c);
finchar := c;
incode := getcode;
while incode <> eofchar do
begin
if lastbyte then
begin
lastbyte := false;
InitializeStringTable;
stackpointer := 0;
unknown := false;
lastchar := empty;
oldcode := getcode;
code := oldcode;
c := stringtable[code].nextchar;
putbyte(c);
finchar := c;
incode := getcode;
end;
code := incode;
if not stringtable[code].used then
begin
lastchar := finchar;
code := oldcode;
unknown := true;
end;
while (stringtable[code].prevchar <> nochar) do
begin
push(stringtable[code].nextchar);
if lzwerr = true then
break;
code := stringtable[code].prevchar;
end;
if lzwerr = true then
break;
finchar := stringtable[code].nextchar;
putbyte(finchar);
pop(tempc);
while (tempc <> empty) do
begin
putbyte(tempc);
pop(tempc);
end;
if unknown then
begin
finchar := lastchar;
putbyte(finchar);
unknown := false;
end;
lastbyte := not (maketableentry(oldcode, finchar));
if not lastbyte then
begin
oldcode := incode;
incode := getcode;
end
end;
end;
procedure push(c: integer);
var
s: string;
begin
if stackpointer < 4096 then
begin
inc(stackpointer);
stack[stackpointer] := char(c);
end;
if stackpointer >= 4096 then
begin
s := 'Stack full at ' + inttostr(inbufpos);
lzwerr := true;
showmessage(s);
end;
end;
procedure pop(var c: integer);
begin
if stackpointer > 0 then
begin
c := integer(stack[stackpointer]);
dec(stackpointer);
end
else
c := empty;
end;
end.
To compress the file add the following code to a button
openinputfile('C:\cdidxtmp\myfile.exe');
openoutputfile('C:\cdidxtmp\myfile.bak');
initialize;
compress;
To Decompress
openinputfile('C:\cdidxtmp\myfile.bak');
openoutputfile('C:\cdidxtmp\myfile.exe');
initialize;
decompress;
Feliratkozás:
Bejegyzések (Atom)