Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / Delphi [игнор отключен] [закрыт для гостей] / SMART SSD в Delphi / 8 сообщений из 8, страница 1 из 1
14.06.2019, 08:48
    #39826405
Maxwellion
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
Товарищи форумчане, подскажите как в Delphi можно получить SMART диска SSD?
Для не IDE-дисков, вывод информации в Memo1, я использовал такой код:
Код: pascal
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
265.
266.
267.
268.
269.
270.
271.
272.
273.
274.
275.
276.
277.
278.
279.
280.
281.
282.
283.
284.
285.
286.
287.
288.
289.
290.
unit Unit1;

interface

uses
  Windows, SMART, Messages, SysUtils, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IdMultipartFormData, IdBaseComponent, IdComponent,
  IdTCPConnection, IdTCPClient, IdHTTP;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Button1: TButton;
    IdHTTP: TIdHTTP;
    function SMART_DoIDENTIFY(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bIDCmd: BYTE;  bDriveNum: BYTE): BOOL;
    function SMART_DoEnableSMART(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bDriveNum: BYTE): BOOL;
    function SMART_DoReadAttributesCmd(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bDriveNum: BYTE): BOOL;
    function SMART_DoReadThresholdsCmd(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bDriveNum: BYTE): BOOL;
    function SMART_OpenSMART(DrvNum:Byte): THandle;
    function SMART_GetVersionSMART(hSMARTIOCTL: THandle):TGetVersionOutParams;

    procedure SMART_DoPrintData(pAttrBuffer: PCHAR;  pThrsBuffer: PCHAR);
    procedure SMART_ChangeByteOrder(szString: PCHAR;  uscStrSize: USHORT);
    procedure GET_INFO_SMART;
    procedure Button1Click(Sender: TObject);
    procedure reporting(txt:String);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1                 : TForm1;
  CurrentHandle         : THandle;
  OSVersionInfo         : TOSVersionInfo;


// Определение глобальных буферов.
  AttrOutCmd            : array [0..(sizeof(SENDCMDOUTPARAMS)-1)+(READ_ATTRIBUTE_BUFFER_SIZE-1)] of BYTE;
  ThreshOutCmd          : array [0..(sizeof(SENDCMDOUTPARAMS)-1)+(READ_THRESHOLD_BUFFER_SIZE-1)] of BYTE;
  IdOutCmd              : array [0..(sizeof(SENDCMDOUTPARAMS)-1)+(IDENTIFY_BUFFER_SIZE-1)] of BYTE;

implementation

{$R *.dfm}

// *****************************************************************************
// ** DoIDENTIFY
// **
// ** Назначение: Посылает диску команду IDENTIFY
// ** bDriveNum = 0-3
// ** bIDCmd = IDE_ID_FUNCTION или IDE_ATAPI_ID
// *****************************************************************************
function TForm1.SMART_DoIDENTIFY(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bIDCmd: BYTE;  bDriveNum: BYTE): BOOL;
var
  lpcbBytesReturned     : DWORD;
begin
  pSCIP.cBufferSize := IDENTIFY_BUFFER_SIZE;
  pSCIP.irDriveRegs.bFeaturesReg := 0;
  pSCIP.irDriveRegs.bSectorCountReg := 1;
  pSCIP.irDriveRegs.bSectorNumberReg := 1;
  pSCIP.irDriveRegs.bCylLowReg := 0;
  pSCIP.irDriveRegs.bCylHighReg := 0;
  pSCIP.irDriveRegs.bDriveHeadReg := $A0 OR ((bDriveNum AND 1) SHL 4);// Вычисляем номер накопителя.
  pSCIP.irDriveRegs.bCommandReg := bIDCmd;// Команда может идентифицировать IDE или ATAPI.
  pSCIP.bDriveNumber := bDriveNum;
  result := DeviceIoControl(hSMARTIOCTL,DFP_RECEIVE_DRIVE_DATA,pSCIP,sizeof(SENDCMDINPARAMS)-1,pSCOP,sizeof(SENDCMDOUTPARAMS)+IDENTIFY_BUFFER_SIZE-1,lpcbBytesReturned,nil);
end;

