Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 18.02.2017, 09:13
Аватар для ffpereverzev
ffpereverzev ffpereverzev вне форума
Новичок
 
Регистрация: 14.02.2017
Сообщения: 50
Версия Delphi: Delphi 7
Репутация: 10
Вопрос Запуск системной утилиты из приложения

Доброго времени суток, уважаемы форумчане!
Что-то не могу найти на форумах похожих тем (наверное плохо ищу), поэтому буду рад любой помощи.
Суть вот в чем. При запуске из командной строки (с правами администратора) какой-нибудь системной утилиты (например sfc /scannow или powercfg /energy) все работает отлично и замечательно. Но стоит запустить эти же утилиты из приложения - начинаются проблемы. Пробовал решить их и так, и сяк - ничего не получается. Под "и так, и сяк" я понимаю ShellExecute(), WinExec(); CreateProcess(). Всегда одно и то же.
Привожу пример выполнения команды powercfg /energy:

С помощью CreateProcess:

Код:
if (Radiobutton1.Checked = True) then 
  FillChar(si,SizeOf(si),0);
  si.cb := SizeOf(si);
  si.dwFlags := STARTF_USESHOWWINDOW;
  si.wShowWindow := SW_SHOWNORMAL;
    if CreateProcess(nil, 'cmd.exe /K "powercfg /energy"', nil, nil, false, 0, nil, nil, si, pi) then begin
      CloseHandle(pi.hProcess);
    end else begin
      MessageBox(0, PChar(IntToStr(GetLastError())), nil, 0);
end;

С помощью ShellExecute:

Код:
if (Radiobutton1.Checked = True) then 
    begin
      Nrg_btn:='/K powercfg /energy'; 
      ShellExecute(Handle, nil, 'cmd.exe', PChar(Nrg_btn), nil, SW_SHOW);
    end;

P.S. Ошибка при выполнении:
Цитата:
Не удалось загрузить библиотеку диагностики эффективности энергопотребления (energy.dll)
Ответить с цитированием
  #2  
Старый 18.02.2017, 13:48
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Очевидно же,в вопросе и сам ответ:
Цитата:
Сообщение от ffpereverzev
...При запуске из командной строки (с правами администратора) какой-нибудь системной утилиты (например sfc /scannow или powercfg /energy) все работает отлично и замечательно...
видимо нужно предварительно получить дополнительные привилегии посредством вызова AdjustTokenPrivileges
Код:
const 
SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege'; 
SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege'; 
SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege'; 
SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege'; 
SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege'; 
SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege'; 
SE_TCB_NAME = 'SeTcbPrivilege'; 
SE_SECURITY_NAME = 'SeSecurityPrivilege'; 
SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege'; 
SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege'; 
SE_SYSTEM_PROFILE_NAME = 'SeSystemProfilePrivilege'; 
SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege'; 
SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege'; 
SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege'; 
SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege'; 
SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege'; 
SE_BACKUP_NAME = 'SeBackupPrivilege'; 
SE_RESTORE_NAME = 'SeRestorePrivilege'; 
SE_SHUTDOWN_NAME = 'SeShutdownPrivilege'; 
SE_DEBUG_NAME = 'SeDebugPrivilege'; 
SE_AUDIT_NAME = 'SeAuditPrivilege'; 
SE_SYSTEM_ENVIRONMENT_NAME = 'SeSystemEnvironmentPrivilege'; 
SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege'; 
SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege'; 
SE_UNDOCK_NAME = 'SeUndockPrivilege'; 
SE_SYNC_AGENT_NAME = 'SeSyncAgentPrivilege'; 
SE_ENABLE_DELEGATION_NAME = 'SeEnableDelegationPrivilege'; 
SE_MANAGE_VOLUME_NAME = 'SeManageVolumePrivilege'; 

// Enables or disables privileges debending on the bEnabled 
function NTSetPrivilege(sPrivilege: string; bEnabled: Boolean): Boolean; 
var 
hToken: THandle; 
TokenPriv: TOKEN_PRIVILEGES; 
PrevTokenPriv: TOKEN_PRIVILEGES; 
ReturnLength: Cardinal; 
begin 
Result := True; 
// Only for Windows NT/2000/XP and later. 
if not (Win32Platform = VER_PLATFORM_WIN32_NT) then Exit; 
Result := False; 

