Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Сети";
Текущий архив: 2009.10.18;
Скачать: [xml.tar.bz2];

Вниз

Асинхронные сокеты "забивают" очередь сообщений   Найти похожие ветки 

 
SpellCaster   (2008-04-01 17:01) [0]

Делаю класс асинхронного сокета и столкнулся с проблемкой зависания проги. Прога коннектится к серверу и начинает принимать от него непрерывный поток данных. Соответственно, после каждого Recv в очередь сразу же ставится следующее сообщение с событием FD_READ, которое тут же и обрабатывается. В итоге сообщения от главного окна просто не могут пробиться сквозь этот цикл. Вместе с тем, если запускать в начале процедуры SocketEvent Application.ProcessMessages, то все работает (естественно). Однако есть у меня сомнение: ведь эта процедура сразу вытаскивает из очереди очередное сообщение. Вполне вероятно, что это может оказаться следующая мессага от того же сокета - и тогда SocketEvent будет запущен второй раз, а уже после обработки этого сообщения управление вернется к первой копии SocketEvent. То есть, к примеру, может получиться так, что сначала обработается FD_CLOSE, а потом придет очередь FD_READ, что не очень-то здорово.
Поэтому хочу спросить у знатоков, есть ли какой-то хороший путь решения данной проблемы? Или, может, я зря волнуюсь насчет перепутывания сообщений? В идеале хотелось бы иметь способ вместо ProcessMessages обработать только мессаги формы, а потом уже с чистой совестью заниматься с сокетом.
Посмотрел сорсы ICS, там реализована собственная выборка собщений. В принципе, можно сделать так же, но я пока не разобрался, как ее применять.
В общем, если что посоветуете, буду рад... только два условия:
1) Не хочется юзать нити
2) Видеть реплики типа "Зачем тебе это надо" и "не занимайся ерундой" также не горю желанием.


 
Сергей М. ©   (2008-04-01 17:09) [1]

Показывай код обработки оконных сообщений. посылаемых созданными тобой гнездами ...


 
SpellCaster   (2008-04-01 17:33) [2]


// Socket message receiver
procedure TAsyncClientSocket.DoEvent(WParam, LParam: Longint);
var SockErr: Integer;
   Event: Word;
begin
 Event:=WSAGetSelectEvent(LParam);
 SockErr:=WSAGetSelectError(LParam);
 // если произошла ошибка
 if SockErr<>0 then
   begin DoError(WParam,Event,SockErr); Exit; end;

 // если пришло ожидаемое событие, обнуляем таймер
 if fRespEvent=Event then fRequestTime:=0;

 // на обработку соединялки с проксей отправляем только события READ и WRITE
 case Event of
   FD_CONNECT        : begin
                         fActive:=True;
                         if fProxyUsed then begin fSocksState:=ssConnecting; Exit; end;
                       end;
   FD_READ, FD_WRITE : if HandleSocksConnect then Exit;
   FD_CLOSE          : begin
                         fSocksState:=ssNone;
                         fRequestTime:=0;
                         Close;
                       end;
   else Exit;
 end;
 if Assigned(OnEvent) then OnEvent(Self,WParam,Event);
end;


ну и в программе


procedure TClient.SocketEvent(Sender: TObject; Sckt: TSocket; Event: Word);
var received: Integer;
   req: ShortString;
   Msg: TMsg;
begin
 Application.ProcessMessages;

 case Event of
   // сокет соединился
   MsgConnect:
     begin
        ...
     end;
   // в буфер сокета поступили данные
   MsgRead:
     begin
          ...
           begin
             // Проверяем результат: если ошибка - соединение закрываем и переподключаемся
             received:=fSock_in.Recv(@fBuf[0],PacketSize);
             if received<=0 then
               begin SetState(nsErr,"receive: "+WSALastErr); Close; Exit; end
             // всё ОК - пишем полученные данные в выходной поток и обнуляем счетчик попыток
             else
             begin
               ...
             end;
     end; // MsgRead
   MsgWrite  :   ;
   MsgClose  : begin
                 SetState(nsStop);
                 SetState(nsInactive);
               end;
 end;
end;



HandleSocksConnect возвращает False, если соединение с соксом уже установдено.


 
Сергей М. ©   (2008-04-02 08:31) [3]