// *****************************************************************************
// ** DoEnableSMART
// ** Назначение: Посылает диску команду SMART_ENABLE_SMART_OPERATIONS
// ** bDriveNum = 0-3
// *****************************************************************************
function TForm1.SMART_DoEnableSMART(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bDriveNum: BYTE): BOOL;
var
  lpcbBytesReturned     : DWORD;
begin
  pSCIP.cBufferSize := 0;
  pSCIP.irDriveRegs.bFeaturesReg := SMART_ENABLE_SMART_OPERATIONS;
  pSCIP.irDriveRegs.bSectorCountReg := 1;
  pSCIP.irDriveRegs.bSectorNumberReg := 1;
  pSCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
  pSCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;
  pSCIP.irDriveRegs.bDriveHeadReg := $A0 OR ((bDriveNum AND 1) SHL 4);// Вычисляем номер накопителя.
  pSCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
  pSCIP.bDriveNumber := bDriveNum;
  result := DeviceIoControl(hSMARTIOCTL,DFP_SEND_DRIVE_COMMAND,pSCIP,sizeof(SENDCMDINPARAMS)-1,pSCOP,sizeof(SENDCMDOUTPARAMS)-1,lpcbBytesReturned,nil);
end;

// *****************************************************************************
// ** DoReadAttributesCmd
// ** Назначение: Посылает диску команду SMART_READ_ATTRIBUTE_VALUES
// ** bDriveNum = 0-3
// *****************************************************************************
function TForm1.SMART_DoReadAttributesCmd(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bDriveNum: BYTE): BOOL;
var
  cbBytesReturned       : DWORD;
begin
  pSCIP.cBufferSize := READ_ATTRIBUTE_BUFFER_SIZE;
  pSCIP.irDriveRegs.bFeaturesReg := SMART_READ_ATTRIBUTE_VALUES;
  pSCIP.irDriveRegs.bSectorCountReg := 1;
  pSCIP.irDriveRegs.bSectorNumberReg := 1;
  pSCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
  pSCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;
  pSCIP.irDriveRegs.bDriveHeadReg := $A0 OR ((bDriveNum AND 1) SHL 4);// Вычисляем номер накопителя.
  pSCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
  pSCIP.bDriveNumber := bDriveNum;
  result := DeviceIoControl(hSMARTIOCTL,DFP_RECEIVE_DRIVE_DATA,pSCIP,sizeof(SENDCMDINPARAMS)-1,pSCOP,sizeof(SENDCMDOUTPARAMS)+READ_ATTRIBUTE_BUFFER_SIZE-1,cbBytesReturned,nil);
end;

// *****************************************************************************
// ** DoReadThresholdsCmd
// ** Назначение: Посылает диску команду SMART_READ_ATTRIBUTE_THRESHOLDS
// ** bDriveNum = 0-3
// *****************************************************************************
function TForm1.SMART_DoReadThresholdsCmd(hSMARTIOCTL: THandle;  pSCIP: PSENDCMDINPARAMS;  pSCOP: PSENDCMDOUTPARAMS;  bDriveNum: BYTE): BOOL;
var
  cbBytesReturned       : DWORD;
begin
  pSCIP.cBufferSize := READ_THRESHOLD_BUFFER_SIZE;
  pSCIP.irDriveRegs.bFeaturesReg := SMART_READ_ATTRIBUTE_THRESHOLDS;
  pSCIP.irDriveRegs.bSectorCountReg := 1;
  pSCIP.irDriveRegs.bSectorNumberReg := 1;
  pSCIP.irDriveRegs.bCylLowReg := SMART_CYL_LOW;
  pSCIP.irDriveRegs.bCylHighReg := SMART_CYL_HI;
  pSCIP.irDriveRegs.bDriveHeadReg := $A0 OR ((bDriveNum AND 1) SHL 4);// Вычисляем номер накопителя.
  pSCIP.irDriveRegs.bCommandReg := IDE_EXECUTE_SMART_FUNCTION;
  pSCIP.bDriveNumber := bDriveNum;
  result := DeviceIoControl(hSMARTIOCTL,DFP_RECEIVE_DRIVE_DATA,pSCIP,sizeof(SENDCMDINPARAMS)-1,pSCOP,sizeof(SENDCMDOUTPARAMS)+READ_THRESHOLD_BUFFER_SIZE-1, cbBytesReturned,nil);
