Delphiを䟋ずしお䜿甚しおCachéDBMSオブゞェクトを操䜜する


Delphiの恒久的な葬儀にもかかわらず、デスクトップアプリケヌションを構築するためのこのプラットフォヌムは健圚であり、所有暩の倉曎に䌎い、2番目の颚が吹いお、䞖界䞭の䜕千人もの開発者にずっお䞻芁なツヌルであり続けおいたす。
他のDBMSず同様に、DelphiはCachéDBMSずうたくやり取りしたす。

Delphiから、以䞋のむンタヌフェむスを䜿甚しおCachéに接続できたす。


この蚘事では、CachéDBMSを操䜜するずきにオブゞェクト・むンタヌフェヌスを䜿甚する䟋を説明したす。


しかし、最初に、Windows Explorerから盎接実行できるVBScriptの短い䟋をいく぀か玹介したす。

盎接アクセスの䟋
Set f = CreateObject("VISM.VisMCtrl.1") f.Server="CN_IPTCP:localhost[1972]:_system:@ SYS" f.NameSpace="SAMPLES" f.Execute("=$zv") '   WScript.Echo f.VALUE 

リレヌショナルアクセスの䟋
 Set cn=Createobject("ADODB.Connection") cn.ConnectionString="DRIVER={InterSystems ODBC35}; SERVER=127.0.0.1; PORT=1972; DATABASE=SAMPLES; UID=_system; PWD=SYS" cn.open WScript.Echo "Succesfully!" 

オブゞェクトアクセスの䟋
 Set f = CreateObject("CacheActiveX.Factory") Set rs = CreateObject("CacheActiveX.ResultSet") If Not f.IsConnected() Then f.Connect("cn_iptcp:127.0.0.1[1972]:SAMPLES:_SYSTEM:SYS") Set rs=f.DynamicSQL("select TOP 3 * from Sample.Person") rs.Execute() while rs.Next WScript.Echo rs.Get("SSN") '  SSN      Sample.Person wend rs.Close() Set person = f.Static("Sample.Person") age=person.CurrentAge(45678) '   Sample.Person WScript.Echo age End If 

同様に、JScript、Visual Basic、C ++ Builderなどを䜿甚しおCachéDBMSを操䜜できたす。
泚より倚くの機胜を提䟛するJavaおよび.NETのネむティブむンタヌフェむスがいく぀かありたす。

埌期および早期バむンディング


DelphiのCachéオブゞェクトを操䜜するには、2぀のアプロヌチがありたす。

これらのアプロヌチにはそれぞれ長所ず短所がありたすが、互いに盞殺されたす。

遅延バむンディングでは、開発者がコヌドヒントを利甚できないため、コヌドの間違いを犯す可胜性が高く、プログラムの実行䞭にのみ怜出されたす。 コヌドは動的に実行されるため、操䜜の速床は事前バむンディングよりもわずかに遅くなりたす。

アヌリヌバむンディングにはこれらの欠点はありたせんが、デヌタベヌス内のナヌザヌクラスむンタヌフェむスを倉曎するずきや、CachéDBMSの新しいバヌゞョンに切り替えるずきは、クラむアントアプリケヌションのプロキシクラスを再生成する必芁があるため、費甚を支払う必芁がありたす。

これらのアプロヌチの䞡方を組み合わせるこずにより、最適なパフォヌマンスず䜿いやすさを実珟できたす。

このアプロヌチは遅延バむンディングよりも説明されおいないため、この蚘事では䞻に事前バむンディングを䜿甚したす。

Cachéオブゞェクトは、デフォルトでCにむンストヌルされる2぀のラむブラリを䜿甚しお動䜜したす。\ Program Files \ Common Files \ InterSystems \ Cache \ 

むンタヌシステムズの開発者は、 Caché5.1以降 、新しいCacheActiveX.dllラむブラリの䜿甚を匷くお勧めしたす。 CacheObject.dllラむブラリは 、叀いアプリケヌションずの互換性のためにのみ残されおいたす。 したがっお、この蚘事ではCacheActiveX.dllに基づいお説明したす。

これらのラむブラリず、新しいバヌゞョンにアップグレヌドするずきに考慮する必芁がある埮劙な違いの違いに぀いおは、「CacheObject.dllからのアップグレヌド」を参照しおください 。