> HandleSocksConnect возвращает False, если соединение с соксом
> уже установдено


А если не установлено, то что творится в этой ф-ции ?


> Application.ProcessMessages;


Это, разумеется, следует убрать.


 
SpellCaster   (2008-04-02 11:48) [4]

> А если не установлено, то что творится в этой ф-ции ?

Устанавливается коннект с проксей. Приводить, наверно, не буду, но смысл в том, что в зависимости от состояния коннекта читаются или отправляются пакеты для установки связи.

> Это, разумеется, следует убрать.
Так тогда мессаги от юзера вообще не проходят!


 
Григорьев Антон ©   (2008-04-02 12:54) [5]

А как огранизован приём сообщений классом TAsyncClientSocket? Какое окно за это отвечает? Кто вызывает TAsyncClientSocket.DoEvent?


 
SpellCaster   (2008-04-02 13:03) [6]

> [5] Григорьев Антон ©   (02.04.08 12:54)

Вот такая процедурка, одна на всех

function InnerWndProc(wnd: hWnd; msg, wParam, lParam: Longint): Longint; stdcall;
var sock: TObject;
  s: string;
begin
 if not ((msg = WM_SOCKMSG) or (msg = WM_TIMER)) then
 begin
   Result:=DefWindowProc(wnd,msg,wparam,lparam);
   Exit;
 end;
 // else
 Result:=0;
 sock:=TObject(Pointer(GetWindowLong(wnd,GWL_USERDATA)));
 if sock=nil then Exit;
 s:=sock.ClassName;
 if s="TAsyncClientSocket" then
   case msg of
     WM_SOCKMSG: TAsyncClientSocket(sock).DoEvent(WParam, LParam);
     WM_TIMER:   TAsyncClientSocket(sock).DoTimer;
   end
 else
 if s="TAsyncServerSocket" then
   case msg of
     WM_SOCKMSG: TAsyncServerSocket(sock).DoEvent(WParam, LParam);
     WM_TIMER:   TAsyncServerSocket(sock).DoTimer;
   end;
end;


Окошко у каждого сокета своё, внутреннее.

P.S. Огромное спасибо за статью на Королевстве. Я именно по ней изучал сокеты :)


 
Сергей М. ©   (2008-04-02 13:18) [7]


> Устанавливается коннект с проксей


Кто такая "прокся" ?
Как устанавливается коннект с ней ?
Каково вообще назначение класса TAsyncClientSocket ?
Это чистой воды транспортный класс ?
Если да, то какого лешего он лезет в прикладной протокол ?


 
SpellCaster   (2008-04-02 13:44) [8]

> [7] Сергей М. ©   (02.04.08 13:18)

Ну вот, снова началось...

Прокси-сервер, работающий по протоколу Socks5.
По спецификациям протокола.
Получать и отправлять данные в асинхронном режиме.
Не знаю, как ты определяешь транспортный или прикладной класс. Однако я сделал так, как сделал. Причем в обоих паках инет-компонентов, что я глядел, соксы упрятаны на самый низкий уровень. Ибо нет смысла вытаскивать их выше.

А теперь, может, закончим оффтоп и вернемся к обработке сообщений?


 
Сергей М. ©   (2008-04-02 13:52) [9]


> закончим оффтоп и вернемся к обработке сообщений?
>


Это далеко не оффтоп.

Но если тебе по барабану все эти важные моменты, то показывай, что у тебя творится в теле HandleSocksConnect


 
SpellCaster   (2008-04-02 14:15) [10]

Прости, но эти важные моменты, на мой взгляд, не имеют никакого отношения к проблеме. Соединение через сокс проходит нормально. Разъясни, как это влияет на ответ по сабжу, может, я чего-то недопонимаю...
// Connect to Socks
function TFrostAsyncClientSocket.HandleSocksConnect: Boolean;
var len: Integer;
   auth:  TSocks5AuthSel;
   ameth: TSocks5AuthMeth;
   req:   TSocks5Request;
   repl:  TSocks5Reply;
   Addr: array[0..255] of Byte;

begin
 Result:=True;
 if fProxyUsed and (fSocksState in [ssConnecting..ssRequesting]) then
 case fSocksState of
   ssConnecting:      // Договариваемся о методах аутентификации
         begin
           fSocksState:=ssMethSelecting;
            ...
           Send(@auth,3,True);
         end;
   ssMethSelecting:   // Устанавливаем связь с хостом
         begin
           if Recv(@ameth,SizeOf(ameth))<=0 then begin Close; WSASetLastError(WSAESOCKSCONNECT); Exit; end;
           ...
           fSocksState:=ssRequesting;
           Send(@req,SizeOf(req));
           Send(@Addr[0],len,True);
         end;
   ssRequesting:  // теперь читаем ответ - ровно столько, сколько прислали
         begin
           // сначала читаем версию, ответ, резерв, тип адреса и 1 байт адреса
           len:=Recv(@repl,SizeOf(repl));
            ...
           if Recv(@Addr,len-1+2)<=0 then begin Close; WSASetLastError(WSAESOCKSCONNECT); Exit; end;
            ...
           fSocksState:=ssEstablished;
           fActive:=True;
           if Assigned(OnEvent) then OnEvent(Self,fSckt,FD_CONNECT);
           PostMessage(fHwnd,WM_SOCKMSG,fSckt,MakeLong(FD_WRITE,0));
         end;
 end  // case
 else Result:=False;
end;

Я убрал заполнение свойств структур, чтобы не очень много получилось. Основнй смысл в том, что метод что-то делает только тогда, когда стоит признак использования прокси и состояние коннекта к сокс-серверу не равно ssEstablished.


 
Сергей М. ©   (2008-04-02 14:38) [11]

Send и Recv - это методы класса TFrostAsyncClientSocket ?
Какой режим гнезда они используют ?


 
SpellCaster   (2008-04-02 16:51) [12]

Да, это методы класса, но они по большому счету просто вызывают винсоковские функции, плюс считают трафик и производят проверку активности сокета.


 
Сергей М. ©   (2008-04-02 17:34) [13]

Какой режим гнезда они используют ?
Они чтго, на время выполнения переводят гнездо в блок.режим ?
Или обращаются совсем к другому
гнезду, нежели то самое неблокирующее гнездо, вокруг которого ты затеял сыр-бор ?


 
SpellCaster   (2008-04-02 19:46) [14]

> Какой режим гнезда они используют ?

Они не меняют режима, т.е. в данном случае в асинхронном. Считай, что это обычные вызовы send/recv winsock-a. А что тебя смутило, разве я как-то не так их использую?


 
Сергей М. ©   (2008-04-02 21:10) [15]


> что тебя смутило, разве я как-то не так их использую?


Так ведь все эти твои send/recv, относящиеся к какому-то там "прокси", точно так же являются инициаторами потенциальных событий FD_READ и FD_WRITE, обрабатывая которые ты вновь вызываешь эту самую  HandleSocksConnect, в которой ты опять вызываешь send/recv, относящиеся к какому-то там "прокси", которые являются инициаторами потенциальных событий FD_READ и FD_WRITE, которые ..

Короче, сказ про то как "у попа была собака")

Кстати, где обработка FD_WRITE ?
Нет ее !
Только не говори, что она не нужна или ты не  знаешь зачем она нужна)


 
SpellCaster   (2008-04-03 15:08) [16]

> [15] Сергей М. ©   (02.04.08 21:10)
> Короче, сказ про то как "у попа была собака")

Для этого как раз и используется переменная fSocksState, которая отслеживает текущее состояние соединения с проксей. Никакого сказа про попа нету, иначе сокет никогда бы не сумел нормально добраться до удалённого хоста, а крутился бы в локалке.

> Кстати, где обработка FD_WRITE ?
> Только не говори, что она не нужна или ты не  знаешь зачем
> она нужна)

Событие FD_WRITE возникает в двух случаях: после FD_CONNECT и когда предыдущий вызов Send закончился ошибкой по причине переполнения выходного буфера. В нашем случае второй вариант не рассматривается. А обработка - она как суслик: она есть, даже если ты ее не видишь ;)
> [2] SpellCaster   (01.04.08 17:33)

FD_READ, FD_WRITE : if HandleSocksConnect then Exit;


Так, это все конечно интересно, но есть ли что сказать по существу вопроса?


 
Сергей М. ©   (2008-04-03 15:18) [17]


> она есть, даже если ты ее не видишь