end;

// *****************************************************************************
// ** DoPrintData
// ** FUNCTION: Отображает атрибуты и пороговые значения SMART
// *****************************************************************************
procedure TForm1.SMART_DoPrintData(pAttrBuffer: PCHAR;  pThrsBuffer: PCHAR);
var
  i, z                     : integer;
  pDA                   : PDRIVEATTRIBUTE;
  pAT                   : PATTRTHRESHOLD;
  s : STRING;
begin
  // Выводим информацию: идентификатор и название атрибута, его текущее и пороговое значение
  pDA:= PDRIVEATTRIBUTE(@pAttrBuffer[2]);
  pAT:= PATTRTHRESHOLD(@pThrsBuffer[2]);
  FOR i:=0 TO NUM_ATTRIBUTE_STRUCTS-1 DO BEGIN
    IF(pDA.bAttrID <> 0) THEN BEGIN
	  s := '';
      for z:= 5 downto 0 do s := s + IntToStr(pDA.bRawValue[z]);
      Memo1.Lines.Add('raw'+#$9+inttostr(pDA.bAttrID)+#$9+inttostr(pDA.bAttrValue)+#$9+inttostr(pAT.bWarrantyThreshold)+#$9+inttostr(pDA.bWorstValue)+#$9+'##'+#$9+IntToStr(pDA.bRawValue[5])+#$9+IntToStr(pDA.bRawValue[4])+#$9+IntToStr(pDA.bRawValue[3])+#$9+IntToStr(pDA.bRawValue[2])+#$9+IntToStr(pDA.bRawValue[1])+#$9+IntToStr(pDA.bRawValue[0])+#$9+s);
    END;
    inc(pDA);
    inc(pAT);
  END;
end;

// *****************************************************************************
// ** Меняем WORD-массив на BYTE-массив ****************************************
// *****************************************************************************
procedure TForm1.SMART_ChangeByteOrder(szString: PCHAR;  uscStrSize: USHORT);
var
  i                     : USHORT;
  temp                  : CHAR;
begin
  i := 0;
  WHILE i<uscStrSize DO BEGIN
    temp := szString[i];
    szString[i] := szString[i+1];
    szString[i+1] := temp;
    i:= i + 2;
  END;
end;

// *****************************************************************************
// ** Открываем дескриптор(хэндл) SMART для операций посредством DeviceIoControl.
// *****************************************************************************
function TForm1.SMART_OpenSMART(DrvNum:Byte): THandle;
var
  hSMARTIOCTL           : THandle;
begin
  IF OSVersionInfo.dwPlatformId = VER_PLATFORM_WIN32_NT THEN BEGIN // Windows NT, Windows 2000
    hSMARTIOCTL := CreateFile(PChar('\\.\PhysicalDrive'+inttostr(DrvNum)),GENERIC_READ OR GENERIC_WRITE,FILE_SHARE_READ OR FILE_SHARE_WRITE,nil,OPEN_EXISTING,0,0);
  END
  ELSE BEGIN // Windows 95 OSR2, Windows 98
    hSMARTIOCTL := CreateFile('\\.\SMARTVSD',0,0,nil,CREATE_NEW,0,0);
    IF hSMARTIOCTL = INVALID_HANDLE_VALUE THEN reporting('Невозможно открыть SMARTVSD, код ошибки: '+inttostr(GetLastError)+' - '+SysErrorMessage(GetLastError))
  END;
  result:= hSMARTIOCTL;
end;


// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
function TForm1.SMART_GetVersionSMART(hSMARTIOCTL: THandle):TGetVersionOutParams;
var
  VersionParams         : TGetVersionOutParams;
  cbBytesReturned       : DWORD;
begin
  ZeroMemory(@VersionParams,sizeof(TGetVersionOutParams));
  IF NOT DeviceIoControl(hSMARTIOCTL,DFP_GET_VERSION,nil,0,@VersionParams,sizeof(VersionParams),cbBytesReturned,nil) THEN reporting(SysErrorMessage(GetLastError));
  result:=VersionParams;
end;


// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
procedure TForm1.GET_INFO_SMART;
var
  hSMARTIOCTL           : THandle;
  i                     : integer;
  VersionParams         : TGetVersionOutParams;
  bIDCmd, bDfpDriveMap  : BYTE; // Команда идентификации IDE или ATAPI
  scip                  : TSendCmdInParams;
  OutCmd                : TSendCmdOutParams;
  bSuccess              : bool;
  pids                  : PIDSECTOR;
begin
  OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  GetVersionEx(OSVersionInfo);
  bDfpDriveMap := 0;
  CurrentHandle := SMART_OpenSMART(0);

  // Получаем версию и т.п. SMART IOCTL
  VersionParams := SMART_GetVersionSMART(CurrentHandle);

  FOR i := 0 TO MAX_IDE_DRIVES-1 DO BEGIN
    Memo1.Lines.Add(IntToStr(i));
    hSMARTIOCTL := SMART_OpenSMART(i);
    // Если устройство с номером "i" - IDE, передаём ему команды.
    IF VersionParams.bIDEDeviceMap SHR i AND 1=1 THEN BEGIN
      // Игнорируем ATAPI-устройства.
      IF VersionParams.bIDEDeviceMap SHR i AND $10=0 THEN BEGIN
        ZeroMemory(@scip,sizeof(scip));
        ZeroMemory(@OutCmd,sizeof(OutCmd));
        // Пытаемся активировать SMART.
        IF SMART_DoEnableSMART(hSMARTIOCTL,@scip,@OutCmd,i) THEN BEGIN
          // Помечаем диск, как накопитель с активным SMART
          bDfpDriveMap := bDfpDriveMap OR (1 SHL i);
          // Теперь получаем ID сектора для всех устройств IDE в системе.
          // Если устройство - ATAPI, используем команду IDE_ATAPI_ID,
          // в противном случае используем команду IDE_ID_FUNCTION.
          IF VersionParams.bIDEDeviceMap SHR i AND $10=1 THEN bIDCmd := IDE_ATAPI_ID
          ELSE bIDCmd := IDE_ID_FUNCTION;
          ZeroMemory(@scip,sizeof(scip));
          ZeroMemory(@IdOutCmd,sizeof(IdOutCmd));
          IF SMART_DoIDENTIFY(hSMARTIOCTL,@scip,PSENDCMDOUTPARAMS(@IdOutCmd),bIDCmd,i) THEN BEGIN
            pids := @PSENDCMDOUTPARAMS(@IdOutCmd).bBuffer;
            SMART_ChangeByteOrder(pids.sModelNumber,sizeof(pids.sModelNumber));
            SMART_ChangeByteOrder(pids.sFirmwareRev,sizeof(pids.sFirmwareRev));
            SMART_ChangeByteOrder(pids.sSerialNumber,sizeof(pids.sSerialNumber));
            Memo1.Lines.Add('disk_1#'+inttostr(i)+'#'+trim(pids.sModelNumber)+'#'+trim(pids.sFirmwareRev)+'#'+trim(pids.sSerialNumber+'#'));
          END
          ELSE reporting('Команда Identify не выполнена на диске: '+inttostr(i)+#10#13+'DriverStatus: bDriverError = '+inttostr(PSENDCMDOUTPARAMS(@IdOutCmd).DriverStatus.bDriverError)+', bIDEStatus = '+inttostr(PSENDCMDOUTPARAMS(@IdOutCmd).DriverStatus.bIDEStatus));
        END
        ELSE reporting('Команда запуска S.M.A.R.T. не выполнена, диск: '+inttostr(i)+#10#13+'DriverStatus: bDriverError = '+inttostr(OutCmd.DriverStatus.bDriverError)+', bIDEStatus = '+inttostr(OutCmd.DriverStatus.bIDEStatus));
      END;
    END;
  END;
  // Перебираем все возможные IDE-приводы и посылаем команды тем, которые поддерживают SMART.
  FOR i := 0 TO MAX_IDE_DRIVES-1 DO BEGIN
    IF bDfpDriveMap SHR i AND 1=1 THEN BEGIN
      ZeroMemory(@AttrOutCmd,sizeof(AttrOutCmd));
      ZeroMemory(@ThreshOutCmd,sizeof(ThreshOutCmd));
      bSuccess := SMART_DoReadAttributesCmd(CurrentHandle,@scip,PSENDCMDOUTPARAMS(@AttrOutCmd),i);
      IF bSuccess=false THEN reporting('Ошибка при выполнении команды чтения атрибутов S.M.A.R.T. на диске: '+inttostr(i)+#10#13+'DriverStatus: bDriverError = '+inttostr(PSENDCMDOUTPARAMS(@AttrOutCmd).DriverStatus.bDriverError)+', bIDEStatus = '+inttostr(PSENDCMDOUTPARAMS(@AttrOutCmd).DriverStatus.bIDEStatus))
        // Команда чтения атрибутов выполнена успешно. Пытаемся прочитать пороговые значения атрибутов.
      ELSE
        IF NOT SMART_DoReadThresholdsCmd(CurrentHandle,@scip,PSENDCMDOUTPARAMS(@ThreshOutCmd),i) THEN reporting('Ошибка при выполнении команды чтения пороговых значений атрибутов S.M.A.R.T. на диске: '+inttostr(i)+#10#13+'DriverStatus: bDriverError = '+inttostr(PSENDCMDOUTPARAMS(@ThreshOutCmd).DriverStatus.bDriverError)+', bIDEStatus = '+inttostr(PSENDCMDOUTPARAMS(@ThreshOutCmd).DriverStatus.bIDEStatus));
      // Если функции DoReadAttributesCmd и DoReadThresholdsCmd выполнены успешно,
      // процедура DoPrintData выводит оба значения атрибутов. Если DoReadThresholdsCmd
      // не поддерживается, выводятся только текущие значения атрибутов
      IF bSuccess<>false THEN SMART_DoPrintData(@PSENDCMDOUTPARAMS(@AttrOutCmd).bBuffer,@PSENDCMDOUTPARAMS(@ThreshOutCmd).bBuffer);
    end;
  END;
end;

// *****************************************************************************
// *****************************************************************************
// *****************************************************************************
procedure TForm1.Button1Click(Sender: TObject);
begin
  GET_INFO_SMART;
end;

end.



Для IDE/ATAPI устройств информацию показывает отлично, но для SSD полный голяк. Как исправить? Подскажите, направьте, дайте ссылку :)
...
Рейтинг: 0 / 0
03.07.2019, 14:37
    #39833315
Maxwellion
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
Никто не читал СМАРТ с SSDшек? :(
...
Рейтинг: 0 / 0
03.07.2019, 14:55
    #39833341
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
...
Рейтинг: 0 / 0
03.07.2019, 15:18
    #39833365
Gator
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
Maxwellion,

ЕМНИП Wmi Delphi Code Creator это умел?
...
Рейтинг: 0 / 0
04.07.2019, 09:17
    #39833656
Maxwellion
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
X-Cite,

Там на С++, я в нём не силён:( Скопировал код, при компиляции ругается на
Код: plaintext
1.
[bcc32 Error] Unit1.cpp(81): E2451 Undefined symbol 'SENDCMDINPARAMS'
...
Рейтинг: 0 / 0
04.07.2019, 11:07
    #39833729
X-Cite
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ntdddisk/ns-ntdddisk-_sendcmdinparams

Опишите структуру самостоятельно, если ее нет из коробки...
...
Рейтинг: 0 / 0
05.07.2019, 07:14
    #39834147
makhaon
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
Maxwellion,

существуют конвертеры заголовков, если вручную сложно.
...
Рейтинг: 0 / 0
05.07.2019, 09:21
    #39834168
defecator
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
SMART SSD в Delphi
makhaonMaxwellion,

существуют конвертеры заголовков, если вручную сложно.
лучше вручную, всё равно за конверторами потом и мусор чистить, и правильность проверять
...
Рейтинг: 0 / 0
Форумы / Delphi [игнор отключен] [закрыт для гостей] / SMART SSD в Delphi / 8 сообщений из 8, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]