䞊蚘のディレクトリで、あなたに圹立぀他のファむルを芋぀けるこずができたす

DelphiでCachéオブゞェクトActiveXコンポヌネントをむンポヌトおよびむンストヌルしたす


このセクションでは、事前バむンディングを䜿甚しおCachéDBMSのオブゞェクトを操䜜するために、Delphi環境で基本的なクラスずむンタヌフェむスを蚭定する方法に぀いお説明したす。 遅延バむンディングを䜿甚するには、このセクションをスキップできたす。

だから、順番に
  1. メニュヌ項目[ コンポヌネント ] > [コンポヌネントのむンポヌト... ]を遞択したす。

    画像

    画像

  2. たず、 CacheActiveX 2.0タむプラむブラリを遞択したす。

    画像

  3. コンポヌネントをむンストヌルするブックマヌクの名前ず他のパラメヌタヌを蚭定したす。

    画像

  4. これたで、むンストヌルせずにモゞュヌルを䜜成したした。

    画像

  5. 次のラむブラリに぀いお手順2〜4を繰り返したす。
    • CacheActiveX 2.0タむプラむブラリ ;
    • CacheList ActiveXコントロヌルモゞュヌル 。
    • CacheQuery ActiveX Controlモゞュヌル 。
    • DelphiCallback 1.0タむプラむブラリ 。
    • VisM 7.2 ActiveXコントロヌル 。
    • TL 1.0タむプラむブラリ 。
    泚Delphiに含たれおいるtlibimp.exeナヌティリティを䜿甚しおモゞュヌルを䜜成するこずもできたす。

  6. パッケヌゞタむプの新しいプロゞェクトを䜜成し、以前に䜜成したすべおのモゞュヌルを远加したす。 プロゞェクトをコンパむルし、パッケヌゞをむンストヌルしたす。 最終的には次のようになりたす。

    画像

    画像
    泚Delphiの以前のバヌゞョンでは、モゞュヌルを䜜成するプロセスが少し異なりたす。
    • メニュヌ項目[ プロゞェクト ] > [タむプラむブラリのむンポヌト... ]を遞択したす。

      画像

    • 䞊蚘の段萜を参照しおください。

カスタムプロキシクラスの生成


このセクションでは、事前バむンディングを䜿甚しおCachéDBMSのオブゞェクトを操䜜するためのカスタムプロキシクラスの生成に぀いお説明したす。 遅延バむンディングを䜿甚するには、このセクションをスキップできたす。

たず、必芁なすべおのナヌザヌクラスを含むODLファむルを生成する必芁がありたす。 これを行うには、 CachéDBMSで提䟛されるodl_generator.exeナヌティリティを䜿甚したす。

䟋
 odl_generator.exe -conn cn_iptcp:localhost[1972]:USER:_system:SYS -class-list test.txt -lib-name test -dir MIDL 

この䟋では、ナヌティリティはUSER領域に接続し、 test.txtファむルにリストされおいるクラスのプロキシクラスを生成し、結果をMIDL \ test.odlファむルに保存したす
泚 このようにしお取埗されたファむルは、 CacheActiveX.dllラむブラリで䜿甚するためのものです。 叀いラむブラリで動䜜するように蚭蚈されたODLファむルを生成するには、 SYSTEM.OBJクラスのExportODLメ゜ッドを䜿甚したす

䟋
set list= "%Library.Status,Sample.Person"
do $system .OBJ . ExportODL (list, "c:\MIDL\test.odl" , "-d" ,.err)

ODLファむルが受信されたので、TLBファむルを取埗するためにコンパむルし、次にPASファむルを取埗する必芁がありたす。 これを行うには、Visual C ++開発キットに含たれおいるmidl.exeたたはmktypelib.exeナヌティリティを䜿甚できたす。

䟋
 midl /I . test.odl /tlb test.tlb tlibimp.exe -C- -P+ -Hr- -Ha- -Hs- -XM- test.tlb 

NoteCachéDBMS MACプログラムを䜿甚しお、䞊蚘のすべおのステップを自動化できたす。

サヌバヌでの準備䜜業


テストデヌタベヌスに次のデヌタクラスを䜜成したしょう。

///
Class pas.s Extends %SerialObject
{

/// (64-).
Property
aInteger As %Integer ;

/// . - 50 .
Property
aString As %String ;

}

