powered by simpleCommunicator - 2.0.49     © 2025 Programmizd 02
Форумы / Delphi [игнор отключен] [закрыт для гостей] / not enough timers available
63 сообщений из 63, показаны все 3 страниц
not enough timers available
    #40120248
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всем привет.

Очень большой проект, с кучей юнитов и компонентов. Думаю по этому глупо приводить здесь код.
На формах и фреймах лежат созданные в дизайнере TTimer. Фреймы создаются "вручную" и добавляются на форму в единичном экземпляре. Сооствественно объекты TTimer тоже создаются не в множественном числе, без create/free. Всего таймеров в проекте 18.
И вот меняя все лишь порядок создания фреймов вываливается исключение 'not enough timers available'. происходит это при попытке включения таймера:
Код: pascal
1.
2.
  if not TimerAus.Enabled then
    TimerAus.Enabled := True;



Кто-нибудь знает причину, почему такое это может происходить и вообще как это можно побороть, кроме замены таймеров на tthread?
...
Рейтинг: 0 / 0
not enough timers available
    #40120267
Bred eFeM
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd, ты часы на руке носишь? больше одной штуки? или как?
...
Рейтинг: 0 / 0
not enough timers available
    #40120274
Fr0sT-Brutal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я бы поставил логирующий брякпоинт на создание таймера и проверил, что код вызывается в самом деле 18 раз, а не 1800
...
Рейтинг: 0 / 0
not enough timers available
    #40120279
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fr0sT-Brutal,

авторЯ бы поставил логирующий брякпоинт на создание таймера и проверил, что код вызывается в самом деле 18 раз, а не 1800

там на разных фреймах это все и показывает, что по разу создается фрейм и так же поразу идет активация. Просто как назло вносил изменения без внесения в CSV и теперь придется откатыватся на большой кусок, но желательно было бы разобратьсяв причинах.


Bred eFeMsvd, ты часы на руке носишь? больше одной штуки? или как?
я вообще не ношу, я счастливый.
...
Рейтинг: 0 / 0
not enough timers available
    #40120326
Fr0sT-Brutal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
То есть ты уверен, что таймеров именно ожидаемое кол-во? Попробуй еще в ProcessExplorer или в диспетчере задач хендлы проверить
...
Рейтинг: 0 / 0
not enough timers available
    #40120349
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fr0sT-Brutal,

А что там можно проверить?

Тут в одном описании попалась попытка, разъясняющая ситуацию. У парнишки было около 400 таймеров на различных формах и ошибка тоже случалась при включении. Объясняли это тем, что в очереди текущего окна уже есть сообшеня WM_TIMER и ощибка просодит во время TTimer.Update(). В процедуре ничего нет особо криминиального, что можно из дельфи проковырять: обращение к Windows.SetTimer(). попробовал перед вызовом сделать Application.ProcessMessages, но это не помогает.
...
Рейтинг: 0 / 0
not enough timers available
    #40120353
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Fr0sT-Brutal
То есть ты уверен, что таймеров именно ожидаемое кол-во? Попробуй еще в ProcessExplorer или в диспетчере задач хендлы проверить


приложение Win64, handles = 16289. Это x3FA. Вроде не должно быть ограничений на хэндлы. Или я что то не знаю?
...
Рейтинг: 0 / 0
not enough timers available
    #40120358
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svdВроде не должно быть ограничений на хэндлы. Или я что то не знаю?

Приложение по-прежнему не может иметь больше 16к окон одновременно. Такое
количество окон просто не имеет смысла, поэтому и лимит никто не увеличивает с
прошлого века.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
not enough timers available
    #40120372
Фотография Maxim Rusov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Более того - координаты объектов GDI по прежнему 16-ти разрядные и не могут превышать 32K. Приятно, когда в большом отчете объекты отскролившиеся далеко "вверх" начинают вылезать снизу. "Привет, это снова мы" :)
...
Рейтинг: 0 / 0
not enough timers available
    #40120382
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Скроллить такой отчёт по пикселю в любом случае стрёмно...
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
not enough timers available
    #40120413
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Большое спасибо за предупреждение по поводу хэндлов. Можно ли проблему ограничения количества решить перенесением части графических форм и фреймов в динамическую библиотеку?
...
Рейтинг: 0 / 0
not enough timers available
    #40120415
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
нашел в реджистри два ключа:

USERProcessHandleQuota и GDIProcessHandleQuota .

Знает кто-нибудь про это, будет ли решением проблемы?
...
Рейтинг: 0 / 0
not enough timers available
    #40120420
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Большое спасибо за предупреждение по поводу хэндлов. Можно ли проблему ограничения количества решить перенесением части графических форм и фреймов в динамическую библиотеку?
Нет, они на процесс
...
Рейтинг: 0 / 0
not enough timers available
    #40120422
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Решением проблемы будет нормальная архитектура приложения, ну или хотя бы динамическое создание и освобождение используемых ресурсов. За каким хреном одновременно создавать столько отжирающих ресурсов форм и фреймов?
...
Рейтинг: 0 / 0
not enough timers available
    #40120423
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
нашел в реджистри два ключа:

USERProcessHandleQuota и GDIProcessHandleQuota .

Знает кто-нибудь про это, будет ли решением проблемы?
Нет, решением проблемы будет только нахождение утечки хэндлов.
...
Рейтинг: 0 / 0
not enough timers available
    #40120424
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
нашел в реджистри два ключа:

USERProcessHandleQuota и GDIProcessHandleQuota .

Знает кто-нибудь про это, будет ли решением проблемы?
Нет, решением проблемы будет только нахождение утечки хэндлов.
...
Рейтинг: 0 / 0
not enough timers available
    #40120425
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
white_nigger...хотя бы динамическое создание и освобождение используемых ресурсов.

чесно говоря, я постоянно задаю этот вопрос. Но шеф утверждает, что обязательное требование.

YuRockНет, решением проблемы будет только нахождение утечки хэндлов.

да, после перезагрузки с таймером это проблему не решило. Остается только жесткое порно в виде поиска кривизны.
...
Рейтинг: 0 / 0
not enough timers available
    #40120427
Aleksandr Sharahov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd,

как уже сказано в 22409947 , нафига тебе столько таймеров ?

Достаточно одного.
...
Рейтинг: 0 / 0
not enough timers available
    #40120433
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Aleksandr Sharahov,

таймеры используются для разных целей. Один все время контролирует протокол выполнения и ошибок и записывет даннве в файл. Другой, например, контролирует в ните ручного сканера наличие новых данных и вытягивет их оттуда. Причем в зависимости от фрейма обработка этих данных идет поразному - в одном случае это могут быть данные товара, в другом места или какого либо бокса со вспомогательным кодом. Причем с таким механизмом, имея один скэнер и одну нить по его обработке, данные скэнера можно использовать даже между разными программами.
Третий контролирует состояние двери у робота и синхронизирует эти данные между парой других программ.
и т.д. по поводу специфических таймеров.
Но есть и довольно тупые задачи для таймеров: например при запросе какого либо поиска данных или просто фетчинг этих данных, запрос выполняется в отдельной нити, а данные из нити вытягиваются по таймеру в компоненты типа AdvSmoothListBox, или раз в секунду обновляет данные со значением из базы для таких компонентов. Такие можно как раз озадачиться и объединить в один.

За 8 лет разработки именно этой части кода, просто так такую проблему не решить - очень сильные изменения придется вносить в фремворк. А потом еще непонятно сколько тестировать под разными конфигурациями у клиентов - у одних один набор фич отображается, у других другое.
...
Рейтинг: 0 / 0
not enough timers available
    #40120435
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Aleksandr Sharahov,

таймеры используются для разных целей. Один все время контролирует протокол выполнения и ошибок и записывет даннве в файл. Другой, например, контролирует в ните ручного сканера наличие новых данных и вытягивет их оттуда. Причем в зависимости от фрейма обработка этих данных идет поразному - в одном случае это могут быть данные товара, в другом места или какого либо бокса со вспомогательным кодом. Причем с таким механизмом, имея один скэнер и одну нить по его обработке, данные скэнера можно использовать даже между разными программами.
Третий контролирует состояние двери у робота и синхронизирует эти данные между парой других программ.
и т.д. по поводу специфических таймеров.
Но есть и довольно тупые задачи для таймеров: например при запросе какого либо поиска данных или просто фетчинг этих данных, запрос выполняется в отдельной нити, а данные из нити вытягиваются по таймеру в компоненты типа AdvSmoothListBox, или раз в секунду обновляет данные со значением из базы для таких компонентов. Такие можно как раз озадачиться и объединить в один.

За 8 лет разработки именно этой части кода, просто так такую проблему не решить - очень сильные изменения придется вносить в фремворк. А потом еще непонятно сколько тестировать под разными конфигурациями у клиентов - у одних один набор фич отображается, у других другое.
И что, таких таймеров 16000 накопилось? Не верю.
И хэндлы - это не только таймеры. Ты можешь "терять" и окна, и эвенты, и файлы, сокеты, к.секции, мьютексы, семафоры... Всё, что угодно.
...
Рейтинг: 0 / 0
not enough timers available
    #40120437
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Запусти ты уже, наконец-то, в каком-нибудь профайлере свою прогу и закрой через время - он покажет, какие ресурсы не удалены.
...
Рейтинг: 0 / 0
not enough timers available
    #40120444
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svdтаймеры используются для разных целей. Один все время контролирует протокол
выполнения и ошибок и записывет даннве в файл. Другой, например, контролирует в
ните ручного сканера наличие новых данных и вытягивет их оттуда.

На это всё достаточно одного таймера, который по истечению заданного периода
запускает ту или иную функцию.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
not enough timers available
    #40120449
rgreat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вангую что заменить его 18 таймеров на 1 не поможет.

Видимо их далеко не 18.
...
Рейтинг: 0 / 0
not enough timers available
    #40120451
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rgreat
Вангую что заменить его 18 таймеров на 1 не поможет.

Видимо их далеко не 18.
Он же показывал, что >16к хэндлов в списке процессов отображается. Там тихий ужас какой-то.
...
Рейтинг: 0 / 0
not enough timers available
    #40120507
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
YuRockЗапусти ты уже, наконец-то, в каком-нибудь профайлере свою прогу и закрой через время - он покажет, какие ресурсы не удалены.

Регклярно так и делаю. Потому как часто коллеги, как пионэры, забывают финализировать объекты.

Но в этом случае fastmm не сообщает ничего полезного.

Dimitry SibiryakovНа это всё достаточно одного таймера, который по истечению заданного периода
запускает ту или иную функцию.

Не всегда. И не везде. Есть задачи, которые очень критичны ко времени обработки. И если в такой ситуации таймер с интервалом 800мс проводит обработку свое функции 1.5 сек. - это ненормально.

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

YuRockОн же показывал, что >16к хэндлов в списке процессов отображается. Там тихий ужас какой-то.

Правильно заметил - в тасклисте не только таймеры отображаются, а все хэндлы, используемые приложением.
Но почему именно этот конкретный таймер чудит - сегодня буду разбираться. Причем в правиле фреймворка стоит, что при деактивации фрейма, таймера и вспомогательный трэд останавливаются, а при активации запускаются заново. И именно этот таймер при новом запуске бросает тоже самое исключение, а вот на других фреймах, где подобная реализация, такого не происходит.
...
Рейтинг: 0 / 0
not enough timers available
    #40120516
Fr0sT-Brutal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Fastmm сообщит только о том, что он сам выделил. А утечки хэндлов надо ловить другими средствами. Process Explorer хотя бы покажет, чего именно там так до фига выделено.

авторВ процедуре ничего нет особо криминиального, что можно из дельфи проковырять: обращение к Windows.SetTimer(). попробовал перед вызовом сделать Application.ProcessMessages, но это не помогает.
Ставишь бряк внутри Timer.Enable с выводом в лог и смотришь, сколько таймеров реально активировалось.

Уменьшить кол-во таймеров можно, но для начала надо убедиться, что именно в этом беда
...
Рейтинг: 0 / 0
not enough timers available
    #40120525
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Но в этом случае fastmm не сообщает ничего полезного.
А причем здесь FastMM к системным хендлам?

Держите каркас модуля, остальное допишите сами
Код: 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.
unit UWinDbg;

interface

uses
  Windows;

{

Access token
Communications device
Console input
Console screen buffer
+ Event
File
+ File mapping
Job
Mailslot
+ Mutex
Named pipe
Process
+ Semaphore
Socket
Thread
+ Waitable timer
}

implementation

uses
  SysUtils, TypInfo, Classes;

const
  {$IFDEF UNICODE}
  CSfx = 'W';
  {$ELSE}
  CSfx = 'A';
  {$ENDIF}

type
  THandleType = (htDuplicate, htMutex, htEvent, htSemaphore, htTimer, htFileMap);

  PIndexHandle = ^TIndexHandle;
  TIndexHandle = record
    Handle: THandle;
    HandleType: THandleType;
    Index: Integer;
  end;

  PJump = ^TJump;
  TJump = packed record
    OpCode: Byte;
    Distance: Pointer;
  end;

var
  GHandlesInfo: TMappedStrings;
  GIndexHandles: TSimpleRecordList;
  GSecAttr: PSecurityAttributes;
  GIsLoaded: Boolean;
//  GLogFile: TextFile;

function WinCloseHandle(hObject: THandle): BOOL; stdcall; external kernel32 name 'CloseHandle';

function WinDuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle: THandle; lpTargetHandle: PHandle; dwDesiredAccess: DWORD; bInheritHandle: BOOL; dwOptions: DWORD): BOOL; stdcall; external kernel32 name 'DuplicateHandle';

function WinCreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PChar): THandle; stdcall; external kernel32 name 'CreateMutex' + CSfx;
function WinCreateMutexA(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'CreateMutexA';
function WinCreateMutexW(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PWideChar): THandle; stdcall; external kernel32 name 'CreateMutexW';

function WinOpenMutex(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PChar): THandle; stdcall; external kernel32 name 'OpenMutex' + CSfx;
function WinOpenMutexA(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'OpenMutexA';
function WinOpenMutexW(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PWideChar): THandle; stdcall; external kernel32 name 'OpenMutexW';

function WinCreateEvent(lpEventAttributes: PSecurityAttributes; bManualReset, bInitialState: BOOL; lpName: PChar): THandle; stdcall; external kernel32 name 'CreateEvent' + CSfx;
function WinCreateEventA(lpEventAttributes: PSecurityAttributes; bManualReset, bInitialState: BOOL; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'CreateEventA';
function WinCreateEventW(lpEventAttributes: PSecurityAttributes; bManualReset, bInitialState: BOOL; lpName: PWideChar): THandle; stdcall; external kernel32 name 'CreateEventW';

function WinOpenEvent(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PChar): THandle; stdcall; external kernel32 name 'OpenEvent' + CSfx;
function WinOpenEventA(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'OpenEventA';
function WinOpenEventW(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PWideChar): THandle; stdcall; external kernel32 name 'OpenEventW';

function WinCreateSemaphore(lpSemaphoreAttributes: PSecurityAttributes; lInitialCount, lMaximumCount: Longint; lpName: PChar): THandle; stdcall; external kernel32 name 'CreateSemaphore' + CSfx;
function WinCreateSemaphoreA(lpSemaphoreAttributes: PSecurityAttributes; lInitialCount, lMaximumCount: Longint; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'CreateSemaphoreA';
function WinCreateSemaphoreW(lpSemaphoreAttributes: PSecurityAttributes; lInitialCount, lMaximumCount: Longint; lpName: PWideChar): THandle; stdcall; external kernel32 name 'CreateSemaphoreW';

function WinOpenSemaphore(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PChar): THandle; stdcall; external kernel32 name 'OpenSemaphore' + CSfx;
function WinOpenSemaphoreA(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'OpenSemaphoreA';
function WinOpenSemaphoreW(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PWideChar): THandle; stdcall; external kernel32 name 'OpenSemaphoreW';

function WinCreateWaitableTimer(lpTimerAttributes: PSecurityAttributes; bManualReset: BOOL; lpTimerName: PChar): THandle; stdcall; external kernel32 name 'CreateWaitableTimer' + CSfx;
function WinCreateWaitableTimerA(lpTimerAttributes: PSecurityAttributes; bManualReset: BOOL; lpTimerName: PAnsiChar): THandle; stdcall; external kernel32 name 'CreateWaitableTimerA';
function WinCreateWaitableTimerW(lpTimerAttributes: PSecurityAttributes; bManualReset: BOOL; lpTimerName: PWideChar): THandle; stdcall; external kernel32 name 'CreateWaitableTimerW';

function WinOpenWaitableTimer(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpTimerName: PChar): THandle; stdcall; external kernel32 name 'OpenWaitableTimer' + CSfx;
function WinOpenWaitableTimerA(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpTimerName: PAnsiChar): THandle; stdcall; external kernel32 name 'OpenWaitableTimerA';
function WinOpenWaitableTimerW(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpTimerName: PWideChar): THandle; stdcall; external kernel32 name 'OpenWaitableTimerW';

function WinCreateFileMapping(hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD; lpName: PChar): THandle; stdcall; external kernel32 name 'CreateFileMapping' + CSfx;
function WinCreateFileMappingA(hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'CreateFileMappingA';
function WinCreateFileMappingW(hFile: THandle; lpFileMappingAttributes: PSecurityAttributes; flProtect, dwMaximumSizeHigh, dwMaximumSizeLow: DWORD; lpName: PWideChar): THandle; stdcall; external kernel32 name 'CreateFileMappingW';

function WinOpenFileMapping(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PChar): THandle; stdcall; external kernel32 name 'OpenFileMapping' + CSfx;
function WinOpenFileMappingA(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PAnsiChar): THandle; stdcall; external kernel32 name 'OpenFileMappingA';
function WinOpenFileMappingW(dwDesiredAccess: DWORD; bInheritHandle: BOOL; lpName: PWideChar): THandle; stdcall; external kernel32 name 'OpenFileMappingW';

procedure AddHandle(AHandleType: THandleType; AHandle: THandle; const AName: string); overload;
begin
  ............
end;

{$IFDEF UNICODE}
procedure AddHandle(AHandleType: THandleType; AHandle: THandle; const AName: AnsiString); inline; overload;
begin
  AddHandleStr(AHandleType, AHandle, string(AName));
end;
{$ENDIF}

procedure RemoveHandle(AHandle: THandle);
begin
  ...........
end;

procedure ProcAddressPatch(ASource, ADestination: Pointer);
var
  LNewJump: PJump;
  LOldProtect: Cardinal;
begin
  if VirtualProtect(ASource, SizeOf(TJump), PAGE_EXECUTE_READWRITE, LOldProtect) then
  begin
    LNewJump := PJump(ASource);
    LNewJump^.OpCode := $E9;  // jmp
    LNewJump^.Distance := Pointer(WPARAM(ADestination) - WPARAM(ASource) - 5);

    FlushInstructionCache(GetCurrentProcess, ASource, SizeOf(TJump));
    VirtualProtect(ASource, SizeOf(TJump), LOldProtect, @LOldProtect);
  end;
end;

function DbgCloseHandle(hObject: THandle): BOOL; stdcall;
begin
  try
    RemoveHandle(hObject);
  finally
    Result := WinCloseHandle(hObject);
  end;
end;

function DbgDuplicateHandle(hSourceProcessHandle, hSourceHandle,
  hTargetProcessHandle: THandle; lpTargetHandle: PHandle; dwDesiredAccess: DWORD;
  bInheritHandle: BOOL; dwOptions: DWORD): BOOL; stdcall;
begin
  Result := WinDuplicateHandle(hSourceProcessHandle, hSourceHandle, hTargetProcessHandle,
    lpTargetHandle, dwDesiredAccess, bInheritHandle, dwOptions);
  if Result then
    AddHandle(htDuplicate, lpTargetHandle^, '');
end;

function DbgCreateMutex(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PChar): THandle; stdcall;
begin
  Result := WinCreateMutex(lpMutexAttributes, bInitialOwner, lpName);
  if Result <> 0 then
    AddHandle(htMutex, Result, lpName);
end;

function DbgCreateMutexA(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PAnsiChar): THandle; stdcall;
begin
  Result := WinCreateMutexA(lpMutexAttributes, bInitialOwner, lpName);
  if Result <> 0 then
    AddHandle(htMutex, Result, lpName);
end;

function DbgCreateMutexW(lpMutexAttributes: PSecurityAttributes; bInitialOwner: BOOL; lpName: PWideChar): THandle; stdcall;
begin
  Result := WinCreateMutexW(lpMutexAttributes, bInitialOwner, lpName);
  if Result <> 0 then
    AddHandle(htMutex, Result, lpName);
end;

.....................

procedure PatchFunctions;
begin
  ProcAddressPatch(@Windows.CloseHandle, @DbgCloseHandle);

  ProcAddressPatch(@Windows.DuplicateHandle, @DbgDuplicateHandle);

  ProcAddressPatch(@Windows.CreateMutex, @DbgCreateMutex);
  ProcAddressPatch(@Windows.CreateMutexA, @DbgCreateMutexA);
  ProcAddressPatch(@Windows.CreateMutexW, @DbgCreateMutexW);

  ProcAddressPatch(@Windows.OpenMutex, @DbgOpenMutex);
  ProcAddressPatch(@Windows.OpenMutexA, @DbgOpenMutexA);
  ProcAddressPatch(@Windows.OpenMutexW, @DbgOpenMutexW);

  ProcAddressPatch(@Windows.CreateEvent, @DbgCreateEvent);
  ProcAddressPatch(@Windows.CreateEventA, @DbgCreateEventA);
  ProcAddressPatch(@Windows.CreateEventW, @DbgCreateEventW);

  ProcAddressPatch(@Windows.OpenEvent, @DbgOpenEvent);
  ProcAddressPatch(@Windows.OpenEventA, @DbgOpenEventA);
  ProcAddressPatch(@Windows.OpenEventW, @DbgOpenEventW);

  ProcAddressPatch(@Windows.CreateSemaphore, @DbgCreateSemaphore);
  ProcAddressPatch(@Windows.CreateSemaphoreA, @DbgCreateSemaphoreA);
  ProcAddressPatch(@Windows.CreateSemaphoreW, @DbgCreateSemaphoreW);

  ProcAddressPatch(@Windows.OpenSemaphore, @DbgOpenSemaphore);
  ProcAddressPatch(@Windows.OpenSemaphoreA, @DbgOpenSemaphoreA);
  ProcAddressPatch(@Windows.OpenSemaphoreW, @DbgOpenSemaphoreW);

  ProcAddressPatch(@Windows.CreateWaitableTimer, @DbgCreateWaitableTimer);
  ProcAddressPatch(@Windows.CreateWaitableTimerA, @DbgCreateWaitableTimerA);
  ProcAddressPatch(@Windows.CreateWaitableTimerW, @DbgCreateWaitableTimerW);

  ProcAddressPatch(@Windows.OpenWaitableTimer, @DbgOpenWaitableTimer);
  ProcAddressPatch(@Windows.OpenWaitableTimerA, @DbgOpenWaitableTimerA);
  ProcAddressPatch(@Windows.OpenWaitableTimerW, @DbgOpenWaitableTimerW);

  ProcAddressPatch(@Windows.CreateFileMapping, @DbgCreateFileMapping);
  ProcAddressPatch(@Windows.CreateFileMappingA, @DbgCreateFileMappingA);
  ProcAddressPatch(@Windows.CreateFileMappingW, @DbgCreateFileMappingW);

  ProcAddressPatch(@Windows.OpenFileMapping, @DbgOpenFileMapping);
  ProcAddressPatch(@Windows.OpenFileMappingA, @DbgOpenFileMappingA);
  ProcAddressPatch(@Windows.OpenFileMappingW, @DbgOpenFileMappingW);
end;

initialization
  PatchFunctions;

end;

Добавить в uses dpr файла первым модулем. Или вторым после FastMM.

Под x64 не проверялось.
...
Рейтинг: 0 / 0
not enough timers available
    #40120564
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_,

Спасибо за помощь попробую присобачить, но чуть попозже.


Наткнулся на такю штуку. Это продолжение вот этой истории.

В общем создал я для эксперемента трэд и внего переместил все создание фреймов:
Код: 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.
procedure T_Thread_CreateFormApp.Execute;
const
  C_MaxStep = 30;
var
  step : Integer;
begin
  FreeOnTerminate := True;
  Priority := tpNormal;
  step := 0;
  Send_TraceMsg(ClassName + '.Exec: Start');
  Synchronize(procedure
              begin
                  SplashForm_Percent := 10;
              end);

  while not Terminated do
  try
    Sleep(1);
    try
      Synchronize(procedure
                  begin
                      SplashForm_Percent := Round(100*Step/C_MaxStep);
                  end);

      if (step = 1)  then Synchronize(invoke_FormAppCreate);
...
     //Если делаю вот таким образом, то все проходит нормально без сучка и задоринки
      if (step = 17) then Synchronize(procedure begin loc_FormDBApp.FormCreate_setFrameAuslagerung; end);

      //А если так, то выкидывает указанное исключение
      //if (step = 17) then loc_FormDBApp.FormCreate_setFrameAuslagerung;

...



вот такие пироги с катятами.
...
Рейтинг: 0 / 0
not enough timers available
    #40120569
rgreat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd,

Создание форм из потоков...

...
Рейтинг: 0 / 0
not enough timers available
    #40120570
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
В общем создал я для эксперемента трэд и внего переместил все создание фреймов:
Зачем? Если все создание у вас происходит в главном потоке? И VCL не умеет работать не в главном потоке. Вообще. Никак.
...
Рейтинг: 0 / 0
not enough timers available
    #40120576
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Уйююй, какой гневный поток проклятий на мою голову. Эксперимент есть эксперимент. В этом и суть. И ничего немешает разобраться почему же все таки "нельзя". А можен даже и "можно", но "осторожно". В любом случае никто от эксперимента не погиб, неудобно было тестировать, так как csv пришлось использовать "на коленке" в виде zip файлов.

_Vasilisk_Зачем? Если все создание у вас происходит в главном потоке? И VCL не умеет работать не в главном потоке. Вообще. Никак.
Как видно, коллега, очень даже "как". Если учесть, что создаваемым в потоке VCL ничего отображать не требуется.

Вообще то с помощью трэда удалось немного ускорить процесс загрузки, секунд на 10. Но это показатели в симуляции на моем компе, а не на реальном железе. Нужно все таки понять, что мешает таймеру нормально стартовать.

@_Vasilisk_
по поводу UWinDbg, я так понимаю в нем нужно гдето в контейнепах типа TList сохранять значения хэндлов? у меня есть нечто подобное в фремворке и именно для отладки, когда выясняю, что в запущеном что то теряется, то я вызываю специальную процедуру, которая в текстовый файлик записивыет стек при создании компонента. имя файлу задаю связанное с хэндлом. При разрушении объекта удаляю файл и смотрю что осталось на диске. Запускается, конечно, медленно, но проколы находит четко.
...
Рейтинг: 0 / 0
not enough timers available
    #40120578
rgreat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Нужно все таки понять, что мешает таймеру нормально стартовать.
Судя по контексту эксперимента таймеру мешает нормально стартовать то, что он создается не в главном потоке.
...
Рейтинг: 0 / 0
not enough timers available
    #40120583
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rgreat,

именно.

Но это единственный фрейм, который сбойнул. Сейчас проверяю другие, где тоже есть таймер. Такой фрейм с 4-мя добавленными подфреймами внутрь нормально прокатил и даже не матюкнулся. Что есть у него, чего не хватило первому? Нужно еще попытаться разбить создание такого фрейма на несколько шагов: создание с synchronize, создание в потоке[без synchronize, где это должно проходить параллельно] , и создание перед показом формы[однозначно synchronize, уже добавил виртульный метод]. В последней я как раз и переместил все операции с базой данных.
...
Рейтинг: 0 / 0
not enough timers available
    #40120609
Fr0sT-Brutal
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
*челодлань* Поначалу было еще нормально, ну мало ли есть жирных систем с прорвой форм... но создание фреймов в потоке это уже клиника. Это ведь вместе с букварем должно было быть выучено.
И вопрос уже другой пошел - как ускорять загрузку тонны визуального и БД-шного дерьма, которое без всякой задней мысли запихнули в стартап приложухи.
...
Рейтинг: 0 / 0
not enough timers available
    #40120614
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svdИ если в такой ситуации таймер с интервалом 800мс проводит обработку свое
функции 1.5 сек. - это ненормально.

А ты понимаешь, что это неизбежно? Что все сообщения всех твоих таймеров
помещаются в одну очередь сообщений и выполняются в главном потоке последовательно?
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
not enough timers available
    #40120667
Фотография wadman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Код: pascal
1.
Synchronize


Есть понимание, что делает эта процедура?
...
Рейтинг: 0 / 0
not enough timers available
    #40120668
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov,

я то понимаю, но другие человеколюди этого не хотят понимать и даже пытаются их в какую-нибудь трэд помещать чтоб организовать таймаут. А потом отрывают меня с вопросом "почему не работает".
...
Рейтинг: 0 / 0
not enough timers available
    #40120669
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadman,

эта процедура и делает магию в тех частях, где "нельзя использовать VCL в трэдах". Но самое интересное, что параллельная загрузка происходит быстрее.
...
Рейтинг: 0 / 0
not enough timers available
    #40120675
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Магии не существует в программном коде. Таймеры работают в потоках, но требуют
цикл обработки сообщений там же.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
not enough timers available
    #40120679
Фотография Квейд
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Всем привет.

Очень большой проект, с кучей юнитов и компонентов. Думаю по этому глупо приводить здесь код.
На формах и фреймах лежат созданные в дизайнере TTimer. Фреймы создаются "вручную" и добавляются на форму в единичном экземпляре. Сооствественно объекты TTimer тоже создаются не в множественном числе, без create/free. Всего таймеров в проекте 18.
И вот меняя все лишь порядок создания фреймов вываливается исключение 'not enough timers available'. происходит это при попытке включения таймера:
Код: pascal
1.
2.
  if not TimerAus.Enabled then
    TimerAus.Enabled := True;



Кто-нибудь знает причину, почему такое это может происходить и вообще как это можно побороть, кроме замены таймеров на tthread?


маленькая реплика в сторону,
первая проверка - лишняя

достаточно

Код: pascal
1.
    TimerAus.Enabled := True;
...
Рейтинг: 0 / 0
not enough timers available
    #40120685
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov,

теоретически "да" если делать новую реализацию класса. Но логичнее вызвать SetTime/KillTimer, хотя там можно и дичь типа SetWaitTimer поробовать[не уверен, что сработает, как планировалось] .

А самое логичное решение в цикле самого трэда организовать проверку наступления события.
...
Рейтинг: 0 / 0
not enough timers available
    #40120691
Dimitry Sibiryakov
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svdя то понимаю, но другие человеколюди этого не хотят понимать

Если ты это понимаешь, то почему порешь чушь про "800 миллисекунд", "один таймер
остановился остальные работают" и прочее "обновление экрана"?
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
not enough timers available
    #40120693
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dimitry Sibiryakov,

я должен правильно описывать логику работы модулей, как ее видят разработчики.
...
Рейтинг: 0 / 0
not enough timers available
    #40120701
Мимопроходящий
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
я должен правильно описывать логику работы модулей, как ее видят разработчики.
они её "видят" не так, как есть на самом деле.
...
Рейтинг: 0 / 0
not enough timers available
    #40120725
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
топикстартер?
Ты окна ненужные с ненужными таймерами убивать не забываешь???
Самое простое
CloseAction := caFree;

Пока окна ты не убиваешь, и таймеры в экземплярах висят...
(сорри, я не всю ветку фанатично прочёл)
...
Рейтинг: 0 / 0
not enough timers available
    #40120727
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...а ещё есть тема:

var JJJ: int64;

JJJ := MAXINT;
For i := 0 to JJJ+12 do
TTimer.Create(.....


Вопрос в студию:
а) почему и нет?
б) почему зашкал?
...
Рейтинг: 0 / 0
not enough timers available
    #40120771
rgreat
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Makar4ik
...а ещё есть тема:

var JJJ: int64;

JJJ := MAXINT;
For i := 0 to JJJ+12 do
TTimer.Create(.....


Вопрос в студию:
а) почему и нет?
б) почему зашкал?

Лучше "For i := 0 to JJJ+42 do"
...
Рейтинг: 0 / 0
not enough timers available
    #40120780
YuRock
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
rgreat
Makar4ik
...а ещё есть тема:

var JJJ: int64;

JJJ := MAXINT;
For i := 0 to JJJ+12 do
TTimer.Create(.....


Вопрос в студию:
а) почему и нет?
б) почему зашкал?

Лучше "For i := 0 to JJJ+42 do"

Дописывай уже до конца.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
for i := 0 to JJJ+42 do
  Synchronize( procedure 
    begin
      TTimer.Create;
      Application.ProcessMessages;
    end 
  );


Так переполнения не будет - вряд ли до этого дойдёт.
...
Рейтинг: 0 / 0
not enough timers available
    #40120857
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
YuRock
rgreat
пропущено...

Лучше "For i := 0 to JJJ+42 do"

Дописывай уже до конца.
Код: pascal
1.
2.
3.
4.
5.
6.
7.
for i := 0 to JJJ+42 do
  Synchronize( procedure 
    begin
      TTimer.Create;
      Application.ProcessMessages;
    end 
  );


Так переполнения не будет - вряд ли до этого дойдёт.

Хватит прикалываться.
У человека, скорее всего, как раз экземпляры форм не убиваются, а вы...
(ну, и я...)
...
Рейтинг: 0 / 0
not enough timers available
    #40120858
ъъъъъ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Makar4ik
как раз экземпляры форм не убиваются, а вы...

2 14 раз?
...
Рейтинг: 0 / 0
not enough timers available
    #40120859
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ъъъъъ
Makar4ik
как раз экземпляры форм не убиваются, а вы...

2 14 раз?
Какой, простите, нах в 14-й? "Два раза расстрелять".

6, не более.
...
Рейтинг: 0 / 0
not enough timers available
    #40120860
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
....а что, бывает ограничение на создание окон ниже, чем таймеров?
...
Рейтинг: 0 / 0
not enough timers available
    #40120861
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Шутки шутками, но реально, 2 в 14-й таймеров - это глюк мозга у разраба.
Так не должно быть, мама должна сыну с молоком в моск вводить ограничения.
...
Рейтинг: 0 / 0
not enough timers available
    #40120863
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...хотя, я под Win 2000 ловил ограничение в 1024 потока для процесса.
Ага, да....
А знаете зачем столько потоков?
А низачем.
Просто так.
...
Рейтинг: 0 / 0
not enough timers available
    #40120873
white_nigger
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Если кто вдруг не читал, то на тему хэндлов и их количества у Руссиновича было
...
Рейтинг: 0 / 0
not enough timers available
    #40120875
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...вообще, есть какая-то там эмпирическая формула...
В общем, не более 8-ми потоков на ядро... И если памяти хватает.
Если потоки фигнёй не страдают.

..перегруз контекста ядра в Х86 занимает чуть ли не 2000 тактов...
...
Рейтинг: 0 / 0
not enough timers available
    #40120963
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
привествую вас, местные зубоскалы, производитель флейма. Я понимаю ваше желание загадить ресурс, чтоб ищущий какое то специфическое решение не мог быстро его найти, благодаря вашему флейму. Хотя админы для вас и завели отдельные неубиваемые топики без срока давности.

Все заданные вами вопросы - находятся в топике. Если бы вы его читали не по диагонали, то и вопросов бы не задавали.
...
Рейтинг: 0 / 0
not enough timers available
    #40120996
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Как видно, коллега, очень даже "как"
Внезапно: Synchronize помещает в очередь выполнения указанную процедуру и усыпляет поток. Главный поток при работе очереди сообщений, вызывает CheckSynchronize и проверяет не нужно ли чего выполнить еще? Находит помещенный в очередь метод, выполняет его, после этого будит тот поток, который поставил в очередь этот метод. А теперь вопрос - где здесь многопоточность?
svd
Если учесть, что создаваемым в потоке VCL ничего отображать не требуется.
При чем здесь отображать если идет несинхронизированный доступ к общим ресурсам? Вы посмотрите на тот же код AllocateHWnd, которая вызывается при создании таймера.
svd
по поводу UWinDbg, я так понимаю в нем нужно гдето в контейнепах типа TList сохранять значения хэндлов?
Да
...
Рейтинг: 0 / 0
not enough timers available
    #40121076
svd
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_А теперь вопрос - где здесь многопоточность?

Трэд имеет приоритет в onidle. Из этого следут вывод, что этот idle у основного процесса гдето есть.

Вопрос 2. Почему тогда скорость действительно ускорилась на 10 секунд?

Ну и получилась плюшка в виде более плавной загрузки и наглядности в виде прогрессбара на сплэше.
...
Рейтинг: 0 / 0
not enough timers available
    #40121607
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
svd
Из этого следут вывод, что этот idle у основного процесса гдето есть.
Какой основной процесс? Вы знаете как работают приоритеты потоков?
svd
Вопрос 2. Почему тогда скорость действительно ускорилась на 10 секунд?
Нам отсюда не видно полного ни исходного кода, ни конечного.
...
Рейтинг: 0 / 0
not enough timers available
    #40122004
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_
Внезапно: Synchronize помещает в очередь выполнения указанную процедуру и усыпляет поток. Главный поток при работе очереди сообщений, вызывает CheckSynchronize и проверяет не нужно ли чего выполнить еще? Находит помещенный в очередь метод, выполняет его, после этого будит тот поток, который поставил в очередь этот метод. А теперь вопрос - где здесь многопоточность?

Внезапно...
Надо указывать версию Delphi???
В разных библиотеках Synchronize работает по разному.
Идеальный, на мой взгляд - D5
Просто SendMessage.


...Про процесс и потоки:
VCL - простите, однопоточный.
Там один поток у одного процесса.
...
Рейтинг: 0 / 0
not enough timers available
    #40122022
Фотография _Vasilisk_
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Makar4ik
В разных библиотеках Synchronize работает по разному.
И в какой-то версии все работает не так, как я описал?
...
Рейтинг: 0 / 0
not enough timers available
    #40131341
Фотография Makar4ik
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
_Vasilisk_
Makar4ik
В разных библиотеках Synchronize работает по разному.
И в какой-то версии все работает не так, как я описал?

В каждой.
Synhronize в D5
Посмотри библиотеки на том же Берлине, 10.1...
УжОс - ужОс...

...мне в 7-ке - прям даже свой TThread писать пришлось, чтобы накладных расходов было меньше.
...
Рейтинг: 0 / 0
63 сообщений из 63, показаны все 3 страниц
Форумы / Delphi [игнор отключен] [закрыт для гостей] / not enough timers available
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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