Я даже по огрызкам вижу, что первым делом при входе в HandleSocksConnect ты тут же озабочен каким-то там fSocksState, хотя первым делом должен идти анализ причин возникновения события - то ли буфер передачи освободился, то ли буфер приема не пуст.


> второй вариант не рассматривается


Почему ? Что за откровение такое тебе пришло, что это событие никогда у тебя не возникнет ?


 
SpellCaster   (2008-04-03 16:07) [18]

> то ли буфер передачи освободился, то ли буфер приема не пуст


> Что за откровение такое тебе пришло, что это событие никогда
> у тебя не возникнет

Учитывая, что эта процедура выполняется сразу после создания сокета, а пересылаемые данные не превышают 100 байт, я ОЧЕНЬ сильно сомневаюсь в вероятности возникновения FD_WRITE как результата освобождения занятого буфера.
Имеется в виду, во время установки связи через соксы. Потом - вполне возможно, но я его и пересылаю в обработчик.

Пожалуйста, не надо давить на меня интеллектом. Я и так знаю, что ты гуру. А вот начальный вопрос так и остается без ответа.


 
Сергей М. ©   (2008-04-03 16:33) [19]


> во время установки связи через соксы


Какие "соксы" ?

Ты о чем - о SOCKS4, о SOCKS5 ?
Там куча режимов, вплоть до необходимости реконнекта кл.гнезда по указанному сервером адресу !
Почем мне знать, какой протокол в каком режиме ты решил использовать ?

Ты бросил огрызок код - нате, мол, догадывайтесь сами о том что я не изволил привести и не собираюсь приводить.

Ну и какого ответа ты после этого ждешь ?


> я ОЧЕНЬ сильно сомневаюсь в вероятности


Не надо сомневаться. Не сильно ни слабо. Надо действовать в точном соответствии с логикой Winsock, а не надеяться на авось.

И уж если тебе приспичило запихнуть в один флакон все это, то проще, надежней и очевидней бфло бы вынести handshake-логику в отдельную процедуру, никак не связанную ни с какими событиями гнезда, для чего нужно всего лишь на время "переговоров" с прокси перевести гнездо в блок.режим.


 
Сергей М. ©   (2008-04-03 17:07) [20]

Если я правильно понял, ты сочиняешь некий "упрощенный" TClientSocket, способный работать с сервером через некий socks-прокси в "прозрачном" режиме ?


 
Сергей М. ©   (2008-04-03 17:20) [21]


> Вполне вероятно, что это может оказаться следующая мессага
> от того же сокета - и тогда SocketEvent будет запущен второй
> раз


Откуда ей, этой "мессаге от того же сокета" взяться, если recv() ты вызываешь после Application.ProcessMessages ?

Очередное FD_READ-событие возникнет не раньше чем будет выполнен вызов recv().

Равно как и очередной вызов send() следует выполнять не раньше FD_WRITE-события, если оно явилось следствием отказа WSAEWOULDBLOCK при предыдущем вызове send()


 
SpellCaster   (2008-04-04 13:53) [22]

> [19] Сергей М. ©   (03.04.08 16:33)
> Ты о чем - о SOCKS4, о SOCKS5 ?

Говорил же...
>Прокси-сервер, работающий по протоколу Socks5.
Обычный метод, без аутентификации.

> Ты бросил огрызок код - нате, мол, догадывайтесь сами о
> том что я не изволил привести и не собираюсь приводить.
>
> Ну и какого ответа ты после этого ждешь ?

"Отгрыз" я код по единственной причине - чтобы не загружать приведенный фрагмент лишними строками, где не происходит ничего важного, кроме заполнения полей записи. Не думаю, что кто-то захочет ковыряться в длинном листинге.
К тому же позволю себе заметить, что данная процедура весьма слабо соотносилась с сабжем.

> для чего нужно всего лишь на время "переговоров" с прокси
> перевести гнездо в блок.режим

Хм, это, конечно, вариант... однако прога на время установки соединения будет блокироваться, чего бы не хотелось. Можно еще с неблокирующими сделать, в цикле проверять готовность и периодически вызывать ProcessMessages. Но мне это кажется слегка нелогичным.

> Если я правильно понял, ты сочиняешь некий "упрощенный"
> TClientSocket, способный работать с сервером через некий
> socks-прокси в "прозрачном" режиме ?