/// .
Class pas.a Extends %Persistent
{

/// aA;
Index
aAIndex On aA;

/// --, "". SQL foreign key.
Relationship
aA As pas.test [ Cardinality = one, Inverse = aChilds ];

Property aInteger As %Integer ;

Property aString As %String ;

}

/// %occIO.inc .
Include
%occIO

/// .
Class pas.test Extends %Persistent
{

/// , ;
Parameter
EXTENTQUERYSPEC As ROWSPEC [ Flags = LIST ] = aBoolean,aInteger,aString,aDate,aTimeStamp" ;

/// (true/false/null);
Property
aBoolean As %Boolean ;

Property aInteger As %Integer ;

Property aString As %String ;

/// ;
Property
aDate As %Date ;

/// +;
Property
aTimeStamp As %TimeStamp ;

/// (CLOB);
Property
aMemo As %GlobalCharacterStream ;

/// (BLOB);
Property
aPhoto As %GlobalBinaryStream ;

/// --, "". SQL .
Relationship
aChilds As pas.a [ Cardinality = many, Inverse = aA ];

/// - ;
///<br> SQL
///<br> -.
Property
aS As pas.s ;

/// - ;
///<br> SQL , , .
Property
aListOfString As list Of %String ;

/// - ;
///<br> SQL , , .
Property
aListOfA As list Of pas.a ;

/// - ;
///<br> SQL .
Property
aArrOfString As array Of %String ;

/// - ;
///<br> SQL .
Property
aArrOfA As array Of pas.a ;

/// .
/// <br> , .
Method
%OnBeforeSave( insert As %Boolean ) As %Status [ Private , ServerOnly = 1 ]
{
;
write "Hello from Cache! (" , $$$CurrentClass , ":" , $$$CurrentMethod , ")" ,!

quit $$$OK
}

/// - : .
Query
test1( ABoolean As %Boolean , AInteger As %Integer , AString As %String , ADate As %Date , ATimeStamp As %TimeStamp ) As %SQLQuery ( CONTAINID = 1 , ROWSPEC = "ID:%String,aBoolean:%Boolean,aInteger:%Integer,aString:%String,aDate:%Date,aTimeStamp:%TimeStamp" ) [ SqlProc ]
{
SELECT %ID , aBoolean , aInteger , aString , aDate , aTimeStamp FROM pas . test WHERE
( aBoolean = :ABoolean or :ABoolean is null )
AND (
aInteger = :AInteger or :AInteger is null )
AND (
aString = :AString or :AString is null )
AND (
aDate < :ADate or :ADate is null )
AND (
aTimeStamp <= :ATimeStamp or :ATimeStamp is null )
}

/// - : ""
///<br> %INLIST.
Query
test2( AList As %List ) As %SQLQuery ( CONTAINID = 1 , ROWSPEC = "ID:%String,aBoolean:%Boolean,aInteger:%Integer,aString:%String,aDate:%Date,aTimeStamp:%TimeStamp" ) [ SqlProc ]
{
SELECT %ID , aBoolean , aInteger , aString , aDate , aTimeStamp FROM pas . test WHERE ID %INLIST :AList
}

/// .
ClassMethod
test3( AList As %List ) As %Status
{
; AList
write AList

;
set ^pastest=AList

quit $$$OK
}

/// .
ClassMethod
test4() As %Status
{
quit $$$ERROR ( $$$GeneralError , "My error!" )
}

/// .
ClassMethod
test5( Arg1... As %List ) As %Status
{
;
write "Invocation has " , $get (Arg1, 0), " element" , $select (( $get (Arg1, 0)=1): "" , 1: "s" ),!
for i = 1 : 1 : $get (Arg1, 0)
{
write :( $data (Arg1(i))>0) "Argument[" ,i, "]:" ,?15, $get (Arg1(i), "<NULL>" ),!
}
quit $$$OK
}

/// : .
/// <br> .
/// <br>:
/// <br><var>ID</var> - ;
/// <br><var>A</var> - ;
/// <br><var>BLOB</var> - ;
/// <br><var>RS1</var> - , Borland ® MyBase (DataSnap (TM)) XML DataSet;
/// <br><var>RS2</var> - , Borland ® MyBase (DataSnap (TM)) XML DataSet;
ClassMethod
test6(
ID As %String ,
ByRef A As pas.a ,
Output BLOB As %BinaryStream ,
Output RS1 As %CharacterStream ,
Output RS2 As %CharacterStream ) As %Status
{
// ,
set A. aString =999

//
set BLOB= ##class ( %GlobalBinaryStream ). %New ()

//
do BLOB. Write ( "123" )

//
set RS1= ##class ( %GlobalCharacterStream ). %New ()
set RS2= ##class ( %GlobalCharacterStream ). %New ()

// , XML TClientDataSet
set cds= ##class ( %XML.ZMyBaseDataSet ). %New ()

//
do cds. Prepare ( "select * from pas.a where id %inlist ?" )

//
do cds. SetArgs ( $listbuild (1,2,3,9))

// XML
do cds. XMLExportToStream (.RS1)

// ( )
do cds. Close ()
do cds. Prepare ( "select ID,aBoolean,aInteger,aString,aDate,aTimeStamp from pas.test" )
do cds. XMLExportToStream (.RS2)
do cds. Close ()

// ""
quit $$$OK
}

}