// obtain the processes token 
if OpenProcessToken(GetCurrentProcess(), 
   TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then 
 begin 
   try 
     // Get the locally unique identifier (LUID) . 
     if LookupPrivilegeValue(nil, PChar(sPrivilege), 
       TokenPriv.Privileges[0].Luid) then 
     begin 
       TokenPriv.PrivilegeCount := 1; // one privilege to set 

       case bEnabled of 
        True: TokenPriv.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED; 
        False:TokenPriv.Privileges[0].Attributes:= 0; 
       end; 

       ReturnLength := 0; // replaces a var parameter 
       PrevTokenPriv := TokenPriv; 

       // enable or disable the privilege 
        AdjustTokenPrivileges(hToken, False, TokenPriv, SizeOf(PrevTokenPriv), 
         PrevTokenPriv, ReturnLength); 
     end; 
   finally 
     CloseHandle(hToken); 
   end; 
end; 
// test the return value of AdjustTokenPrivileges. 
Result := GetLastError = ERROR_SUCCESS; 
if not Result then raise Exception.Create(SysErrorMessage(GetLastError)); 
end;
©Drkb::02246
Ответить с цитированием
Этот пользователь сказал Спасибо Alegun за это полезное сообщение:
ffpereverzev (18.02.2017)
  #3  
Старый 18.02.2017, 14:24
Аватар для ffpereverzev
ffpereverzev ffpereverzev вне форума
Новичок
 
Регистрация: 14.02.2017
Сообщения: 50
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Решение, конечно, элегантное и не лишено смысла, но не помогло. Результат точно такой же.
Да, я написал, что от имени администратора все хорошо, но забыл упомянуть, что все хорошо, когда я ОТДЕЛЬНО запускаю командную строку от имени администратора. Если я запускаю свою программу от имени администратора, то, вроде как, командная строка запускается тоже от имени администратора (факт), но результат, почему-то другой.

Цитата:
Сообщение от Alegun
Очевидно же,в вопросе и сам ответ:видимо нужно предварительно получить дополнительные привилегии посредством вызова AdjustTokenPrivileges
Код:
const 
SE_CREATE_TOKEN_NAME = 'SeCreateTokenPrivilege'; 
SE_ASSIGNPRIMARYTOKEN_NAME = 'SeAssignPrimaryTokenPrivilege'; 
SE_LOCK_MEMORY_NAME = 'SeLockMemoryPrivilege'; 
SE_INCREASE_QUOTA_NAME = 'SeIncreaseQuotaPrivilege'; 
SE_UNSOLICITED_INPUT_NAME = 'SeUnsolicitedInputPrivilege'; 
SE_MACHINE_ACCOUNT_NAME = 'SeMachineAccountPrivilege'; 
SE_TCB_NAME = 'SeTcbPrivilege'; 
SE_SECURITY_NAME = 'SeSecurityPrivilege'; 
SE_TAKE_OWNERSHIP_NAME = 'SeTakeOwnershipPrivilege'; 
SE_LOAD_DRIVER_NAME = 'SeLoadDriverPrivilege'; 
SE_SYSTEM_PROFILE_NAME = 'SeSystemProfilePrivilege'; 
SE_SYSTEMTIME_NAME = 'SeSystemtimePrivilege'; 
SE_PROF_SINGLE_PROCESS_NAME = 'SeProfileSingleProcessPrivilege'; 
SE_INC_BASE_PRIORITY_NAME = 'SeIncreaseBasePriorityPrivilege'; 
SE_CREATE_PAGEFILE_NAME = 'SeCreatePagefilePrivilege'; 
SE_CREATE_PERMANENT_NAME = 'SeCreatePermanentPrivilege'; 
SE_BACKUP_NAME = 'SeBackupPrivilege'; 
SE_RESTORE_NAME = 'SeRestorePrivilege'; 
SE_SHUTDOWN_NAME = 'SeShutdownPrivilege'; 
SE_DEBUG_NAME = 'SeDebugPrivilege'; 
SE_AUDIT_NAME = 'SeAuditPrivilege'; 
SE_SYSTEM_ENVIRONMENT_NAME = 'SeSystemEnvironmentPrivilege'; 
SE_CHANGE_NOTIFY_NAME = 'SeChangeNotifyPrivilege'; 
SE_REMOTE_SHUTDOWN_NAME = 'SeRemoteShutdownPrivilege'; 
SE_UNDOCK_NAME = 'SeUndockPrivilege'; 
SE_SYNC_AGENT_NAME = 'SeSyncAgentPrivilege'; 
SE_ENABLE_DELEGATION_NAME = 'SeEnableDelegationPrivilege'; 
SE_MANAGE_VOLUME_NAME = 'SeManageVolumePrivilege'; 

// Enables or disables privileges debending on the bEnabled 
function NTSetPrivilege(sPrivilege: string; bEnabled: Boolean): Boolean; 
var 
hToken: THandle; 
TokenPriv: TOKEN_PRIVILEGES; 
PrevTokenPriv: TOKEN_PRIVILEGES; 
ReturnLength: Cardinal; 
begin 
Result := True; 
// Only for Windows NT/2000/XP and later. 
if not (Win32Platform = VER_PLATFORM_WIN32_NT) then Exit; 
Result := False; 

// obtain the processes token 
if OpenProcessToken(GetCurrentProcess(), 
   TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken) then 
 begin 
   try 
     // Get the locally unique identifier (LUID) . 
     if LookupPrivilegeValue(nil, PChar(sPrivilege), 
       TokenPriv.Privileges[0].Luid) then 
     begin 
       TokenPriv.PrivilegeCount := 1; // one privilege to set 

       case bEnabled of 
        True: TokenPriv.Privileges[0].Attributes:= SE_PRIVILEGE_ENABLED; 
        False:TokenPriv.Privileges[0].Attributes:= 0; 
       end; 

       ReturnLength := 0; // replaces a var parameter 
       PrevTokenPriv := TokenPriv; 

       // enable or disable the privilege 
        AdjustTokenPrivileges(hToken, False, TokenPriv, SizeOf(PrevTokenPriv), 
         PrevTokenPriv, ReturnLength); 
     end; 
   finally 
     CloseHandle(hToken); 
   end; 
end; 
// test the return value of AdjustTokenPrivileges. 
Result := GetLastError = ERROR_SUCCESS; 
if not Result then raise Exception.Create(SysErrorMessage(GetLastError)); 
end;
©Drkb::02246
Ответить с цитированием
  #4  
Старый 18.02.2017, 14:45
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Цитата:
Сообщение от ffpereverzev
...забыл упомянуть, что все хорошо, когда я ОТДЕЛЬНО запускаю командную строку от имени администратора. Если я запускаю свою программу от имени администратора, то, вроде как, командная строка запускается тоже от имени администратора (факт), но результат, почему-то другой.
А не факт, что одмином запускается, утилита ведь системная и должна выпоняться из-под System, можно кстати и проверить на администратора
Код:
type
PTOKEN_GROUPS = TOKEN_GROUPS^;

function RunningAsAdministrator(): Boolean;
var
SystemSidAuthority: SID_IDENTIFIER_AUTHORITY = SECURITY_NT_AUTHORITY;
psidAdmin: PSID;
ptg: PTOKEN_GROUPS = nil;
htkThread: Integer; { HANDLE }
cbTokenGroups: Longint; { DWORD }
iGroup: Longint; { DWORD }
bAdmin: Boolean;
begin
Result := false;
if not OpenThreadToken(GetCurrentThread(), // get security token
   TOKEN_QUERY, FALSE, htkThread) then
   if GetLastError() = ERROR_NO_TOKEN then
   begin
     if not OpenProcessToken(GetCurrentProcess(),
       TOKEN_QUERY, htkThread) then
       Exit;
   end   else     Exit;
if GetTokenInformation(htkThread, // get #of groups
   TokenGroups, nil, 0, cbTokenGroups) then   Exit;
if GetLastError() <> ERROR_INSUFFICIENT_BUFFER then   Exit;
ptg := PTOKEN_GROUPS(getmem(cbTokenGroups));
if not Assigned(ptg) then   Exit;
if not GetTokenInformation(htkThread, // get groups
   TokenGroups, ptg, cbTokenGroups, cbTokenGroups) then  Exit;
if not AllocateAndInitializeSid(SystemSidAuthority,
   2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
   0, 0, 0, 0, 0, 0, psidAdmin) then   Exit;
iGroup := 0;
while iGroup < ptg^.GroupCount do // check administrator group
begin
   if EqualSid(ptg^.Groups[iGroup].Sid, psidAdmin) then
   begin
     Result := TRUE;
     break;
   end;
   Inc(iGroup);
end;
FreeSid(psidAdmin);
end;
©там же
Ответить с цитированием
  #5  
Старый 18.02.2017, 15:10
Аватар для ffpereverzev
ffpereverzev ffpereverzev вне форума
Новичок
 
Регистрация: 14.02.2017
Сообщения: 50
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Чего-то я не очень понимаю, куда мне деть этот код. У меня не консольное приложение. А при объявлении новой категории
Код:
type
PTOKEN_GROUPS = TOKEN_GROUPS^;
проект перестает собираться. Ну и дальше по коду вылезает целая куча ошибок.

Цитата:
Сообщение от Alegun
А не факт, что одмином запускается, утилита ведь системная и должна выпоняться из-под System, можно кстати и проверить на администратора
Код:
type
PTOKEN_GROUPS = TOKEN_GROUPS^;

function RunningAsAdministrator(): Boolean;
var
SystemSidAuthority: SID_IDENTIFIER_AUTHORITY = SECURITY_NT_AUTHORITY;
psidAdmin: PSID;
ptg: PTOKEN_GROUPS = nil;
htkThread: Integer; { HANDLE }
cbTokenGroups: Longint; { DWORD }
iGroup: Longint; { DWORD }
bAdmin: Boolean;
begin
Result := false;
if not OpenThreadToken(GetCurrentThread(), // get security token
   TOKEN_QUERY, FALSE, htkThread) then
   if GetLastError() = ERROR_NO_TOKEN then
   begin
     if not OpenProcessToken(GetCurrentProcess(),
       TOKEN_QUERY, htkThread) then
       Exit;
   end   else     Exit;
if GetTokenInformation(htkThread, // get #of groups
   TokenGroups, nil, 0, cbTokenGroups) then   Exit;
if GetLastError() <> ERROR_INSUFFICIENT_BUFFER then   Exit;
ptg := PTOKEN_GROUPS(getmem(cbTokenGroups));
if not Assigned(ptg) then   Exit;
if not GetTokenInformation(htkThread, // get groups
   TokenGroups, ptg, cbTokenGroups, cbTokenGroups) then  Exit;
if not AllocateAndInitializeSid(SystemSidAuthority,
   2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS,
   0, 0, 0, 0, 0, 0, psidAdmin) then   Exit;
iGroup := 0;
while iGroup < ptg^.GroupCount do // check administrator group
begin
   if EqualSid(ptg^.Groups[iGroup].Sid, psidAdmin) then
   begin
     Result := TRUE;
     break;
   end;
   Inc(iGroup);
end;
FreeSid(psidAdmin);
end;
©там же
Ответить с цитированием
  #6  
Старый 18.02.2017, 15:20
Аватар для ffpereverzev
ffpereverzev ffpereverzev вне форума
Новичок
 
Регистрация: 14.02.2017
Сообщения: 50
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

Кстати, по-поводу запуска приложения от имени администратора. Если я запускаю приложение обычным способом и затем выполняю операцию вызывающую командную строку, то вижу такую картину:

А если запуск был от имени администратора, то тогда такую:


Может это и не важно (или не говорит ни о чем). Я поэтому и обратился к знающим людям.
Ответить с цитированием
  #7  
Старый 18.02.2017, 16:45
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

У меня этот код работает на W7Х32, под W10Х64 не пошел, всегда проблемы в 64х разрядных системах такого рода возникают,требуется более тонкая настройка среды, конкретно - это индивидуально для каждого случая - больше подсказать нечего, извнт
Ответить с цитированием
Этот пользователь сказал Спасибо Alegun за это полезное сообщение:
ffpereverzev (18.02.2017)
  #8  
Старый 18.02.2017, 17:18
Аватар для ffpereverzev
ffpereverzev ffpereverzev вне форума
Новичок
 
Регистрация: 14.02.2017
Сообщения: 50
Версия Delphi: Delphi 7
Репутация: 10
По умолчанию

У меня как раз W10. Ну что ж....будем думать, размышлять и обмозговывать...
Ответить с цитированием
  #9  
Старый 18.02.2017, 20:21
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Во, через вызов ShellExecuteEx удалось в десятке запустить нужную вам ситемную утилиту с правами одмина
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
shInfo: PSHELLEXECUTEINFOA;
s: string;
begin
s:= 'cmd.exe';
New(shInfo);
shInfo^.cbSize:= sizeof(SHELLEXECUTEINFO);
shInfo^.fMask:= 0;
shInfo^.Wnd:= 0;
shInfo^.lpVerb:= 'runas';
shInfo^.lpFile:= PAnsiChar(ExtractFileName(s));
shInfo^.lpParameters:= '/K powercfg /energy';
shInfo^.lpDirectory:= PAnsiChar(ExtractFilePath(s));
shInfo^.nShow:= SW_SHOWNORMAL;
shInfo^.hInstApp:= 0;
ShellExecuteEx(shInfo);
Dispose(shInfo);
shInfo:= nil;
end;
а если ещё и манифест прикрутить (*.exe.manifest), то тогда автоматом будет под админом это выполняться, без оконца уведомления от UAC
Ответить с цитированием
Этот пользователь сказал Спасибо Alegun за это полезное сообщение:
ffpereverzev (28.02.2017)
  #10  
Старый 19.02.2017, 18:02
Аватар для Karsh
Karsh Karsh вне форума
Активный
 
Регистрация: 22.09.2007
Адрес: SPb
Сообщения: 228
Версия Delphi: 7, 2009, XE2
Репутация: 70
По умолчанию

Цитата:
Не удалось загрузить библиотеку диагностики эффективности энергопотребления (energy.dll)
Это из-за того, что ваша программа 32-битная. Если у вас Delphi "умеет" компилировать 64-битные приложения (версия XE2 или выше), то скомпилируйте 64-битную версию вашей программы, и все должно получиться.
__________________
Начинающий программист уверен, что в 1 килобайте 1000 байт.
Законченный программист уверен, что в 1 километре 1024 метра.
Ответить с цитированием
Этот пользователь сказал Спасибо Karsh за это полезное сообщение:
ffpereverzev (28.02.2017)
  #11  
Старый 19.02.2017, 19:14
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Цитата:
Сообщение от Karsh
...Если у вас Delphi "умеет" компилировать 64-битные приложения (версия XE2 или выше)...
У ТС по всей видимости D7, у меня тоже, запускается средой cmd.exe с системной утилитой, а это от разрядности системы не зависит
Ответить с цитированием
  #12  
Старый 19.02.2017, 23:12
Аватар для Karsh
Karsh Karsh вне форума
Активный
 
Регистрация: 22.09.2007
Адрес: SPb
Сообщения: 228
Версия Delphi: 7, 2009, XE2
Репутация: 70
По умолчанию

Цитата:
запускается средой cmd.exe с системной утилитой, а это от разрядности системы не зависит
А среда определяется приложением, запросившим запуск. Если делать вызов из 32-битного приложения, то запускаться будет 32-битная версия cmd.exe из папки SysWOW64, которая в 64-битной ОС и будет вам выдавать "Не удалось загрузить библиотеку диагностики эффективности энергопотребления (energy.dll)".
__________________
Начинающий программист уверен, что в 1 килобайте 1000 байт.
Законченный программист уверен, что в 1 километре 1024 метра.

Последний раз редактировалось Karsh, 19.02.2017 в 23:44.
Ответить с цитированием
Этот пользователь сказал Спасибо Karsh за это полезное сообщение:
Alegun (20.02.2017)
  #13  
Старый 20.02.2017, 00:18
Аватар для Alegun
Alegun Alegun вне форума
LMD-DML
 
Регистрация: 12.07.2009
Адрес: Богородское
Сообщения: 3,025
Версия Delphi: D7E
Репутация: 1834
По умолчанию

Оффтоп:
Да, действительно, здесь вы совершенно правы - оказалось я в торопях по ошибке не ту версию виндовстэн под проверку загружал, всё зло от мультизагрузки, так вот выпендриться побыстрее хотелось, сейчас перепроверил - всё точно как в описании автора, значит "делфийцам-семеристам" этот путь в Х64 заказан, жаль Спасибо, УК Karsh!

Последний раз редактировалось Alegun, 20.02.2017 в 00:37.
Ответить с цитированием
  #14  
Старый 28.02.2017, 12:34
Аватар для ffpereverzev
ffpereverzev ffpereverzev вне форума
Новичок
 
Регистрация: 14.02.2017
Сообщения: 50
Версия Delphi: Delphi 7
Репутация: 10
Хорошо Спасибо!

Спасибо ВСЕМ огромное за столь быстрые, качественные и информативные ответы!
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 17:55.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter