Советы по Delphi

         

Реализация собственного потока


Я хотел бы создать конструктор Load, загружающий список из потока...

Новые потоки в Delphi более разносторонние, чем в BP7. Поскольку вы знаете как пользоваться потоками в BP7, а размер статьи ограничен, то я думаю, что для начала вам необходимо попробовать в действии описанный ниже модуль, инкапсулирующий класс для работы с потоками в стиле BP7. Класс является наследником TComponent, но в нашем случае не было бы никакой разницы, если бы он был наследником TObject. К примеру, вы могли бы адаптировать данный код к своему наследнику TList.

Более важен тот факт, что вы можете использовать поток так, как вам это необходимо, исходя из вашей задачи и специфики. Я сделал работу потока похожую по стилю на BP7, где вначале идет ID класса. В каком-нибудь месте вам необходимо вызвать RegisterType( TYourClass, UniqueIDLikeBP7 ), после чего TYourClass готов к работе с потоками.

Вы наверняка обратили внимание, что я реализовал список зарегистрированных классов (регистратор), где с помощью ID легко можно найти классы, читающие и пишущие в поток в момент вызова конструктора Load соответствующего класса. Код простой и не требующий пояснений. Имейте в виду, что данный код можно использовать для организации передачи данных между существующим файловым потоком BP7 в объекты Delphi - я создал это для осуществления миграции с текущего приложения BP7 в Delphi и осуществления совместимости.

Если вам необходима более подробная информацио о работе потоков в Delphi, обратитесь к соответствующему разделу электронной справки Delphi.

Успехов.

Mike Scott.

    unit CompStrm;
interface


uses
Classes ;
type TCompatibleStream       = class ;
{ TStreamObject }
TStreamObject = class( TComponent ) constructor Load( S : TCompatibleStream ) ; virtual ; abstract ; procedure Store( S : TCompatibleStream ) ; virtual ; abstract ; function GetObjectType : word ; virtual ; abstract ; end ;
TStreamObjectClass = class of TStreamObject ;
{ TCompatibleStream }
TCompatibleStream = class( TFileStream ) function  ReadString : string ; procedure WriteString( var S : string ) ; function  StrRead : PChar ; procedure StrWrite( P : PChar ) ; function  Get : TStreamObject ; virtual ; procedure Put( AnObject : TStreamObject ) ; virtual ; end ;
{ Register Type : используйте это для регистрации ваших объектов для работы с потоками с тем же ID, который они имели в OWL }
procedure RegisterType( AClass : TStreamObjectClass ; AnID   : word ) ;
implementation
uses
SysUtils, Controls ;
var Registry : TList ;  { хранение ID объекта и информации о классе }
{ TClassInfo }
type TClassInfo = class( TObject ) ClassType : TStreamObjectClass ; ClassID   : word ; constructor Create( AClassType : TStreamObjectClass ; AClassID   : word ) ; virtual ; end ;
constructor TClassInfo.Create( AClassType : TStreamObjectClass ; AClassID   : word ) ;
var AnObject : TStreamObject ;
begin if not Assigned( AClassType ) then Raise EInvalidOperation.Create( 'Класс не инициализирован' ) ;
if not AClassType.InheritsFrom( TStreamObject ) then Raise EInvalidOperation.Create( 'Класс ' + AClassType.ClassName + ' не является потомком TStreamObject' ) ;
ClassType := AClassType ; ClassID := AClassID ; end ;

{ функции поиска информации о классе }
function  FindClassInfo( AClass : TClass ) : TClassInfo ;
var i : integer ;
begin for i := Registry.Count - 1 downto 0 do begin Result := TClassInfo( Registry.Items[ i ] ) ; if Result.ClassType = AClass then exit ; end ; Raise EInvalidOperation.Create( 'Класс ' + AClass.ClassName + ' не зарегистрирован для работы с потоком' ) ; end ;

function  FindClassInfoByID( AClassID : word ) : TClassInfo ;
var i : integer ; AName : string[ 31 ] ;
begin for i := Registry.Count - 1 downto 0 do begin Result := TClassInfo( Registry.Items[ i ] ) ; AName := TClassInfo( Registry.Items[ i ] ).ClassType.ClassName ; if Result.ClassID = AClassID then exit ; end ; Raise EInvalidOperation.Create( 'ID класса ' + IntToStr( AClassID ) + ' отсутствует в регистраторе классов' ) ;
end ;

procedure RegisterType( AClass : TStreamObjectClass ; AnID   : word ) ;
var i : integer ;
begin { смотрим, был ли класс уже зарегистрирован } for i := Registry.Count - 1 downto 0 do with TClassInfo( Registry[ i ] ) do if ClassType = AClass then begin if ClassID <> AnID then Raise EInvalidOperation.Create( 'Класс ' + AClass.ClassName + ' уже зарегистрирован с ID ' + IntToStr( ClassID ) ) ; exit ; end ; Registry.Add( TClassInfo.Create( AClass, AnID ) ) ; end ;

{ TCompatibleStream }
function  TCompatibleStream.ReadString : string ;
begin ReadBuffer( Result[ 0 ], 1 ) ; if byte( Result[ 0 ] ) > 0 then ReadBuffer( Result[ 1 ], byte( Result[ 0 ] ) ) ;
end ;

procedure TCompatibleStream.WriteString( var S : string ) ;
begin WriteBuffer( S[ 0 ], 1 ) ; if Length( S ) > 0 then WriteBuffer( S[ 1 ], Length( S ) ) ; end ;

function TCompatibleStream.StrRead : PChar ;
var L : Word ; P : PChar ;
begin ReadBuffer( L, SizeOf( Word ) ) ; if L = 0 then StrRead := nil else begin P := StrAlloc( L + 1 ) ; ReadBuffer( P[ 0 ], L ) ; P[ L ] := #0 ; StrRead := P ; end ; end ;

procedure TCompatibleStream.StrWrite( P : PChar ) ;
var L : Word ;
begin if P = nil then L := 0 else L := StrLen( P ) ; WriteBuffer( L, SizeOf( Word ) ) ; if L > 0 then WriteBuffer( P[ 0 ], L ) ; end;

function  TCompatibleStream.Get : TStreamObject ;
var AClassID : word ;
begin { читаем ID объекта, находим это в регистраторе и загружаем объект } ReadBuffer( AClassID, sizeof( AClassID ) ) ; Result := FindClassInfoByID( AClassID ).ClassType.Load( Self ) ; end ;

procedure TCompatibleStream.Put( AnObject : TStreamObject ) ;
var AClassInfo : TClassInfo ; ANotedPosition : longint ; DoTruncate     : boolean ;
begin { получает объект из регистратора } AClassInfo := FindClassInfo( AnObject.ClassType ) ;
{ запоминаем позицию в случае проблемы } ANotedPosition := Position ; try { пишем id класса и вызываем метод store } WriteBuffer( AClassInfo.ClassID, sizeof( AClassInfo.ClassID ) ) ; AnObject.Store( Self ) ; except { откатываемся в предыдущую позицию и, если EOF, тогда truncate } DoTruncate := Position = Size ; Position := ANotedPosition ; if DoTruncate then Write( ANotedPosition, 0 ) ; Raise ; end ; end ;

{ выход из обработки, очистка регистратора }
procedure DoneCompStrm ; far ;
var i : integer ;
begin { освобождаем регистратор } for i := Registry.Count - 1 downto 0 do TObject( Registry.Items[ i ] ).Free ;
Registry.Free ; end ;

begin Registry := TList.Create ; AddExitProc( DoneCompStrm ) ; end.

[000613]



Содержание раздела