これらのプロキシクラスず、次のクラスのプロキシクラスを生成したす。

生成されたPASファむルをプロゞェクトに接続するず、クラスの最も重芁なメ゜ッドずプロパティが利甚可胜になりたす。

画像
増す

Note ストリヌムクラスおよびその他のクラスは、CachéDBMSからむンポヌトしないでください 。これらはすでにCacheActiveX.dllラむブラリに接続されおおり、生成されたプロキシクラスず互換性がありたせん。

DBMSCachéぞの接続


遅延バむンディング叀いラむブラリ
 var _f:variant; begin _f:=CreateOleObject('CacheObject.Factory'); if _f.Connect(_f.ConnectDlg('+%up')) then ShowMessage('OK') else ShowMessage('ERROR'); 

遅延バむンディング新しいラむブラリ
 var _f:variant; begin _f:=CreateOleObject('CacheActiveX.Factory'); if _f.Connect(_f.ConnectDlg('+%up')) then ShowMessage('OK') else ShowMessage('ERROR'); 

事前バむンディング新しいラむブラリ
 type Tfm = class(TForm) f: TFactory; ... begin ... if f.Connect1(f.ConnectDlg('+%up')) then ShowMessage('OK') else ShowMessage('ERROR'); ... 

泚 叀いラむブラリはService_CacheDirectサヌビスず認蚌されおいないアクセスのみを䜿甚し 、新しいラむブラリはService_Bindingsおよびその他のアクセス方法を䜿甚したす。

TFactobryクラスおよび他のクラスのメ゜ッドの詳现な説明は、 ActiveX APIリファレンスにありたす。

アヌリヌバむンディングを䜿甚しおク゚リずメ゜ッドを呌び出す䟋


コヌルバック機胜を䜿甚する

DelphiのCallBack機胜を䜿甚するには、 DelphiCallback.dllファむルの既補のTCallbackクラスを䜿甚する必芁がありたす。
 var f:TFactory; Callback1: TCallback; mm: TMemo; ... f.SetOutput(Callback1.OleObject); ... procedure Tfm.Callback1TextChanged(Sender: TObject; const p_bstrText: WideString); begin mm.Lines.Append(p_bstrText); end; 

すべおのデヌタを削陀する

  mm.Lines.Text:='KillExtent'#10#13; //   test_(f.Static('pas.test')).SYS_KillExtent(1); a(f.Static('pas.a')).SYS_KillExtent(1); 

さたざたなタむプのオブゞェクトの䜜成、入力、保存、および解攟の䟋

Sergey Kudinovの蚘事「ActiveXで䜜業するずきのオブゞェクトを閉じる機胜、CPP-binding」で蚘事を読むこずができたす。

