Компонент для последовательного устройства (TRS232)
Компонент для последовательного устройства (TRS232)
Компонент, который представлен здесь, выполняет функции синхронного чтения и записи в последовательный интерфейс RS232.
В цикле выполняется Application.ProcessMessages, чтобы все сообщения от основной программы обрабатывались.
// ----------------------------------------------------------------------
// | RS232 - Basic Driver for the RS232 port 1.0 |
// ----------------------------------------------------------------------
// | © 1997 by Marco Cocco |
// | © 1998 by enhanced by Angerer Bernhard |
// ----------------------------------------------------------------------
unit uRS232;
interface
uses
Windows, Messages, SysUtils, Classes, Forms,
ExtCtrls; // TTimer
////////////////////////////////////////////////////////////////////////////////
type
TReceiveDataEvent = procedure(Sender: TObject; Msg, lParam, wParam:longint) of object;
// COM Port Baud Rates
TComPortBaudRate = ( br110, br300, br600, br1200, br2400, br4800,
br9600, br14400, br19200, br38400, br56000,
br57600, br115200 );
// COM Port Numbers
TComPortNumber = ( pnCOM1, pnCOM2, pnCOM3, pnCOM4 );
// COM Port Data bits
TComPortDataBits = ( db5BITS, db6BITS, db7BITS, db8BITS );
// COM Port Stop bits
TComPortStopBits = ( sb1BITS, sb1HALFBITS, sb2BITS );
// COM Port Parity
TComPortParity = ( ptNONE, ptODD, ptEVEN, ptMARK, ptSPACE );
// COM Port Hardware Handshaking
TComPortHwHandshaking = ( hhNONE, hhRTSCTS );
// COM Port Software Handshaing
TComPortSwHandshaking = ( shNONE, shXONXOFF );
TCommPortDriver = class(TComponent)
private
hTimer: TTimer;
FActive: boolean;
procedure SetActive(const Value: boolean);
protected
FComPortHandle : THANDLE; // COM Port Device Handle
FComPort : TComPortNumber; // COM Port to use (1..4)
FComPortBaudRate : TComPortBaudRate; // COM Port speed (brXXXX)
FComPortDataBits : TComPortDataBits; // Data bits size (5..8)
FComPortStopBits : TComPortStopBits; // How many stop bits to use
// (1,1.5,2)
FComPortParity : TComPortParity; // Type of parity to use
// (none,odd,even,mark,space)
FComPortHwHandshaking : TComPortHwHandshaking; // Type of hw
// handshaking to use
FComPortSwHandshaking : TComPortSwHandshaking; // Type of sw
// handshaking to use
FComPortInBufSize : word; // Size of the input buffer
FComPortOutBufSize : word; // Size of the output buffer
FComPortReceiveData : TReceiveDataEvent;
FComPortPollingDelay : word; // ms of delay between COM port pollings
FTimeOut : integer; // sec until timeout
FTempInBuffer : pointer;
procedure SetComPort( Value: TComPortNumber );
procedure SetComPortBaudRate( Value: TComPortBaudRate );
procedure SetComPortDataBits( Value: TComPortDataBits );
procedure SetComPortStopBits( Value: TComPortStopBits );
procedure SetComPortParity( Value: TComPortParity );
procedure SetComPortHwHandshaking( Value: TComPortHwHandshaking );
procedure SetComPortSwHandshaking( Value: TComPortSwHandshaking );
procedure SetComPortInBufSize( Value: word );
procedure SetComPortOutBufSize( Value: word );
procedure SetComPortPollingDelay( Value: word );
procedure ApplyCOMSettings;
procedure TimerEvent(Sender: TObject); virtual;
public
constructor Create( AOwner: TComponent ); override;
destructor Destroy; override;
function Connect: boolean; //override;
function Disconnect: boolean; //override;
function Connected: boolean;
function SendData( DataPtr: pointer; DataSize: integer ): boolean;
function SendString( aStr: string ): boolean;
// Event to raise when there is data available (input buffer has data)
property OnReceiveData: TReceiveDataEvent read FComPortReceiveData
write FComPortReceiveData;
published
// Which COM Port to use
property ComPort: TComPortNumber read FComPort write SetComPort
default pnCOM2;
// COM Port speed (bauds)
property ComPortSpeed: TComPortBaudRate read FComPortBaudRate
write SetComPortBaudRate default br9600;
// Data bits to used (5..8, for the 8250 the use of 5 data bits with 2 stop
// bits is an invalid combination, as is 6, 7, or 8 data bits with 1.5
// stop bits)
property ComPortDataBits: TComPortDataBits read FComPortDataBits
write SetComPortDataBits default db8BITS;
// Stop bits to use (1, 1.5, 2)
property ComPortStopBits: TComPortStopBits read FComPortStopBits
write SetComPortStopBits default sb1BITS;
// Parity Type to use (none,odd,even,mark,space)
property ComPortParity: TComPortParity read FComPortParity
write SetComPortParity default ptNONE;
// Hardware Handshaking Type to use:
// cdNONE no handshaking
// cdCTSRTS both cdCTS and cdRTS apply (This is the more common method)
property ComPortHwHandshaking: TComPortHwHandshaking
read FComPortHwHandshaking write SetComPortHwHandshaking default hhNONE;
// Software Handshaking Type to use:
// cdNONE no handshaking
// cdXONXOFF XON/XOFF handshaking
property ComPortSwHandshaking: TComPortSwHandshaking
read FComPortSwHandshaking write SetComPortSwHandshaking default shNONE;
// Input Buffer size
property ComPortInBufSize: word read FComPortInBufSize
write SetComPortInBufSize default 2048;
// Output Buffer size
property ComPortOutBufSize: word read FComPortOutBufSize
write SetComPortOutBufSize default 2048;
// ms of delay between COM port pollings
property ComPortPollingDelay: word read FComPortPollingDelay
write SetComPortPollingDelay default 100;
property TimeOut: integer read FTimeOut write FTimeOut default 30;
property Active: boolean read FActive write SetActive default false;
end;
TRS232 = class(TCommPortDriver)
protected
public
// new comm parameters are set
constructor Create( AOwner: TComponent ); override;
// ReadStrings reads direct from the comm-buffer and waits for
// more characters and handles the timeout
function ReadString(var aResStr: string; aCount: word ): boolean;
published
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Additional', [TRS232]);
end;
constructor TCommPortDriver.Create( AOwner: TComponent );
begin
inherited Create( AOwner );
// Initialize to default values
FComPortHandle := 0; // Not connected
FComPort := pnCOM2; // COM 2
FComPortBaudRate := br9600; // 9600 bauds
FComPortDataBits := db8BITS; // 8 data bits
FComPortStopBits := sb1BITS; // 1 stop bit
FComPortParity := ptNONE; // no parity
FComPortHwHandshaking := hhNONE; // no hardware handshaking
FComPortSwHandshaking := shNONE; // no software handshaking
FComPortInBufSize := 2048; // input buffer of 512 bytes
FComPortOutBufSize := 2048; // output buffer of 512 bytes
FComPortReceiveData := nil; // no data handler
FTimeOut := 30; // sec until timeout
FComPortPollingDelay := 500;
GetMem( FTempInBuffer, FComPortInBufSize ); // Temporary buffer
// for received data
// Timer for teaching and messages
hTimer := TTimer.Create(Self);
hTimer.Enabled := false;
hTimer.Interval := 500;
hTimer.OnTimer := TimerEvent;
if ComponentState = [csDesigning] then
EXIT;
if FActive then
hTimer.Enabled := true; // start the timer only at application start
end;
destructor TCommPortDriver.Destroy;
begin
// Be sure to release the COM device
Disconnect;
// Free the temporary buffer
FreeMem( FTempInBuffer, FComPortInBufSize );
// Destroy the timer's window
inherited Destroy;
end;
procedure TCommPortDriver.SetComPort( Value: TComPortNumber );
begin
// Be sure we are not using any COM port
if Connected then
exit;
// Change COM port
FComPort := Value;
end;
procedure TCommPortDriver.SetComPortBaudRate( Value: TComPortBaudRate );
begin
// Set new COM speed
FComPortBaudRate := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortDataBits( Value: TComPortDataBits );
begin
// Set new data bits
FComPortDataBits := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortStopBits( Value: TComPortStopBits );
begin
// Set new stop bits
FComPortStopBits := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortParity( Value: TComPortParity );
begin
// Set new parity
FComPortParity := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortHwHandshaking(Value: TComPortHwHandshaking);
begin
// Set new hardware handshaking
FComPortHwHandshaking := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortSwHandshaking(Value: TComPortSwHandshaking);
begin
// Set new software handshaking
FComPortSwHandshaking := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortInBufSize( Value: word );
begin
// Free the temporary input buffer
FreeMem( FTempInBuffer, FComPortInBufSize );
// Set new input buffer size
FComPortInBufSize := Value;
// Allocate the temporary input buffer
GetMem( FTempInBuffer, FComPortInBufSize );
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortOutBufSize( Value: word );
begin
// Set new output buffer size
FComPortOutBufSize := Value;
// Apply changes
if Connected then
ApplyCOMSettings;
end;
procedure TCommPortDriver.SetComPortPollingDelay( Value: word );
begin
FComPortPollingDelay := Value;
hTimer.Interval := Value;
end;
const
Win32BaudRates: array[br110..br115200] of DWORD =
( CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400, CBR_4800, CBR_9600,
CBR_14400, CBR_19200, CBR_38400, CBR_56000, CBR_57600, CBR_115200 );
const
dcb_Binary = $00000001;
dcb_ParityCheck = $00000002;
dcb_OutxCtsFlow = $00000004;
dcb_OutxDsrFlow = $00000008;
dcb_DtrControlMask = $00000030;
dcb_DtrControlDisable = $00000000;
dcb_DtrControlEnable = $00000010;
dcb_DtrControlHandshake = $00000020;
dcb_DsrSensivity = $00000040;
dcb_TXContinueOnXoff = $00000080;
dcb_OutX = $00000100;
dcb_InX = $00000200;
dcb_ErrorChar = $00000400;
dcb_NullStrip = $00000800;
dcb_RtsControlMask = $00003000;
dcb_RtsControlDisable = $00000000;
dcb_RtsControlEnable = $00001000;
dcb_RtsControlHandshake = $00002000;
dcb_RtsControlToggle = $00003000;
dcb_AbortOnError = $00004000;
dcb_Reserveds = $FFFF8000;
// Apply COM settings.
procedure TCommPortDriver.ApplyCOMSettings;
var dcb: TDCB;
begin
// Do nothing if not connected
if not Connected then
exit;
// Clear all
fillchar( dcb, sizeof(dcb), 0 );
// Setup dcb (Device Control Block) fields
dcb.DCBLength := sizeof(dcb); // dcb structure size
dcb.BaudRate := Win32BaudRates[ FComPortBaudRate ]; // baud rate to use
dcb.Flags := dcb_Binary or // Set fBinary: Win32 does not support non
// binary mode transfers
// (also disable EOF check)
dcb_RtsControlEnable; // Enables the RTS line when the device
// is opened and leaves it on
// dcb_DtrControlEnable; // Enables the DTR line when the device
// is opened and leaves it on
case FComPortHwHandshaking of // Type of hw handshaking to use
hhNONE:; // No hardware handshaking
hhRTSCTS: // RTS/CTS (request-to-send/clear-to-send) hardware handshaking
dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake;
end;
case FComPortSwHandshaking of // Type of sw handshaking to use
shNONE:; // No software handshaking
shXONXOFF: // XON/XOFF handshaking
dcb.Flags := dcb.Flags or dcb_OutX or dcb_InX;
end;
dcb.XONLim := FComPortInBufSize div 4; // Specifies the minimum number
// of bytes allowed
// in the input buffer before the
// XON character is sent
dcb.XOFFLim := 1; // Specifies the maximum number of bytes allowed in the
// input buffer before the XOFF character is sent.
// The maximum number of bytes allowed is calculated by
// subtracting this value from the size, in bytes,
// of the input buffer
dcb.ByteSize := 5 + ord(FComPortDataBits); // how many data bits to use
dcb.Parity := ord(FComPortParity); // type of parity to use
dcb.StopBits := ord(FComPortStopbits); // how many stop bits to use
dcb.XONChar := #17; // XON ASCII char
dcb.XOFFChar := #19; // XOFF ASCII char
SetCommState( FComPortHandle, dcb );
// Setup buffers size
SetupComm( FComPortHandle, FComPortInBufSize, FComPortOutBufSize );
end;
function TCommPortDriver.Connect: boolean;
var comName: array[0..4] of char;
tms: TCOMMTIMEOUTS;
begin
// Do nothing if already connected
Result := Connected;
if Result then exit;
// Open the COM port
StrPCopy( comName, 'COM' );
comName[3] := chr( ord('1') + ord(FComPort) );
comName[4] := #0;
FComPortHandle := CreateFile(
comName,
GENERIC_READ or GENERIC_WRITE,
0, // Not shared
nil, // No security attributes
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0 // No template
) ;
Result := Connected;
if not Result then exit;
// Apply settings
ApplyCOMSettings;
// Setup timeouts: we disable timeouts because we are polling the com port!
tms.ReadIntervalTimeout := 1; // Specifies the maximum time, in milliseconds,
// allowed to elapse between the arrival of two
// characters on the communications line
tms.ReadTotalTimeoutMultiplier := 0; // Specifies the multiplier, in
// milliseconds, used to calculate
// the total time-out period
// for read operations.
tms.ReadTotalTimeoutConstant := 1; // Specifies the constant, in milliseconds,
// used to calculate the total time-out
// period for read operations.
tms.WriteTotalTimeoutMultiplier := 0; // Specifies the multiplier, in
// milliseconds, used to calculate
// the total time-out period
// for write operations.
tms.WriteTotalTimeoutConstant := 0; // Specifies the constant, in
// milliseconds, used to calculate
// the total time-out period
// for write operations.
SetCommTimeOuts( FComPortHandle, tms );
Sleep(1000); // to avoid timing problems, wait until the Comm-Port is opened
end;
function TCommPortDriver.Disconnect: boolean;
begin
Result:=false;
if Connected then
begin
CloseHandle( FComPortHandle );
FComPortHandle := 0;
end;
Result := true;
end;
function TCommPortDriver.Connected: boolean;
begin
Result := FComPortHandle > 0;
end;
function TCommPortDriver.SendData(DataPtr: pointer; DataSize: integer): boolean;
var nsent: DWORD;
begin
Result := WriteFile( FComPortHandle, DataPtr^, DataSize, nsent, nil );
Result := Result and (nsent=DataSize);
end;
function TCommPortDriver.SendString( aStr: string ): boolean;
begin
if not Connected then
if not Connect then raise Exception.CreateHelp('RS232.SendString:'+
' Connect not possible !', 101);
Result:=SendData( pchar(aStr), length(aStr) );
if not Result then raise
Exception.CreateHelp('RS232.SendString: Send not possible !', 102);
end;
// Event for teaching and messages
procedure TCommPortDriver.TimerEvent(Sender: TObject);
var InQueue, OutQueue: integer;
// Test if data in inQueue(outQueue)
procedure DataInBuffer(Handle: THandle; var aInQueue, aOutQueue: integer);
var ComStat: TComStat;
e: cardinal;
begin
aInQueue := 0;
aOutQueue := 0;
if ClearCommError(Handle, e, @ComStat) then
begin
aInQueue := ComStat.cbInQue;
aOutQueue := ComStat.cbOutQue;
end;
end;
begin
if not Connected then
if not Connect then raise Exception.CreateHelp('RS232.TimerEvent:'+
' Connect not possible !', 101);
if Connected then
begin
DataInBuffer(FComPortHandle, InQueue, OutQueue);
// data in inQueue
if InQueue > 0 then
if Assigned(FComPortReceiveData) then FComPortReceiveData(Self , 0, 0, 0);
end;
end;
// RS232 implementation ////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
constructor TRS232.Create( AOwner: TComponent );
begin
inherited Create( AOwner );
//OnReceiveData := ReceiveData;
FComPort := pnCOM1; // COM 1
FComPortBaudRate := br9600; // 9600 bauds
FComPortDataBits := db8BITS; // 8 data bits
FComPortStopBits := sb1BITS; // 1 stop bits
FComPortParity := ptEVEN; // even parity
FComPortHwHandshaking := hhNONE; // no hardware handshaking
FComPortSwHandshaking := shNONE; // no software handshaking
FComPortInBufSize := 2048; // input buffer of 512 ? bytes
FComPortOutBufSize := 2048; // output buffer of 512 ? bytes
FTimeOut := 30; // sec until timeout
end;
function TRS232.ReadString(VAR aResStr: string; aCount: word ): boolean;
var
nRead: dword;
Buffer: string;
Actual, Before: TDateTime;
TimeOutMin, TimeOutSec, lCount: word;
begin
Result := false;
if not Connected then
if not Connect then raise Exception.CreateHelp('RS232.ReadString:'+
' Connect not possible !', 101);
aResStr := '';
TimeOutMin:=TimeOut div 60;
TimeOutSec:=TimeOut mod 60;
if (not Connected) or (aCount <= 0) then EXIT;
nRead := 0; lCount := 0;
Before := Time;
while lCount<aCount do
begin
Application.ProcessMessages;
SetLength(Buffer,1);
if ReadFile( FComPortHandle, PChar(Buffer)^, 1, nRead, nil ) then
begin
if nRead > 0 then
begin
aResStr := aResStr + Buffer;
inc(lCount);
end;
Actual := Time;
if Actual-Before>EncodeTime(0, TimeOutMin, TimeOutSec, 0)
then raise Exception.CreateHelp('RS232.ReadString: TimeOut !', 103);
end
else begin
raise Exception.CreateHelp('RS232.ReadString: Read not possible !', 104);
end;
end; // while
Result:=true;
end;
{$A+,B-,C+,D-,E-,F-,G+,H+,I+,J+,K-,L-,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y-,Z1}
{$MINSTACKSIZE $00004000}
{$MAXSTACKSIZE $00100000}
{$IMAGEBASE $51000000}
{$APPTYPE GUI}
unit ComportDriverThread;
interface
uses
//Include "ExtCtrl" for the TTimer component.
Windows, Messages, SysUtils, Classes, Forms, ExtCtrls;
type
TComPortNumber = (pnCOM1,pnCOM2,pnCOM3,pnCOM4);
TComPortBaudRate = (br110,br300,br600,br1200,br2400,br4800,br9600,
br14400,br19200,br38400,br56000,br57600,br115200);
TComPortDataBits = (db5BITS,db6BITS,db7BITS,db8BITS);
TComPortStopBits = (sb1BITS,sb1HALFBITS,sb2BITS);
TComPortParity = (ptNONE,ptODD,ptEVEN,ptMARK,ptSPACE);
TComportHwHandshaking = (hhNONE,hhRTSCTS);
TComPortSwHandshaking = (shNONE,shXONXOFF);
TTimerThread = class(TThread)
private
{ Private declarations }
FOnTimer : TThreadMethod;
FEnabled: Boolean;
protected
{ Protected declarations }
procedure Execute; override;
procedure SupRes;
public
{ Public declarations }
published
{ Published declarations }
property Enabled: Boolean read FEnabled write FEnabled;
end;
TComportDriverThread = class(TComponent)
private
{ Private declarations }
FTimer : TTimerThread;
FOnReceiveData : TNotifyEvent;
FReceiving : Boolean;
protected
{ Protected declarations }
FComPortActive : Boolean;
FComportHandle : THandle;
FComportNumber : TComPortNumber;
FComportBaudRate : TComPortBaudRate;
FComportDataBits : TComPortDataBits;
FComportStopBits : TComPortStopBits;
FComportParity : TComPortParity;
FComportHwHandshaking : TComportHwHandshaking;
FComportSwHandshaking : TComPortSwHandshaking;
FComportInputBufferSize : Word;
FComportOutputBufferSize : Word;
FComportPollingDelay : Word;
FTimeOut : Integer;
FTempInputBuffer : Pointer;
procedure SetComPortActive(Value: Boolean);
procedure SetComPortNumber(Value: TComPortNumber);
procedure SetComPortBaudRate(Value: TComPortBaudRate);
procedure SetComPortDataBits(Value: TComPortDataBits);
procedure SetComPortStopBits(Value: TComPortStopBits);
procedure SetComPortParity(Value: TComPortParity);
procedure SetComPortHwHandshaking(Value: TComportHwHandshaking);
procedure SetComPortSwHandshaking(Value: TComPortSwHandshaking);
procedure SetComPortInputBufferSize(Value: Word);
procedure SetComPortOutputBufferSize(Value: Word);
procedure SetComPortPollingDelay(Value: Word);
procedure ApplyComPortSettings;
procedure TimerEvent; virtual;
procedure doDataReceived; virtual;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function Connect: Boolean;
function Disconnect: Boolean;
function Connected: Boolean;
function Disconnected: Boolean;
function SendData(DataPtr: Pointer; DataSize: Integer): Boolean;
function SendString(Input: String): Boolean;
function ReadString(var Str: string): Integer;
published
{ Published declarations }
property Active: Boolean read FComPortActive write SetComPortActive default False;
property ComPort: TComPortNumber read FComportNumber write SetComportNumber
default pnCOM1;
property ComPortSpeed: TComPortBaudRate read FComportBaudRate write
SetComportBaudRate default br9600;
property ComPortDataBits: TComPortDataBits read FComportDataBits write
SetComportDataBits default db8BITS;
property ComPortStopBits: TComPortStopBits read FComportStopBits write
SetComportStopBits default sb1BITS;
property ComPortParity: TComPortParity read FComportParity write
SetComportParity default ptNONE;
property ComPortHwHandshaking: TComportHwHandshaking read FComportHwHandshaking
write SetComportHwHandshaking default
hhNONE;
property ComPortSwHandshaking: TComPortSwHandshaking read FComportSwHandshaking
write SetComportSwHandshaking default
shNONE;
property ComPortInputBufferSize: Word read FComportInputBufferSize
write SetComportInputBufferSize default
2048;
property ComPortOutputBufferSize: Word read FComportOutputBufferSize
write SetComportOutputBufferSize default
2048;
property ComPortPollingDelay: Word read FComportPollingDelay write
SetComportPollingDelay default 100;
property OnReceiveData: TNotifyEvent read FOnReceiveData
write FOnReceiveData;
property TimeOut: Integer read FTimeOut write FTimeOut default 30;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Self-made Components', [TComportDriverThread]);
end;
{ TComportDriver }
constructor TComportDriverThread.Create(AOwner: TComponent);
begin
inherited;
FReceiving := False;
FComportHandle := 0;
FComportNumber := pnCOM1;
FComportBaudRate := br9600;
FComportDataBits := db8BITS;
FComportStopBits := sb1BITS;
FComportParity := ptNONE;
FComportHwHandshaking := hhNONE;
FComportSwHandshaking := shNONE;
FComportInputBufferSize := 2048;
FComportOutputBufferSize := 2048;
FOnReceiveData := nil;
FTimeOut := 30;
FComportPollingDelay := 500;
GetMem(FTempInputBuffer,FComportInputBufferSize);
if csDesigning in ComponentState then
Exit;
FTimer := TTimerThread.Create(False);
FTimer.FOnTimer := TimerEvent;
if FComPortActive then
FTimer.Enabled := True;
FTimer.SupRes;
end;
destructor TComportDriverThread.Destroy;
begin
Disconnect;
FreeMem(FTempInputBuffer,FComportInputBufferSize);
inherited Destroy;
end;
function TComportDriverThread.Connect: Boolean;
var
comName: array[0..4] of Char;
tms: TCommTimeouts;
begin
if Connected then
Exit;
StrPCopy(comName,'COM');
comName[3] := chr(ord('1') + ord(FComportNumber));
comName[4] := #0;
FComportHandle := CreateFile(comName,GENERIC_READ OR GENERIC_WRITE,0,nil,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
if not Connected then
Exit;
ApplyComPortSettings;
tms.ReadIntervalTimeout := 1;
tms.ReadTotalTimeoutMultiplier := 0;
tms.ReadTotalTimeoutConstant := 1;
tms.WriteTotalTimeoutMultiplier := 0;
tms.WriteTotalTimeoutConstant := 0;
SetCommTimeouts(FComportHandle,tms);
Sleep(1000);
end;
function TComportDriverThread.Connected: Boolean;
begin
Result := FComportHandle > 0;
end;
function TComportDriverThread.Disconnect: Boolean;
begin
Result := False;
if Connected then
begin
CloseHandle(FComportHandle);
FComportHandle := 0;
end;
Result := True;
end;
function TComportDriverThread.Disconnected: Boolean;
begin
if (FComportHandle <> 0) then
Result := False
else
Result := True;
end;
const
Win32BaudRates: array[br110..br115200] of DWORD =
(CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_14400,
CBR_19200,CBR_38400,CBR_56000,CBR_57600,CBR_115200);
const
dcb_Binary = $00000001;
dcb_ParityCheck = $00000002;
dcb_OutxCtsFlow = $00000004;
dcb_OutxDsrFlow = $00000008;
dcb_DtrControlMask = $00000030;
dcb_DtrControlDisable = $00000000;
dcb_DtrControlEnable = $00000010;
dcb_DtrControlHandshake = $00000020;
dcb_DsrSensitvity = $00000040;
dcb_TXContinueOnXoff = $00000080;
dcb_OutX = $00000100;
dcb_InX = $00000200;
dcb_ErrorChar = $00000400;
dcb_NullStrip = $00000800;
dcb_RtsControlMask = $00003000;
dcb_RtsControlDisable = $00000000;
dcb_RtsControlEnable = $00001000;
dcb_RtsControlHandshake = $00002000;
dcb_RtsControlToggle = $00003000;
dcb_AbortOnError = $00004000;
dcb_Reserveds = $FFFF8000;
procedure TComportDriverThread.ApplyComPortSettings;
var
//Device Control Block (= dcb)
dcb: TDCB;
begin
if not Connected then
Exit;
FillChar(dcb,sizeOf(dcb),0);
dcb.DCBlength := sizeOf(dcb);
dcb.Flags := dcb_Binary or dcb_RtsControlEnable;
dcb.BaudRate := Win32BaudRates[FComPortBaudRate];
case FComportHwHandshaking of
hhNONE : ;
hhRTSCTS:
dcb.Flags := dcb.Flags or dcb_OutxCtsFlow or dcb_RtsControlHandshake;
end;
case FComportSwHandshaking of
shNONE : ;
shXONXOFF:
dcb.Flags := dcb.Flags or dcb_OutX or dcb_Inx;
end;
dcb.XonLim := FComportInputBufferSize div 4;
dcb.XoffLim := 1;
dcb.ByteSize := 5 + ord(FComportDataBits);
dcb.Parity := ord(FComportParity);
dcb.StopBits := ord(FComportStopBits);
dcb.XonChar := #17;
dcb.XoffChar := #19;
SetCommState(FComportHandle,dcb);
SetupComm(FComportHandle,FComPortInputBufferSize,FComPortOutputBufferSize);
end;
function TComportDriverThread.ReadString(var Str: string): Integer;
var
BytesTrans, nRead: DWORD;
Buffer : String;
i : Integer;
temp : string;
begin
BytesTrans := 0;
Str := '';
SetLength(Buffer,1);
ReadFile(FComportHandle,PChar(Buffer)^, 1, nRead, nil);
while nRead > 0 do
begin
temp := temp + PChar(Buffer);
ReadFile(FComportHandle,PChar(Buffer)^, 1, nRead, nil);
end;
//Remove the end token.
BytesTrans := Length(temp);
SetLength(str,BytesTrans-2);
for i:=0 to BytesTrans-2 do
begin
str[i] := temp[i];
end;
Result := BytesTrans;
end;
function TComportDriverThread.SendData(DataPtr: Pointer;
DataSize: Integer): Boolean;
var
nsent : DWORD;
begin
Result := WriteFile(FComportHandle,DataPtr^,DataSize,nsent,nil);
Result := Result and (nsent = DataSize);
end;
function TComportDriverThread.SendString(Input: String): Boolean;
begin
if not Connected then
if not Connect then
raise Exception.CreateHelp('Could not connect to COM-port !',101);
Result := SendData(PChar(Input),Length(Input));
if not Result then
raise Exception.CreateHelp('Could not send to COM-port !',102);
end;
procedure TComportDriverThread.TimerEvent;
var
InQueue, OutQueue: Integer;
Buffer : String;
nRead : DWORD;
procedure DataInBuffer(Handle: THandle; var aInQueue, aOutQueue: Integer);
var
ComStat : TComStat;
e : Cardinal;
begin
aInQueue := 0;
aOutQueue := 0;
if ClearCommError(Handle,e,@ComStat) then
begin
aInQueue := ComStat.cbInQue;
aOutQueue := ComStat.cbOutQue;
end;
end;
begin
if csDesigning in ComponentState then
Exit;
if not Connected then
if not Connect then
raise Exception.CreateHelp('TimerEvent: Could not connect to COM-port !',101);
Application.ProcessMessages;
if Connected then
begin
DataInBuffer(FComportHandle,InQueue,OutQueue);
if InQueue > 0 then
begin
if (Assigned(FOnReceiveData) ) then
begin
FReceiving := True;
FOnReceiveData(Self);
end;
end;
end;
end;
procedure TComportDriverThread.SetComportBaudRate(Value: TComPortBaudRate);
begin
FComportBaudRate := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportDataBits(Value: TComPortDataBits);
begin
FComportDataBits := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportHwHandshaking(Value: TComportHwHandshaking);
begin
FComportHwHandshaking := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportInputBufferSize(Value: Word);
begin
FreeMem(FTempInputBuffer,FComportInputBufferSize);
FComportInputBufferSize := Value;
GetMem(FTempInputBuffer,FComportInputBufferSize);
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportNumber(Value: TComPortNumber);
begin
if Connected then
exit;
FComportNumber := Value;
end;
procedure TComportDriverThread.SetComportOutputBufferSize(Value: Word);
begin
FComportOutputBufferSize := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportParity(Value: TComPortParity);
begin
FComportParity := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportPollingDelay(Value: Word);
begin
FComportPollingDelay := Value;
end;
procedure TComportDriverThread.SetComportStopBits(Value: TComPortStopBits);
begin
FComportStopBits := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.SetComportSwHandshaking(Value: TComPortSwHandshaking);
begin
FComportSwHandshaking := Value;
if Connected then
ApplyComPortSettings;
end;
procedure TComportDriverThread.DoDataReceived;
begin
if Assigned(FOnReceiveData) then FOnReceiveData(Self);
end;
procedure TComportDriverThread.SetComPortActive(Value: Boolean);
var
DumpString : String;
begin
FComPortActive := Value;
if csDesigning in ComponentState then
Exit;
if FComPortActive then
begin
//Just dump the contents of the input buffer of the com-port.
ReadString(DumpString);
FTimer.Enabled := True;
end
else
FTimer.Enabled := False;
FTimer.SupRes;
end;
{ TTimerThread }
procedure TTimerThread.Execute;
begin
Priority := tpNormal;
repeat
Sleep(500);
if Assigned(FOnTimer) then Synchronize(FOnTimer);
until Terminated;
end;
procedure TTimerThread.SupRes;
begin
if not Suspended then
Suspend;
if FEnabled then
Resume;
end;
end.
Взято с Исходников.ru
procedure TCommPortDriver.SetActive(const Value: boolean);
begin
FActive := Value;
end;
end.
Взято с Исходников.ru
Компонент для работы с PCX файлами
Компонент для работы с PCX файлами
Fully supports reading and writing of: 1, 8 and 24 bit PCX images.
///////////////////////////////////////////////////////////////////////
////
// TPCXImage //
// ========= //
// //
// Completed: The 10th of August 2001 //
// Author: M. de Haan //
// Email: M.deHaan@inn.nl //
// Tested: under W95 SP1, NT4 SP6, WIN2000 //
// Version: 1.0 //
//-------------------------------------------------------------------//
// Update: The 14th of August 2001 to version 1.1. //
// Reason: Added version check. //
// Added comment info on version. //
// Changed PCX header ID check. //
//-------------------------------------------------------------------//
// Update: The 19th of August 2001 to version 2.0. //
// Reason: Warning from Delphi about using abstract methods, //
// caused by not implementing ALL TGraphic methods. //
// (Thanks goes to R.P. Sterkenburg for his diagnostic.) //
// Added: SaveToClipboardFormat, LoadFromClipboardFormat, //
// GetEmpty. //
//-------------------------------------------------------------------//
// Update: The 13th of October 2001 to version 2.1. //
// Reason: strange errors, read errors, EExternalException, IDE //
// hanging, Delphi hanging, Debugger hanging, windows //
// hanging, keyboard locked, and so on. //
// Changed: Assign procedure. //
//-------------------------------------------------------------------//
// Update: The 5th of April 2002 to version 2.2. //
// Changed: RLE compressor routine. //
// Reason: Incompatibility problems with other programs caused //
// by the RLE compressor. //
// Other programs encode: $C0 as: $C1 $C0. //
// ($C0 means: repeat the following byte 0 times //
// $C1 means: repeat the following byte 1 time.) //
// Changed: File read routine. //
// Reason: Now detects unsupported PCX data formats. //
// Added: 'Unsupported data format' in exception handler. //
// Added: 1 bit PCX support in reading. //
// Added: Procedure Convert1BitPCXDataToImage. //
// Renamed: Procedure ConvertPCXDataToImage to //
// Convert24BitPCXDataToImage. //
//-------------------------------------------------------------------//
// Update: The 14th of April 2002 to version 2.3. //
// Now capable of reading and writing 1 and 24 bit PCX //
// images. //
// Added: 1 bit PCX support in writing. //
// Added: Procedure ConvertImageTo1bitPCXData. //
// Changed: Procedure CreatePCXHeader. //
// Changed: Procedure TPCXImage.SaveToFile. //
//-------------------------------------------------------------------//
// Update: The 19th of April 2002 to version 2.4. //
// Now capable of reading and writing: 1, 8 and 24 bit //
// PCX images. //
// Added: 8 bit PCX support in reading and writing. //
// Renamed: Procedure ConvertImageTo1And8bitPCXData. //
// Renamed: Procedure Convert1And8bitPCXDataToImage. //
// Changed: Procedure fSetPalette, fGetPalette. //
//-------------------------------------------------------------------//
// Update: The 7th of May 2002 to version 2.5. //
// Reason: The palette of 8-bit PCX images couldn't be read in //
// the calling program. //
// Changed: Procedures Assign, AssignTo, fSetPalette, fGetPalette. //
// Tested: All formats were tested with the following programs: //
// - import in Word 97, //
// * (Word ignores the palette of 1 bit PCX images!) //
// - import and export in MigroGrafX. //
// * (MicroGrafX also ignores the palette of 1 bit PCX //
// images.) //
// No problems were detected. //
// //
//===================================================================//
// //
// The PCX image file format is copyrighted by: //
// ZSoft, PC Paintbrush, PC Paintbrush plus //
// Trademarks: N/A //
// Royalty fees: NONE //
// //
//===================================================================//
// //
// The author can not be held responsable for using this software //
// in anyway. //
// //
// The features and restrictions of this component are: //
// ---------------------------------------------------- //
// //
// The reading and writing (import / export) of files / images: //
// - PCX version 5 definition, PC Paintbrush 3 and higher, //
// - RLE-compressed, //
// - 1 and 8 bit PCX images WITH palette and //
// - 24 bit PCX images without palette, //
// are supported by this component. //
// //
// Known issues //
// ------------ //
// //
// 1) GetEmpty is NOT tested. //
// //
// 2) SaveToClipboardFormat is NOT tested. //
// //
// 3) LoadFromClipboardFormat is NOT tested. //
// //
// 4) 4 bit PCX images (with palette) are NOT (yet) implemented. //
// (I have no 4-bit PCX images to test it on...) //
// //
///////////////////////////////////////////////////////////////////////
unit
PCXImage;
interface
uses
Windows,
SysUtils,
Classes,
Graphics;
const
WIDTH_OUT_OF_RANGE = 'Illegal width entry in PCX file header';
HEIGHT_OUT_OF_RANGE = 'Illegal height entry in PCX file header';
FILE_FORMAT_ERROR = 'Invalid file format';
VERSION_ERROR = 'Only PC Paintbrush (plus) V3.0 and ' +
'higher are supported';
FORMAT_ERROR = 'Illegal identification byte in PCX file' +
' header';
PALETTE_ERROR = 'Invalid palette signature found';
ASSIGN_ERROR = 'Can only Assign a TBitmap or a TPicture';
ASSIGNTO_ERROR = 'Can only AssignTo a TBitmap';
PCXIMAGE_EMPTY = 'The PCX image is empty';
BITMAP_EMPTY = 'The bitmap is empty';
INPUT_FILE_TOO_LARGE = 'The input file is too large to be read';
IMAGE_WIDTH_TOO_LARGE = 'Width of PCX image is too large to handle';
// added 19/08/2001
CLIPBOARD_LOAD_ERROR = 'Loading from clipboard failed';
// added 19/08/2001
CLIPBOARD_SAVE_ERROR = 'Saving to clipboard failed';
// added 14/10/2001
PCX_WIDTH_ERROR = 'Unexpected line length in PCX data';
PCX_HEIGHT_ERROR = 'More PCX data found than expected';
PCXIMAGE_TOO_LARGE = 'PCX image is too large';
// added 5/4/2002
ERROR_UNSUPPORTED = 'Unsupported PCX format';
const
sPCXImageFile = 'PCX V3.0+ image';
// added 19/08/2001
var
CF_PCX: WORD;
///////////////////////////////////////////////////////////////////////
// //
// PCXHeader //
// //
///////////////////////////////////////////////////////////////////////
type
QWORD = Cardinal; // Seems more logical to me...
type
fColorEntry = packed record
ceRed: BYTE;
ceGreen: BYTE;
ceBlue: BYTE;
end; // of packed record fColorEntry
type
TPCXImageHeader = packed record
fID: BYTE;
fVersion: BYTE;
fCompressed: BYTE;
fBitsPerPixel: BYTE;
fWindow: packed record
wLeft,
wTop,
wRight,
wBottom: WORD;
end; // of packed record fWindow
fHorzResolution: WORD;
fVertResolution: WORD;
fColorMap: array[0..15] of fColorEntry;
fReserved: BYTE;
fPlanes: BYTE;
fBytesPerLine: WORD;
fPaletteInfo: WORD;
fFiller: array[0..57] of BYTE;
end; // of packed record TPCXImageHeader
///////////////////////////////////////////////////////////////////////
// //
// PCXData //
// //
///////////////////////////////////////////////////////////////////////
type
TPCXData = object
fData: array of BYTE;
end; // of Type TPCXData
///////////////////////////////////////////////////////////////////////
// //
// ScanLine //
// //
///////////////////////////////////////////////////////////////////////
const
fMaxScanLineLength = $FFF; // Max image width: 4096 pixels
type
mByteArray = array[0..fMaxScanLineLength] of BYTE;
pmByteArray = ^mByteArray;
// The "standard" pByteArray from Delphi allocates 32768 bytes,
// which is a little bit overdone here, I think...
const
fMaxImageWidth = $FFF; // Max image width: 4096 pixels
type
xByteArray = array[0..fMaxImageWidth] of BYTE;
///////////////////////////////////////////////////////////////////////
// //
// PCXPalette //
// //
///////////////////////////////////////////////////////////////////////
type
TPCXPalette = packed record
fSignature: BYTE;
fPalette: array[0..255] of fColorEntry;
end; // of packed record TPCXPalette
///////////////////////////////////////////////////////////////////////
// //
// Classes //
// //
///////////////////////////////////////////////////////////////////////
type
TPCXImage = class;
TPCXFile = class;
///////////////////////////////////////////////////////////////////////
// //
// PCXFile //
// //
// File handler //
// //
///////////////////////////////////////////////////////////////////////
TPCXFile = class(TPersistent)
private
fHeight: Integer;
fWidth: Integer;
fPCXHeader: TPCXImageHeader;
fPCXData: TPCXData;
fPCXPalette: TPCXPalette;
fColorDepth: QWORD;
fPixelFormat: BYTE; // added 5/4/2002
fCurrentPos: QWORD;
fHasPalette: Boolean; // added 7/5/2002
protected
// Protected declarations
public
// Public declarations
constructor Create;
destructor Destroy; override;
procedure LoadFromFile(const Filename: string);
procedure LoadFromStream(Stream: TStream);
procedure SaveToFile(const Filename: string);
procedure SaveToStream(Stream: TStream);
published
// Published declarations
// The publishing is done in the TPCXImage section
end;
///////////////////////////////////////////////////////////////////////
// //
// TPCXImage //
// //
// Image handler //
// //
///////////////////////////////////////////////////////////////////////
TPCXImage = class(TGraphic)
private
// Private declarations
fBitmap: TBitmap;
fPCXFile: TPCXFile;
fRLine: xByteArray;
fGLine: xByteArray;
fBLine: xByteArray;
fP: pmByteArray;
fhPAL: HPALETTE;
procedure fConvert24BitPCXDataToImage;
procedure fConvert1And8BitPCXDataToImage;
procedure fConvertImageTo24BitPCXData;
procedure fConvertImageTo1And8BitPCXData(ImageWidthInBytes:
QWORD);
procedure fFillDataLines(const fLine: array of BYTE);
procedure fCreatePCXHeader(const byBitsPerPixel: BYTE;
const byPlanes: BYTE; const wBytesPerLine: DWORD);
procedure fSetPalette(const wNumColors: WORD);
procedure fGetPalette(const wNumColors: WORD);
function fGetPixelFormat: TPixelFormat; // Added 07/05/2002
function fGetBitmap: TBitmap; // Added 07/05/2002
protected
// Protected declarations
procedure Draw(ACanvas: TCanvas; const Rect: TRect); override;
function GetHeight: Integer; override;
function GetWidth: Integer; override;
procedure SetHeight(Value: Integer); override;
procedure SetWidth(Value: Integer); override;
function GetEmpty: Boolean; override;
public
// Public declarations
constructor Create; override;
destructor Destroy; override;
procedure Assign(Source: TPersistent); override;
procedure AssignTo(Dest: TPersistent); override;
procedure LoadFromFile(const Filename: string); override;
procedure LoadFromStream(Stream: TStream); override;
procedure SaveToFile(const Filename: string); override;
procedure SaveToStream(Stream: TStream); override;
procedure LoadFromClipboardFormat(AFormat: WORD;
AData: THandle; APalette: HPALETTE); override;
procedure SaveToClipboardFormat(var AFormat: WORD;
var AData: THandle; var APalette: HPALETTE); override;
published
// Published declarations
property Height: Integer
read GetHeight write SetHeight;
property Width: Integer
read GetWidth write SetWidth;
property PixelFormat: TPixelFormat
read fGetPixelFormat;
property Bitmap: TBitmap
read fGetBitmap; // Added 7/5/2002
end;
implementation
///////////////////////////////////////////////////////////////////////
// //
// TPCXImage //
// //
// Image handler //
// //
///////////////////////////////////////////////////////////////////////
constructor TPCXImage.Create;
begin
inherited Create;
// Init HPALETTE
fhPAL := 0;
// Create a private bitmap to hold the image
if not Assigned(fBitmap) then
fBitmap := TBitmap.Create;
// Create the PCXFile
if not Assigned(fPCXFile) then
fPCXFile := TPCXFile.Create;
end;
//---------------------------------------------------------------------
destructor TPCXImage.Destroy;
begin
// Reversed order of create
// Free fPCXFile
fPCXFile.Free;
// Free private bitmap
fBitmap.Free;
// Delete palette
if fhPAL <> 0 then
DeleteObject(fhPAL);
// Distroy all the other things
inherited Destroy;
end;
//---------------------------------------------------------------------
procedure TPCXImage.SetHeight(Value: Integer);
begin
if Value >= 0 then
fBitmap.Height := Value;
end;
//---------------------------------------------------------------------
procedure TPCXImage.SetWidth(Value: Integer);
begin
if Value >= 0 then
fBitmap.Width := Value;
end;
//---------------------------------------------------------------------
function TPCXImage.GetHeight: Integer;
begin
Result := fPCXFile.fHeight;
end;
//---------------------------------------------------------------------
function TPCXImage.GetWidth: Integer;
begin
Result := fPCXFile.fWidth;
end;
//---------------------------------------------------------------------
function TPCXImage.fGetBitmap: TBitmap;
begin
Result := fBitmap;
end;
//-------------------------------------------------------------------//
// The credits for this procedure go to his work of TGIFImage by //
// Reinier P. Sterkenburg //
// Added 19/08/2001 //
//-------------------------------------------------------------------//
// NOT TESTED!
procedure TPCXImage.LoadFromClipboardFormat(AFormat: WORD;
ADAta: THandle; APalette: HPALETTE);
var
Size: QWORD;
Buf: Pointer;
Stream: TMemoryStream;
BMP: TBitmap;
begin
if (AData = 0) then
AData := GetClipBoardData(AFormat);
if (AData <> 0) and (AFormat = CF_PCX) then
begin
Size := GlobalSize(AData);
Buf := GlobalLock(AData);
try
Stream := TMemoryStream.Create;
try
Stream.SetSize(Size);
Move(Buf^, Stream.Memory^, Size);
Self.LoadFromStream(Stream);
finally
Stream.Free;
end;
finally
GlobalUnlock(AData);
end;
end
else if (AData <> 0) and (AFormat = CF_BITMAP) then
begin
BMP := TBitmap.Create;
try
BMP.LoadFromClipboardFormat(AFormat, AData, APalette);
Self.Assign(BMP);
finally
BMP.Free;
end;
end
else
raise Exception.Create(CLIPBOARD_LOAD_ERROR);
end;
//-------------------------------------------------------------------//
// The credits for this procedure go to his work of TGIFImage by //
// Reinier P. Sterkenburg //
// Added 19/08/2001 //
//-------------------------------------------------------------------//
// NOT TESTED!
procedure TPCXImage.SaveToClipboardFormat(var AFormat: WORD;
var AData: THandle; var APalette: HPALETTE);
var
Stream: TMemoryStream;
Data: THandle;
Buf: Pointer;
begin
if Empty then
Exit;
// First store the bitmap to the clipboard
fBitmap.SaveToClipboardFormat(AFormat, AData, APalette);
// Then try to save the PCX
Stream := TMemoryStream.Create;
try
SaveToStream(Stream);
Stream.Position := 0;
Data := GlobalAlloc(HeapAllocFlags, Stream.Size);
try
if Data <> 0 then
begin
Buf := GlobalLock(Data);
try
Move(Stream.Memory^, Buf^, Stream.Size);
finally
GlobalUnlock(Data);
end;
if SetClipBoardData(CF_PCX, Data) = 0 then
raise Exception.Create(CLIPBOARD_SAVE_ERROR);
end;
except
GlobalFree(Data);
raise;
end;
finally
Stream.Free;
end;
end;
//-------------------------------------------------------------------//
// NOT TESTED!
function TPCXImage.GetEmpty: Boolean; // Added 19/08/2002
begin
if Assigned(fBitmap) then
Result := fBitmap.Empty
else
Result := (fPCXFile.fHeight = 0) or (fPCXFile.fWidth = 0);
end;
//---------------------------------------------------------------------
procedure TPCXImage.SaveToFile(const Filename: string);
var
fPCX: TFileStream;
W, WW: QWORD;
begin
if (fBitmap.Width = 0) or (fBitmap.Height = 0) then
raise Exception.Create(BITMAP_EMPTY);
W := fBitmap.Width;
WW := W div 8;
if (W mod 8) > 0 then
Inc(WW);
case fBitmap.PixelFormat of
pf1bit:
begin
// Fully supported by PCX and by this component
fCreatePCXHeader(1, 1, WW);
fConvertImageTo1And8BitPCXData(WW);
fGetPalette(2);
end;
pf4bit:
begin
// I don't have 4-bit PCX images to test with
// It will be treated as a 24 bit image
fCreatePCXHeader(8, 3, W);
fConvertImageTo24BitPCXData;
end;
pf8bit:
begin
// Fully supported by PCX and by this component
fCreatePCXHeader(8, 1, W);
fConvertImageTo1And8BitPCXData(W);
fGetPalette(256);
end;
pf15bit:
begin
// Is this supported in PCX?
// It will be treated as a 24 bit image
fCreatePCXHeader(8, 3, W);
fConvertImageTo24BitPCXData;
end;
pf16bit:
begin
// Is this supported in PCX?
// It will be treated as a 24 bit image
fCreatePCXHeader(8, 3, W);
fConvertImageTo24BitPCXData;
end;
pf24bit:
begin
// Fully supported by PCX and by this component
fCreatePCXHeader(8, 3, W);
fConvertImageTo24BitPCXData;
end;
pf32bit:
begin
// Not supported by PCX
fCreatePCXHeader(8, 3, W);
fConvertImageTo24BitPCXData;
end;
else
begin
fCreatePCXHeader(8, 3, W);
fConvertImageTo24BitPCXData;
end; // of else
end; // of Case
fPCX := TFileStream.Create(Filename, fmCreate);
try
fPCX.Position := 0;
SaveToStream(fPCX);
finally
fPCX.Free;
end; // of finally
SetLength(fPCXFile.fPCXData.fData, 0);
end; // of Procedure SaveToFile
//-------------------------------------------------------------------//
procedure TPCXImage.AssignTo(Dest: TPersistent);
var
bAssignToError: Boolean;
begin
bAssignToError := True;
if Dest is TBitmap then
begin
// The old AssignTo procedure was like this.
// But then the palette was couldn't be accessed in the calling
// program for some reason.
// --------------------------
// (Dest as TBitmap).Assign(fBitmap);
// If fBitmap.Palette <> 0 then
// (Dest as TBitmap).Palette := CopyPalette(fBitmap.Palette);
// --------------------------
// Do the assigning
(Dest as TBitmap).Assign(fBitmap);
if fPCXFile.fHasPalette then
(Dest as TBitmap).Palette := CopyPalette(fhPAL);
// Now the calling program can access the palette
// (if it has one)!
bAssignToError := False;
end;
if Dest is TPicture then
begin
(Dest as TPicture).Graphic.Assign(fBitmap);
bAssignToError := False;
end;
if bAssignToError then
raise Exception.Create(ASSIGNTO_ERROR);
// You can write other assignments here, if you want...
end;
//-------------------------------------------------------------------//
procedure TPCXImage.Assign(Source: TPersistent);
var
iX, iY: DWORD;
bAssignError: Boolean;
begin
bAssignError := True;
if (Source is TBitmap) then
begin
fBitmap.Assign(Source as TBitmap);
if (Source as TBitmap).Palette <> 0 then
begin
fhPAL := CopyPalette((Source as TBitmap).Palette);
fBitmap.Palette := fhPAL;
end;
bAssignError := False;
end;
if (Source is TPicture) then
begin
iX := (Source as TPicture).Width;
iY := (Source as TPicture).Height;
fBitmap.Width := iX;
fBitmap.Height := iY;
fBitmap.Canvas.Draw(0, 0, (Source as TPicture).Graphic);
bAssignError := False;
end;
// You can write other assignments here, if you want...
if bAssignError then
raise Exception.Create(ASSIGN_ERROR);
end;
//---------------------------------------------------------------------
procedure TPCXImage.Draw(ACanvas: TCanvas; const Rect: TRect);
begin
// Faster
// ACanvas.Draw(0,0,fBitmap);
// Slower
ACanvas.StretchDraw(Rect, fBitmap);
end;
//---------------------------------------------------------------------
procedure TPCXImage.LoadFromFile(const Filename: string);
begin
fPCXFile.LoadFromFile(Filename);
// added 5/4/2002
case fPCXFile.fPixelFormat of
1: fConvert1And8BitPCXDataToImage;
8: fConvert1And8BitPCXDataToImage;
24: fConvert24BitPCXDataToImage;
end;
end;
//---------------------------------------------------------------------
procedure TPCXImage.SaveToStream(Stream: TStream);
begin
fPCXFile.SaveToStream(Stream);
end;
//---------------------------------------------------------------------
procedure TPCXImage.LoadFromStream(Stream: TStream);
begin
fPCXFile.LoadFromStream(Stream);
end;
///////////////////////////////////////////////////////////////////////
// //
// Called by RLE compressor //
// //
///////////////////////////////////////////////////////////////////////
procedure TPCXImage.fFillDataLines(const fLine: array of BYTE);
var
By: BYTE;
Cnt: WORD;
I: QWORD;
W: QWORD;
begin
I := 0;
By := fLine[0];
Cnt := $C1;
W := fBitmap.Width;
repeat
Inc(I);
if By = fLine[I] then
begin
Inc(Cnt);
if Cnt = $100 then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] :=
BYTE(Pred(Cnt));
Inc(fPCXFile.fCurrentPos);
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By;
Inc(fPCXFile.fCurrentPos);
Cnt := $C1;
By := fLine[I];
end;
end;
if (By <> fLine[I]) then
begin
if (Cnt = $C1) then
begin
// If (By < $C1) then
if (By < $C0) then // changed 5/4/2002
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By;
Inc(fPCXFile.fCurrentPos);
end
else
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt);
Inc(fPCXFile.fCurrentPos);
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By;
Inc(fPCXFile.fCurrentPos);
end;
end
else
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt);
Inc(fPCXFile.fCurrentPos);
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By;
Inc(fPCXFile.fCurrentPos);
end;
Cnt := $C1;
By := fLine[I];
end;
until I = W - 1;
// Write the last byte(s)
if (Cnt > $C1) then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt);
Inc(fPCXFile.fCurrentPos);
end;
if (Cnt = $C1) and (By > $C0) then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := BYTE(Cnt);
Inc(fPCXFile.fCurrentPos);
end;
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := By;
Inc(fPCXFile.fCurrentPos);
end;
//-------------------------------------------------------------------//
// RLE Compression algorithm //
//-------------------------------------------------------------------//
procedure TPCXImage.fConvertImageTo24BitPCXData; // Renamed 5/4/2002
var
H, W: QWORD;
X, Y: QWORD;
I: QWORD;
begin
H := fBitmap.Height;
W := fBitmap.Width;
fPCXFile.fCurrentPos := 0;
SetLength(fPCXFile.fPCXData.fData, 6 * H * W); // To be sure...
fBitmap.PixelFormat := pf24bit; // Always do this if you're using
// ScanLine!
for Y := 0 to H - 1 do
begin
fP := fBitmap.ScanLine[Y];
I := 0;
for X := 0 to W - 1 do
begin
fRLine[X] := fP[I];
Inc(I); // Extract a red line
fGLine[X] := fP[I];
Inc(I); // Extract a green line
fBLine[X] := fP[I];
Inc(I); // Extract a blue line
end;
fFillDataLines(fBLine); // Compress the blue line
fFillDataLines(fGLine); // Compress the green line
fFillDataLines(fRLine); // Compress the red line
end;
// Correct the length of fPCXData.fData
SetLength(fPCXFile.fPCXData.fData, fPCXFile.fCurrentPos);
end;
//-------------------------------------------------------------------//
procedure TPCXImage.fConvertImageTo1And8BitPCXData(ImageWidthInBytes:
QWORD);
var
H, W, X, Y: QWORD;
oldByte, newByte: BYTE;
Cnt: BYTE;
begin
H := fBitmap.Height;
W := ImageWidthInBytes;
fPCXFile.fCurrentPos := 0;
SetLength(fPCXFile.fPCXData.fData, 2 * H * W); // To be sure...
oldByte := 0; // Otherwise the compiler issues a warning about
// oldByte not being initialized...
Cnt := $C1;
for Y := 0 to H - 1 do
begin
fP := fBitmap.ScanLine[Y];
for X := 0 to W - 1 do
begin
newByte := fP[X];
if X > 0 then
begin
if (Cnt = $FF) then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt;
Inc(fPCXFile.fCurrentPos);
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte;
Inc(fPCXFile.fCurrentPos);
Cnt := $C1;
end
else if newByte = oldByte then
Inc(Cnt);
if newByte <> oldByte then
begin
if (Cnt > $C1) or (oldByte >= $C0) then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt;
Inc(fPCXFile.fCurrentPos);
Cnt := $C1;
end;
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte;
Inc(fPCXFile.fCurrentPos);
end;
end;
oldByte := newByte;
end;
// Write last byte of line
if (Cnt > $C1) or (oldByte >= $C0) then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt;
Inc(fPCXFile.fCurrentPos);
Cnt := $C1;
end;
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte;
Inc(fPCXFile.fCurrentPos);
end;
// Write last byte of image
if (Cnt > $C1) or (oldByte >= $C0) then
begin
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := Cnt;
Inc(fPCXFile.fCurrentPos);
// Cnt := 1;
end;
fPCXFile.fPCXData.fData[fPCXFile.fCurrentPos] := oldByte;
Inc(fPCXFile.fCurrentPos);
// Correct the length of fPCXData.fData
SetLength(fPCXFile.fPCXData.fData, fPCXFile.fCurrentPos);
end;
//-------------------------------------------------------------------//
// RLE Decompression algorithm //
//-------------------------------------------------------------------//
procedure TPCXImage.fConvert24BitPCXDataToImage; // Renamed 5/4/2002
var
I: QWORD;
By: BYTE;
Cnt: BYTE;
H, W: QWORD;
X, Y: QWORD;
K, L: QWORD;
begin
H := fPCXFile.fPCXHeader.fWindow.wBottom -
fPCXFile.fPCXHeader.fWindow.wTop + 1;
W := fPCXFile.fPCXHeader.fWindow.wRight -
fPCXFile.fPCXHeader.fWindow.wLeft + 1;
Y := 0; // First line of image
fBitmap.Width := W; // Set bitmap width
fBitmap.Height := H; // Set bitmap height
fBitmap.PixelFormat := pf24bit; // Always do this if you're using
// ScanLine!
I := 0; // Pointer to data byte of fPXCFile
repeat
// Process the red line
// ProcessLine(fRLine,W);
X := 0; // Pointer to position in Red / Green / Blue line
repeat
By := fPCXFile.fPCXData.fData[I];
Inc(I);
// one byte
if By < $C1 then
if X <= W then // added 5/4/2002
begin
fRLine[X] := By;
Inc(X);
end;
// multiple bytes (RLE)
if By > $C0 then
begin
Cnt := By and $3F;
By := fPCXFile.fPCXData.fData[I];
Inc(I);
//FillChar(fRLine[J],Cnt,By);
//Inc(J,Cnt);
for K := 1 to Cnt do
if X <= W then // added 5/4/2002
begin
fRLine[X] := By;
Inc(X);
end;
end;
until X >= W;
// Process the green line
// ProcessLine(fGLine,W);
X := 0;
repeat
By := fPCXFile.fPCXData.fData[I];
Inc(I);
// one byte
if By < $C1 then
if X <= W then // added 5/4/2002
begin
fGLine[X] := By;
Inc(X);
end;
// multiple bytes (RLE)
if By > $C0 then
begin
Cnt := By and $3F;
By := fPCXFile.fPCXData.fData[I];
Inc(I);
for K := 1 to Cnt do
if X <= W then // added 5/4/2002
begin
fGLine[X] := By;
Inc(X);
end;
end;
until X >= W;
// Process the blue line
// ProcessLine(fBLine,W);
X := 0;
repeat
By := fPCXFile.fPCXData.fData[I];
Inc(I);
// one byte
if By < $C1 then
if X <= W then // added 5/4/2002
begin
fBLine[X] := By;
Inc(X);
end;
// multiple bytes (RLE)
if By > $C0 then
begin
Cnt := By and $3F;
By := fPCXFile.fPCXData.fData[I];
Inc(I);
for K := 1 to Cnt do
if X <= W then // added 5/4/2002
begin
fBLine[X] := By;
Inc(X);
end;
end;
until X >= W;
// Write the just processed data RGB lines to the bitmap
fP := fBitmap.ScanLine[Y];
L := 0;
for X := 0 to W - 1 do
begin
fP[L] := fBLine[X];
Inc(L);
fP[L] := fGLine[X];
Inc(L);
fP[L] := fRLine[X];
Inc(L);
end;
Inc(Y); // Process the next RGB line
until Y >= H;
SetLength(fPCXFile.fPCXData.fData, 0);
end;
//-------------------------------------------------------------------//
procedure TPCXImage.fConvert1And8BitPCXDataToImage; // added 5/4/2002
var
I, J: QWORD;
By: BYTE;
Cnt: BYTE;
H, W, WW: QWORD;
X, Y: QWORD;
begin
H := fPCXFile.fPCXHeader.fWindow.wBottom -
fPCXFile.fPCXHeader.fWindow.wTop + 1;
W := fPCXFile.fPCXHeader.fWindow.wRight -
fPCXFile.fPCXHeader.fWindow.wLeft + 1;
fBitmap.Width := W; // Set bitmap width
fBitmap.Height := H; // Set bitmap height
WW := W;
// 1 bit PCX
if fPCXFile.fPixelFormat = 1 then
begin
// All 1 bit images have a palette
fBitmap.PixelFormat := pf1bit; // Always do this if you're using
// ScanLine!
WW := W div 8; // Correct width for pf1bit
if W mod 8 > 0 then
begin
Inc(WW);
fBitMap.Width := WW * 8;
end;
fSetPalette(2);
end;
// 8 bit PCX
if fPCXFile.fPixelFormat = 8 then
begin
// All 8 bit images have a palette!
// This is how to set the palette of a bitmap
// 1. First set the bitmap to pf8bit;
// 2. then set the palette of the bitmap;
// 3. then set the pixels with ScanLine or with Draw.
// If you do it with StretchDraw, it won't work. Don't ask me why.
// If you don't do it in this order, it won't work either! You'll
// get strange colors.
fBitmap.PixelFormat := pf8bit; // Always do this if you're using
// ScanLine!
fSetPalette(256);
end;
I := 0;
Y := 0;
repeat
fP := fBitmap.ScanLine[Y];
X := 0; // Pointer to position in line
repeat
By := fPCXFile.fPCXData.fData[I];
Inc(I);
// one byte
if By < $C1 then
if X <= WW then
begin
fP[X] := By;
Inc(X);
end;
// multiple bytes (RLE)
if By > $C0 then
begin
Cnt := By and $3F;
By := fPCXFile.fPCXData.fData[I];
Inc(I);
for J := 1 to Cnt do
if X <= WW then
begin
fP[X] := By;
Inc(X);
end;
end;
until X >= WW;
Inc(Y); // Next line
until Y >= H;
end;
//---------------------------------------------------------------------
procedure TPCXImage.fCreatePCXHeader(const byBitsPerPixel: BYTE;
const byPlanes: BYTE; const wBytesPerLine: DWORD);
var
H, W: WORD;
begin
W := fBitmap.Width;
H := fBitmap.Height;
// PCX header
fPCXFile.fPCXHeader.fID := BYTE($0A); // BYTE (1)
fPCXFile.fPCXHeader.fVersion := BYTE(5); // BYTE (2)
fPCXFile.fPCXHeader.fCompressed := BYTE(1); // BYTE (3)
// 0 = uncompressed, 1 = compressed
// Only RLE compressed files are supported by this component
fPCXFile.fPCXHeader.fBitsPerPixel := BYTE(byBitsPerPixel);
// BYTE (4)
fPCXFile.fPCXHeader.fWindow.wLeft := WORD(0); // WORD (5,6)
fPCXFile.fPCXHeader.fWindow.wTop := WORD(0); // WORD (7,8)
fPCXFile.fPCXHeader.fWindow.wRight := WORD(W - 1); // WORD (9,10)
fPCXFile.fPCXHeader.fWindow.wBottom := WORD(H - 1); // WORD (11,12)
fPCXFile.fPCXHeader.fHorzResolution := WORD(72); // WORD (13,14)
fPCXFile.fPCXHeader.fVertResolution := WORD(72); // WORD (15,16)
FillChar(fPCXFile.fPCXHeader.fColorMap, 48, 0); // Array of Byte
// (17..64)
fPCXFile.fPCXHeader.fReserved := BYTE(0); // BYTE (65)
fPCXFile.fPCXHeader.fPlanes := BYTE(byPlanes);
// BYTE (66)
fPCXFile.fPCXHeader.fBytesPerLine := WORD(wBytesPerLine);
// WORD (67,68)
// must be even
// rounded above
fPCXFile.fPCXHeader.fPaletteInfo := WORD(1); // WORD (69,70)
FillChar(fPCXFile.fPCXHeader.fFiller, 58, 0); // Array of Byte
// (71..128)
fPCXFile.fPixelFormat := fPCXFile.fPCXHeader.fPlanes *
fPCXFile.fPCXHeader.fBitsPerPixel;
fPCXFile.fColorDepth := 1 shl fPCXFile.fPixelFormat;
end;
//---------------------------------------------------------------------
(*
// From Delphi 5.0, graphics.pas
Function CopyPalette(Palette: HPALETTE): HPALETTE;
Var
PaletteSize : Integer;
LogPal : TMaxLogPalette;
Begin
Result := 0;
If Palette = 0 then
Exit;
PaletteSize := 0;
If GetObject(Palette,SizeOf(PaletteSize),@PaletteSize) = 0 then
Exit;
If PaletteSize = 0 then
Exit;
With LogPal do
Begin
palVersion := $0300;
palNumEntries := PaletteSize;
GetPaletteEntries(Palette,0,PaletteSize,palPalEntry);
End;
Result := CreatePalette(PLogPalette(@LogPal)^);
End;
*)
//---------------------------------------------------------------------
// From Delphi 5.0, graphics.pas
(*
Procedure TPCXImage.fSetPixelFormat(Value : TPixelFormat);
Const
BitCounts : Array [pf1Bit..pf32Bit] of BYTE = (1,4,8,16,16,24,32);
Var
DIB : TDIBSection;
Pal : HPALETTE;
DC : hDC;
KillPal : Boolean;
Begin
If Value = GetPixelFormat then
Exit;
Case Value of
pfDevice : Begin
HandleType := bmDDB;
Exit;
End;
pfCustom : InvalidGraphic(@SInvalidPixelFormat);
else
FillChar(DIB,sizeof(DIB), 0);
DIB.dsbm := FImage.FDIB.dsbm;
KillPal := False;
With DIB, dsbm,dsbmih do
Begin
bmBits := nil;
biSize := SizeOf(DIB.dsbmih);
biWidth := bmWidth;
biHeight := bmHeight;
biPlanes := 1;
biBitCount := BitCounts[Value];
Pal := FImage.FPalette;
Case Value of
pf4Bit : Pal := SystemPalette16;
pf8Bit : Begin
DC := GDICheck(GetDC(0));
Pal := CreateHalftonePalette(DC);
KillPal := True;
ReleaseDC(0, DC);
End;
pf16Bit : Begin
biCompression := BI_BITFIELDS;
dsBitFields[0] := $F800;
dsBitFields[1] := $07E0;
dsBitFields[2] := $001F;
End;
End; // of Case
Try
CopyImage(Handle, Pal, DIB);
PaletteModified := (Pal <> 0);
Finally
if KillPal then
DeleteObject(Pal);
End; // of Try
Changed(Self);
End; // of With
End; // of Case
End; // of Procedure
*)
//---------------------------------------------------------------------
procedure TPCXImage.fSetPalette(const wNumColors: WORD);
(* From Delphi 5.0, graphics.pas
Type
TPalEntry = packed record
peRed : BYTE;
peGreen : BYTE;
peBlue : BYTE;
End;
Type
tagLOGPALETTE = packed record
palVersion : WORD;
palNumEntries : WORD;
palPalEntry : Array[0..255] of TPalEntry
End;
Type
TMAXLogPalette = tagLOGPALETTE;
PMAXLogPalette = ^TMAXLogPalette;
Type
PRGBQuadArray = ^TRGBQuadArray;
TRGBQuadArray = Array[BYTE] of TRGBQuad;
Type
PRGBQuadArray = ^TRGBQuadArray;
TRGBQuadArray = Array[BYTE] of TRGBQuad;
*)
var
pal: TMaxLogPalette;
W: WORD;
begin
pal.palVersion := $300; // The "Magic" number
pal.palNumEntries := wNumColors;
for W := 0 to 255 do
begin
pal.palPalEntry[W].peRed :=
fPCXFile.fPCXPalette.fPalette[W].ceRed;
pal.palPalEntry[W].peGreen :=
fPCXFile.fPCXPalette.fPalette[W].ceGreen;
pal.palPalEntry[W].peBlue :=
fPCXFile.fPCXPalette.fPalette[W].ceBlue;
pal.palPalEntry[W].peFlags := 0;
end;
(* Must we delete the old palette first here? I dont know.
If fhPAL <> 0 then
DeleteObject(fhPAL);
*)
fhPAL := CreatePalette(PLogPalette(@pal)^);
if fhPAL <> 0 then
fBitmap.Palette := fhPAL;
end;
//---------------------------------------------------------------------
function TPCXImage.fGetPixelFormat: TPixelFormat;
// Only pf1bit, pf4bit and pf8bit images have a palette.
// pf15bit, pf16bit, pf24bit and pf32bit images have no palette.
// You can change the palette of pf1bit images in windows.
// The foreground color and the background color of pf1bit images
// do not have to be black and white. You can choose any tow colors.
// The palette of pf4bit images is fixed.
// The palette entries 0..9 and 240..255 of pf8bit images are reserved
// in windows.
begin
Result := pfDevice;
case fPCXFile.fPixelFormat of
01: Result := pf1bit; // Implemented WITH palette.
// 04 : Result := pf4bit; // Not yet implemented in this component,
// is however implemented in PCX format.
08: Result := pf8bit; // Implemented WITH palette.
// 15 : Result := pf15bit; // Not implemented in PCX format?
// 16 : Result := pf16bit; // Not implemented in PCX format?
24: Result := pf24bit; // Implemented, has no palette.
// 32 : Result := pf32bit; // Not implemented in PCX format.
end;
end;
//---------------------------------------------------------------------
procedure TPCXImage.fGetPalette(const wNumColors: WORD);
var
pal: TMaxLogPalette;
W: WORD;
begin
fPCXFile.fPCXPalette.fSignature := $0C;
pal.palVersion := $300; // The "Magic" number
pal.palNumEntries := wNumColors;
GetPaletteEntries(CopyPalette(fBitmap.Palette), 0, wNumColors,
pal.palPalEntry);
for W := 0 to 255 do
if W < wNumColors then
begin
fPCXFile.fPCXPalette.fPalette[W].ceRed :=
pal.palPalEntry[W].peRed;
fPCXFile.fPCXPalette.fPalette[W].ceGreen :=
pal.palPalEntry[W].peGreen;
fPCXFile.fPCXPalette.fPalette[W].ceBlue :=
pal.palPalEntry[W].peBlue;
end
else
begin
fPCXFile.fPCXPalette.fPalette[W].ceRed := 0;
fPCXFile.fPCXPalette.fPalette[W].ceGreen := 0;
fPCXFile.fPCXPalette.fPalette[W].ceBlue := 0;
end;
end;
//=====================================================================
///////////////////////////////////////////////////////////////////////
// //
// TPCXFile //
// //
///////////////////////////////////////////////////////////////////////
constructor TPCXFile.Create;
begin
inherited Create;
fHeight := 0;
fWidth := 0;
fCurrentPos := 0;
end;
//---------------------------------------------------------------------
destructor TPCXFile.Destroy;
begin
SetLength(fPCXData.fData, 0);
inherited Destroy;
end;
//---------------------------------------------------------------------
procedure TPCXFile.LoadFromFile(const Filename: string);
var
fPCXStream: TFileStream;
begin
fPCXStream := TFileStream.Create(Filename, fmOpenRead);
try
fPCXStream.Position := 0;
LoadFromStream(fPCXStream);
finally
fPCXStream.Free;
end;
end;
//---------------------------------------------------------------------
procedure TPCXFile.SaveToFile(const Filename: string);
var
fPCXStream: TFileStream;
begin
fPCXStream := TFileStream.Create(Filename, fmCreate);
try
fPCXStream.Position := 0;
SaveToStream(fPCXStream);
finally
fPCXStream.Free;
end;
end;
//---------------------------------------------------------------------
procedure TPCXFile.LoadFromStream(Stream: TStream);
var
fFileLength: Cardinal;
begin
// Read the PCX header
Stream.Read(fPCXHeader, SizeOf(fPCXHeader));
// Check the ID byte
if fPCXHeader.fID <> $0A then
raise Exception.Create(FORMAT_ERROR);
(*
Check PCX version byte
======================
Versionbyte = 0 => PC PaintBrush V2.5
Versionbyte = 2 => PC Paintbrush V2.8 with palette information
Versionbyte = 3 => PC Paintbrush V2.8 without palette information
Versionbyte = 4 => PC Paintbrush for Windows
Versionbyte = 5 => PC Paintbrush V3 and up, and PC Paintbrush Plus
with 24 bit image support
*)
// Check the PCX version
if fPCXHeader.fVersion <> 5 then
raise Exception.Create(VERSION_ERROR);
// Calculate width
fWidth := fPCXHeader.fWindow.wRight - fPCXHeader.fWindow.wLeft + 1;
if fWidth < 0 then
raise Exception.Create(WIDTH_OUT_OF_RANGE);
// Calculate height
fHeight := fPCXHeader.fWindow.wBottom - fPCXHeader.fWindow.wTop + 1;
if fHeight < 0 then
raise Exception.Create(HEIGHT_OUT_OF_RANGE);
// Is it too large?
if fWidth > fMaxImageWidth then
raise Exception.Create(IMAGE_WIDTH_TOO_LARGE);
// Calculate pixelformat
fPixelFormat := fPCXHeader.fPlanes * fPCXHeader.fBitsPerPixel;
// Calculate number of colors
fColorDepth := 1 shl fPixelFormat;
// Is this image supported?
if not (fPixelFormat in [1, 8, 24]) then
raise Exception.Create(ERROR_UNSUPPORTED);
// The lines following are NOT tested!!!
(*
If fColorDepth <= 16 then
For I := 0 to fColorDepth - 1 do
Begin
If fPCXHeader.fVersion = 3 then
Begin
fPCXPalette.fPalette[I].R := fPCXHeader.fColorMap[I].R shl 2;
fPCXPalette.fPalette[I].G := fPCXHeader.fColorMap[I].G shl 2;
fPCXPalette.fPalette[I].B := fPCXHeader.fColorMap[I].B shl 2;
End
else
Begin
fPCXPalette.fPalette[I].R := fPCXHeader.fColorMap[I].R;
fPCXPalette.fPalette[I].G := fPCXHeader.fColorMap[I].G;
fPCXPalette.fPalette[I].B := fPCXHeader.fColorMap[I].B;
End;
End;
*)
// Calculate number of data bytes
// If fFileLength > fMaxDataFileLength then
// Raise Exception.Create(INPUT_FILE_TOO_LARGE);
if fPixelFormat = 24 then
begin
fFileLength := Stream.Size - Stream.Position;
SetLength(fPCXData.fData, fFileLength);
// Read the data
Stream.Read(fPCXData.fData[0], fFileLength);
fHasPalette := False;
end;
if fPixelFormat in [1, 8] then
begin
fFileLength := Stream.Size - Stream.Position - 769;
SetLength(fPCXData.fData, fFileLength);
// Correct number of data bytes
Stream.Read(fPCXData.fData[0], fFilelength);
// Read the palette
Stream.Read(fPCXPalette, SizeOf(fPCXPalette));
fHasPalette := True;
// Check palette signature byte
if fPCXPalette.fSignature <> $0C then
raise Exception.Create(PALETTE_ERROR);
end;
end;
//---------------------------------------------------------------------
procedure TPCXFile.SaveToStream(Stream: TStream);
begin
fHasPalette := False;
Stream.Write(fPCXHeader, SizeOf(fPCXHeader));
Stream.Write(fPCXData.fData[0], fCurrentPos);
if fPixelFormat in [1, 8] then
begin
Stream.Write(fPCXPalette, SizeOf(fPCXPalette));
fHasPalette := True;
end;
end;
//---------------------------------------------------------------------
// Register PCX format
initialization
TPicture.RegisterFileFormat('PCX', sPCXImageFile, TPCXImage);
CF_PCX := RegisterClipBoardFormat('PCX Image');
TPicture.RegisterClipBoardFormat(CF_PCX, TPCXImage);
//---------------------------------------------------------------------
// Unregister PCX format
finalization
TPicture.UnRegisterGraphicClass(TPCXImage);
//---------------------------------------------------------------------
end.
//=====================================================================
Взято с
Delphi Knowledge BaseКомпонент FontListBox
Компонент FontListBox
Автор: Maarten de Haan
Надеюсь, что любители Delphi уже не один раз приукрашивали всякие ЛистБоксы и тому подобное. Автор исходника предлагает создать этот компонент своими силами. Впрочем, Вы сами можете увидеть как можно играться со шрифтами в ListBox.
{
==================================================================
Написан в Delphi V5.0.
Тестировался под: Windows 95, version A, servicepack 1
и Windows NT4.0, servicepack 5.
===================================================================
}
Unit FontListBox;
Interface
Uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
Type
TFontListBox = class(TCustomListbox)
Private
{ Private declarations }
fFontSample : Boolean; // Добавляемое свойство
fShowTrueType : Boolean; // Добавляемое свойство
fCanvas : TControlCanvas; // Необходимо
Procedure SetFontSample(B : Boolean); // внутренняя процедура
Procedure SetShowTrueType(B : Boolean); // внутренняя процедура
Protected
{ Protected declarations }
Procedure CreateWnd; override;
Public
{ Public declarations }
Constructor Create(AOwner : TComponent); override;
Destructor Destroy; override;
Procedure DrawItem(Index : Integer; R : TRect;
State : TOwnerDrawState); override;
Published
{ Published declarations }
{ Properties }
Property Fontsample : Boolean // Добавляемое свойство
Read fFontSample Write SetFontSample;
Property Align;
Property Anchors;
Property BiDiMode;
Property BorderStyle;
Property Color;
Property Columns;
Property Constraints;
Property Cursor;
Property DragCursor;
Property DragKind;
Property DragMode;
Property Enabled;
//Poperty ExtendedSelection; Не существует в базовом классе
Property Font;
Property Height;
Property HelpContext;
Property Hint;
Property ImeMode;
Property ImeName;
Property IntegralHeight;
Property Itemheight;
Property Items;
Property Left;
Property MultiSelect;
Property Name;
Property ParentBiDiMode;
Property ParentColor;
Property ParentFont;
Property ParentShowHint;
Property PopupMenu;
Property ShowTrueType : Boolean // Добавляемое свойство
Read fShowTrueType Write SetShowTrueType;
Property ShowHint;
Property Sorted;
Property Style;
Property TabOrder;
Property TabStop;
Property TabWidth;
Property Tag;
Property Top;
Property Visible;
Property Width;
{ Events }
Property OnClick;
Property OnContextPopup;
Property OnDblClick;
Property OnDragDrop;
Property OnDragOver;
Property OnDrawItem;
Property OnEndDock;
Property OnEnter;
Property OnExit;
Property OnKeyDown;
Property OnKeyPress;
Property OnKeyUp;
Property OnMeasureItem;
Property OnMouseDown;
Property OnMouseMove;
Property OnMouseUp;
Property OnStartDock;
Property OnStartDrag;
End;
Procedure Register;
Implementation
{--------------------------------------------------------------------}
Procedure Register; // Hello
Begin
RegisterComponents('Samples', [TFontListBox]);
End;
{--------------------------------------------------------------------}
Procedure TFontListBox.SetShowTrueType(B : Boolean);
Begin
If B <> fShowTrueType then
Begin
fShowTrueType := B;
Invalidate; // Заставляет апдейтится во время прорисовки
End;
End;
{--------------------------------------------------------------------}
Procedure TFontListBox.SetFontSample(B : Boolean);
Begin
If fFontSample <> B then
Begin
fFontSample := B;
Invalidate; // Заставляет апдейтится во время прорисовки
End;
End;
{--------------------------------------------------------------------}
Destructor TFontListBox.Destroy;
Begin
fCanvas.Free; // освобождает холст
Inherited Destroy;
End;
{-----------------------------------------------------------------------}
Constructor TFontListBox.Create(AOwner : TComponent);
Begin
Inherited Create(AOwner);
// Initialize properties
ParentFont := True;
Font.Size := 8;
Font.Style := [];
Sorted := True;
fFontSample := False;
Style := lbOwnerDrawFixed;
fCanvas := TControlCanvas.Create;
fCanvas.Control := Self;
ItemHeight := 16;
fShowTrueType := False;
End;
{--------------------------------------------------------------------}
procedure TFontListBox.CreateWnd;
Begin
inherited CreateWnd;
Items := Screen.Fonts; // Копируем все шрифты в ListBox.Items
ItemIndex := 0; // Выбираем первый фонт
End;
{--------------------------------------------------------------------}
procedure TFontListBox.DrawItem(Index : Integer; R : TRect;
State : TOwnerDrawState);
Var
Metrics : TTextMetric;
LogFnt : TLogFont;
oldFont,newFont : HFont;
IsTrueTypeFont : Boolean;
fFontStyle : TFontStyles;
fFontName : TFontName;
fFontColor : TColor;
Begin
LogFnt.lfHeight := 10;
LogFnt.lfWidth := 10;
LogFnt.lfEscapement := 0;
LogFnt.lfWeight := FW_REGULAR;
LogFnt.lfItalic := 0;
LogFnt.lfUnderline := 0;
LogFnt.lfStrikeOut := 0;
LogFnt.lfCharSet := DEFAULT_CHARSET;
LogFnt.lfOutPrecision := OUT_DEFAULT_PRECIS;
LogFnt.lfClipPrecision := CLIP_DEFAULT_PRECIS;
LogFnt.lfQuality := DEFAULT_QUALITY;
LogFnt.lfPitchAndFamily := DEFAULT_PITCH or FF_DONTCARE;
StrPCopy(LogFnt.lfFaceName,Items[Index]);
newFont := CreateFontIndirect(LogFnt);
oldFont := SelectObject(fCanvas.Handle,newFont);
GetTextMetrics(fCanvas.Handle,Metrics);
// Теперь вы можете проверить на TrueType-ность
IsTrueTypeFont := True;
If (Metrics.tmPitchAndFamily and TMPF_TRUETYPE) = 0 then
IsTrueTypeFont := False;
Canvas.FillRect(R);
If fShowTrueType and IsTrueTypeFont then
Begin
// Записываем параметры шрифтов
fFontName := Canvas.Font.Name;
fFontStyle := Canvas.Font.Style;
fFontColor := Canvas.Font.Color;
// Устанавливаем новые параметры шрифтов
Canvas.Font.Name := 'Times new roman';
Canvas.Font.Style := [fsBold];
//Canvas.Font.Color := clBlack;
Canvas.TextOut(R.Left + 2,R.Top,'T');
If fFontColor <> clHighLightText then
Canvas.Font.Color := clGray;
Canvas.TextOut(R.Left + 7,R.Top + 3,'T');
//Восстанавливаем параметры шрифтов
Canvas.Font.Style := fFontStyle;
Canvas.Font.Color := fFontColor;
Canvas.Font.Name := fFontName;
End;
If fFontSample then
// Шрифт будет прорисован фактически как шрифт
Canvas.Font.Name := Items[Index]
else
// Шрифт будет прорисован в свойстве "Font"
Canvas.Font.Name := Font.Name;
If fShowTrueType then
Canvas.TextOut(R.Left + 20,R.Top,Items[Index]) // Показывать TrueType
else
Canvas.TextOut(R.Left,R.Top,Items[Index]); // Не показывать TrueType
SelectObject(fCanvas.Handle,oldFont);
DeleteObject(newFont);
End;
{--------------------------------------------------------------------}
End.
{====================================================================}
Взято с Исходников.ru
Компонент PowerControl
Компонент PowerControl
Вопрос: А как реализовать в одном компоненте такие функции как выключение компьютера, перезагрузка, завершение сеанса работы пользователя, Eject CD, выключение питания монитора и т.д.? Ответ: предлагаем посмотреть следующий пример ...
procedure TForm1.Button1Click(Sender: TObject);
begin
PowerControl1.Action:=actCDEject;// Или... actLogOFF, actShutDown...
PowerControl1.Execute;
end;
=========================
Component Code:
unit PowerControl;
interface
uses WinTypes, WinProcs, Messages, SysUtils, Classes, Controls,
Forms, Graphics, MMSystem;
type
TAction = (actLogOFF,actShutDown,actReBoot,actForce,actPowerOFF,
actForceIfHung,actMonitorOFF,actMonitorON,actCDEject,actCDUnEject);
type
TPowerControl = class(TComponent)
private
FAction : TAction;
procedure SetAction(Value : TAction);
protected
public
function Execute : Boolean;
published
property Action : TAction read FAction write SetAction;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('K2', [TPowerControl]);
end;
procedure TPowerControl.SetAction(Value : TAction);
begin
FAction := Value;
end;
function TPowerControl.Execute : Boolean;
begin
with (Owner as TForm) do
case FAction of
actLogOff: ExitWindowsEx(EWX_LOGOFF,1);
actShutDown: ExitWindowsEx(EWX_SHUTDOWN,1);
actReBoot: ExitWindowsEx(EWX_REBOOT,1);
actForce: ExitWindowsEx(EWX_FORCE,1);
actPowerOff: ExitWindowsEx(EWX_POWEROFF,1);
actForceIfHung: ExitWindowsEx(EWX_FORCEIFHUNG,1);
actMonitorOFF: SendMessage(Application.Handle,
WM_SYSCOMMAND, SC_MONITORPOWER, 0);
actMonitorON: SendMessage(Application.Handle, WM_SYSCOMMAND,
SC_MONITORPOWER, -1);
actCDEject: mciSendstring('SET CDAUDIO DOOR OPEN
WAIT',nil,0, Handle);
actCDUnEject: mciSendstring('SET CDAUDIO DOOR CLOSED
WAIT',nil,0, Handle);
end; {Case}
Result := True;
end;
end.
Взято с Исходников.ru
Компонент TStoredProc
компонент TStoredProc
Недавно я перешел на использование Oracle, но все мои попытки использовать компонент TStoredProc оказываются неудачными. Почему?
Причины неработоспособности компонента TStoredProc могут быть следующими.
Во-первых, при использовании ODBC-доступа может оказаться, что применяемый вами ODBC-драйвер не поддерживает хранимые процедуры (как известно, не все ODBC-драйверы их поддерживают).
Во-вторых, имеется известная проблема, описание которой содержится в разделе Developers Support корпоративного сайта Inprise (http://www.inprise.com). Дело в том, что число параметров хранимой процедуры, с которой взаимодействует компонент TStoredProc, не должно превышать 10. В случае, если реальное число параметров превышает 10, многие разработчики переписывают хранимые процедуры так, чтобы они использовали строковые параметры, содержащие по несколько реальных параметров.
--------------------------------------------------------------------------------
Взято из
Конфигурация и настройка
Конфигурация и настройка
Each function listed below returns information about the client application environment, such as the supported table, field and index types for the driver type, or the available driver types. Functions in this category can also perform tasks that affect the client application environment, such as loading a driver.
DbiAddAlias:
Adds an alias to the BDE configuration file (IDAPI.CFG).
DbiAddDriver:
Adds a driver to the BDE configuration file (IDAPI.CFG). NEW FUNCTION BDE 4.0
DbiAnsiToNative:
Multipurpose translate function.
DbiDebugLayerOptions:
Activates, deactivates, or sets options for the BDE debug layer. OBSOLETE FUNCTION BDE 4.0
DbiDeleteAlias:
Deletes an alias from the BDE configuration file (IDAPI.CFG).
DbiDeleteDriver:
Deletes a driver from the BDE configuration file (IDAPI.CFG). NEW FUNCTION BDE 4.0
DbiDllExit:
Prepares the BDE to be disconnected within a DLL. NEW FUNCTION BDE 4.0
DbiExit:
Disconnects the client application from BDE.
DbiGetClientInfo:
Retrieves system-level information about the client application environment.
DbiGetDriverDesc:
Retrieves a description of a driver.
DbiGetLdName:
Retrieves the name of the language driver associated with the specified object name (table name).
DbiGetLdObj:
Retrieves the language driver object associated with the given cursor.
DbiGetNetUserName:
Retrieves the user's network login name. User names should be available for all networks
supported by Microsoft Windows.
DbiGetProp:
Returns a property of an object.
DbiGetSysConfig:
Retrieves BDE system configuration information.
DbiGetSysInfo:
Retrieves system status and information.
DbiGetSysVersion:
Retrieves the system version information, including the BDE version number, date, and time,
and the client interface version number.
DbiInit:
Initializes the BDE environment.
DbiLoadDriver:
Load a given driver.
DbiNativeToAnsi:
Translates a string in the native language driver to an ANSI string.
DbiOpenCfgInfoList:
Returns a handle to an in-memory table listing all the nodes in the configuration file
accessible by the specified path.
DbiOpenDriverList:
Creates an in-memory table containing a list of driver names available to the client application.
DbiOpenFieldTypesList:
Creates an in-memory table containing a list of field types supported by the table type for
the driver type.
DbiOpenFunctionArgList:
Returns a list of arguments to a data source function.
DbiOpenFunctionList:
Returns a description of a data source function.
DbiOpenIndexTypesList:
Creates an in-memory table containing a list of all supported index types for the driver type.
DbiOpenLdList:
Creates an in-memory table containing a list of available language drivers.
DbiOpenTableList:
Creates an in-memory table with information about all the tables accessible to the client application.
DbiOpenTableTypesList:
Creates an in-memory table listing table type names for the given driver.
DbiOpenUserList:
Creates an in-memory table containing a list of users sharing the same network file.
DbiSetProp:
Sets the specified property of an object to a given value.
DbiUseIdleTime:
Allows BDE to accomplish background tasks during times when the client application is idle.
OBSOLETE FUNCTION BDE 4.0
Взято с
Delphi Knowledge BaseКонфигурация железа
Конфигурация железа
Cодержание раздела:
См. также статьи в других разделах:
Конфигурирование ODBC
Конфигурирование ODBC
Автор: Mark Nelson (Delphi Tech Support)
Представляю вашему вниманию инструкцию по конфигурированию ODBC и источника данных.
Для того, чтобы Borland Database Engine мог пользоваться драйверами ODBC, они должны быть сконфигурированы следующим образом:
Инсталлируйте драйвер и установите его в Панель Управления/ODBC administrator.
Если это файл базового драйвера типа dBase, Paradox, Excel, FoxPro и др., то вы должны указать каталог, содержащий ваши файлы.
Access и Local Btrieve файлы должны указывать на конкретный файл базы данных (Btreive на File.ddf, Access должен указывать на файл с расширением .MDB).
Если это линк (connection) к базе данных, например Oracle, Sybase, Interbase, и др., вы должны указать на этот линк. Обычно транспорт посредством TCPIP, SPX/IPX, NetBEUI и т.п. предусматривается поставщиком программного обеспечения. Каждый драйвер работает с транспортным протоколом по-своему.
Запустите Database Engine Configuration Utility. (BDECFG.EXE)
Выберите New ODBC Driver.
Задайте имя вашему драйверу (к имени автоматически добавится "ODBC_".)
Выберите ваш ODBC Driver из списка Default ODBC Driver List.
Выберите имя источника данных по умолчанию.
Создайте псевдоним (alias), указывающий на установленный вами драйвер.
Установите ссылку с именем псевдонима на ваш драйвер.
Примечание относительно баз данных Access 2.0: вам необходимо иметь самые последние драйверы от Microsoft. Имеющиеся драйверы позволяют работать только с базами данных Access 1.0, не выше.
Стоимость нового набора драйверов у самой Microsoft составляет $10, что эквивалентно цене носителя и пересылки.
Для работы с Access 2.0 вам необходимо использовать источник данных с именем "Microsoft Access Driver" или "Microsoft Access 2.0 Databases".
Имейте в виду, что "Access Data" и "Access Files" по умолчанию являются источниками данных только для Access 1.0. Поэтому для получения доступа к MDB-файлам Access 1.0 пользуйтесь "Access Data".
Btrieve также создает источник данных по умолчанию "Btrieve Data" и "Btrieve Files". Используйте "Btrieve Data" как ваш источник данных по умолчанию. Драйверы Btrieve позволяют использовать данные btrieve вплоть до версии 5.0. Для работы с Btrive данными более новой версии, необходим новый набор драйверов Microsoft или Intersolve Q+E.
Взято с
Конфигурирование ODBC и псевдонима
Конфигурирование ODBC и псевдонима
Настройка ODBC в Панели Управления
При инсталляции, Delphi устанавливает в Панель Управления апплет "ODBC" (далее - "настройка ODBC"). "Настройка ODBC" содержит доступные источники данных (драйвера), установленных для использования ODBC. Как вы можете видеть на главной странице "Data Sources", ODBC содержит внушительный набор форматов, которые могут использоваться в Delphi. Дополнительные форматы поддерживаются установленными драйверами и могут быть добавлены с помощью кнопки "Add...".
Для добавления нового или удаления существующего драйвера:
В окне "Data Sources" нажмите кнопку "Drivers...". В появившемся диалоговом окне "Drivers" нажмите кнопку "Add..." и укажите путь к новому драйверу ODBC.
Возвратитесь в окно "Data Sources" и добавьте доступные с новым драйвером источники данных с помощью кнопки "Add...".
Для настройки конкретного источника данных используйте кнопку "Setup...". Функция кнопки "Setup..." меняется с каждым форматом данных. Частенько настройки типа рабочей директории для драйвера настраиваются как раз в этом месте.
Разделы электронной справки доступны для каждой страницы и диалогового окна "Настроки ODBC".
BDE CONFIGURATION UTILITY
После установки драйвера ODBC, запустите BDE Configuration utility для конфигурации BDE для работы с новым драйвером.
На странице драйверов нажмите на кнопку "New ODBC driver".
Появится диалог с заголовком "Add ODBC driver". Опция "SQL link driver" позволяет выяснить, с какими типами баз данных можно работать с помощью данного драйвера ODBC.
Затем выбирайте default ODBC driver (драйвер ODBC по-умолчанию). Выпадающий список содержит список типов файлов, поддерживаемых установленными в системе драйверами ODBC.
Выберите для ODBC-драйвера источник данных по-умолчанию (default data source). Имея уже установленный на шаге 3 драйвер ODBC, список этого combobox'а будет содержать имена источников данных, подходящих для использования с выбранным драйвером.
Нажмите Ok.
Возвратитесь на страницу драйверов, выберите File/Save из главного меню и сохраните данную конфигурацию.
Создание псевдонима в DATABASE DESKTOP
Хотя создать псевдоним ODBC можно и из BDE Configuration utility, Database Desktop предоставляет более комфортное решение.
В меню "File" выберите пункт "Aliases..".
В появившемся диалоге "Alias Manager" нажмите кнопку "New".
Введите имя вашего нового псевдонима в поле редактирования, помеченной как "Database Alias".
Используя выпадающий список "Driver Type" (типы драйверов), выберите драйвер, подходящий для данного псевдонима. Таблицы Paradox и dBase считаются STANDARD. Если в BDE Configuration utility драйвер ODBC был правильно сконфигурирован, то его имя появится в списке.
Дополнительные опции опции могут появляться в зависимости от выбранного типа драйвера.
После завершения всех описанных действий сохраните новый псевдоним, выбрав "Keep New". Затем нажмите "Ok". Появится подсказка, спрашивающая о необходимости сохранения псевдонима в IDAPI.CFG. Выберите "Ok".
Теперь псевдоним будет работать и в Database Desktop, и в Delphi.
Взято с
Коннект к серверу и загрузка файла (используя wininet.dll)
Коннект к серверу и загрузка файла (используя wininet.dll)
uses
WinInet, ComCtrls;
function FtpDownloadFile(strHost, strUser, strPwd: string;
Port: Integer; ftpDir, ftpFile, TargetFile: string; ProgressBar: TProgressBar):
Boolean;
function FmtFileSize(Size: Integer): string;
begin
if Size >= $F4240 then
Result := Format('%.2f', [Size / $F4240]) + ' Mb'
else if Size < 1000 then
Result := IntToStr(Size) + ' bytes'
else
Result := Format('%.2f', [Size / 1000]) + ' Kb';
end;
const
READ_BUFFERSIZE = 4096; // or 256, 512, ...
var
hNet, hFTP, hFile: HINTERNET;
buffer: array[0..READ_BUFFERSIZE - 1] of Char;
bufsize, dwBytesRead, fileSize: DWORD;
sRec: TWin32FindData;
strStatus: string;
LocalFile: file;
bSuccess: Boolean;
begin
Result := False;
{ Open an internet session }
hNet := InternetOpen('Program_Name', // Agent
INTERNET_OPEN_TYPE_PRECONFIG, // AccessType
nil, // ProxyName
nil, // ProxyBypass
0); // or INTERNET_FLAG_ASYNC / INTERNET_FLAG_OFFLINE
{
Agent contains the name of the application or
entity calling the Internet functions
}
{ See if connection handle is valid }
if hNet = nil then
begin
ShowMessage('Unable to get access to WinInet.Dll');
Exit;
end;
{ Connect to the FTP Server }
hFTP := InternetConnect(hNet, // Handle from InternetOpen
PChar(strHost), // FTP server
port, // (INTERNET_DEFAULT_FTP_PORT),
PChar(StrUser), // username
PChar(strPwd), // password
INTERNET_SERVICE_FTP, // FTP, HTTP, or Gopher?
0, // flag: 0 or INTERNET_FLAG_PASSIVE
0); // User defined number for callback
if hFTP = nil then
begin
InternetCloseHandle(hNet);
ShowMessage(Format('Host "%s" is not available', [strHost]));
Exit;
end;
{ Change directory }
bSuccess := FtpSetCurrentDirectory(hFTP, PChar(ftpDir));
if not bSuccess then
begin
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
ShowMessage(Format('Cannot set directory to %s.', [ftpDir]));
Exit;
end;
{ Read size of file }
if FtpFindFirstFile(hFTP, PChar(ftpFile), sRec, 0, 0) <> nil then
begin
fileSize := sRec.nFileSizeLow;
// fileLastWritetime := sRec.lastWriteTime
end
else
begin
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
ShowMessage(Format('Cannot find file ', [ftpFile]));
Exit;
end;
{ Open the file }
hFile := FtpOpenFile(hFTP, // Handle to the ftp session
PChar(ftpFile), // filename
GENERIC_READ, // dwAccess
FTP_TRANSFER_TYPE_BINARY, // dwFlags
0); // This is the context used for callbacks.
if hFile = nil then
begin
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
Exit;
end;
{ Create a new local file }
AssignFile(LocalFile, TargetFile);
{$I-}
Rewrite(LocalFile, 1);
{$I+}
if IOResult <> 0 then
begin
InternetCloseHandle(hFile);
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
Exit;
end;
dwBytesRead := 0;
bufsize := READ_BUFFERSIZE;
while (bufsize > 0) do
begin
Application.ProcessMessages;
if not InternetReadFile(hFile,
@buffer, // address of a buffer that receives the data
READ_BUFFERSIZE, // number of bytes to read from the file
bufsize) then
Break; // receives the actual number of bytes read
if (bufsize > 0) and (bufsize <= READ_BUFFERSIZE) then
BlockWrite(LocalFile, buffer, bufsize);
dwBytesRead := dwBytesRead + bufsize;
{ Show Progress }
ProgressBar.Position := Round(dwBytesRead * 100 / fileSize);
Form1.Label1.Caption := Format('%s of %s / %d %%', [FmtFileSize(dwBytesRead),
FmtFileSize(fileSize), ProgressBar.Position]);
end;
CloseFile(LocalFile);
InternetCloseHandle(hFile);
InternetCloseHandle(hFTP);
InternetCloseHandle(hNet);
Result := True;
end;
Взято с
Delphi Knowledge BaseКонсольные приложения
Консольные приложения
Cодержание раздела:
Контрольные суммы и шифрование, проверка контрольных сумм
Контрольные суммы и шифрование, проверка контрольных сумм
Cодержание раздела:
См. также статьи в других разделах:
Конвертация арабских цифр в римские
Конвертация арабских цифр в римские
functionIntToRoman(num: Cardinal): String; {returns num in capital roman digits}
const
Nvals = 13;
vals: array [1..Nvals] of word = (1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000);
roms: array [1..Nvals] of string[2] = ('I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M');
var
b: 1..Nvals;
begin
result := '';
b := Nvals;
while num > 0 do
begin
while vals[b] > num do
dec(b);
dec (num, vals[b]);
result := result + roms[b]
end;
end;
Взято из
Конвертация римских цифр в арабские
Конвертация римских цифр в арабские
functionRomanToDec(const Value: string): integer;
var
i, lastValue, curValue: integer;
begin
Result := 0;
lastValue := 0;
for i := Length(Value) downto 1 do
begin
case UpCase(Value[i]) of
'C':
curValue := 100;
'D':
curValue := 500;
'I':
curValue := 1;
'L':
curValue := 50;
'M':
curValue := 1000;
'V':
curValue := 5;
'X':
curValue := 10;
else
raise Exception.CreateFmt('Invalid character: %s', [Value[i]]);
end;
if curValue < lastValue then
Dec(Result, curValue)
else
Inc(Result, curValue);
lastValue := curValue;
end;
end;
Взято с
Delphi Knowledge BaseКонвертировать INI файл в XML
Конвертировать INI файл в XML
usesXMLIntf, XMLDoc, INIFiles;
procedure INI2XML(const pINIFileName: string; const pXML: IXMLNode;
const AsAttributes: Boolean = true);
var
lINIFile: TIniFile;
lSections, lItems: TStringList;
iSections, iItems: integer;
lNode: IXMLNode;
begin
lINIFile := TIniFile.Create(pINIFileName);
try
lSections := TStringList.Create;
try
lItems := TStringList.Create;
try
lINIFile.ReadSections(lSections);
for iSections := 0 to pred(lSections.Count) do
begin
lItems.Clear;
lINIFile.ReadSection(lSections[iSections], lItems);
lNode := pXML.AddChild(StringReplace(lSections[iSections], ' ', '',
[rfReplaceAll]));
for iItems := 0 to pred(lItems.Count) do
begin
if AsAttributes then
lNode.Attributes[lItems[iItems]] :=
lINIFile.ReadString(lSections[iSections], lItems[iItems], '')
else
lNode.AddChild(lItems[iItems]).Text :=
lINIFile.ReadString(lSections[iSections], lItems[iItems], '');
end;
lNode := nil;
end;
finally lItems.Free;
end;
finally lSections.Free;
end;
finally lINIFile.Free;
end;
end;
Взято с
Delphi Knowledge BaseКонвертировать результат запроса в XML и обратно
Конвертировать результат запроса в XML и обратно
unitADOXMLUnit;
interface
uses
Classes, ADOInt;
function RecordsetToXML(const Recordset: _Recordset): string;
function RecordsetFromXML(const XML: string): _Recordset;
implementation
uses
ComObj;
function RecordsetToXML(const Recordset: _Recordset): string;
var
RS: Variant;
Stream: TStringStream;
begin
Result := '';
if Recordset = nil then
Exit;
Stream := TStringStream.Create('');
try
RS := CreateOleObject('ADODB.Recordset');
RS := Recordset;
RS.Save(TStreamAdapter.Create(stream) as IUnknown, adPersistXML);
Stream.Position := 0;
Result := Stream.DataString;
finally
Stream.Free;
end;
end;
function RecordsetFromXML(const XML: string): _Recordset;
var
RS: Variant;
Stream: TStringStream;
begin
Result := nil;
if XML = '' then
Exit;
try
Stream := TStringStream.Create(XML);
Stream.Position := 0;
RS := CreateOleObject('ADODB.Recordset');
RS.Open(TStreamAdapter.Create(Stream) as IUnknown);
Result := IUnknown(RS) as _Recordset;
finally
Stream.Free;
end;
end;
end.
Взято с
Delphi Knowledge BaseКонвертировать таблицу в XML
Конвертировать таблицу в XML
{SMExport suite's free demo
Data export from dataset into XML-file
Copyright(C) 2000, written by Scalabium, Shkolnik Mike
E-Mail: smexport@scalabium.com
mshkolnik@yahoo.com
WEB: http://www.scalabium.com
http://www.geocities.com/mshkolnik
}
unit DS2XML;
interface
uses
Classes, DB;
procedure DatasetToXML(Dataset: TDataset; FileName: string);
implementation
uses
SysUtils;
var
SourceBuffer: PChar;
procedure WriteString(Stream: TFileStream; s: string);
begin
StrPCopy(SourceBuffer, s);
Stream.Write(SourceBuffer[0], StrLen(SourceBuffer));
end;
procedure WriteFileBegin(Stream: TFileStream; Dataset: TDataset);
function XMLFieldType(fld: TField): string;
begin
case fld.DataType of
ftString: Result := '"string" WIDTH="' + IntToStr(fld.Size) + '"';
ftSmallint: Result := '"i4"'; //??
ftInteger: Result := '"i4"';
ftWord: Result := '"i4"'; //??
ftBoolean: Result := '"boolean"';
ftAutoInc: Result := '"i4" SUBTYPE="Autoinc"';
ftFloat: Result := '"r8"';
ftCurrency: Result := '"r8" SUBTYPE="Money"';
ftBCD: Result := '"r8"'; //??
ftDate: Result := '"date"';
ftTime: Result := '"time"'; //??
ftDateTime: Result := '"datetime"';
else
end;
if fld.Required then
Result := Result + ' required="true"';
if fld.Readonly then
Result := Result + ' readonly="true"';
end;
var
i: Integer;
begin
WriteString(Stream,
'<?xml version="1.0" standalone="yes"?><!-- Generated by SMExport --> ' +
'<DATAPACKET Version="2.0">');
WriteString(Stream, '<METADATA><FIELDS>');
{write th metadata}
with Dataset do
for i := 0 to FieldCount - 1 do
begin
WriteString(Stream, '<FIELD attrname="' +
Fields[i].FieldName +
'" fieldtype=' +
XMLFieldType(Fields[i]) +
'/>');
end;
WriteString(Stream, '</FIELDS>');
WriteString(Stream, '<PARAMS DEFAULT_ORDER="1" PRIMARY_KEY="1" LCID="1033"/>');
WriteString(Stream, '</METADATA><ROWDATA>');
end;
procedure WriteFileEnd(Stream: TFileStream);
begin
WriteString(Stream, '</ROWDATA></DATAPACKET>');
end;
procedure WriteRowStart(Stream: TFileStream; IsAddedTitle: Boolean);
begin
if not IsAddedTitle then
WriteString(Stream, '<ROW');
end;
procedure WriteRowEnd(Stream: TFileStream; IsAddedTitle: Boolean);
begin
if not IsAddedTitle then
WriteString(Stream, '/>');
end;
procedure WriteData(Stream: TFileStream; fld: TField; AString: ShortString);
begin
if Assigned(fld) and (AString <> '') then
WriteString(Stream, ' ' + fld.FieldName + '="' + AString + '"');
end;
function GetFieldStr(Field: TField): string;
function GetDig(i, j: Word): string;
begin
Result := IntToStr(i);
while (Length(Result) < j) do
Result := '0' + Result;
end;
var
Hour, Min, Sec, MSec: Word;
begin
case Field.DataType of
ftBoolean: Result := UpperCase(Field.AsString);
ftDate: Result := FormatDateTime('yyyymmdd', Field.AsDateTime);
ftTime: Result := FormatDateTime('hhnnss', Field.AsDateTime);
ftDateTime:
begin
Result := FormatDateTime('yyyymmdd', Field.AsDateTime);
DecodeTime(Field.AsDateTime, Hour, Min, Sec, MSec);
if (Hour <> 0) or (Min <> 0) or (Sec <> 0) or (MSec <> 0) then
Result := Result + 'T' + GetDig(Hour, 2) + ':' + GetDig(Min, 2) + ':' +
GetDig(Sec, 2) + GetDig(MSec, 3);
end;
else
Result := Field.AsString;
end;
end;
procedure DatasetToXML(Dataset: TDataset; FileName: string);
var
Stream: TFileStream;
bkmark: TBookmark;
i: Integer;
begin
Stream := TFileStream.Create(FileName, fmCreate);
SourceBuffer := StrAlloc(1024);
WriteFileBegin(Stream, Dataset);
with DataSet do
begin
DisableControls;
bkmark := GetBookmark;
First;
{write a title row}
WriteRowStart(Stream, True);
for i := 0 to FieldCount - 1 do
WriteData(Stream, nil, Fields[i].DisplayLabel);
{write the end of row}
WriteRowEnd(Stream, True);
while (not EOF) do
begin
WriteRowStart(Stream, False);
for i := 0 to FieldCount - 1 do
WriteData(Stream, Fields[i], GetFieldStr(Fields[i]));
{write the end of row}
WriteRowEnd(Stream, False);
Next;
end;
GotoBookmark(bkmark);
EnableControls;
end;
WriteFileEnd(Stream);
Stream.Free;
StrDispose(SourceBuffer);
end;
end.
Взято с
Delphi Knowledge BaseКонвертируем Unix дату
Конвертируем Unix дату
The value is a Unix Time, defined as seconds since 1970-01-01T00:00:00,0Z. Important is the Letter Z, you live in Sweden, in consequence you must add 1 hour for StandardDate and 2 hours for DaylightDate to the date. The infos you can get with GetTimeZoneInformation. But you must determine, which Bias (Standard or Daylight) is valid for the date (in this case -60). You can convert the date value with the function below.
The Date for 977347109 is 2000-12-20T22:18:29+01:00.
const
UnixDateDelta = 25569; { 1970-01-01T00:00:00,0 }
SecPerMin = 60;
SecPerHour = SecPerMin * 60;
SecPerDay = SecPerHour * 24;
MinDayFraction = 1 / (24 * 60);
{Convert Unix time to TDatetime}
function UnixTimeToDateTime(AUnixTime: DWord; ABias: Integer): TDateTime;
begin
Result := UnixDateDelta + (AUnixTime div SecPerDay) { Days }
+ ((AUnixTime mod SecPerDay) / SecPerDay) { Seconds }
- ABias * MinDayFraction { Bias to UTC in minutes };
end;
{Convert Unix time to String with locale settings}
function UnixTimeToStr(AUnixTime: DWord; ABias: Integer): string;
begin
Result := FormatDateTime('ddddd hh:nn:ss', UnixTimeToDateTime(AUnixTime, ABias));
end;
{Convert TDateTime to Unix time}
function DateTimeToUnixTime(ADateTime: TDateTime; ABias: Integer): DWord;
begin
Result := Trunc((ADateTime - UnixDateDelta) * SecPerDay) + ABias * SecPerMin;
end;
procedure TForm1.Button4Click(Sender: TObject);
begin
Label1.Caption := UnixTimeToStr(977347109, -60);
end;
Взято с
Delphi Knowledge BaseКопирование файлов
Копирование файлов
Копирование методом Pascal
Type
TCallBack=procedure (Position,Size:Longint); {Для индикации процесса копирования}
procedure FastFileCopy(Const InfileName, OutFileName: String; CallBack: TCallBack);
Const BufSize = 3*4*4096; { 48Kbytes дает прекрасный результат }
Type
PBuffer = ^TBuffer;
TBuffer = array [1..BufSize] of Byte;
var
Size : integer;
Buffer : PBuffer;
infile, outfile : File;
SizeDone,SizeFile: Longint;
begin
if (InFileName <> OutFileName) then
begin
buffer := Nil;
AssignFile(infile, InFileName);
System.Reset(infile, 1);
try
SizeFile := FileSize(infile);
AssignFile(outfile, OutFileName);
System.Rewrite(outfile, 1);
try
SizeDone := 0; New(Buffer);
repeat
BlockRead(infile, Buffer^, BufSize, Size);
Inc(SizeDone, Size);
CallBack(SizeDone, SizeFile);
BlockWrite(outfile,Buffer^, Size)
until Size < BufSize;
FileSetDate(TFileRec(outfile).Handle,
FileGetDate(TFileRec(infile).Handle));
finally
if Buffer <> Nil then Dispose(Buffer);
System.close(outfile)
end;
finally
System.close(infile);
end;
end else
Raise EInOutError.Create('File cannot be copied into itself');
end;
Копирование методом потока
Procedure FileCopy(Const SourceFileName, TargetFileName: String);
Var
S,T : TFileStream;
Begin
S := TFileStream.Create(sourcefilename, fmOpenRead );
try
T := TFileStream.Create(targetfilename, fmOpenWrite or fmCreate);
try
T.CopyFrom(S, S.Size ) ;
FileSetDate(T.Handle, FileGetDate(S.Handle));
finally
T.Free;
end;
finally
S.Free;
end;
end;
Копирование методом LZExpand
uses LZExpand;
procedure CopyFile(FromFileName, ToFileName : string);
var
FromFile, ToFile: File;
begin
AssignFile(FromFile, FromFileName);
AssignFile(ToFile, ToFileName);
Reset(FromFile);
try
Rewrite(ToFile);
try
if LZCopy(TFileRec(FromFile).Handle, TFileRec(ToFile).Handle)<0 then
raise Exception.Create('Error using LZCopy')
finally
CloseFile(ToFile);
end;
finally
CloseFile(FromFile);
end;
end;
Копирование методами Windows
uses ShellApi; // !!! важно
function WindowsCopyFile(FromFile, ToDir : string) : boolean;
var F : TShFileOpStruct;
begin
F.Wnd := 0; F.wFunc := FO_COPY;
FromFile:=FromFile+#0; F.pFrom:=pchar(FromFile);
ToDir:=ToDir+#0; F.pTo:=pchar(ToDir);
F.fFlags := FOF_ALLOWUNDO or FOF_NOCONFIRMATION;
result:=ShFileOperation(F) = 0;
end;
// пример копирования
procedure TForm1.Button1Click(Sender: TObject);
begin
if not WindowsCopyFile('C:\UTIL\ARJ.EXE', GetCurrentDir) then
ShowMessage('Copy Failed');
end;
Источник:
Копирование экрана
Копирование экрана
unit ScrnCap;
interface
uses WinTypes, WinProcs, Forms, Classes, Graphics, Controls;
{ Копирует прямоугольную область экрана }
function CaptureScreenRect(ARect : TRect) : TBitmap;
{ Копирование всего экрана }
function CaptureScreen : TBitmap;
{ Копирование клиентской области формы или элемента }
function CaptureClientImage(Control : TControl) : TBitmap;
{ Копирование всей формы элемента }
function CaptureControlImage(Control : TControl) : TBitmap;
{===============================================================}
implementation
function GetSystemPalette : HPalette;
var
PaletteSize : integer;
LogSize : integer;
LogPalette : PLogPalette;
DC : HDC;
Focus : HWND;
begin
result:=0;
Focus:=GetFocus;
DC:=GetDC(Focus);
try
PaletteSize:=GetDeviceCaps(DC, SIZEPALETTE);
LogSize:=SizeOf(TLogPalette)+(PaletteSize-1)*SizeOf(TPaletteEntry);
GetMem(LogPalette, LogSize);
try
with LogPalette^ do
begin
palVersion:=$0300;
palNumEntries:=PaletteSize;
GetSystemPaletteEntries(DC, 0, PaletteSize, palPalEntry);
end;
result:=CreatePalette(LogPalette^);
finally
FreeMem(LogPalette, LogSize);
end;
finally
ReleaseDC(Focus, DC);
end;
end;
function CaptureScreenRect(ARect : TRect) : TBitmap;
var
ScreenDC : HDC;
begin
Result:=TBitmap.Create;
with result, ARect do begin
Width:=Right-Left;
Height:=Bottom-Top;
ScreenDC:=GetDC(0);
try
BitBlt(Canvas.Handle, 0,0,Width,Height,ScreenDC, Left, Top, SRCCOPY );
finally
ReleaseDC(0, ScreenDC);
end;
Palette:=GetSystemPalette;
end;
end;
function CaptureScreen : TBitmap;
begin
with Screen do
Result:=CaptureScreenRect(Rect(0,0,Width,Height));
end;
function CaptureClientImage(Control : TControl) : TBitmap;
begin
with Control, Control.ClientOrigin do
result:=CaptureScreenRect(Bounds(X,Y,ClientWidth,ClientHeight));
end;
function CaptureControlImage(Control : TControl) : TBitmap;
begin
with Control do
if Parent=Nil then
result:=CaptureScreenRect(Bounds(Left,Top,Width,Height))
else
with Parent.ClientToScreen(Point(Left, Top)) do
result:=CaptureScreenRect(Bounds(X,Y,Width,Height));
end;
end.
Зайцев О.В.
Владимиров А.М.
Взято с Исходников.ru
Копирование таблицы с помощью DBE
Копирование таблицы с помощью DBE
functionCopyTable(tbl: TTable; dest: string): boolean;
var
psrc, pdest: array[0..DBIMAXTBLNAMELEN] of char;
rslt: DBIResult;
begin
Result := False;
StrPCopy(pdest, dest);
with tbl do
begin
try
DisableControls;
StrPCopy(psrc, TableName);
rslt := DbiCopyTable(DBHandle, True, psrc, nil, pdest);
Result := (rslt = 0);
finally
Refresh;
EnableControls;
end;
end;
end;
Взято из
Когда копирую текст на русском
Копируем русский текст в буфер обмена в Windows2000
У меня Windows NT/2000. Когда копирую текст на русском языке, скажем, из TMemo в Ворд 97/2000, то получаю в результате каракули. Эти каракули исправляются, если перед копированием насильно переключить клавиатуру пользователя на русский язык. Но если у него нет этой клавиатуры, или если лучше не переключать ее, то как можно сообщить системе, что мы будем копировать РУССКИЙ текст. На форме создается невидимый TRichEdit (я обозвал его TRE в коде). Далее текст копируется в клипборд как обычно, после чего вызывается следующая процедура
procedureTMainForm.ConvertClipboard;
begin
TRE.SelectAll;
TRE.ClearSelection;
TRE.Lines.Add(Clipboard.AsText);
TRE.SelectAll;
TRE.Font.Name := 'Times New Roman'; //тут нужный шрифт
TRE.Font.Size := 12; // тут нужный размер
// или просто берите TRE.Font := MainMemo.Font;
TRE.SelAttributes.Charset := RUSSIAN_CHARSET;
TRE.CopyToClipboard;
end
Взято с
Корректная функция возведения в степень
Корректная функция возведения в степень
function power(Base, Exponent: Extended): Extended;
var ex: extended;
begin
power := 0;
if (exponent <> 0)
and (frac(trunc((1 / exponent) * 1000000) / 1000000) = 0)
and (base < 0) then
begin
if round(1 / exponent) mod 2 = 0 then exit;
ex := Exp(Exponent * Ln(-Base));
power := -ex;
end
else
begin
if Exponent = 0.0 then
power := 1.0
else if (Base = 0.0) and (Exponent > 0.0) then
power := 0.0
else if (Frac(Exponent) = 0.0) and (Abs(Exponent) <= MaxInt) then
power := IntPower(Base, Integer(Trunc(Exponent)))
else
power := Exp(Exponent * Ln(Base))
end;
end;
Теперь можем вычислить нечетный корень из отрицательного числа
Автор ответа: Mikel
Взято с Vingrad.ru
Краткая история Delphi
Краткая история Delphi
Delphi - это потомок Турбо Паскаля, который был выпущен для операционной системы CP/M в 1983 году. В феврале 1994 года Турбо Паскаль был перенесен на операционную систему MS-DOS.
На раннем этапе развития компьютеров IBM PC, Турбо Паскаль являлся одним из наиболее популярных языков разработки программного обеспечения - главным образом потому, что это было вполне серьезный компилятор, который, включая компилятор, редактор и все остальное, стоил всего $19.95 и работал на машине с 64 Kb оперативной памяти.
Под Windows - Турбо Паскаль был перенесен фирмой Borland в 1990 году. А самая последняя версия Borland Pascal 7.0 (имеющая теперь такое название), не считая Delphi, вышла в свет в 1992 году.
Разработка Delphi началась в 1993 году. После проведения beta-тестирования Delphi показали на "Software Development '95". И 14 февраля 1995 года официально объявили о ее продаже в США. В торговлю Delphi попала спустя 14 дней, 28 февраля 1995 года.
Источник:
Краткое описание Foxpro
Краткое описание Foxpro
Visual FoxPro последняя версия 8.0.
Производитель: Microsoft.
Домашняя страница: http://msdn.microsoft.com/vfoxpro/
После выпуска 7-ой версии Microsoft заявил о прекращении развития данного продукта, однако через год после этого была выпущена 8-ая версия.
Цена лицензионной 8-ой версии: полной версии по данным сайта производителя $649 US; для пользователей ранними версиями $349 US
В Москве 7-ая версия стоит ок. $600 US с доставкой по Москве.
Ограничения VFoxPro 7.0
Для людей имеющих представление, что такое БД привожу некоторые ограничения для 7-го Фокса (это лучше любых хар-тик), выводы сами напросятся. Для остальных распишу все позже.
Ограничения работы с базами
Максимум записей в файл таблицы...................................................1 миллиард
Максимальный размер файла таблицы.............................................2 гигабайта
Максимум символов в записи (MEMO).................................................65,500
Максимум символов в поле таблицы..................................................254
Максимальный размер числовых (и с плавающей точкой) полей....20
Максимум полей записей...................................................................255
Максимум таблиц открывается в одно время...................................255
(ограниченно памятью и доступными дескрипторами файла.)
Максимум байтов в индексном ключе в некомпактном индексе.....100
Максимум байтов в индексную ключе в компактном индексе.........240
Максимум открытых индексных файлов в таблице.........................неограниченно
Максимум открытых индексов во всех базах...............................неограниченно
Максимум отношений.......................................................................неограниченно
Максимальные поля, которые могут быть выбраны SQL SELECT........255
Ограничения языка
Минимальное значение целого числа............................-2,147,483,647
Максимальное значение целого числа............................2,147,483,647
Цифры точности в числовых вычислениях: VFoxPro может обрабатывать числа до 9007199254740992 (2^53) в точных вычислениях.
Значение по умолчанию переменных............................1,024
Максимум переменных.....................................................65,000
Максимум массивов...........................................................65,000
Максимум элементов в массив.........................................65,000
Максимум вложений DO...................................................128
Максимум вложенных структурированных команд программирования....384
Максимум транзакций......................................................5
Максимальная длина символьной переменой.................255
Максимум открытых окон.................................................неограниченно
Максимум открытых окон Browse....................................255
Максимум символов в макроподстановке........................8,192
Общие сведения.
FoxPro работает с таблицами формата *.dbf
Visual FoxPro 7.0 снабжено мощным набором мастеров, которые позволяют новичку достаточно быстро создать мощное работоспособное приложение, однако не могу рекомендовать использовать эту возможность людям, желающим, создать рациональное приложение, т.к. мастера используют навороченные библиотеки и не рационально используют память. Программы созданные с помощью мастеров очень грамозки.
FoxPro сильно интегрирован с SQL, позволяет вставлять блоки SQL-комманд непосредственно в тело основного кода и использовать в SQL-запросах собственные (фоксовские) функции. Что позволяет быстро и эффективно обробатывать большие базы, даже если таблицы не индексированы. Кроме того, FoxPro облодает собственными, очень мощными, и главное, удобными средствами работы с БД.
Наиболее крупные БД созданные на FoxPro (из мне известных):
База данных ГУВД г. Москвы.
База данных МГТС.
База данных Пенсионного фонда России.
Это основной язык написания программ Главного Научно-Вычеслительного Центра (ГНИВЦ) Министерства РФ по налогам и сборам
К достоинствам VFoxPro можно так же отнести: возможность создания составного индекса, работа с курсорами (виртуальными таблицами, не отличающимися от обычных, но исчезающих после завершения сеанса работа, т.е. не остаются ни в памяти ни на диске), возможность экспортировать\импортировать данные в\из XML, VFoxPro работает с другими базами по средствам драйвера ODBC, VFoxPro обладает высокой надежностью при работе с БД (в т.ч. и с индексами) и полность соответствует объявленным возможностям.
На VFoxPro можно создавать и клиент-серверные БД, однако в этой области VFoxPro значительно уступает большинству из известных серверов БД.
Резюме VFoxPro мощнейшее средство для работы с локальными базами данных, значительно превосходящее, по эффективности, все остальные системы БД.
Недостатки VFoxPro
Все вышесказанное относится к VFoxPro как средству разработки баз данных, однако на нем не представляется интересным создавать приложения не связанные с БД, хотя сам FoxPro является языком самодостаточным. Это связано с тем, что с VFoxPro поставляется очень убогий класс для работы с визуальными компонентами, а исходник этого класса закрыт. Поэтому разработчик ограничен в возможностях "украшать" свою программу. Следующее, в FoxPro не происходит объявления типа переменных, это лишает возможности контролировать правильность обращения к переменной и, главное, под переменную отводится максимальное кол-во памяти, поэтому программы написанные на VFoxPro очень ресурсоемкие. Большой недостаток VFoxPro - это крайне слабый конструктор отчетов, крайне осложняющий разработку приложения. Для работы программы, созданной на VFoxPro, необходимы библиотеки поддержки, что увеличивает размер поставляемой программы еще, примерно, на 4 Mb. Есть еще более мелкие недостатки.
Резюме Основными недостатками VFoxPro являются: большие требования к памяти, программами созданными на VFoxPro и слабые возможности по работе с визуальными компонентами.
Рекомендации для выбора Могу порекомендовать выбрать VFoxPro, тем кому требуется ввести и обрабатывать большие базы данных на локальных компьютерах или в небольших сетях. Т.к. VFoxPro позволяет создавать запросы, практически любой сложности. Разумеется не рекомендую использовать этот язык для составления больших серверных баз и для написания программ не связанных с ведением баз данных.
Обращаю внимание еще раз: FoxPro - это не только средство работы с базами данных, но это еще и язык программирования высокого уровня.
Типы данных Visual FoxPro
Тип данных Описание Размер Диапазон
Character Произвольный текст. 1 байт на символ; до 254 байтов Любые символы
Currency Денежная сумма. 8 байтов от -922337203685477.5808 до 922337203685477.5807
Date Хронологическая дата, состоящая из месяца, года и числа. 8 байтов от 01/01/100 до 12/31/9999
DateTime Хронологическая дата, состоящая из месяца, года, числа и времени. 8 байтов от 01/01/100 до 12/31/9999, плюс от 00:00:00 утра до 11:59:59 вечера
Logical Логическое значение истинности. 1 байт Истина (.T.) или Ложь (.F.)
Numeric Целое или с дробной частью число. 8 байтов в памяти;
от 1 до 20 байтов в таблице от
- .9999999999E+19
до
.9999999999E+20
Variant размер и диапазон принимает в соответствии с веденными данными
Типы данных полей таблиц Visual FoxPro
Тип поля Описание Размер Диапазон
Double Число с плавающей запятой двойной точности. 8 байтов от
+/-4.94065645841247E-324 до +/-1.79769313486232E308
Float Такое же, как Numeric. 8 байтов в памяти;
от 1 до 20 байтов в таблице от
- .9999999999E+19
до
.9999999999E+20
General Ссылка на OLE-объект. 4 байта в таблице. Ограничен только доступной памятью
Integer Целочисленные значения 4 байта от -2147483647 до 2147483646
Memo Ссылка на блок данных 4 байта в таблице Ограничен только доступной памятью
Character (Двоичный) Любые символьные данные, которые нужно сопровождать без изменения кодовых страниц 1 байт на символ до 254 байтов Любые символы
Memo (Двоичный) Любые memo-поля, которые нужно сопровождать без изменения кодовых страниц 4 байта в таблице Ограничен только доступной памятью
Вот пример работы с ADO
Зависимость от сервера не велика
conado=CREATEOBJECT(`adodb.connection`)
conado
TYPE(`conado`)
conado.open(`ora_ado`)
rd=CREATEOBJECT(`adodb.recordset`)
rd.Open(`select * from prof`,conado)
TYPE(`rd.Fields`)
rd.Fields(`pro`).Value
rd.MoveNext()
rd.Fields(1).Type
Пример работы через ODBC
con=sqlconnect('oradsn','scott','tiger')
if con>0 and sqlexec(con,'select * from scott.dept')>0
copy to dept
endif
если необходимо сделать параметризованный запрос:
m.depno=10
con=sqlconnect('oradsn','scott','tiger')
if con>0 and sqlexec(con,'select * from scott.dept where depno=? m.depno')>0
copy to dept
endif
Системные требования (для Visual FoxPro 7)
Процессор Pentium 133 или выше
Минимальный объем оперативной памяти - 64Mb, рекомендуется не менее 128Mb
Жесткий диск не менее 115Mb, до 200Mb, при полной установке.
Операционная система MS Windows98, ME, NT4, 2000, XP.
Видеорижим - SVGA.
Кроме того, необходимо, что бы на компьютере были установленны Internet Explorer 5.5 и Service Pack 1 (для Win2000).
Поддержка триггеров - да.
Поддержка хранимых процедур - да.
Поддержка транзакций - да.
Защита данных, шифрование - нет, шифрование данных осуществляется перед занесением данных в таблицу.
Возможность удалённого и Web администрирования - нет
Возможность импорта данных из
- MS Excell 2.0 - 97
- Lotus 1-3
- Paradox (до версии 4.0)
- Symphony
- FrameWork II
- MultiPlan 4.01
- RapidFile
Возможность экспорта данных в
- FoxBase+ (DBF)
- dBase IV (DBF)
- текстовый файл (txt)
- Lotus 1-2-3 1-A (WK1, WRK)
- Symphony (WR1, WRK)
- MultiPlan 4.01 (MOD)
System Data Format (SDF)
и некоторые другие, мало кому известные.
Наличие утилит для автоматизации операций для работы в командной строке, наличие собственных утилит для отладки запросов (выполнение SQL, построение плана выполнения кверей, профайлер и т.п.), утилиты для слежения за производительностью сервера. - Сам FoxPro
Автор Cashey (Vingrad.ru)
Краткое описание Paradox
Краткое описание Paradox
1) Краткое описание
- Парадокс - одна из самых старых локальных баз данных. Изначально развивалась фирмой Борланд (до 7й версии включительно), последние 3 версии: 8я, 9я и 10я выпущены фирмой Corel. В настоящее время судьба проекта не известна, но есть основания предполагать, что ни Борланд, ни Corel не имеют далекоидущих планов в отношении этой базы данных, а объёмы её продаж в чистом виде незначительны, впрочем она входит в поставку некоторых версий Corel Office.2) возможности
- количество баз данных на сервере - собственно база данных представляет собой каталог на диске который содержит файлы таблиц, индексов, view, форм и т.п. На одном компьютере может быть практически ничем не ограниченное количенство баз данных.
- количество таблиц в базе - неограниченно, но одновременно на одном компьютере не может быть открыто более 127 таблиц и 512 физических файлов
- размер таблиц - 255 полей, 2000000 записей в таблице (реально меньше, так как лимит блокировок исчерпывается быстрее, реальное количество записей где-то около полумиллиона),
10800 Bytes максимальный размер записи (без Memo/Blob полей), 127 вторичных индексов на таблицу,
256 Mb максимум в Blob поле
- количество пользователей и количество одновременных подключений - теоретический предел - 300 пользователей, но к этой информации надо относится с известной долей скептицизма, как и любая другая локальная база данных, парадокс в силу своей архитектуры очень плохо работает в многопользовательском режиме и реальное количество работающих одновременных подключений вряд ли может быть больше 10-20.
- наличие View - есть возможность создания View, которые представляют собой обычные текстовые файлы, отвечающие следующим требованиям:
- лежат в каталоге базы данных
- имеют расширение sql
- в качестве наполнения имеют обычное выражение SQL
Кроме того имеется другая разновидность View - файлы QBE (Query By Example) - очень древний, но довольно удобный способ работы с данными - альтернатива SQL, отличается гораздо большей гибкостью, чем SQL, но тем ни менее с развитием серверных баз данных отошедший в прошлое. Итак возможно создание текстового файла с расширением qbe и выражением qbe-запроса внутри, который можно использовать как View
- наличие SP, языка программирования - сам парадокс поддерживает хранимые процедуры на своём собственном языке, однако я не знаю, можно ли их использовать вне самого парадокса из других програм. Вот пример SP:
method pushButton (var eventInfo Event)
var
txt String
myNum Number
endVar
myNum = 123.321
txt = String(myNum)
msgInfo("myNum = ", txt)
endMethod
method pushButton (var eventInfo Event)
- наличие триггеров - нет
- репликация и синхронизация, перенос данных, средства backup - нет, однако, так как каждый объект базы данных является по сути отдельным файлом можно применять любые утилиты для Backup/Restore файлов.
- поддержка кластеров - нет
- поддержка XML, объектов, внешних процедур. - нет
- возможность взаимодействия между серверами, включая сервера других типов. - BDE имеет средства для взаимодействия между любыми базами данных, для которых есть BDE драйвера.
Вот пример SQL запроса к 2м базам данных:
Select T1.Field1, T2.Field2 From :Alias1:Table1 T1, :Alias2:Table2 T2
Where T1.Field3=T2.Field4
В данном случае не важно в какого типа базы данных под Alias1 и Alias2 - это могут быть базы данных любых разновидностей.
- поддерживаемые типы данных
Alpha - строка
Number - 15 знаков, плавающая запятая, плюс-минус 10 в 308 степени
Money - обычное поле с плавающей запятой, возможно прнименение различных форматов вывода
Short - целое от -32,767 до +32,767
Long Integer - oт -2147483648 дo 2147483647
BCD - плавающая запятая, повышенная точность, применяется для финансовых рассчётов
Date - для хранения даты
Time - для хранения времени
Timestamp - для хранения даты/времени
Memo
Formatted Memo
Graphic - вариант Blob поля
OLE - вариант Blob поля
Logical - True/False
Autoincrement
Binary - вариант Blob поля
Bytes - вариант Blob поля
- поддерживаемые конструкции SQL - поддерживаются все стандартные конструкции SQL: Select, Update, Insert, Create Table, Create Index, Alter Table, Alter Index, Join и т.д. Нет поддержки всторенных запросов типа: Select * From (Select * ...), нет поддержки использования переменных в запросах, нет возможности использования нескольких SQL statement в одном запросе.
- поддержка транзакций - есть
- системы репортинга, в том числе для Web - имеется встроенных не очень мощный репортинг, возможно использование других репортинговых систем: Crystal Report, Fast Report, Quick Report, Rave и д.р. Собственного репортинга ориентированного на Web нет.
- наличие собственного агента для выполнения заданий по расписанию - нет
3) Защита данных, шифрование - очень слабая защита паролем, шифрования нет, защита может предохранить только от неискушённого пользователя, так как присутствуют универсальные пароли, то профессионалом защита снимается за пару минут.
4) простота использования - в целом Парадокс достаточно дружелюбная среда. Администрирование и освоение трудностей не составляет.
- наличие встроенных средств администрирования с GUI интерфейсом - сам Парадокс - программа с довольно продуманным GUI интерфейсом, кроме того с BDE поставляется бесплатная программа Database Desktop, которая практически 90% дублирует функциональность самого Парадокса
- возможность удалённого и Web администрирования - нет
- сложность перевода проекта написанного под другую базу данных на рассматриваемую - Следует различать программы написанные именно при помощи самого Парадокса и программы на других языках программирования использующих таблицы формата парадокс.
а)Перевод програм с языка других баз данных на язык Парадокс достаточно трудоёмок и не стоит потраченных усилий.
б)Если же таблицы формата парадокс используются в третьесторонних программах то перевод с таблиц формата dbf на парадоксовский db обычно почти автоматический.
Перевод с формата db на dbf может быть связан с трудностями, так как db немного более "богатый" формат и могут быть нужны небольшие переделки в коде.
Перевод MS Access в Парадокс и наоборот будет связан с более значительными переделками.
Перевод с серверных баз данных на парадокс практически бессмысленен и подчас очень труден.
Перевод с парадокса на серверные базы данных обычно достаточно лёгок.
Итог: с Парадокса легко перейти на почти любые другие базы данных, обратное зачастую намного сложнее. Наибольшая совместимость с форматом dbf.
- сложность в установке и настройке - установка автоматическая, проблема может быть только одна - на больших винчестерах програма инсталляции может неправильно определять объём свободного места и не ставится (выход - временно занять свободное место оставив свободным только 1 гигабайт на время установки). Настройка не требуется. Практически не конфликтует с другим софтом
- насколько сложно администрирование сервера - администрирование практически не требуется
- наличие утилит для автоматизации операций для работы в командной строке - нет
- наличие собственных утилит для отладки запросов (выполнение SQL, построение плана выполнения кверей, профайлер и т.п.), утилиты для слежения за производительностью сервера. - Сам парадокс, Database Desktop
5) платформы
- на которых может работать сервер - MS DOS, MS Windows всех версий после Win 3.1
- на которых может работать клиент - MS DOS, MS Windows всех версий после Win 3.1
6) версии продуктов, краткая характеристика отличий
- наиболее широкораспространена версия 7 - под win32 платформу, последняя версия от Борланд. Последующие версии привносили практически лишь косметические изменения
7) способы доступа
- собственные API - есть DBI - API поставляемые с BDE
ODBC - поддержка только таблиц до 5й версии включительно, для более поздних версий - с ограничениями.
JDBC- есть
ADO (OLE DB) - фактически работает через драйвер ODBC
DAO - поддержка только таблиц до 5й версии включительно, для более поздних версий - с ограничениями.
BDE - "родной" и самый быстрый способ доступа
DBExpress - нет
Другие: - большое количество сторонних производителей компоеннтов, классов и библиотек для работы с форматами таблиц Парадокс.
оценка производительности - наиболее быстрый способ доступа - через BDE, на 1-2 порядка превосходит по скорости все остальные способы. Он же обеспечивает и максимальную функциональность.
- языки программирования - Парадокс является очень распространённым форматом для работы с базами данных самых различных языков программирования. Является "родным" для програмных сред от фирмы Борланд: Дельфи, СBuilder. Из этих сред работа с таблицами в формате Парадокса очень простая.
8) производительность
- монопольный доступ - очень высокая, особенно при локальном доступе. Например скорость выполнения Insert квери на локальной таблице при подключении BDE может достигать 2000-3000 кверей в минуту.
- для большого количества одновременных подключений - очень низкая, крайне желательен перевод на бузу данных с серверной архитектурой.
- для больших таблиц - при монопольном доступе весьма большая.
- для больших массивов данных при монопольном доступе - большая
9) цена - отдельно уже производителями не продаётся, вместе с Corel Office стоит порядка $100. Однако для написания програм которые работают с базами данных Парадокс сам Парадокс не требуется, достаточно иметь набор бесплатных утилит, таких как Database Desktop, тем более Парадокс не нужен для run-time среды таких програм.
10) явные недостатки и преимущества по сравнению с системами такого же класса - парадокс показывает практически такую-же или даже превосходящую производительность чем DBase/FoxPro, однако имеет значительно более слабую собственную среду программирования.
В сравнении с MS Access Парадокс показывает значительно более высокую производительность(примерно на порядок!), но проигрывает в мощности языка программирования и интегрированности с другими продуктами. Кроме того в отличие от MS Access развитие среды Парадокс практически прекращено.
11) предпочтительные проекты для использования
- с точки зрения количества данных - для проектов манипулирующих не более чем сотней тысяч записей.
- количества пользователей - 1-3 пользователя (одновременных подключений)
- уровня защищённости - не требующие защиты информации
- цены - для продуктов, которые не должны требовать дополнительных вложений в базы данных
распространённости - годится для распространяемых приложений, как весьма компактная и лёгкая в распространении бизнес-модель
Итог: - оптимально использование баз данных Парадокс для небольших проектов персонального пользования написанных на Дельфи или CБилдере
12) проекты в которых не рекомендуется использовать - для многопользовательских проектов, там где требуется высокий уровень защитыы данных и большие массивы информации.
13) Известные глюки и проблемы - система очень старая, что характеризует не только значительно устаревший интерфейс и отсутствие новомодных наворотов, но и очень низкое количество глюков. 2 основных глюка:
1. При многопользовательском доступе довольно частое возникновение corrupted table особенно если есть BLOB/Memo поля
2. При принудительном прерывании транзакции не освобождение ресурсов и как следствие "разбухание" служебных файлов.
14) Краткий обзор основных прилагаемых утилит и средств для обслуживания сервера
- Database Desktop - полноценная среда для управления любыми базами данных через BDE
- DataPump - утилита переноса данных из одной базы данных в другую
- BDE Administrator - программа управления BDE
15) Краткий обзор наиболее примечательных продуктов третьесторонних производителей для работы с данной базой данных. - огромное количество софта для работы с Парадоксовскими таблицами, практически можно найти программы на любой вкус и для любых целей.
Кросс-таблица через pivot-таблицу
Кросс-таблица через pivot-таблицу
Мне нужна помощь по реализации запроса кросс-таблицы в Delphi. У кого-нибудь имеется соответствующий опыт?
Использовать pivot-таблицу должен все тот-же общий механизм (относительно к любой базе данных SQL).
Предположим, что у нас есть данные продаж в таблице с полями Store, Product, Month, Sales, и вам необходимо отображать данные по продуктам за каждый месяц. (Примем, что поле 'month' для простоты имеет значения 1..12.)
Оригинальные данные примера:
Store Product Month Sales
#1 Toys 1 100
#2 Toys 1 68
#1 Toys 2 150
#1 Books 1 75
...
Желаемый отчет должен выглядеть похожим на этот:
Product January February March .....
Toys 168 150
Books 75 .....
Установите pivot-таблицу с именем tblPivot и 12 строками:
pvtMonth pvtJan pvtFeb pvtMar pvtApr ....
1 1 0 0 0 ....
2 0 1 0 0
3 0 0 1 0
4 0 0 0 1
.....
Теперь запрос, выполненный в виде:
select Product, January=sum(Sales*pvtJan),
February=sum(Sales*pvtFeb),
March=sum(Sales*pvtMar),
April=sum(Sales*pvtApr),...
where Month = pvtMonth
group by Product
даст вам информацию, опубликованную выше.
Поскольку pivot-таблица имеет только 12 строк, большинство SQL-движков сохранят результат в кэшовой памяти, так что скорость выполнения запроса весьма велика.
Взято из
Куки (Cookies)
Куки (Cookies)
Куки (Cookies)
Введение
Давайте вспомним, каким образом мы можем передавать CGI-программе параметры. При использовании метода GET параметры передаются в строке URL.
При использовании метода POST параметры передаются отдельно и не видны пользователю.
(отметим также, что в Internet Explorer 3 есть ошибка, из-за которой он ТЕРЯЕТ параметры при нажатии кнопки "Обновить").
Таким образом, нам не хватает возможности хранить информацию о передаваемых параметрах.
Что такое Cookie ?
"Cookie" - это небольшая порция информации, которая сохраняется на компьютере пользователя и привязана к конкретному URL. Когда браузер обращается к данному URL, он передает на удаленный сервер этот блок информации. В результате на сервере будет сформирована строка окружения HTTP_COOKIE, в которой содержится список всех cookies, которые относятся к данному URL. Перменная окружения HTTP_COOKIE будет содержать пары имен/значений параметров, разделенных символом ";" в следующем виде:
имя1=значение1;имя2=значение
;...Таким образом мы можем задавать сразу несколько параметров в одном cookie для данного URL.
Как создать Cookies ?
Давайте разберемся, как это делается! Для создания куков достаточно использовать HTTP-заголовок. Вспомним, каким образом задается тип передаваемого документа в заголовке: Content-type: text/html
Точно таким же образом в заголовке задаются и cookie! Просто добавляем еще одну строку:
Content-type: text/html
Set-Cookie: Name=Value
Таким образом, к примеру, можно сохранить на компьютере пользователя его имя и пароль, необходимые для входа на защищенный сайт или защищенную страничку. Многие сайты запрашивают у пользователя имя и пароль, передают их по методу POST, и затем сохраняют на компьютере полдьзователя в виде cookie.
Пример использования Cookies
По просьбам читателей я сделал программу Cook, демонстрирующую, как использовать cookies для авторизации пользователя. В архиве COOK.ZIP находится самая последняя версия моего модуля CGI и модуль Base64.
Курсоры в ADO
Курсоры в ADO
( перевод одноимённой статьи с сайта delphi.about.com )
В Delphi компоненты ADOExpress довольно приятны в использовании. Однако программирование компонент ADOExpress весьма отличается от традиционного Delphi программирования в компонентах TTable и TQuery, основанных на BDE. Естевственно, что если Вы привыкли к компонентам BDE dataset, то сразу же заметите различие в количестве возможностей и свойств, а так же в стиле программирования BDE и ADO.
В основе ADO лежит объект Recordset (aka Dataset). Этот объект является результатом Query команды (например, выражение SELECT компонента TADOQuery). Когда ADO-приложение получает строки из базы данных, то объект ADO Recordset формирует необходимую информацию и операции, допустимые для получаемых данных. При этом ADO использует курсоры, чтобы хранить набор строк для обработанной записи. Так же курсор содержит в себе текущую позицию в записи (recordset). Обычно, при разработке приложения, курсоры используются при создании записей, а так же при перемещении по записям (вперёд или назад).
В ADO курсоры имеют три функции. Первая, это тип курсора, определющая допустимые перемещения в пределах курсора, а так же будут ли отражаться изменения пользователей в записи. Вторая, это местоположение курсора, определяющая место хранения записи, в то время, пока курсор остаётся открытым. Третья, это тип блокировки курсора, которая определяет как ADO datastore будет блокировать строки, если Вам потребуется внести изменения.
Каждый раз когда мы будем открывать определённую запись ADO, то мы будем открывать её с определённым типом курсора.
Класс TCustomADODataSet содержит набор свойств, событий и методов, для работы с данными, доступными через ADO datastore. Все классы-потомки от TCustomADODataSet (такие как TADODataSet, TADOTable, TADOQuery, и TADOStoredProc) совместно используют некоторые общие свойства. В каждом из этих классов присутствуют три свойства, соответствующие функциям курсоров, описанным выше: CursorType, CursorLocation, и LockType. Давайте рассмотрим эти свойства по-подробнее.
CursorType
ADO содержит четы опции для данного типа курсора: dynamic, keyset, forward-only и static. Так как каждый тип курсора ведёт себя работает по разному, то несомненно Вы извлечёте пользу из каждого из этих видов курсоров.
Свойство CursorType указывает на то, каким образом Вы будете перемещаться по записям а так же какие изменения будут видны в записях базы данных, после того как Вы получите из неё данные. В Delphi классах ADO типы курсоров задаются в TCursorType.
ctDynamic
Позволит Вам видеть добавления, изменения и удаления, сделанные другими пользователями, а также позволит делать все типы перемещения по записи (Recordset) не полагаясь на закладки. Однако закладки можно использовать, если они поддерживаются. Для этого существует метод Supports в ADODataset, который сигнализирует о поддержке определённых типов операций. Следующее выражение позволяет определить, поддерживаются закладки или нет:
if ADOTable1.Supports(coBookmark) then ...
Если несколько пользователей одновременно вставляют (insert), апдейтят (update), или удаляют (delete) строки в базе данных, то лучше всего выбрать курсор dynamic.
ctKeyset
Ведет себя подобно динамическому курсору, за исключением того, что Вы не сможете видеть записи, которые добавляют другие пользователи, а так же не сможете получить доступ к записям, которые удаляются другими пользователями. Изменение данных другими пользователями будет все еще видимо. Этот тип всегда поддерживает закладки и поэтому позволяет все типы перемещения по записям (Recordset).
ctStatic
Обеспечивает статическую копию набора записей, чтобы использовать её для поиска данных и генерации отчётов. Данный тип всегда разрешает закладки и поэтому позволяет все типы движения по записям. Добавления, изменения, или удаления другими пользователями не будут видимы. Статический курсор ведет себя подобно компоненту BDE Query со свойством RequestLive установленным в False.
ctForward-only
Ведет себя идентично динамическому курсору за исключением того, что позволяет Вам пролистывать по записям только вперед. Это увеличивает производительность в ситуациях, где Вы необходимо делать только один проход по набору записей (Recordset).
Обратите внимание: если свойство CursorLocation в компоненте ADO dataset установлено в clUseClient, то Вы сможете использовать только опцию ctStatic.
Так же обратите внимание: что если Вы запросите тип курсора неподдерживаемый базой данных, то она может вернуть другой тип курсора. То есть если Вы пробуете установить CursorLocation в clUseServer и CursorType в ctDynamic, в базе данных Access, то Delphi заменит CursorType на ctKeyset.
CursorLocation
Свойство CursorLocation определяет, где будет создан набор записей (recordset) когда он будет открыт ? у клиента или на сервере.
Данные в клиентском (client-side) курсоре не сязаны постоянно ("inherently disconnected") с базой данных. ADO получает результаты запроса (все строки) и копирует данные клиенту перед тем, как Вы начнёте использовать их (в курсоре ADO). После того, как Вы сделаете изменения в наборе записей (Recordset), ADO преобразует эти изменения в запрос и отправляет этот запрос в Вашу базу данных через OLE DB. Клиентский (client-side) курсор ведёт себя подобно локальному кэшу.
В большинстве случаев, клиентский (client-side) курсор предпочтителен, потому что перемещения и обновления быстрее и более эффективны. Но, соответственно, увеличивается сетевой трафик при возвращении данных клиенту.
Использование серверного (server-side) курсора означает получение только необходимых записей. Естевственно, что на сервер падает большая нагрузка. Серверные (Server-side) курсоры полезны при вставке, модификации, удалении записей. Данный тип курсоров иногда обеспечивает лучшую производительность чем клиентский курсор, особенно когда сеть перегружена.
При выборе типа курсора Вам необходимо продумать множество факторов, таких как: будет ли у Вас большое количество обновлений либо Вы будете производить только выборку из базы данных; будете ли Вы использовать ADO как настольное приложение или Ваше приложение будет Internet-ориентированным; размер получаемых данных и т.д. Так же есть некоторое ограничения: например, MS Access не поддерживает динамических курсоров; вместо этого он использует keyset. Некоторые средства доступа к данным автоматически мастабируют свойства CursorType и CursorLocation, в то время как другие генерируют ошибку при использовании неподдерживаемых CursorType или CursorLocation.
LockType
Свойство LockType сообщает провайдеру о блокировках, которые будут помещены в записи в процессе редактирования. Блокировка позволяет предотвратить чтение данных одним пользователем в то время как другой пользователь изменяет эти данные, а так же не дать пользователю изменить данные, если они были изменены другим пользователем.
Такой эффект наблюдается в базе данных Access, которая блокирует некоторые соседние записи. Дело в том, что Access использует так называемую стратегию фиксации страницы. Поэтому, если пользователь редактирует запись, то другой пользователь уже не сможет получить доступ к изменению данной записи и, даже не сможет модифицировать ближе стоящие записи (до или после неё).
В Delphi, для этой цели используется TADOLockType в которой указывается тип блокировки, которая будет использоваться. Вы можете управлять строкой и блокировкой страницы, устанавливая соответствующую опцию блокировки курсора. Чтобы использовать определенную схему блокировки, провайдер и тип базы данных должны поддержать эту схему.
ltOptimistic
Оптимистическая блокировка блокирует запись только в том случае, если она была физически изменена. Этот тип блокировки полезен, если существует очень маленький шанс того, что второй пользователь может модифицировать строку в интервале между тем, когда курсор открыт, и когда строка окончательно модифицирована. Текущие значения в строке сравниваются со значением полученным когда строка была последний раз выбрана.
ltPessimistic
Пессимистическая блокировка блокирует каждую запись, до тех пор пока она находится в процессе редактирования. Эта опция заставляет ADO устанавливать исключительную блокировку на строку, когда пользователь делает любое изменения в любом столбцу записи. Компоненты ADOExpress непосредственно не поддерживают пессимистическую блокировку записей, потому что сама ADO не имеет возможности произвольно блокировать данную запись и до сих пор поддерживает навигацию в другие записи.
ltReadOnly
Данная блокировка просто не позволяет редактировать данные. Полезна в тех случаях, когда Ваше приложение должно временно предотвратить изменение данных, при этом чтение данной записи разрешено. Самый идеальный способ использования данной блокировки для создания отчётов, если установить CursorType как ctForwardOnly.
ltBatchOptimistic
Блокировка BatchOptimistic используется в клиентских курсорах. Наборы записей с данным курсором апдейтятся локально и все изменения отправляются в базу данных пакетами.
Взято с Исходников.ru
Kylix
Kylix
Cодержание раздела:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
См. также статьи в других разделах:
Kylix 3 encounters declaration syntax errors in TIME.H
Kylix 3 encounters declaration syntax errors in TIME.H
I am using Kylix 3, and get declaration syntax errors in TIME.H when attempting to compile any project. How can I solve this problem?
The TIME.H declaration syntax errors can be resolved by going into the Project Options and moving the reference to /usr/include up in the Include path. Preferably, /usr/include should be moved to the very first position in the ordered Include path.
The exact cause of this problem is not yet known, but it could be related to Kylix 3 finding a different version of TIME.H elsewhere on the system. For reference, Kylix 3 contains four instances of TIME.H, in the following locations:
/usr/include/linux
/usr/include/bits
/usr/include/sys
/usr/include
Kylix Tutorial. Часть 1. Установка
Kylix Tutorial. Часть 1. Установка
Итак, свершилось, проходя мимо ларька с CD я увидел компакт с этикеткой синего цвета и магической надписью Kylix. Вернее диска было два и оба были немедленно куплены.
Беглое изучение их показало, что один содержит непосредственно Kylix, а второй дополнительные компоненты и утилиты третьих фирм и Interbase 6. По хорошей традиции пиратами была слита самая "продвинутая" версия Kylix Server Developer.
Ставить Kylix я пробовал под RedHat Linux 7.0.
Для установки нам потребуется
а) Компьютер с установленной ОС Linux
б) В составе Linux должны быть установлены графические оболочки GNOME и / или KDE (возможно Kylix будет работать и с другими, но эти две наиболее распространенны и Borland гарантирует работу под ними). Лично я пользуюсь KDE, посему про нее и буду рассказывать.
в) Права пользователя root
Первым делом необходимо установить патч на библиотеку glibc, по каким-то причинам не устроившую фирму Borland. Для этого и необходимо иметь права root. Заплатки (patches) сложены в директории /patches CD-ROM. Для Redhat 7.0 необходимо перейти в директорию <путь к CD>/patches/glibc_redhat/7.0. Установка патчей выполняется командами
rpm -Uvh glibc-[2c]*
rpm -Fvh glibc-[dp]* nscd-*
Возможно также Вам придется установить пакет libjpeg либо из дистрибутива RedHat либо из папки <путь к CD>/patches/jpeg6.2.0
Далее установка может проводиться как под аккаунтом root, так и под простым пользователем. Для инсталляции необходимо запустить на выполнение скрипт setup.sh. из корня CD.Графическая оболочка установки сделана удобно и интуитивно понятна. По умолчанию установка предлагается в домашнюю директорию пользователя, однако, чтобы при наличии нескольких пользователей лучше устанавливать в /usr/kylix, чтобы Kylix был доступен всем.
После установки запуск IDE Kylix можно выполнить
1) из командной строки: <путь инсталляции>/bin/startkylix
2) из выпадающего меню KDE (аналог кнопки Пуск в Windows). Меню Borland Kylix/Kylix
3) из выпадающего меню KDE (аналог кнопки Пуск в Windows). Меню /RedHat/ Borland Kylix/Kylix
Далее обнаруживается следующая неприятная вещь: в среде разработки программы компилируются и работают, однако при запуске вне IDE возникают ошибки. Лечится это так
1. Прописать пути к библиотекам. В файле .bash_profile в строку PATH добавить путь к директории bin Kylix-а. Например, если он установлен в /usr/kylix - то добавить надо путь /usr/kylix/bin.
2. В файл /etc/ld.so.conf добавить строчку с путем к libqtintf.so (в нашем примере /usr/kylix/bin)
3. запустить ldconfig.
Автор: Mike Goblin
Взято из
с разрешения автора.
Kylix Tutorial. Часть 2. Работа с базами данных через dbExpress.
Kylix Tutorial. Часть 2. Работа с базами данных через dbExpress.
Фирма Borland в Kylix и Delphi 6 реализовала новый движок для работы с базой данных dbExpress. Данный движок предназначен для работы с серверными БД. На сегодняшний день поддерживаются MySQL, Oracle, Interbase, DB2. К сожалению, на момент выпуска Kylix он работал не со всеми версиями MySQL. В частности, он не заработал с MySQL 3.23.22-beta. За неимением Oracle и DB2 я пользовался Interbase 6, находившемся на втором диске дистрибутива.
Архитектура доступа к данным
В самом виде приложение для работы с базами данных может быть представлено в следующем виде:
Ничего принципиально нового здесь нет, по сравнению с Delphi. Но это только на первый взгляд. В dbExpress датасеты делятся на два вида:
1. Клиентский датасет (client dataset)
2. Однонаправленные датасеты (unidirectional dataset)
Клиентский датасет хранит выбранные записи в памяти. Это позволяет осуществлять навигацию в любом направлении, фильтровать записи, кешировать изменения итд. Именно данный вид используется для отображения данных пользователю.
Однонаправленные запросы не кешируют данные. Передвигаться по ним можно только в направлении указанном в конструкции ORDER BY SQL запроса, данные не могут быть изменены. Однако они предоставляют быстрый доступ к большим массивам записей.
Компоненты закладки dbExpress
Посмотрим, что приготовила нам фирма Borland по части компонентов на закладке dbExpress
СвойстваОписание SQLConnection Компонент для организации связи с сервером базы данных. Аналог Database в BDE. Позволяет управлять параметрами соединения с сервером БД, такие как путь к базе данных, имя и пароль пользователя итд. Connected:booleanПризнак установления соединения с БД. True - соединение активно. ConnectionName: stringИмя конфигурации, содержащей параметры соединения. Аналог AliasName в TDatabase для BDE DriverName: stringИмя драйвера для соединения. (DB2, Interbase,Oracle, MySQL). Устанавливается автоматически при установке св-ва ConnectionName KeepConnection: booleanПоддерживать соединение с сервером БД, если в приложении нет активизированных датасетов. LibraryName: stringИмя библиотеки, содержащей драйвер для связи с сервером БД LoadParamsOnConnect: booleanЗагружать ли параметры соединения, ассоциированные с именем соединения, перед установкой соединения в run time. Полезно в случае когда параметры соединения могут быть изменены вне приложения или меняются в design time LoginPrompt: BooleanЗапрашивать логин и пароль при соединении Name: TComponentName Имя компонента Params: TStringsПараметры соединения TableScope: TTableScopesПараметры видимости таблиц
TsSynonym - видеть синонимы
TsSysTable - видеть системные таблицы
TsTable - видеть таблицы
TsView - видеть просмотры VendorLib: stringИмя библиотеки с клиентской частью БД SQLDataSet Однонаправленный датасет общего назначения. Active: booleanАктивность датасета CommandText: stringТекст команды (запроса) на получение или манипуляции с данными CommandType: TSQLCommandTypeТип датасета
CtQuery - SQL запрос
CtTable - таблица, автоматически генерируется запрос на выборку всех записей по всем полям
CtStoredProc - хранимая процедура DataSource: TDataSourceИсточник данных для мастер датасета MaxBlobSize: integerМаксимальный размер BLOB полей ObjectView: BooleanВключить иерархическое представление для вложенных полей ParamCheck:BooleanОбновлять список параметров при изменении текста команды Params:TparamsСписок параметров команды SortFieldNames: stringСписок полей для сортировки датасета, поля разделяются точкой с запятой. Действует для CommandType ctTable SQLConnection: TSQLConnectionИмя компонента SQLConnection через который будет происходить работа с БД Tag: integerТэг SQLQuery Запрос к БД (однонаправленный) Active: booleanАктивность запроса DataSource: TDataSourceИсточник данных для мастер датасета MaxBlobSize: integerМаксимальный размер BLOB полей ObjectView: BooleanВключить иерархическое представление для вложенных полей ParamCheck:BooleanОбновлять список параметров при изменении текста запроса Params:TparamsСписок параметров запроса SQL:TStringsТекст запроса SQLConnection: TSQLConnectionИмя компонента SQLConnection через который будет происходить работа с БД Tag: integerТэг SQLStoredProc Хранимая процедура (в случае получения данных однонаправленная) Active: booleanАктивность хранимой процедуры MaxBlobSize: integerМаксимальный размер BLOB полей ObjectView: BooleanВключить иерархическое представление для вложенных полей ParamCheck:BooleanОбновлять список параметров при изменении процедуры Params:TparamsСписок параметров процедуры SQLConnection: TSQLConnectionИмя компонента SQLConnection через который будет происходить работа с БД Tag: integerТэг SQLTable Таблица базы данных (однонаправленный датасет) Active: booleanАктивность таблицы IndexFieldNames: stringСписок полей сортировки (через точку с запятой) IndexName: stringИмя индекса сортировки. Возможно использование либо IndexName или IndexFieldNames MasterSource: TdataSourceМастер источник данных для организации отношений главный-подчиненный (master-detail) MasterFields:stringПоля связи главный-подчиненный MaxBlobSize: integerМаксимальный размер BLOB полей ObjectView: BooleanВключить иерархическое представление для вложенных полей SQLConnection: TSQLConnectionИмя компонента SQLConnection через который будет происходить работа с БД TableName: stringИмя таблицы БД из которой будут выбраны данные Tag: integerТэг SQLMonitor Организация наблюдения за работой компонентов доступа к данным Active: booleanАктивность монитора AutoSave: BooleanАвтоматическое сохранения журнала событий в файл, указанный в FileName FileName: stringИмя файла для хранения журнала событий SQLConnection: TSQLConnectionИмя компонента SQLConnection через который будет происходить работа с БД Tag: integerТэг TraceList:TstringsЖурнал событий SQLClientDataSet Клиентский датасет общего назначения Active: booleanАктивность датасета Aggregates: TaggregatesСписок доступных агрегатов AggregatesActive: booleanВычисление агрегатов AutoCalcFields: booleanГенерировать событие OnCalcFields и обновлять Lookup поля True -
· при открытии датасета
· при переходе датасета в состояни dsEdit
· Передача фокуса ввода другому компоненту или другому столбцу (для сетки) при наличии изменений в текущей ячейке
False
· при открытии датасета
· при переходе датасета в состояни dsEdit
· Запись извлекается из БД CommandText: stringТекст команды для выполнения (SQL запрос). При установке св-ва FileName данное св-во игнорируется При сбросе флага poAllowCommandText в св-ве Options также текст команды игнорируется CommandType: TSQLCommandTypeТип датасета
CtQuery - SQL запрос
CtTable - таблица, автоматически генерируется запрос на выборку всех записей по всем полям
CtStoredProc - хранимая процедура ConnectionName: stringИмя конфигурации, содержащей параметры соединения. Аналог AliasName в TDatabase для BDE Constraints: TConstraintsОграничения на значения на уровне одной записи DBConnection: TSQLConnectionИмя компонента SQLConnection через который будет происходить работа с БД DisableStringTrim: booleanУдалять конечные пробелы в строковых полях при их вставке БД FetchOnDemand: booleanПолучать данные по мере необходимости FieldDefs: TFieldDefsОпределения полей FileName: stringИмя файла для сохранения кеша данных Filter: stringФильтр Filtered: BooleanВключение фильтрации FilterOptions: TFilterOptionsПараметры фильтрации IndexDefs: TindexDefsОпределения индексов IndexFieldNames: stringСписок полей сортировки (через точку с запятой) IndexName: stringИмя индекса сортировки. Возможно использование либо IndexName либо IndexFieldNames MasterSource: TdataSourceМастер источник данных для организации отношений главный-подчиненный (master-detail) MasterFields:stringПоля связи главный-подчиненный ObjectView: BooleanВключить иерархическое представление для вложенных полей Options: TProviderOptionsПараметры работы с данными PacketRecord: integerКоличество записей в одном пакете данных
-1 - все
>0 - количество
0 - включать в пакет только метаданные Params: TparamsЗначение параметров для выборки данных ReadOnly: BooleanДоступ только для чтения Tag: integerТэг UpdateMode: TUpdateModeСпособ поиска записи при записи изменений
UpWhereAll - использовать все поля
UpWhereChanged - ключевые поля+старые значения измененных полей
UpWhereKeyOnly - только ключевые поля
Попробуем написать простейшее приложение для просмотра данных из базы в /usr/ibdb (будем считать что папка уже создана). Для этого выполним следующие шаги:
1. Создадим базу данных в Interbase 6. У меня он проинсталировался в /opt/interbase .
1.1 Запустим сервер /opt/interbase/bin/ibguard &
1.2 Войдем в оболочку isql - /opt/interbase/bin/isql
1.3 Введем SQL запросы на создание БД и таблицы users:
createdatabase '/usr/ibdb/test.gdb';
create table users( ID integer not null primary key, NAME varchar(20));
commit;
quit;
Если все выполнено правильно - то в папке /usr/ibdb появится файл test.gdb.
2. Создадим новое приложение. Меню File/NewApplication в IDE Kylix
3. На главной форме приложения разместим с закладки dbExpress компоненты: SQLConnection и SQLDataSet. SQLConnection - это "соедиение" с базой данных, т.е с его помощью можно управлять параметрами соединения, такими как тип драйвера, имя пользователя и пароль. Двойной щелчок левой кнопкой мыши на SQLConnection1 вызовет окно работы с соединениями.
Name - test_connect. После добавления установим следующие параметры:
Database - /usr/ibdb/test.gdb
ServerCharSet - win1251
Кнопкой "ОК" закроем диалог. Свойство Connected установим в True. В диалоге запроса пароля введем пароль masterkey. Соединение установлено.
Компонент SQLClientDataSet1 будет извлекать данные из таблицы users. Почему мы используем его а не SQLQuery? Ответ очень прост - SQLQuery - однонаправленный датасет. Поэтому он не может обеспечить навигации в обе стороны и редактирование данных.
Свойство DBConnection компонента SQLClientDataSet1 установим равным SQLConnection1. Введем запрос на выборку данных из таблицы users - select * from users - в св-во CommandText, либо воспользуемся диалогом для данного св-ва. Активизируем запрос, установив св-во Active в True.
Далее с закладки Data Access на форму положим компонент TDataSource. Данный компонент делает данные из датасетов доступными для отображения в пользовательских элементах управления (сетках итд). Его св-во DataSet установим в ClientDataSet1.
Перейдем на закладку DataControls и с нее разместим на форме сетку данных DBGrid и DBNavigator. Для обоих компонентов св-во DataSource установим в DataSource1. При этом в DBGrid1 появится заголовок с наименованиями полей таблицы users.
Теперь можно запустить приложение на выполнение (F9 однака).
Автор: Mike Goblin
Взято из
с разрешения автора.
Kylix Tutorial. Часть 3. Работа с базами данных через dbExpress. Коннект - есть коннект.
Kylix Tutorial. Часть 3. Работа с базами данных через dbExpress. Коннект - есть коннект.
Как говорилось раньше, SQLConnection предназначен для
1. Подключения к базе данных с заданными параметрами
2. Управления параметрами соединения и драйвера БД.
3. Получения списка установленных драйверов баз данных и доступных соединений
4. Создания, удаления, редактирования соединений.
5. Управление транзакциями
6. Выполнение SQL операторов
7. Получение метаданных
Подключения к базе данных
Подключение к базе данных осуществляется установкой св-ва Connected в True, либо вызвом метода Open. Отключение - установка Connected в False или вызов метода Close. Перед подключением необходимо установить имя соединения (св-во ConnectionName).
Пример установления соединения в run-time:
SQLConnection1.Connected:=false;
SQLConnection1.ConnectionName:='test_connect';
SQLConnection1.Connected:=true;
Управления параметрами соединения и драйвера БД.
Значения параметров по умолчанию для соединений хранятся в файле <домашняя директория пользователя>/.borland/dbxconnections или при отсутствии файлов по указанному пути в /usr/local/etc/dbxconnections.conf. Он представляет собой текстовый файл формата ini файлов. То есть содержит набор секций, заключенных в квадратные скобки и параметров в формате Имя параметра=значение параметра. Вот кускок этого файла:
[DB2Connection]
DriverName=DB2
BlobSize=-1
Database=DBNAME
Для драйверов настройки по умолчанию хранятся по указанным выше путям в файле dbxdrivers
Для получения настроек, в принципе можно анализировать содержимое этих файлов, написав собственный код, однако гораздо удобнее пользоваться стандартными решениями. Свойство Params типа TStrings предоставляет нам доступ к параметрам соединения.
Пример - установка имени и пароля пользователя:
SQLConection1.Params.Values['User_Name']:='SYSDBA';
SQLConection1.Params.Values['Password']:='masterkey';
Свойство LoadParamsOnConnect типа Boolean позволяет управлять автоматической установкой св-в DriverName и Params в значения по умолчанию для данного соединения перед установкой соединения. Данное св-во полезно лишь во время выполнения приложения при смене имени соединения. Во время разработки загрузка установка св-в по умолчанию происходит автоматически.
Получения списка установленных драйверов баз данных и доступных соединений
Способ №1: анализировать содержимое файлов dbxconnections и dbxdrivers. Неудобно надо писать много собственного кода (который уже написан программистами Borland).
Способ №2. Использование функций из модуля SqlExpr
GetDriverNames(List: TStrings; DesignMode:Boolean = True) - получение списка доступных драйверов.
List - список для заполнения именами драйверов
DesignMode - в каком режиме разработки (true) или выполнения программы (false) вызывается функция.
Пример получение списка драйверов dbExpress:
procedure TForm1.Button2Click(Sender:TObject);
begin
// cb_drivers - это комбобокс для отображения списка драйверов
GetDriverNames(cb_drivers.Items, false);
end;
procedure GetConnectionNames(List: TStrings; Driver:string = ''; DesignMode:Boolean = True);
Получение списка доступных соединений.
List - список для заполнения именами драйверов
Driver - имя драйвера, к которому подключаются соединения
DesignMode - в каком режиме разработки (true) или выполнения программы (false) вызывается функция.
Пример получение списка соединений доступных для INTERBASE:
procedure TForm1.Button1Click(Sender:TObject);
begin
// cb_conn - это комбобокс для отображения списка драйверов
GetDriverNames(cb_conn.Items, 'INTERBASE', false);
end;
Использование данных функций удобно, но не дает полного контроля за соединениями. Сами эти функции в своей работе используют интерфейс IConnectionAdmin.
Способ №3 Использование интерфейса IConnectionAdmin (определен в модуле DBConnAdmin). Данный способ дает наиболее мощные возможности по работе с соединениями.
Пример получение списка драйверов:
Var
ConnAdm: IConnectionAdmin;
Begin
// Получаем ссылку на интрефейс
ConnAdm:=GetConnctionAdmin;
ConnAdm. GetConnectionNames(cb_drivers);
End;
Методы интерфейса IConnectionAdmin;
IConnectionAdmin = interface
// Получение имен драйверов доступа к серверам БД
function GetDriverNames(List: TStrings): Integer;
// Получение параметров драйвера по умолчанию
function GetDriverParams(const DriverName: string; Params: TStrings): Integer;
// Получение имен файлов библиотек
procedure GetDriverLibNames(const DriverName: string;
var LibraryName, VendorLibrary: string);
// Получание списка соединений, доступных для заданного типа драйвера
function GetConnectionNames(List: TStrings; DriverName: string): Integer;
// Получение параметров соединения по умолчанию
function GetConnectionParams(const ConnectionName: string; Params: TStrings): Integer;
// Получение значения параметра соединения
procedure GetConnectionParamValues(const ParamName: string; Values: TStrings);
// Добавление нового соединения
procedure AddConnection(const ConnectionName, DriverName: string);
// Удаление соединения
procedure DeleteConnection(const ConnectionName: string);
// Изменение параметров соединения по умолчанию
procedure ModifyConnection(const ConnectionName: string; Params: TStrings);
// Изменение имени соединения
procedure RenameConnection(const OldName, NewName: string);
// Изменение параметров драйвера по умолчанию
procedure ModifyDriverParams(const DriverName: string; Params: TStrings);
end;
Создания, удаления, редактирования соединений.
Наверно, Вы уже догадались, что данные операции выполняются методами интерфейса IconnectionAdmin. Можно также и руками поправить файлы с конфигурациями, а можно двойным щелчком мыши на компоненте SQLConnection открыть диалог редактирования - выбирайте, что Вам удобнее.
Управление транзакциями
Вообще говоря, можно подтверждать или откатывать транзакции, передавая SQL запросы commit или rollback. Однако в SQLConnection определены специальные методы для управления транзакциями.
Начать новую транзакцию можно вызовом процедуры
Procedure StartTransaction( TransDesc: TTransactionDesc);
В качестве параметра передается структура с описанием транзакции, поля структуры:
TransactionID: LongWord - уникальный (уникальность обеспечивает программист) идентификатор транзакции.
GlobalID:LongWord - как написано в доке, используется для транзакций в Oracle, зачем пока не ясно
IsolationLevel - уровень изоляции транзакции, значения
xilDIRTYREAD - "грязное" чтение, транзакция видит все изменения других транзакций, даже если они еще не подтверждены
xilREADCOMMITES - видны только результаты подтвержденных транзакций, но изменения, сделанные другими после старта транзакции (во время ее выполнения) не видны в транзакции.
xilREPEATABLEREAD - гарантируется состоятельность полученных данных, даже если другие транзакции подтверждаются после старта текущей транзакции.
XilCUSTOM - специфический для данного сервера БД уровень изоляции, значение уровня изоляции определяется членом структуры CustomIsolationLevel. На данный момент не поддерживается в dbExpress
CustomIsolationLevel:LongWord - см выше
Завершаться транзакции, как известно, могут либо подтверждением, либо откатом изменений.
Подтверждение
Procedure Commit (TransDesc: TTransactionDesc);
TransDesc - структура с описанием подтверждаемой транзакции
Откат
Procedure Rollback (TransDesc: TTransactionDesc);
TransDesc - структура с описанием откатываемой транзакции
Другие методы и свойства, связанные с управлением транзакциями
TransactionLevel: SmallInt - идентификатор текущей транзакции, совпадает с TransactionID в описании транзакции
TransactionSupported : LongBool - поддерживает ли БД транзакции
InTransaction: Boolean - открыта ли транзакция?
Выполнение SQL операторов
В SQLConnection определены два метода для выполнения запросов к БД.
Function Execute (const SQL: string; Params:TParams; ResultSet: Pointer = nil):integer;
Выполняет запрос определенный в параметре SQL c параметрами, переданными в Params. Если в результате запроса были получены записи, то в ResultSet возвращается указатель на TCustomSQLDataSet, содержащий полученные записи. Возвращает количество полученных записей.
Если запрос не содержит параметров и не возвращает записей проще использовать вызов
Function ExecuteDirect(const SQL: string):LongWord;
Возвращает 0 при успешном завершении и код ошибки в случае неудачи.
Получение метаданных
В SQLConnection определен ряд методов для получения метаданных.
Получение списка таблиц и списка полей в таблице
Procedure GetTableNames(List: TStrings; SystemTables: boolean = false);
Если установить SystemTables в True будут выбраны только системные таблицы, в обратном случае набор таблиц определяется установками св-ва TableScope.
ВНИМАНИЕ: я пробовал устанавливать в TableScope все элементы, но при этом не получал списка таблиц (должны выбираться и системные и обычные таблицы)- кривые руки? Скорее глюк. Может Вам повезет больше.
Список полей может быть получен вызвом метода GetFieldNames
В SQLConnection он определен как
Procedure GetFieldNames(const TableName: string; List: TStrings);
Получение списка процедур и их параметров
GetProcedureNames и GetProcedureParams
Получение списка индексов
GetIndexNames
Автор: Mike Goblin
Взято из
с разрешения автора.
Kylix Tutorial. Часть 4. Использование однонаправленных наборов данных
Kylix Tutorial. Часть 4. Использование однонаправленных наборов данных
Однонаправленные наборы данных предоставляют механизм доступа для чтения данных из таблиц сервера БД с помощью SQL команд. Они созданы для быстрого и "легковесного" доступа к информации сервера БД с минимальной загрузкой последнего. Однонаправленные наборы данных посылают команды серверу БД и в случае наличия набора записей, получают однонаправленный курсор для доступа к этим записям. Однонаправленные наборы данных не буферизируют полученные данные в памяти, что делает их более быстрыми и менее ресурсоемкими по сравнению с другими типами наборов данных. Однако, из-за отсутствия буфера записей, однонаправленные наборы данных менее гибки. Многие из возможностей, объявленных в TDataSet либо не реализованы, либо вызывают генерацию исключительных ситуаций. Например:
· Из методов навигации поддерживаются только First и Next. Большинство других методов вызывают исключения. Некоторые, такие как методы работы с закладками (Bookmarks), представляют собой просто заглушки.
· Нет встроенной поддержки для редактирования записей, поскольку для этого требуется буферизация.
· Свойство CanModify всегда равно False, и попытки перевода датасета в режим редактирования всегда приводят к неудаче. Редактирование данных, однако, может осуществляться с помощью SQL команды UPDATE или при помощи подключения клиентского набора данных.
· Отсутствует поддержка фильтров, поскольку это требует буферизации. При попытке фильтрации вызывается исключение. Все ограничения на диапазон выбираемых записей должны содержаться в SQL команде.
· Отсутствует поддержка lookup полей, т.к при этом необходимо буферизировать возможные значения такого поля.
Однако отсутствие буферизации делает однонаправленные наборы данных быстрыми, простыми в реализации и распространении. На закладке dbExpress палитры компонентов представлены четыре типа однонаправленных наборов данных: TSQLDataSet, TSQLQuery, TSQLTable и TSQLStoredProc.
TSQLDataSet наиболее общий из всех четырех. Может использоваться для выборки любых данных или выполнения SQL команд. Данный компонент рекомендуется использовать для работы через dbExpress.
TSQLQuery можно использовать так же как и TSQLDataSet. Главное отсутсвии поддержки хранимых процедур (хотя многие сервера позволяют вызывать хранимые процедуры из запросов, но каждый делает это по-своему, стандартный синтаксис отсутствует). Данный компонент присутствует, в основном, для обеспечения совместимости при переносе приложений из Windows.
TSQLTable компонент, дающий доступ ко всем записям и полям таблицы. Данный компонент присутствует, в основном, для обеспечения совместимости при переносе приложений из Windows.
TStoredProc компонент доступа к хранимым процедурам сервера. Данный компонент присутствует, в основном, для обеспечения совместимости при переносе приложений из Windows.
Подключение к базе и открытие. Управление транзакциями.
Так как TSQLDataSet рекомендован в качестве универсального датасета доступа к данным далее мы его и рассмотрим, хотя многие моменты, как Вы заметите, будут верны для всех однонаправленных датасетов. Для получения данных с SQL сервера необходимо подключить однонаправленный датасет к БД, задать комаду и активизировать (открыть) его.
Для подключения к БД необходимо установить свойство SQLConnection равным имени компонента TSQLConection, который обеспечивает подключение к базе. В инспекторе объектов на этапе разработки автоматически отображается выпадающий список доступных подключений.
Текст команды необходимо указать в свойстве CommandText, интерпретация текста команды зависит от типа команды, указанного в свойстве CommandType. TSQLDataSet поддерживает 3 типа команд:
· ctQuery - в свойстве CommandText должен присутсвовать SQL запрос;
· ctStoredProc - в свойстве CommandText указывается имя хранимой процедуры;
· ctTable - в свойстве CommandText указывается имя таблицы из которой будут выбраны записи.
Активизация датасета в случае выполнения команды, возвращающей набор данных, (запросы SELECT, выборка таблицы итд) производится установкой свойства Active в True, либо вызовом метода Open. В обратном случае, если набор данных при выполнении команды не формируется (не путайте с пустым набором данных), нужно использовать метод ExecSQL.
Управление транзакцией, в которой осуществляется работа с сервером БД, однонаправленный датасет реализует через public свойство TransactionLevel. При одновременном открытии нескольких транзакций в компоненте TSQLConnection, можно установить TransactionLevel в значение идентификатора желаемой транзакции (поле TransactionID в структуре описания транзакции TTransactionDesc) при этом выполнение команд будет происходить в заданной транзакции.
Примеры:
//Выполнение запроса возвращающего набора данных
begin
SQLDataSet1.CommandType:=ctQuery;
SQLDataSet1.CommandText:='select * from MyTable';
SQLDataSet1.ExecSQL;
end;
// Выполнение запроса не возвращающего набора данных
begin
SQLDataSet1.CommandType:=ctQuery;
SQLDataSet1.CommandText:='update MyTable set StrField1 = '''+Edit1.Text+''' where id = 5';
SQLDataSet1.ExecSQL;
end;
// Выполнение запроса возвращающего таблицу с SQL сервера.
begin
SQLDataSet1.CommandType:=ctTable;
SQLDataSet1.CommandText:='MyTable';
SQLDataSet1.ExecSQL;
end;
Навигация по записям. Свойства Eof и Bof. Количество записей, номер записи.
Если Вы работали с базами данных в Delphi, можете пропустить этот раздел.
Как уже говорилось ранее, поддерживаются всегод два метода навигации по набору данных First и Next. Метод First делает текущей первую запись, метод Next позволяет передвинуться на одну запись вперед.
Свойство Eof типа boolean определяет достигнут ли конец набора данных. Свойство Bof также типа boolean индицирует нахождение в начале набора данных. Приведем в качестве примера код процедуры перебора записей набора данных:
begin
with SQLDataSet1 do
begin
Open;
while not Eof do
begin
Next;
end;
end;
end;
Количество записей в наборе данных содержится в public свойстве RecordCount, а порядковый номер записи в свойстве RecNo. Однако нумерация записей поддерживается не всеми типами серверов БД. Чтобы узнать поддерживается ли возможность нумерации необходимо проанализировать возвращаемое значение функции IsSequenced, если значение равно True, то нумерация поддерживается, иначе для всех записей значение свойства RecNo равно -1.
Порядок сортировки.
Порядок сортировки записей для типа команд ctQuery при выборке данных определяется конструкцией SQL ORDER BY, заданной в тексте запроса ( св-во CommandText).
Для типа команды ctTable порядок сортировки по умолчанию определяет SQL сервер. Для изменения порядка сортировки в этом случае необходимо в свойстве SortFieldNames указать имена полей сортировки, разделив их точкой с запятой. При генерации запроса на выборку таблицы данные поля будут вставлены в запрос в конструкции ORDER BY. Данный способ пригоден и для команды ctQuery. При этом в тексте SQL команды не должно содержаться ORDER BY. Однако первый описанный способ сортировки для типа команд ctQuery более предпочтителен.
Для типа команды ctStoredProc порядок сортировки определяется в самой хранимой процедуре.
Использование параметров в запросах и хранимых процедурах,подготовка запросов
Если Вы работали с базами данных в Delphi, можете пропустить этот раздел.
Свойство Params позволяет работать с параметрами запроса или хранимой процедуры. Параметры - это переменные запроса, которые Вы можете менять в design и runtime без изменения текста запроса. Например, в данном запросе параметр P_ID используется для задания критерия отбора записей.
SELECT * FROM mytable where id < :P_ID
Параметр заменяет конкретное значение, которое подставляется на этапе выполнения запроса. Имена параметров начинаются со знака :. Перед выполнением запроса необходимо задать значения всех его параметров.
При изменении текста запроса параметры, указанные в тексте добавляются в свойсвтво Params автоматически, если необходимо отключить такое поведение сбросьте свойство ParamCheck в False.
Свойство Params является коллекцией объектов типа TParam. Класс TParam описывает параметр запроса или хранимой процедуры. Наиболее важные свойства этого класса:
· DataType:TFieldType - тип данных параметра
· ParamType:TParamType - тип параметра :
· ptUnknown - неопределенный тип, перед выполнением запроса или процедуры надо задать конкретный тип из следующих возможных
· ptInput - входной параметр (используется для передачи параметров),
· ptOutput - выходной (используется для возврата значений из хранимой процедуры),
· ptInputOutput - и входной и выходной,
· ptResult - результат работы процедуры может быть только один параметр данныого типа для одной процедуры.
· Value: Variant - значение параметра. Должно быть заполено перед выполнением запроса.
В режиме разработки данные свойства устанаваливаются с помощью Object Inspector. В режиме выполнения для установки свойств и значений параметров используются следующие методы:
· ParamByName - доступ к параметру по имени
Например:
SQLDataSet1.ParamByName('Country').AsString:='Russia';
· Params - доступ по индексу(номеру). Индексирование ведется с 0.
Например:
SQLDataSet1.Params[0].AsString:='Russia';
· ParamValues - установка значений нескольких параметров.
Например:
SQLDataSet1.ParamValues['Name;Country']:=VarArrayOf([Edit1.Text, Edit2.Text]);
Подготовка запроса подразумевает анализ и разбор текста запроса SQL сервером, подстановку параметров и подготовку к выполнению. После выполнения команды сервер освобождает задействованные ресурсы. Однако при частом исполнении одного и того же запроса (даже с разными значениями параметров) для повышения быстродействия имеет смысл хранить скомпилированный запрос на сервере. Для этого свойство Prepared необходимо установить в True.
Запросы главный-подчиненный.
Если Вы работали с базами данных в Delphi, можете пропустить этот раздел.
Наличие у TSQLDataSet свойства TDataSource позволяет связать два набора данных отношениями главный-подчиненный, т.е отображать в подчиненном наборе только те записи, которые связаны с текущей записью главного набора данных. Для этого необходимо связать главный набор данных с компонентом TDataSource, установив у последнего св-во DataSet равным имени компонента с главным набором данных. Затем у подчиненного набора данных установить свойство DataSource равным имени компонента TDataSource, связанного с главным набором. После описанных манипуляций в тексте команды подчиненного запроса можно использовать поля главного набора для связи, указывая их в качестве параметров.
Доступ к метаданным
Метаданные - это данные о структуре базы данных, такие как созданые таблицы, хранимые процедуры, поля таблиц и параметры процедур итд. Получение метаданных осуществляется вызовом метода SetSchemaInfo. Метод объявлен как:
procedure SetSchemaInfo(SchemaType: TSchemaType; SchemaObjectName, SchemaPattern: string);
Первый параметр определяет тип метаданных. Его возможные значения
stNoSchema - схема отсутствует - производится выборка результатов запроса
stTables - получение списка таблиц
stSysTables - получение информации о системных таблицах
stProcedures - получение информации о хранимых процедурах
stColumns - информация о полях таблицы
stProcedureParams - параметры хранимой процедуры
stIndexes - информация об индексах таблицы
Второй параметр - имя таблицы или хранимой процедуры, о полях или параметрах которой мы получаем информацию. Имеет смысл лишь при следующих значениях первого параметра
stColumns, stProcedureParams, stIndexes, иначе параметр игнорируется,
Третий параметр SchemaPattern - позволяет задать маску для фильтрации выбранной информации. При этом допустимо применение следующих подстановок
% - любые символы
_ - одиночный символ
Если же в маску должны входить вышеприведенные два символа, то они записываются как %% - знак процента, __ - подчеркивание.
При изменении свойства CommandText набор данных автоматически устанавливает значение свойства SchemaInfo равным stNoSchema.
Пример:
// Выборка информации о таблицах
SQLDataSet1.Close;
SQLDataSet1.SetSchemaInfo(stTables,'','%');
SQLDataSet1.Open;
Автор: Mike Goblin
Взято из
с разрешения автора.
Kylix Tutorial. Работа с DBExpress (статья)
Kylix Tutorial. Работа с DBExpress (статья)
Эта серия статей была любезно предоставлена автором Mike Goblin для нашего FAQ. Оригинал статьи находится на сайте
л
л
You may need to know at runtime what properties are available for a particular component at runtime. The list can be obtained by a call to GetPropList. The types, functions and procedures, including GetPropList, that allow access to this property information reside in the VCL source file TYPINFO.PAS.
GetPropList Parameters
functionGetPropList(TypeInfo: PTypeInfo; TypeKinds: TTypeKinds; PropList: PPropList): Integer;
The first parameter for GetPropList is of type PTypeInfo, and is part of the RTTI (Run Time Type Information) available for any object. The record structure defined:
PPTypeInfo = ^PTypeInfo;
PTypeInfo = ^TTypeInfo;
TTypeInfo = record
Kind: TTypeKind;
Name: ShortString;
{TypeData: TTypeData}
end;
The TTypeInfo record can be accessed through the objects ClassInfo property. For example, if you were getting the property list of a TButton, the call might look, so far, like this:
GetPropList(Button1.ClassInfo, ....
The second parameter, of type TTypeKinds, is a set type that acts as a filter for the kinds of properties to include in the list. There are a number of valid entries that could be included in the set (see TYPEINFO.PAS), but tkProperties covers the majority. Now our call to GetPropList would look like:
GetPropList(Button1.ClassInfo, tkProperties ....
The last parameter, PPropList is an array of PPropInfo and is defined in TYPEINFO.PAS:
PPropList = ^TPropList;
TPropList = array[0..16379] of PPropInfo;
Now the call might read:
procedure TForm1.FormCreate(Sender: TObject);
var
PropList: PPropList;
begin
PropList := AllocMem(SizeOf(PropList^));
GetPropList(TButton.ClassInfo, tkProperties + [tkMethod], PropList);
{...}
Getting Additional Information from the TTypeInfo Record:
The example at the end of this document lists not just the property name, but it's type. The name of the property type resides in an additional set of structures. Let's take a second look at the TPropInfo record. Notice that it contains a PPTypeInfo that points ultimately to a TTypeInfo record. TTypeInfo contains the class name of the property.
PPropInfo = ^TPropInfo;
TPropInfo = packed record
PropType: PPTypeInfo;
GetProc: Pointer;
SetProc: Pointer;
StoredProc: Pointer;
Index: Integer;
Default: Longint;
NameIndex: SmallInt;
Name: ShortString;
end;
PPTypeInfo = ^PTypeInfo;
PTypeInfo = ^TTypeInfo;
TTypeInfo = record
Kind: TTypeKind;
Name: ShortString;
{TypeData: TTypeData}
end;
The example below shows how to set up the call to GetPropList, and how to access the array elements. TForm will be referenced in this example instead of TButton, but you can substitute other values in the GetPropList call. The visible result will be to fill the list with the property name and type of the TForm properties.
This project requires a TListBox. Enter the code below in the forms OnCreate event handler.
uses
TypInfo;
procedure TForm1.FormCreate(Sender: TObject);
var
PropList: PPropList;
i: integer;
begin
PropList := AllocMem(SizeOf(PropList^));
i := 0;
try
GetPropList(TForm.ClassInfo, tkProperties + [tkMethod], PropList);
while (PropList^[i] <> Nil) and (i < High(PropList^)) do
begin
ListBox1.Items.Add(PropList^[i].Name + ': ' + PropList^[i].PropType^.Name);
Inc(i);
end;
finally
FreeMem(PropList);
end;
end;
Взято из
Левый SHIFT+ENTER/Правый SHIFT+ENTER
Левый SHIFT+ENTER/Правый SHIFT+ENTER
procedure TDecEditForm.Memo1KeyPress(Sender: TObject; var Key: Char);
VAR s:String; RL:Byte;
begin
IF key=CHR(VK_RETURN) Then
Begin
//WIN NT/2000
If (GetVersion() and $80000000)=0 then
BEGIN
IF ((Word(GetKeyState(VK_LSHIFT)) and $8000)<>0) Then
Begin
End;
IF ((Word(GetKeyState(VK_RSHIFT)) and $8000)<>0) Then
Begin
End;
End
ELSE
//WIN 9.x
Begin
asm
mov ah,2
int $16
mov RL,al
end;
if 1 = (RL and 1) then // ПРАВЫЙ SHIFT НАЖАТ+ENTER
Begin
End;
if 2 = (RL and 2) then // ЛЕВЫЙ SHIFT НАЖАТ+ENTER
Begin
End;
End;
//WIN 9.x
END;
End;
Автор ответа: CHERRY
Взято с Vingrad.ru
Limit reached for installs using Kylix serial number
Limit reached for installs using Kylix serial number
I am attempting to register Kylix, but cannot and get the following error:
--------------------
Error registering software. The limit has been reached for installs using this serial number, please contact customer service to increase the allowed install count.
--------------------
How do I get around this and register Kylix?
Borland has a registration server that keeps count of how many times a product has been registered. The default count is five registrations. This error indicates that your serial number has already been registered five times, and the registration currently being attempted exceeds the count.
You can get this count increased by calling Borland's registration hotline at (888) 588-2230. Explain to the representative who answers the phone what product you have, as well as the full error message, and he or she (after asking for your relevant information) will be able to increase the count to allow for additional registrations.
Липкие окошки
Липкие окошки
одноимённой статьи с сайта delphi.about.com
В статье рассматривается приём создания обработчиков сообщений, которые позволяют форме при перетаскивании "прилипать" к краям экранной области.
Конечно же в Win API такой возможности не предусмотрено, поэтому мы воспользуемся сообщениями Windows. Как нам извесно, Delphi обрабатывает сообщения через события, генерируя его в тот момент, когда Windows посылает сообщений приложению. Однако некоторые сообщения не доходят до нас. Например, при изменении размеров формы, генерируется событие OnResize, соотвествующее сообщению WM_SIZE, но при перетаскивании формы никакой реакции не происходит. Конечно же форма может получить это сообщение, но изначально никаких действий для данного сообщения не предусмотрено.
Итак, при перемещении, окну посылается сообщение WM_MOVING. Обрабатывая данной сообщение, приложение может отслеживать размер и расположение перетаскиваемого квадрата и, при необходимости, изменять их.
Так же существует сообщение WM_WINDOWPOSCHANGING, которое посылается окну, в случае, если его размер, расположение или место в Z порядке собираются измениться, как результат вызова функции SetWindowPos либо другой функции управления окном.
Чаще всего с сообщением передаются дополнительные параметры, которые сообщают нам необходимую информацию. Например, сообщение WM_MOVE, указывающее на то, что форма изменила своё местоположение, так же передаёт в параметре LPARAM новые координаты X и Y.
Сообщение WM_WINDOWPOSCHANGING передаёт нам ТОЛЬКО один параметр - указатель на структуру WindowPos, которая содержит информацию о новом размере и местоположении окна. Вот как выглядит структура WindowPos:
TWindowPos = packed record
hwnd: HWND; {Identifies the window.}
hwndInsertAfter: HWND; {Window above this one}
x: Integer; {Left edge of the window}
y: Integer; {Right edge of the window}
cx: Integer; {Window width}
cy: Integer; {Window height}
flags: UINT; {Window-positioning options.}
end;
Наша задача проста: нам необходима, чтобы форма прилипла к краю экрана, если она находится на определённом расстоянии от окна (допустим 20 пикселей).
Пример
К новой форме добавьте Label, один контрол Edit и четыре Check boxes. Измените имя контрола Edit на edStickAt. Измените имена чекбоксов на chkLeft, chkTop, и т.д... Для установки количества пикселей используем edStickAt, который будет использоваться для определения необходимого расстояния до края экрана достаточного для приклеивания формы.
Нас интересует только одно сообщение WM_WINDOWPOSCHANGING. Обработчик для данного сообщения будет объявлен в секции private. Ниже приведён полный код этого процедуры "прилипания" вместе с комментариями. Обратите внимание, что Вы можете предотвратить "прилипание" формы к определённому краю, путё снятия нужной галочки.
Для получения рабочей области декстопа (минус панель задач, панель Microsoft и т.д.), используем SystemParametersInfo, первый параметр которой SPI_GETWORKAREA.
...
private
procedure WMWINDOWPOSCHANGING
(Var Msg: TWMWINDOWPOSCHANGING);
message WM_WINDOWPOSCHANGING;
...
procedure TfrMain.WMWINDOWPOSCHANGING
(var Msg: TWMWINDOWPOSCHANGING);
const
Docked: Boolean = FALSE;
var
rWorkArea: TRect;
StickAt : Word;
begin
StickAt := StrToInt(edStickAt.Text);
SystemParametersInfo
(SPI_GETWORKAREA, 0, @rWorkArea, 0);
with Msg.WindowPos^ do begin
if chkLeft.Checked then
if x <= rWorkArea.Left + StickAt then begin
x := rWorkArea.Left;
Docked := TRUE;
end;
if chkRight.Checked then
if x + cx >= rWorkArea.Right - StickAt then begin
x := rWorkArea.Right - cx;
Docked := TRUE;
end;
if chkTop.Checked then
if y <= rWorkArea.Top + StickAt then begin
y := rWorkArea.Top;
Docked := TRUE;
end;
if chkBottom.Checked then
if y + cy >= rWorkArea.Bottom - StickAt then begin
y := rWorkArea.Bottom - cy;
Docked := TRUE;
end;
if Docked then begin
with rWorkArea do begin
// не должна вылезать за пределы экрана
if x < Left then x := Left;
if x + cx > Right then x := Right - cx;
if y < Top then y := Top;
if y + cy > Bottom then y := Bottom - cy;
end; {ширина rWorkArea}
end; {}
end; {с Msg.WindowPos^}
inherited;
end;
end.
Теперь достаточно запустить проект и перетащить форму к любому краю экрана.
А также можно взять готовый пример (~6Kb)
Вот собственно и всё.
Комментарии:
Автор: Nashev
а так короче... И, ИМХО, лучше:
procedure TCustomGlueForm.WMWindowPosChanging1(var Msg: TWMWindowPosChanging);
var
WorkArea: TRect;
StickAt : Word;
begin
StickAt := 10;
SystemParametersInfo(SPI_GETWORKAREA, 0, @WorkArea, 0);
with WorkArea, Msg.WindowPos^ do
begin
// Сдвигаем границы для сравнения с левой и верхней сторонами
Right:=Right-cx;
Bottom:=Bottom-cy;
if abs(Left - x) <= StickAt then x := Left;
if abs(Right - x) <= StickAt then x := Right;
if abs(Top - y) <= StickAt then y := Top;
if abs(Bottom - y) <= StickAt then y := Bottom;
end;
inherited;
end;
Скачать демонстрационный проект с исходниками - 167Kb
В проекте осталось 2 глюка:
1) Если у формы, к которой прицепили другую форму за правую/нижнюю границы попробовать переместить эти границы, прицепленная форма останется на месте но все равно будет прикрепленной.
2) Иногда 3 формы прикрепляются друг к другу, и иначе, как воспользовавшись 1-ым глюком, их не расцепить.
Состав проекта:
сам проект, uCustomGlueForm - форма с добавленной липкостью 3 формы - пустышки, наследники TCustomGlueForm
Для использования сделанного в своих проектах надо добавить в проект, и свои формы создавать, наследуя от него, например, через мастер "File/New..."
В принципе, если липкость нужна без прилипания (а это уже работает без глюков) можно выкинуть все методы, кроме
procedure WMWindowPosChanging(var Msg: TWMWindowPosChanging);message WM_WINDOWPOSCHANGING;
и все переменные, а в самом WMWindowPosChanging удалить все упоминания этих переменных.
Взято с Исходников.ru
ListBox, CheckListBox, DBLookUp, Другие списки
ListBox, CheckListBox, DBLookUp, Другие списки
Cодержание раздела:
См. также статьи в других разделах:
Local SQL и временная таблица
Local SQL и временная таблица
Local SQL не поддерживает вложенные запросы, но после того как я заработал клок седых волос, я нашел в высшей степени простое решение: использование временной таблицы.
Пример:
withGeneralQuery do
begin
SQL.Clear;
SQL.Add(.... внутренний SQL);
SQL.Open;
DbiMakePermanent(handle, 'temp.db',true);
SQL.Clear;
SQL.Add(SELECT ... FROM 'temp.db'....);
SQL.Open;
end;
Единственное: необходимо убедиться в том, что имя таблицы не вступает в конфликт с именами нескольких работающих копий таблицы. И, разумеется, данная технология не даст "живой" набор!
Взято из
Локальные операторы языка SQL (BDE)
Локальные операторы языка SQL (BDE)
Вывод нужных полей
SELECTLastName, FirstName, Salary FROM employee
Хотим вывести только имя, фамилию и оклад служащих
Вывод всех полей из таблицы
SELECT * FROM employee
* обозначает все поля
Задание псевдонима таблице
SELECT * FROM employee emp
where emp.salary>35000
Таблице employee в качестве псевдонима задано emp
Выводим всех служащих с окладом свыше 35000
Исключение дубликатов
SELECT DISTINCT Country FROM vendors
Хотим узнать из каких стран поставляют продукцию
Постановка условия
SELECT * FROM vendors
Where Country='Canada'
Выводим поставщиков из Канады
Использование логические операторов
SELECT * FROM vendors
Where Country='U.S.A.' and Preferred='True'
Выводим только предпочитаемых поставщиков из США.
Когда используем оператор AND должны удовлетворяться оба условия
SELECT * FROM animals
Where AREA='South America' or AREA='New Orleans'
Хотим видеть только тех животных, которые обитают в Южной Америке или Новом Орлеане Когда используем оператор OR должно удовлетворяться хотя бы одно условие
SELECT * FROM animals
Where AREA='South America' and not Weight<7
Выводим животных, обитающих в Южной Америке с весом не менее 7 кг
Когда используем оператор AND NOT должно удовлетворяться первое условие и не должно - второе
SELECT * FROM animals
Where Weight<5 or not Weight<10
Выводим животных, с весом менее 5 кг или более 10 кг
Когда используем оператор OR NOT должно либо удовлетворяться первое условие, либо не должно - второе
Упорядочивание записей по возрастанию/убыванию/по номеру столбца
SELECT * FROM animals order by Weight
Выводим животных в порядке увеличения веса: сначала самые лёгкие, в конце самые тяжелые
SELECT * FROM animals order by Weight desc
...наоборот - по убыванию
SELECT * FROM animals order by 3
Упорядочить по третьему столбцу (отсчёт начинается с 1 )
Объединение нескольких запросов
SELECT * FROM animals
Where Area='South America'
UNION
SELECT * FROM animals
Where Area='New Orleans'
Выводим тех животных, которые обитают в Южной Америке, а так же тех, которые обитают в Новом Орлеане Оператором UNION можем объединять несколько запросов
Максимальное/минимальное значение поля
SELECT MAX(Salary) FROM employee
Выводим максимальный оклад из таблицы служащих
SELECT MIN(Salary) FROM employee
Выводим минимальный оклад из таблицы служащих
Сумма всех значений/среднее значение
SELECT SUM(Salary) FROM employee
Так можем узнать сколько получают служащие некой фирмы вместе взятые
SELECT AVG(Salary) FROM employee
Так можем узнать среднестатистический оклад
Количество записей в таблице/в поле
SELECT COUNT(*) FROM employee
Находим количество записей в таблице - в данном случае количество служащих
SELECT COUNT(*) FROM clients
Where occupation='Programmer'
Посчитали сколько человек увлекаются программированием
Группировка записей
SELECT Continent, MAX(Area) FROM country group by Continent
С помощью конструкции "group by" можем узнать какая страна занимает самую большую площадь для каждого континента
Конструкция IN
select * from Customer
Where Country IN ('US','Canada','Columbia')
Выводим покупателей из США, Канады и Колумбии
select * from Customer
Where Country NOT IN ('US','Canada')
Выводим всех покупателей за исключением тех, кто проживает в США, Канаде
Вывод пустых/непустых значений
select * from Customer
Where State is NULL
Выводит те записи, где не введено значение в поле State
select * from Customer
Where State is NOT NULL
Выводит те записи, где введено значение в поле State
Вывод значений приблизительно соответствующих нужным
select * from employee
Where LastName like 'L%'
Выводим только тех служащих, у которых фамилия начинается на букву 'L'. Знак '%' - означает любые символы
select * from employee
Where LastName like 'Nels_n'
Например, мы не помним: как правильно пишется 'Nelson' или 'Nelsan', тогда нужно будет воспользоваться знаком подчёркивания, который означает любой символ
Диапазон значений
select * from employee
Where Salary BETWEEN 25000 AND 50000
Можем вывести только тех, кто получает от 25000 до 50000 включительно
ANY, SOME, ALL
SELECT * FROM orders.db
where custno= ANY (select custno from customer.db where city = 'Largo');
или
SELECT * FROM orders.db
where custno= SOME (select custno from customer.db where city = 'Largo');
или
SELECT * FROM orders.db
where custno IN (select custno from customer.db where city = 'Largo');
Выводим заказы покупателей из города 'Largo'
SELECT * FROM clients
where birth_date>All(select birth_date from clients where city='Los Altos')
Вывести тех клиентов, которые моложе всех из 'Los Altos'
EXISTS
SELECT * FROM orders.db
where custno= ANY (select custno from customer where city = 'Largo')
and Exists(SELECT * FROM customer WHERE City='Largo')
Выводим заказы покупателей из города 'Largo' если вообще есть покупатели с этого города
Использование параметров
SELECT * FROM clients
where Last_Name=:LastNameParam
Если мы хотим дать возможность пользователю самому указывать фамилию нужного ему клиента. мы вместо значения для поля фамилия указываем параметр. Параметры указываются после двоеточия. И получить доступ к ним можно по индексу из свойства Params компонента Query. Индексация начинается с нуля. Затем, например, по нажатию на кнопке напишем код:
Query1.Active:=false;
Query1.Params[0].AsString:=Edit1.Text;
Query1.Active:=true;
Вывод дополнительного текста[использование выражений]
SELECT LastName, Salary/100, '$' FROM employee
Если зарплата указана не в долларах, а какой-то другой валюте, курс которой равен 1 к 100, мы можем вывести данные в $, используя вышеуказанное выражение
Использование нескольких таблиц
SELECT o.orderno,o.AmountPaid, c.Company FROM orders o, customer c
where o.custno=c.custno and c.city='Largo'
Выводим номер и сумму заказа из таблицы заказов и компанию сделавшую заказ из таблицы покупателей
Вложенные подзапросы
SELECT * FROM employee
where Salary=(select MAX(Salary) from employee)
Мы научились выводить максимальное значение, например, можем узнать максимальный оклад у служащих, но куда полезнее было бы узнать кто тот счастливчик. Именно здесь используется механизм вложенных подзапросов
Взято с
Локальный общий доступ
Локальный общий доступ
...я так понимаю, что LocalShare относится к ситуации, когда другие не-IDAPI приложения могут одновременно иметь доступ к одним и тем же файлам...
Примерно на такие мысли наталкивает поставляемая документация... к сожалению это не так.
LOCALSHARE=False говорит BDE о том, что он должен сам решать при необходимости вопрос о блокировке таблицы/записи в типичных ситуациях, например, когда BDE 'думает' что таблица находится на локальном диске он выключает блокировку для увеличения скорости доступа. К сожалению, логические диски общего пользования в сетях 'p-t-p' программно идентифицируются как локальные с предсказуемо липовыми результатами. Установка LOCALSHARE=True заставляет блокирующий механизм 'включаться' для всех дисков и, следовательно, решает эту проблему.
- Eryk Bottomley
Взято из
Советов по Delphi от
Сборник Kuliba
Ловим баги или почему программы допускают недопустимые операции
Ловим баги или почему программы допускают недопустимые операции
Ошибки - неизбежное зло программирования. Видимо пока трудно даже представить средство с помощью которого можно избавится от них. Человеку, которые выдумает это чудодейственное лекарство, благодарные потомки-программисты, несомненно, воздвигнут памятник. Пока же остается лишь заниматься обычным делом: ловлей багов.
"Нарушение Доступа" - фраза, которую пользователи видят, когда приложение делает попытки обратиться к памяти, которая не обозначена для их использования - и как следствие происходит сбой в работе программы:
Accessviolation at address <HEX_value>
in module <Application.Exe>.
Read of address <HEX_value_2>
Ситуация при которой Windows давала бы полную свободу программам - записывай данные куда хочешь, скорее всего бы привела к разноголосице программ и полной потери управления над компьютером. Но этого не происходит - Windows стоит на страже "границ памяти" и отслеживает недопустимые операции. Если сама она справиться с ними не в силах - происходит запуск утилиты Dr. Watson, которая записывает данные о возникшей ошибки, а сама программа закрывается.
Известно что, при программирование, особенно крупных программных продуктов, уследить за всеми процессами в коде невозможно, да и нет необходимости. Использование сторонних компонентов и библиотек только усложняет дело. Именно поэтому программисты Delphi, порой и сталкиваются со "своенравными" программами, которые то и дело норовят "сбросить пользователя". Итак, давайте рассмотрим некоторые вопросы, связанные с корректной средой программирования, так и непосредственно проблем написания кода, которые ведут к возникновению ошибок типа "ошибка доступа" (AVS) и очертим наиболее известные пути их исправления.
Мы можем поделить AVS, с которыми сталкиваются при разработке в Delphi на два основных типах: ошибки при выполнения и некорректная разработка проекта, что вызывает ошибки при работе программы.
Ошибки возникают при старте и закрытии Delphi или формировании проекта. Причиной могут являться сбои в "железе" компьютера.
Эти ошибки могут быть вызваны различными источниками, включая систему BIOS, операционную систему или аппаратные подпрограммы драйверов. Некоторые видео-, звуковые или сетевые платы могут фактически вызывать подобного рода ошибки в Delphi. Для решения подобных аппаратных проблем можно предпринять последовательность неких "стандартных" ходов:
Проверить, что не имеется никаких конфликтов между установленными устройствами, устранить обнаруженные конфликты;
Попробовать слегка уменьшить "аппетиты" видеодрайвера - поставить меньшее разрешение;
В случае если у вас двухпроцесорная система обеспечить равное изменение шага для каждого процессора;
И в конце концов просто попытаться заменить драйвера на более свежие.
Но помимо чисто железных проблем - большую головную боль могут вызвать ошибки в работе программного обеспечения. Особенно это касается непосредственно операционной системы. Зачастую Windows терпит крах спонтанно. Вот рекомендации которые помогут вам создать более устойчивую среду программирования:
Хотя Windows 9X популярная система, разработку лучше проводить в Windows NT или Windows 2000 - это более устойчивые операционные системы. Естественно при переходе на них придется отказаться от некоторых благ семейства Windows 95/98/Me - в частности не все программы адоптированы для Windows NT/2000. Зато вы получите более надежную и стабильную систему.
Не забывайте о том, как важно всегда иметь под рукой свежие версии компонентов для Delphi и дополнительных библиотек. В отличие от Windows создатели данных пакетов стараются от версии к версии уменьшать количество ошибок.
Следите за тем, что бы устанавливаемые компоненты были предназначены непосредственно для вашей версии Delphi. Попробуйте деинсталлировать чужеродные компоненты один за другим (или пакет за пакетом) пока проблема не будет устранена.
Контролируйте все программные продукты установленные на вашей машине и деинсталлируйте те из них, которые сбоят. Фаворитами AV среди них являются шароварные утилиты и программы и бета версии программных продуктов.
Все вышеперечисленное в основном не касалось самого процесса программирования и в малой степени зависит от разработчика. Теперь же обратимся к теме, как не допустить при разработки программного продукта ситуации при которой, он сам будет являться причиной ошибки.
Вы могли бы рассмотреть компилирование вашего приложения с директивой {$D}, данная директива компилятора может создавать файлы карты (файлы с расширением map, которые можно найти в том же каталоге, что и файлы проекта), которые могут послужить большой справкой в локализации источника подобных ошибок. Для лучшего "контроля" за своим приложением, компилируйте его с директивой {$D}. Таким образом, вы заставите Delphi генерировать информацию для отладки, которая может послужить подспорьем при выявление возникающих ошибок.
Следующая позиция в Project Options - Linker & Compiler позволяет вам, определить все для последующей отладки. Лучше всего, если помимо самого выполняемого кода будет доступна и отладочная информация - это поможет при поиске ошибок. Отладочная информация увеличивает размер файла и занимает дополнительную память при компилировании программ, но непосредственно на размер или быстродействие выполняемой программы не влияет. Включение опций отладочной информации и файла карты дают детальную информацию только, если вы компилируете программу с директивой {$D+}.
Эта информация состоит из таблицы номеров строк для каждой процедуры, которая отображает адреса объектных кодов в номера строк исходного текста. Директива $D обычно используется совместно с другой директивой - $L, что позволяет или запрещает генерацию информации о локальных символах для отладки.
Таким образом вы без труда сможете найти точный адрес той подпрограммы, которая была ответственна за ошибку. Одна из наиболее общих причин ошибок выполнения - использование объекта, который еще не был создан. Если второй адрес при выдачи ошибки - FFFFFFF (или 0000000) Вы можете почти утверждать, что было обращение к объекту, который еще не был создан. Например, вызов метода формы, которая не была создана.
procedure TfrMain.OnCreate(Sender: TObject);
var
BadForm: TBadForm;
begin
BadForm.Refresh; // причина ошибки
end;
Попытаемся разобратся в этой ситуации. Предположим, что BadForm есть в списке "Available forms " в окне Project Options|Forms. В этом списке находятся формы, которые должны быть созданы и уничтожены вручную. В коде выше происходит вызов метода Refresh формы BadForm, что вызывает нарушение доступа, так как форма еще не была создана, т.е. для объекта формы не было выделено памяти.
Если вы установите "Stop on Delphi Exceptions " в Language Exceptions tab в окне Debugger Options, возможно возникновение сообщения об ошибке, которое покажет, что произошло ошибка типа EACCESSVIOLATION. EACCESSVIOLATION - класс исключение для недопустимых ошибок доступа к памяти. Вы будете видеть это сообщение при разработке вашего приложения, т.е. при работе приложения, которое было запущено из среды Delphi.
Следующее окно сообщения будет видеть пользователь - и программа будет закрыта при совершение недопустимой операции:
Access violation at address 0043F193
in module 'Project1.exe'
Read of address 000000.
Первое шестнадцатиричное число ('0043F193') - адрес ошибки во время выполнения программы. Выберите, опцию меню 'Search|Find Error', введите адрес, в котором произошла ошибка ('0043F193') в диалоге и нажмите OK. Теперь Delphi перетранслирует ваш проект и покажет вам, строку исходного текста, где произошла ошибка во время выполнения программы, то есть BadForm.Refresh.
Естественно, что списка наиболее общих причин ошибок, вызывающих аварийное завершение работы программы, написанной в Delphi в чистом виде нет. Есть несколько общих "узких мест" в коде и структуре программы, когда подобная ошибка может произойти. Перечислим наиболее распространенные.
Недопустимый параметр API
Если вы пытаетесь передать недопустимый параметр в процедуру Win API, может произойти ошибка. Необходимо отслеживать все нововведения в API при выходе новых версий операционных систем и их обновлений.
Уничтожение исключения
Никогда не уничтожайте временный объект исключения. Обработка исключения автоматически уничтожает объект исключения. Если вы уничтожите объект самостоятельно, то приложение попытается уничтожать объект снова, и произойдет ошибка.
Zero := 0;
try
dummy := 10 / Zero;
except
on E: EZeroDivide do
MessageDlg('Can not divide by zero!', mtError, [mbOK], 0);
E.free. // причина ошибки
end;
Индексация пустой строки
Пустая строка не имеет никаких достоверных данных. Следовательно, попытка индексировать пустую строку - подобно попытке обратиться к нулю, что приведет также к ошибке:
var
s: string;
begin
s := '';
s[1] := 'a'; // причина ошибки
end;
Обращение к динамической переменной
Вы должны строить обращение к динамической переменной корректно, иначе вы перемещаете адреса указателей и возможно разрушаете другие выделенные ячейки памяти.
procedure TForm1.Button1Click(Sender: TObject);
var
p1, p2: pointer;
begin
GetMem(p1, 128);
GetMem(p2, 128);
{эта строка может быть причиной ошибки}
Move(p1, p2, 128);
{данная строка корректна }
Move(p1^, p2^, 128);
FreeMem(p1, 128);
FreeMem(p2, 128);
end;
Перечисленные подходы позволят избежать наиболее частых недочетов в разработке, которые могут вызвать столь неприятное как для пользователя, так и для разработчика сообщение о том, что программа выполнила "недопустимую операцию".
Взято с
Манипуляции с текстом
Манипуляции с текстом
Cодержание раздела:
См. также статьи в других разделах:
Манипуляции с заголовком формы
Манипуляции с заголовком формы
Cодержание раздела:
См. также статьи в других разделах:
Масштабирование окон приложений, в зависимости от разрешения экрана
Масштабирование окон приложений, в зависимости от разрешения экрана
1.В ранней стадии создания приложения решите для себя хотите ли вы позволить форме масштабироваться. Преимущество немасштабируемой формы в том, что ничего не меняется во время выполнения. В этом же заключается и недостаток (ваша форма может бать слишком маленькой или слишком большой в некоторых случаях).
2.Если вы не собираетесь делать форму масштабируемой, установите св-во Scaled=False и дальше не читайте. В противном случае Scaled=True.
3.Установите AutoScroll=False. AutoScroll = True означает не менять размер окна формы при выполнении что не очень хорошо выглядит, когда содержимое формы размер меняет.
4.Установите фонты в форме на TrueType фонты, например Arial. Если такого фонта не окажется на пользовательском компьютере, то Windows выберет альтернативный фонт из того же семейства. Этот фонт может не совпадать по размеру, что вызовет проблемы.
5.Установите св-во Position в любое значение, отличное от poDesigned. poDesigned оставляет форму там, где она была во время дизайна, и, например, при разрешении 1280x1024 форма окажется в левом верхнем углу и совершенно за экраном при 640x480.
6.Оставляйте по-крайней мере 4 точки между компонентами, чтобы при смене положения границы на одну позицию компоненты не "наезжали" друг на друга. Для однострочных меток (TLabel) с выравниванием alLeft или alRight установите AutoSize=True. Иначе AutoSize=False.
7.Убедитесь, что достаточно пустого места у TLabel для изменения ширины фонта - 25\% пустого места многовато, зато безопасно. При AutoSize=False Убедитесь, что ширина метки правильная, при AutoSize=True убедитесь, что есть ссвободное место для роста метки.
8.Для многострочных меток (word-wrapped labels), оставьте хотя бы одну пустую строку снизу.
9.Будьте осторожны при открытии проекта в среде Delphi при разных разрешениях. Свойство PixelsPerInch меняется при открытии формы. Лучше тестировать приложения при разных разрешениях, запуская готовый скомпилированный проект, а редактировать его при одном разрешении. Иначе это вызовет проблемы с размерами.
10.Не изменяйте свойство PixelsPerInch !
11.В общем, нет необходимости тестировать приложение для каждого разрешения в отдельности, но стоит проверить его на 640x480 с маленькими и большими фонтами и на более высоком разрешении перед продажей.
12.Уделите пристальное внимание принципиально однострочным компонентам типа TDBLookupCombo. Многострочные компоненты всегда показывают только целые строки, а TEdit покажет урезанную снизу строку. Каждый компонент лучше сделать на несколько точек больше.
Автор Song
Масштабирование размера формы и размера шрифтов
Масштабирование размера формы и размера шрифтов
Когда мои программы работают на системах с установленными маленькими шрифтами, я получаю странный вид моей формы. К примеру, расположенные на форме компоненты Label становятся малы для размещения указанного теста, обрезая его в правой или нижней части. StringGrid не осуществляет положенного выравнивания и т.д.
Попробуй следующий код. Он масштабирует как размер формы, так и размер шрифтов. Вызывай его в Form.FormCreate. Надеюсь это поможет.
unitgeScale;
interface
uses Forms, Controls;
procedure geAutoScale(MForm: TForm);
implementation
type
TFooClass = class(TControl); { необходимо выяснить защищенность }
{ свойства Font }
procedure geAutoScale(MForm: TForm);
const
cScreenWidth: integer = 800;
cScreenHeight: integer = 600;
cPixelsPerInch: integer = 96;
cFontHeight: integer = -11; {В режиме проектирование значение из Font.Height}
var
i: integer;
begin
{
ВАЖНО!! : Установите в Инспекторе Объектов свойство Scaled TForm в FALSE.
Следующая программа масштабирует форму так, чтобы она выглядела одинаково
внезависимости от размера экрана и пикселей на дюйм. Расположенный ниже
участок кода проверяет, отличается ли размер экрана во время выполнения
от размера во время проектирования. Если да, Scaled устанавливается в True
и компоненты снова масштабируются так, чтобы они выводились в той же
позиции экрана, что и во время проектирования.
}
if (Screen.width &; lt > cScreenWidth) or (Screen.PixelsPerInch <>
cPixelsPerInch) then
begin
MForm.scaled := TRUE;
MForm.height := MForm.height * screen.Height div cScreenHeight;
MForm.width := MForm.width * screen.width div cScreenWidth;
MForm.ScaleBy(screen.width, cScreenWidth);
end;
{
Этот код проверяет, отличается ли размер шрифта во времы выполнения от
размера во время проектирования. Если во время выполнения pixelsperinch
формы отличается от pixelsperinch во время проектирования, шрифты снова
масштабируются так, чтобы форма не отличалась от той, которая была во
время разработки. Масштабирование производится исходя из коэффициента,
получаемого путем деления значения font.height во время проектирования
на font.height во время выполнения. Font.size в этом случае работать не
будет, так как это может дать результат больший, чем текущие размеры
компонентов, при этом текст может оказаться за границами области компонента.
Например, форма создана при размерах экрана 800x600 с установленными
маленькими шрифтами, имеющими размер font.size = 8. Когда вы запускаете
в системе с 800x600 и большими шрифтами, font.size также будет равен 8,
но текст будет бОльшим чем при работе в системе с маленькими шрифтами.
Данное масштабирование позволяет иметь один и тот же размер шрифтов
при различных установках системы.
}
if (Screen.PixelsPerInch <> cPixelsPerInch) then
begin
for i := MForm.ControlCount - 1 downto 0 do
TFooClass(MForm.Controls[i]).Font.Height :=
(MForm.Font.Height div cFontHeight) *
TFooClass(MForm.Controls[i]).Font.Height;
end;
end;
end.
Взято из
Массив без ограничения типа и размера
Массив без ограничения типа и размера
//кпримеру опишем свой тип
type
MyType = record
zap1: longword;
zap2: char;
zap3: string[10];
end;
//опишем НЕОГРАНИЧЕННЫЙ массив переменный типа MyType
//хотя, может использоваться абсолютно любой
var
m: array of MyType;
....
procedure TForm1.Button1Click(Sender: TObject);
var i: byte;
begin
for i := 0 to 9 do // нумерация элементов начинается с нуля!
begin
SetLength(m, Length(m) + 1); // увеличение длины массива на 1
m[i].zap1 := i; // присвоение
m[i].zap2 := chr(i); // полям
m[i].zap3 := inttostr(i); // значений
end;
end;
....
SetLength(m, 0); // освобождение памяти
end.
C Уважением,
Сергей Дьяченко, sd@arzamas.nnov.ru
Взято из
Советов по Delphi от
Сборник Kuliba
Массив в Delphi
Массив в Delphi
Вот несколько функций для операций с двухмерными массивами. Самый простой путь для создания собственной библиотеки. Процедуры SetV и GetV позволяют читать и сохранять элементы массива VArray (его Вы можете объявить как угодно). Например:
type
VArray: array[1..1] of double;
var
X: ^VArray;
NR, NC: Longint;
begin
NR := 10000;
NC := 100;
if AllocArray(pointer(X), N * Sizeof(VArray)) then exit;
SetV(X^, NC, 2000, 5, 3.27); { X[2000,5] := 3.27 }
end;
function AllocArray(var V: pointer; const N: longint): Boolean;
begin {распределяем память для массива V размера N}
try
GetMem(V, N);
except
ShowMessage('ОШИБКА выделения памяти. Размер:' + IntToStr(N));
Result := True;
exit;
end;
FillChar(V^, N, 0); {в случае включения длинных строк заполняем их нулями}
Result := False;
end;
procedure SetV(var X: Varray; const N, ir, ic: LongInt; const value:
double);
begin {заполняем элементами двухмерный массив X размером ? x N : X[ir,ic] := value}
X[N * (ir - 1) + ic] := value;
end;
function GetV(const X: Varray; const N, ir, ic: Longint): double;
begin {возвращаем величины X[ir,ic] для двухмерного массива шириной N столбцов}
Result := X[N * (ir - 1) + ic];
end;
Самый простой путь - создать массив динамически
Myarray := GetMem(rows * cols * sizeof(byte,word,single,double и пр.)
сделайте функцию fetch_num типа
function fetch_num(r,c:integer) : single;
result := pointer + row + col*rows
и затем вместо myarray[2,3] напишите
myarray.fetch_num(2,3)
поместите эти функции в ваш объект и работа с массивами станет пустячным делом. Я экспериментировал с многомерными (вплоть до 8) динамическими сложными массивами и эти функции показали отличный результат.
Вот способ создания одно- и двухмерных динамических массивов:
(*
--
-- модуль для создания двух очень простых классов обработки динамических массивов
-- TDynaArray : одномерный массив
-- TDynaMatrix : двумерный динамический массив
--
*)
unit DynArray;
interface
uses
SysUtils;
type
TDynArrayBaseType = double;
const
vMaxElements = (High(Cardinal) - $F) div sizeof(TDynArrayBaseType);
{= гарантирует максимально возможный массив =}
type
TDynArrayNDX = 1..vMaxElements;
TArrayElements = array[TDynArrayNDX] of TDynArrayBaseType;
{= самый большой массив TDynArrayBaseType, который мы может объявить =}
PArrayElements = ^TArrayElements;
{= указатель на массив =}
EDynArrayRangeError = class(ERangeError);
TDynArray = class
private
fDimension: TDynArrayNDX;
fMemAllocated: word;
function GetElement(N: TDynArrayNDX): TDynArrayBaseType;
procedure SetElement(N: TDynArrayNDX; const NewValue: TDynArrayBaseType);
protected
Elements: PArrayElements;
public
constructor Create(NumElements: TDynArrayNDX);
destructor Destroy; override;
procedure Resize(NewDimension: TDynArrayNDX); virtual;
property dimension: TDynArrayNDX
read fDimension;
property Element[N: TDynArrayNDX]: TDynArrayBaseType
read GetElement
write SetElement;
default;
end;
const
vMaxMatrixColumns = 65520 div sizeof(TDynArray);
{= построение матрицы класса с использованием массива объектов TDynArray =}
type
TMatrixNDX = 1..vMaxMatrixColumns;
TMatrixElements = array[TMatrixNDX] of TDynArray;
{= каждая колонка матрицы будет динамическим массивом =}
PMatrixElements = ^TMatrixElements;
{= указатель на массив указателей... =}
TDynaMatrix = class
private
fRows: TDynArrayNDX;
fColumns: TMatrixNDX;
fMemAllocated: longint;
function GetElement(row: TDynArrayNDX;
column: TMatrixNDX): TDynArrayBaseType;
procedure SetElement(row: TDynArrayNDX;
column: TMatrixNDX;
const NewValue: TDynArrayBaseType);
protected
mtxElements: PMatrixElements;
public
constructor Create(NumRows: TDynArrayNDX; NumColumns: TMatrixNDX);
destructor Destroy; override;
property rows: TDynArrayNDX
read fRows;
property columns: TMatrixNDX
read fColumns;
property Element[row: TDynArrayNDX; column: TMatrixNDX]: TDynArrayBaseType
read GetElement
write SetElement;
default;
end;
implementation
(*
--
-- методы TDynArray
--
*)
constructor TDynArray.Create(NumElements: TDynArrayNDX);
begin {==TDynArray.Create==}
inherited Create;
fDimension := NumElements;
GetMem(Elements, fDimension * sizeof(TDynArrayBaseType));
fMemAllocated := fDimension * sizeof(TDynArrayBaseType);
FillChar(Elements^, fMemAllocated, 0);
end; {==TDynArray.Create==}
destructor TDynArray.Destroy;
begin {==TDynArray.Destroy==}
FreeMem(Elements, fMemAllocated);
inherited Destroy;
end; {==TDynArray.Destroy==}
procedure TDynArray.Resize(NewDimension: TDynArrayNDX);
begin {TDynArray.Resize==}
if (NewDimension < 1) then
raise EDynArrayRangeError.CreateFMT('Индекс вышел за границы диапазона : %d', [NewDimension]);
Elements := ReAllocMem(Elements, fMemAllocated, NewDimension * sizeof(TDynArrayBaseType));
fDimension := NewDimension;
fMemAllocated := fDimension * sizeof(TDynArrayBaseType);
end; {TDynArray.Resize==}
function TDynArray.GetElement(N: TDynArrayNDX): TDynArrayBaseType;
begin {==TDynArray.GetElement==}
if (N < 1) or (N > fDimension) then
raise EDynArrayRangeError.CreateFMT('Индекс вышел за границы диапазона : %d', [N]);
result := Elements^[N];
end; {==TDynArray.GetElement==}
procedure TDynArray.SetElement(N: TDynArrayNDX; const NewValue: TDynArrayBaseType);
begin {==TDynArray.SetElement==}
if (N < 1) or (N > fDimension) then
raise EDynArrayRangeError.CreateFMT('Индекс вышел за границы диапазона : %d', [N]);
Elements^[N] := NewValue;
end; {==TDynArray.SetElement==}
(*
--
-- методы TDynaMatrix
--
*)
constructor TDynaMatrix.Create(NumRows: TDynArrayNDX; NumColumns: TMatrixNDX);
var col: TMatrixNDX;
begin {==TDynaMatrix.Create==}
inherited Create;
fRows := NumRows;
fColumns := NumColumns;
{= выделение памяти для массива указателей (т.е. для массива TDynArrays) =}
GetMem(mtxElements, fColumns * sizeof(TDynArray));
fMemAllocated := fColumns * sizeof(TDynArray);
{= теперь выделяем память для каждого столбца матрицы =}
for col := 1 to fColumns do
begin
mtxElements^[col] := TDynArray.Create(fRows);
inc(fMemAllocated, mtxElements^[col].fMemAllocated);
end;
end; {==TDynaMatrix.Create==}
destructor TDynaMatrix.Destroy;
var col: TMatrixNDX;
begin {==TDynaMatrix.Destroy;==}
for col := fColumns downto 1 do
begin
dec(fMemAllocated, mtxElements^[col].fMemAllocated);
mtxElements^[col].Free;
end;
FreeMem(mtxElements, fMemAllocated);
inherited Destroy;
end; {==TDynaMatrix.Destroy;==}
function TDynaMatrix.GetElement(row: TDynArrayNDX;
column: TMatrixNDX): TDynArrayBaseType;
begin {==TDynaMatrix.GetElement==}
if (row < 1) or (row > fRows) then
raise EDynArrayRangeError.CreateFMT('Индекс строки вышел за границы диапазона : %d', [row]);
if (column < 1) or (column > fColumns) then
raise EDynArrayRangeError.CreateFMT('Индекс столбца вышел за границы диапазона : %d', [column]);
result := mtxElements^[column].Elements^[row];
end; {==TDynaMatrix.GetElement==}
procedure TDynaMatrix.SetElement(row: TDynArrayNDX;
column: TMatrixNDX;
const NewValue: TDynArrayBaseType);
begin {==TDynaMatrix.SetElement==}
if (row < 1) or (row > fRows) then
raise EDynArrayRangeError.CreateFMT('Индекс строки вышел за границы диапазона : %d', [row]);
if (column < 1) or (column > fColumns) then
raise EDynArrayRangeError.CreateFMT('Индекс столбца вышел за границы диапазона : %d', [column]);
mtxElements^[column].Elements^[row] := NewValue;
end; {==TDynaMatrix.SetElement==}
end.
----Тестовая программа для модуля DynArray----
--------------------------------------------------------------------------------
uses DynArray, WinCRT;
const
NumRows: integer = 7;
NumCols: integer = 5;
var
M: TDynaMatrix;
row, col: integer;
begin
M := TDynaMatrix.Create(NumRows, NumCols);
for row := 1 to M.Rows do
for col := 1 to M.Columns do
M[row, col] := row + col / 10;
writeln('Матрица');
for row := 1 to M.Rows do
begin
for col := 1 to M.Columns do
write(M[row, col]: 5: 1);
writeln;
end;
writeln;
writeln('Перемещение');
for col := 1 to M.Columns do
begin
for row := 1 to M.Rows do
write(M[row, col]: 5: 1);
writeln;
end;
M.Free;
end.
Взято из
Советов по Delphi от
Сборник Kuliba
Математика, алгоритмы
Математика, алгоритмы
Cодержание раздела:
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·
·