В целом, верно.

> Откуда ей, этой "мессаге от того же сокета" взяться, если
> recv() ты вызываешь после Application.ProcessMessages ?

Воот, наконец-то вернулись к сути вопроса. Разумеется, я не имел в виду FD_READ. Но ведь событие может быть и другим, например, FD_CLOSE. Тогда-то и случится засада: если я вызову Application.ProcessMessages в начале обработчика FD_READ, будет вызван обработчик FD_CLOSE, который спокойно закроет сокет и вернет управление обработчику FD_READ, который будет безуспешно пытаться прочитать данные из закрытого сокета. Так ведь получается?

> Равно как и очередной вызов send() следует выполнять не
> раньше FD_WRITE-события, если оно явилось следствием отказа
> WSAEWOULDBLOCK при предыдущем вызове send()

Учту...


 
Сергей М. ©   (2008-04-04 14:30) [23]


> если я вызову Application.ProcessMessages в начале обработчика
> FD_READ


Событие готовности гнезда к чтению предполагает чтение из гнезда, а не выкрутасы с польз.интерфейсом !


> FD_CLOSE, который спокойно закроет сокет и вернет управление
> обработчику FD_READ, который будет безуспешно пытаться прочитать
> данные из закрытого сокета


Ну и что ? Первая же попытка чтения вернет отказ, на который твой обработчик должен адекватно отреагировать.


 
SpellCaster   (2008-04-04 17:31) [24]

> Событие готовности гнезда к чтению предполагает чтение из
> гнезда, а не выкрутасы с польз.интерфейсом !

А я что, спорю? Предложи альтернативный способ, я его с радостью рассмотрю.

> Ну и что ? Первая же попытка чтения вернет отказ, на который
> твой обработчик должен адекватно отреагировать.

Да, но пришедшие данные будут утеряны!


 
Сергей М. ©   (2008-04-04 21:57) [25]


> Предложи альтернативный способ, я его с радостью рассмотрю


Альтернатив нет - сначала recv(), а потом выкрутасы.

Не нравится ?

Выноси транспорт с его событиями в доп.поток.


> пришедшие данные будут утеряны


С чего ты так уверен ?


 
SpellCaster   (2008-04-09 14:10) [26]

> С чего ты так уверен ?

Из закрытого сокета вроде как читать не получится...
Хорошо, тогда будет 2 вопроса:
1) Как организовать обработку событий сокета в отдельном потоке, потому что мессаги все равно ведь приходят в Application. Возможно, их надо фильтровать по коду мессаги?

2) Как насчет варианта вместо ProcessMessages сделать свой аналог, который будет извлекать из очереди только сообщения, не относящиеся к сокетам.


 
Сергей М. ©   (2008-04-09 14:24) [27]


> Из закрытого сокета вроде как читать не получится


Чтение-то не из сокета происходит, а из внутреннего буфера приема !

Если там что-то имеется на момент закрытия сокета, то recv вернет это самое "что-то".


> 1)


Ты обработку с выборкой не путаешь ?


> 2)


Никак.
Да и нафих оно не нужно, потому что извращенная это логика.

Но если изврат твоя стихия, то можно извлекать из очереди сообщения, относящиеся только к твоим сокетам и тут же ставить их в хвост очереди сообщений безо всякой обработки.


 
SpellCaster   (2008-04-09 18:15) [28]

> Чтение-то не из сокета происходит, а из внутреннего буфера
> приема !
> Если там что-то имеется на момент закрытия сокета, то recv
> вернет это самое "что-то".

Ну да. Но разве он не освобождается после закрытия? К тому же хэндл уэ точно не будет иметь смысла после Close.

> Ты обработку с выборкой не путаешь ?

Угу... есть такое дело. Так как сделать это в отдельном потоке? Или там нужно будет PostThreadMessage юзать?


 
Сергей М,   (2008-04-09 20:31) [29]


> разве он не освобождается после закрытия?


Смотря с какой стороны его закрыть.


> как сделать это в отдельном потоке?


Сделать что ?
Выборку ?
Обработку ?
И то и другое ?


 
SpellCaster   (2008-04-10 12:59) [30]

Выборку


 
Сергей М. ©   (2008-04-10 14:19) [31]

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


 
SpellCaster   (2008-04-10 16:06) [32]