コヌド䟋
 ... uses test_TLB, AxCtrls, ComObj, ActiveX, Types; ... const N = 3; var i: integer; _t: test_; _a: A; _s: s; rel: RelationshipObject; listStr: ListOfDataTypes; listA: ListOfObjects; arrStr: ArrayOfDataTypes; arrA: ArrayOfObjects; stream: IDispatch; begin Screen.Cursor := crSQLWait; mm.Lines.Text:='Save'#10#13; try try //     pas.test _t := test_(f.New('pas.test')); //     _t.aBoolean := true; _t.aInteger := 50; _t.aString := ' '; //     null    // (%Integer,%Boolean,%Date  ..) Variant(_t).aDate := nil; _t.aDate := _t.aDateDisplayToLogical('02.03.2001'); //  t.aDate:=StrToDate('02.03.2001'); _t.aTimeStamp := _t.aTimeStampDisplayToLogical('1900-01-02 12:34:55'); //  t.aTimeStamp:=StrToDateTime('02.01.1900 12:34:55'); //     stream := _t.aMemo; ICharStream(stream).Write(' '); stream := nil; //     stream := _t.aPhoto; IBinaryStream(stream).FileRead('C:\test.jpg'); stream := nil; //  N ""   pas.a rel := RelationshipObject(_t.aChilds); for i := 1 to N do begin _a := A(f.New('pas.a')); _a.aInteger := i; _a.aString := 'rel' + IntToStr(i); rel.Insert(_a); _a.SYS_Close; end; _t.aChilds := rel; rel.SYS_Close; //     _s := s(_t.aS_); _s.aInteger := 1; _s.aString := 's1'; _s.SYS_Close; //     .     listStr := ListOfDataTypes(f.New('%ListOfDataTypes')); for i := 1 to N do listStr.Insert('str' + IntToStr(i)); _t.aListOfString := listStr; listStr.SYS_Close; //    pas.a     listA := ListOfObjects(f.New('%ListOfObjects')); for i := 1 to N do begin _a := A(f.New('pas.a')); _a.aInteger := i; _a.aString := 'listA' + IntToStr(i); listA.Insert(_a); _a.SYS_Close; end; _t.aListOfA := listA; listA.SYS_Close; //     .     arrStr := ArrayOfDataTypes(f.New('%ArrayOfDataTypes')); for i := 1 to N do arrStr.SetAt('astr' + IntToStr(i), 'arraykey' + IntToStr(i)); _t.aArrOfString := arrStr; arrStr.SYS_Close; //    pas.a     arrA := ArrayOfObjects(f.New('%ArrayOfObjects')); for i := 1 to N do begin _a := A(f.New('pas.a')); _a.aInteger := i; _a.aString := 'arrayA' + IntToStr(i); arrA.SetAt(_a, 'arraykey' + IntToStr(i)); _a.SYS_Close; end; _t.aArrOfA := arrA; arrA.SYS_Close; //         .   //          // . _t.SYS_Save(0); //          stream := _t.aPhoto; SetOlePicture(img.Picture, IBinaryStream(stream).GetPicture); stream := nil; //    .      //   ,  "" (%sysOrefs). //         //       . _t.SYS_Close; //     _t := nil; mm.Lines.Append('OK'); except on E: Exception do begin mm.Lines.Append(E.Message); end; end; finally Screen.Cursor := crDefault; //      , //      f.ForceSync; end; 

゚クステントリク゚スト保存されたクラスのすべおのむンスタンスを取埗

 var mm: TMemo; rs: TResultSet; ... mm.Lines.Text:='Extent'#10#13; rs.ConnectTo(IResultSet(f.ResultSet('pas.test', 'Extent'))); rs.Execute; while rs.Next do begin mm.Lines.Append(Format('ID = %s',[rs.GetDataAsString(1)])); mm.Lines.Append(Format('aBoolean = %s',[rs.Get('aBoolean')])); mm.Lines.Append(Format('aInteger = %s',[rs.Get('aInteger')])); mm.Lines.Append(Format('aString = %s',[rs.Get('aString')])); mm.Lines.Append(Format('aDate = %s',[rs.Get('aDate')])); mm.Lines.Append(Format('aTimeStamp = %s',[rs.Get('aTimeStamp')])); mm.Lines.Append('-----'); end; rs.Close; rs.Disconnect; 

ク゚リtest1

 var i: integer; ... mm.Lines.Text:='test1'#10#13; rs.ConnectTo(IResultSet(f.ResultSet('pas.test', 'test1'))); rs.SetParam(1, null); rs.SetParam(2, 50); rs.SetParam(3, null); rs.SetParam(4, '03.03.2001'); rs.SetParam(5, '1900-01-02 12:34:55.0'); rs.Execute; while rs.Next do begin for i := 1 to rs.GetColumnCount do mm.Lines.Append(rs.GetColumnName(i)+' = '+rs.GetDataAsString(i)); mm.Lines.Append('-----'); end; rs.Close; rs.Disconnect; 

ク゚リtest2

 var i: integer; syslist: TSyslist; ... mm.Lines.Text:='test2'#10#13; //    %List syslist.Clear; syslist.Add(1); syslist.Add(2); syslist.Add(3); rs.ConnectTo(IResultSet(f.ResultSet('pas.test', 'test2'))); rs.Execute(syslist.DefaultInterface); while rs.Next do begin for i := 1 to rs.GetColumnCount do mm.Lines.Append(rs.GetColumnName(i)+' = '+rs.GetDataAsString(i)); mm.Lines.Append('-----'); end; rs.Close; rs.Disconnect; 

メ゜ッドtest3。 クラスTSysListCachéのtypeListのオブゞェクトの操䜜

  mm.Lines.Text:='test3'#10#13; //    %List syslist.Clear; syslist.Add('16'); syslist.Add('42'); syslist.Add('35'); test_(f.Static('pas.test')).test3(syslist.DefaultInterface); 

メ゜ッドtest4。 ゚ラヌ凊理

  mm.Lines.Text:='test4'#10#13; try test_(f.Static('pas.test')).test4(); except on E: Exception do begin mm.Lines.Append(E.Message); end; end; 

Test5メ゜ッド

  mm.Lines.Text:='test5'#10#13; //    %List syslist.Clear; syslist.Add('16'); syslist.Add('42'); syslist.Add('35'); test_(f.Static('pas.test')).test5(syslist.DefaultInterface); 

Test6メ゜ッド

この䟋では、 XML.ZMyBaseDataSetクラスを䜿甚したす 。これは、 こちらにありたす 。 その助けにより、サヌバヌ䞊でBorland®MyBaseDataSnapTMXML DataSetの圢匏でデヌタを圢成するこずができたす。 Webサヌビスを含む。

 cds1,cds2:TClientDataSet; ... var _a:a; __a,blob,rs1,rs2:IDispatch; cs1,cs2:ICharStream; begin mm.Lines.Text:='test6'#10#13; try _a:=a(f.OpenId('pas.a','1')); __a:=_a; test_(f.Static('pas.test')).test6('1',__a,blob,rs1,rs2); cs1:=ICharStream(rs1); cs2:=ICharStream(rs2); mm.Lines.Append('A.aString = '+_a.aString); mm.Lines.Append('BLOB.Size = '+IntToStr(IBinaryStream(blob).size)); mm.Lines.Append('RS1.Size = '+IntToStr(cs1.size)); mm.Lines.Append('RS2.Size = '+IntToStr(cs2.size)); cds1.XMLData:=cs1.Data; cds2.XMLData:=cs2.Data; finally _a.SYS_Close; _a:=nil; __a:=nil; blob:=nil; rs1:=nil; rs2:=nil; cs1:=nil; cs2:=nil; f.ForceSync; end; 

UnicodeのデヌタのXML.ZMyBaseDataSetクラスおよびその他の未実珟デヌタ型をサポヌトするには、自分で倉曎する必芁がありたす。

アプリケヌション配垃

アプリケヌションで必芁なすべおのドラむバヌを新しいコンピュヌタヌにむンストヌルするには、 C\ Program Files \ Common Files \ InterSystems \ Cache \ディレクトリからすべおのファむル簡単にするために必芁なファむルのセットを制限できたすをコピヌし、 regsvr32ナヌティリティを䜿甚しおそれらの䞀郚をシステムに登録する必芁がありたす.exe

䟋
 regsvr32.exe /s "C:\Program Files (x86)\Common Files\Intersystems\Cache\CacheQuery.ocx" regsvr32.exe /s "C:\Program Files (x86)\Common Files\Intersystems\Cache\CacheFormWizard.dll" regsvr32.exe /s "C:\Program Files (x86)\Common Files\Intersystems\Cache\CacheList.ocx" regsvr32.exe /s "C:\Program Files (x86)\Common Files\Intersystems\Cache\CacheActiveX.dll" regsvr32.exe /s "C:\Program Files (x86)\Common Files\Intersystems\Cache\vism.ocx" regsvr32.exe /s "C:\Program Files (x86)\Common Files\Intersystems\Cache\TL.dll" 

゜ヌスコヌド

Source: https://habr.com/ru/post/J144306/


All Articles