Ну и...?


 
Сергей М. ©   (2008-04-10 16:11) [33]

Что "ну и" ?


 
SpellCaster   (2008-04-10 17:38) [34]

В каком смысле > созданы в его собственном контексте.? Функция CreateЦindow была вызвана внутри Execute?
И тогда если в том же Execute я организую выборку сообщений, то она будет распространяться только на эти окна?


 
SpellCaster   (2008-04-10 18:14) [35]

Хотя мне все равно кажется, что это не выход... блин, ну как-то же делают серваки с сотнями клиентов, которые не тормозят... и этот ICS все хвалили, а там ведь такая же система


 
DiamondShark ©   (2008-04-10 20:09) [36]


> ну как-то же делают серваки с сотнями клиентов, которые
> не тормозят

Без окон


> и этот ICS все хвалили

ай да, конечно.


 
Сергей М,   (2008-04-10 20:19) [37]


> В каком смысле > созданы в его собственном контексте.? Функция
> CreateЦindow была вызвана внутри Execute?


Да.
Хотя каким боком к тебе этот Execute ?
Ты вон все апями озабочен да BeginThread"ами)


> если в том же Execute я организую выборку сообщений, то
> она будет распространяться только на эти окна?


По умолчанию - да.


> как-то же делают серваки с сотнями клиентов, которые
> не тормозят


Их по-другому делают.
Ты же не озаботился исследованием, ты сразу ринулся код лепить)
Еще и оффтопом меня попрекнул)

Чудо, блин)


 
SpellCaster   (2008-04-14 13:34) [38]

> Без окон

Может, есть и без окон, но большинтсво все-таки с гуем... виндоус все-таки. Ладно, уйдём от серваков. Возьмем "качалки" с кучей потоков загрузки - они-то не тормозят...

> Ты вон все апями озабочен да BeginThread"ами)

Вот не надо, треды через апи меня пока не привлекают)

> Ты же не озаботился исследованием, ты сразу ринулся код
> лепить)
> Еще и оффтопом меня попрекнул)

Гм. Исследованием чего?
Ну а насчет оффа - все-таки речь не о соксах, согласись.


 
SpellCaster   (2008-04-14 13:36) [39]

> Их по-другому делают

Вот я и хочу узнать, какой там принцип юзается. С нитями понятно, хотя все равно не хочется с ними дело иметь - иначе смысл городить огород, легче все на синхронных оставить...


 
Сергей М. ©   (2008-04-14 14:53) [40]


> большинтсво все-таки с гуем


Причем здей гуй ?


> уйдём от серваков. Возьмем "качалки" с кучей потоков загрузки
> - они-то не тормозят


И у тебя не будет "тормозить", если втанешь на "правильный курс".


> хочу узнать, какой там принцип юзается


Там транспортная и/или прикладная логика вынесена в дополнительные потоки.


> иначе смысл городить огород, легче все на синхронных оставить


Решать тебе.


 
SpellCaster   (2008-04-14 16:46) [41]

То есть "правильный курс" - это дополнительные потоки? И в одном потоке никак?


 
Сергей М. ©   (2008-04-14 16:58) [42]

В одном потоке твои клиенты будут обслуживаться последовательно.
Если тебя это устраивает, делай в одном.

А потоки на то и придуманы, чтобы распараллелить длительные вычислительные алгоритмы, а не выполнять их последовательно.


 
SpellCaster   (2008-04-16 10:39) [43]

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


 
Сергей М. ©   (2008-04-16 11:27) [44]

Если теряется, значит не устраивает.



Страницы: 1 2 вся ветка

Форум: "Сети";
Текущий архив: 2009.10.18;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.6 MB
Время: 0.01 c
2-1250069179
AlexDan
2009-08-12 13:26
2009.10.18
Распечатать три memo..


15-1250678829
DSKalugin
2009-08-19 14:47
2009.10.18
Переведите пожалуйста с паскаля на php


11-1205291090
Trible
2008-03-12 06:04
2009.10.18
AlphaBlend и восстановление формы


2-1250517034
ford
2009-08-17 17:50
2009.10.18
RichEdit текст в верхнем индексе


2-1250853074
Начинающий1234
2009-08-21 15:11
2009.10.18
DrawItem (TListBox)





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский