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

Вниз

Отдельный поток в DLL   Найти похожие ветки 

 
GRF   (2004-02-04 23:39) [0]

Доброго времени. Ситуация такая: есть некторое приложение которое вызывает форму из DLL и в этой форме начинаются некторые действия. Как запустить эти действия в отдельном потоке. Т.е. короче как в DLL создать поток. Я пробовал как в книжках пишут, но в exe-шнике получается, а в DLL нет. Может там какие ньюансы есть?


 
Digitman   (2004-02-05 08:18) [1]

приводи код потока


 
darkhunter   (2004-02-05 09:37) [2]

в dll опиши своего наследника от TThread. Экспортируй из dll функцию, которая создаст его и вернёт в результат.


 
panov   (2004-02-05 09:45) [3]

У меня в инфе есть ссылка, там есть пара примеров работы с потоками в DLL.


 
GRF   (2004-02-05 10:21) [4]

to darkhunter
А почему тогда TAnimate работает в отдельном потоке куда его ни запихни, хоть в DLL. И никакой отдельной функции экспортировать не надо.


 
Digitman   (2004-02-05 10:36) [5]


> GRF


ты код приведешь свой ? или будешь продолжать почемукать ?


 
TUser   (2004-02-05 11:47) [6]


> Я пробовал как в книжках пишут

А как там пишут? Я к тому, что написать там могут по-разному, но никто не знает, что ты за книжки читал. В принцыпе, особых особенностей нет - пишешь наследника от TThread, где-то далаешь Resume - и вперед.


 
GRF   (2004-02-05 12:07) [7]

Привожу код.
1. Exe-шник с главной формой. У него три кнопки: Button1 - начинает какие-то вычисления (не в отдельном потоке); Button2 - показывает DLL-форму; Button3 - скрывает DLL-форму;
Код EXE-шника:
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

procedure ShowDLLForm(OWN : HWND); stdcall; external "Project2.dll";
procedure HideDLLForm; stdcall; external "Project2.dll";

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
I : Real;
begin
Form1.Button1.Enabled := False;
I := 100000000;
while I > 0 do
I := I-0.1;
Form1.Button1.Enabled := True;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
ShowDllForm(Application.Handle);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
HideDLLForm;
end;

end.


Код library:
library Project2;

uses
Windows,
SysUtils,
Classes,
Controls,
Forms,
Unit2 in "Unit2.pas" {Form2},
Unit3 in "Unit3.pas";

{$R *.res}

procedure ShowDLlForm(OWN : HWND); stdcall; export;
begin
If Application.Handle <> OWN
then Application.Handle := OWN;
ShowForm2;
end;

procedure HideDLLForm; stdcall; export;
begin
HideForm2;
end;

exports
ShowDllForm name "ShowDLLForm",
HideDLLForm name "HideDLLForm";

begin
end.


Код DLL-формы:
unit Unit2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Unit3;

type
TForm2 = class(TForm)
Label2: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;

procedure ShowForm2;
procedure HideForm2;

var
Form2: TForm2;

implementation

{$R *.dfm}

procedure ShowForm2;
begin
HideForm2;
Form2 := TForm2.Create(Application);
Form2.Show;
MyThread := TMyThread.Create(False);
Mythread.FreeOnTerminate := True;
end;

procedure HideForm2;
begin
If Assigned(MyThread)
then MyThread.Terminate;
If Assigned(Form2)
then Form2.Free;
Form2 := nil;
end;

initialization
Form2 := nil;

finalization
HideForm2;

end.


Код потока:
unit Unit3;

interface

uses
Windows,
Classes;

type
TMyThread = class(TThread)
private
{ Private declarations }
protected
procedure Execute; override;
procedure UpdateLabel2;
end;

var MyThread : TMyThread;

implementation

uses Unit2;

procedure TMyThread.Execute;
begin
Synchronize(UpdateLabel2);
end;

procedure TMyThread.UpdateLabel2;
begin
while True do
begin
Form2.Label2.Caption := "Раз";
Sleep(500);
Form2.Label2.Caption := "Два";
Sleep(500);
Form2.Label2.Caption := "Три";
Sleep(500);
Form2.Label2.Caption := "Елочка гори!";
Sleep(500);
end;
end;

end.


Уж не знаю что я там понаписал, но у меня поток в DLl даже не запускается - форма появляется а действий никаких.
Вычисления в главной форме происходят не в отдельном потоке. Т.е. мне нужно чтобы:
1. Запустился поток в DLL.
2. Потом(!!!) начались вычисления в главной форме, а поток в DLL продолжал работать.
Спасибо.


 
Digitman   (2004-02-05 12:24) [8]



type
TMyThread = class(TThread)
private
{ Private declarations }
FCaption: String;
protected
procedure Execute; override;
procedure UpdateLabel2;
end;

const Msgs: array[0..3] of String = ("Раз", "Два", "Три", "Читаем внимательно документацию !");

procedure TMyThread.Execute;
var i: Integer;
begin
i := 0;
while not Terminated do
begin
FCaption := Msgs[i];
i := (i + 1) mod 4;
Synchronize(UpdateLabel2);
Sleep(500);
end;
end;

procedure TMyThread.UpdateLabel2;
begin
Form2.Label2.Caption := FCaption;
end;

procedure ShowForm2;
begin
HideForm2;
Form2 := TForm2.Create(Application);
Form2.Show;
MyThread := TMyThread.Create(False);
end;

procedure HideForm2;
begin
FreeAndNil(MyThread);
FreeAndNil(Form2);
end;



 
TUser   (2004-02-05 12:28) [9]

Может я чего-то не понимаю - а где он у тебя запускается.

MyThread := TMyThread.Create(False);
Mythread.FreeOnTerminate := True;

Надо еще написать Resume, или Create(true).


 
TUser   (2004-02-05 12:30) [10]

Хотя, извини, погорячился. CreateSuspeneded = false, так что нормально. Попробуй Resume;


 
Digitman   (2004-02-05 12:31) [11]


> TUser © (05.02.04 12:28) [9]


> Надо еще написать Resume, или Create(true).


не выдумывай, все запускается


 
GRF   (2004-02-05 15:44) [12]

Люди, может у меня что с головой, но опять ничего не выходит. Исправил как сказал Digitman, вроде бы.
Привожу, что направил (простите, что забиваю форум всяким мусором, но на пальцах то тут вроде не покажешь).
1. Код exe-шника и library не менял.
2. Код DLL-формы

unit Unit2;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Unit3;

type
TForm2 = class(TForm)
Label2: TLabel;
private
{ Private declarations }
public
{ Public declarations }
end;

procedure ShowForm2;
procedure HideForm2;

var
Form2: TForm2;

implementation

{$R *.dfm}

procedure ShowForm2;
begin
HideForm2;
Form2 := TForm2.Create(Application);
Form2.Show;
MyThread := TMyThread.Create(False);
end;

procedure HideForm2;
begin
FreeAndNil(MyThread);
FreeAndNil(Form2);
end;

initialization
Form2 := nil;

finalization
HideForm2;

end.


3. Код потока

unit Unit3;

interface

uses
Windows,
Forms,
SysUtils,
Classes;

type
TMyThread = class(TThread)
private
FCaption : String;
protected
procedure Execute; override;
procedure UpdateLabel2;
end;

var
MyThread : TMyThread;

const Msgs : array[0..3] of String = ("Раз", "Два", "Три", "Читаем внимательно документацию !");

implementation

uses Unit2;

procedure TMyThread.Execute;
var
I : Integer;
begin
I := 0;
while not Terminated do
begin
FCaption := Msgs[I];
I := (I+1) mod 4;
Synchronize(UpdateLabel2);
Sleep(500);
end;
end;

procedure TMyThread.UpdateLabel2;
begin
Form2.Label2.Caption := FCaption;
end;

end.


Поток снова молчит. Помогите разобраться.


 
Digitman   (2004-02-05 16:02) [13]


> procedure TMyThread.Execute;
> var
> I : Integer;
> begin
> I := 0; <-- сюда поставь брейкпойнт
> while not Terminated do
> begin
> FCaption := Msgs[I];
> I := (I+1) mod 4;
> Synchronize(UpdateLabel2);
> Sleep(500);
> end;
> end;


 
GRF   (2004-02-05 16:41) [14]

А че с ним делать с этим брейкпоинтом? Я попробовал поставить, задал HostApplication, нажал Run и комп повис.


 
Digitman   (2004-02-05 17:04) [15]


> комп повис


)))
а я здесь причем ?!

бросай отладку DLL, переходи на отладку хост-приложения и ищи (хоть пошагово хоть с расстановкой брейкпойнтов), при выполнении какого оператора в хост-программе происходят такие чудеса


 
GRF   (2004-02-05 19:32) [16]

Так а все-таки. Что в данном случае происходит? "Чудеса" или ошибка в коде? Если я правильно Вас понял, то вышепреведенный код (исправленный) должен работать. Я хоть и не очень в этом рублю, но мне кажется, что проблем быть не должно.
P.S. Вы здесь абсолютно не причем. Разве кто-то так говорил?


 
Verg   (2004-02-05 19:47) [17]


> Form2 := TForm2.Create(Application);


1) Попробуй

procedure ShowForm2;
begin
HideForm2;
Form2 := TForm2.Create(nil);
MyThread := TMyThread.Create(False);
Form2.ShowModal;
end;


 
GRF   (2004-02-05 20:13) [18]

to Verg
ShowModal я уже пробовал. Пробовал для того, чтобы убедиться, что поток создается "в принципе". Так вот "в принципе" он создается. Но этот вариант также "в принципе" не подходит, т.к. приложение вызвавшее форму умирает и на это не влияет ни Create(nil) ни Create(Application).
Мне же нужно, что вызвавшее приложение продолжало работать, тем более, что в моем случае на это приложение я никак не смогу повлиять.
Может быть все-таки есть у кого рабочий примерчик или на крайняк скажите где можно увидеть код компонента TAnimate, т.к. он как раз и работает в отдельном потоке. Для проверки запускал искомую DLl-форму из искомого приложения и все было замечательно.


 
Verg   (2004-02-05 20:37) [19]

А все! я понял - это же D7. т.е. >= D6. Там же Synchronize и вообще весь этот механизм сделан по-другому.
Понмнишь, Digitman, я об этом писал?

Ключевым в данном случае является то, что DLL и прога пользуются разными копиями основных синх. объектов:

SyncList - список ожидающих потоков
ThreadLock - критическая секция

Короче, все это надо обдумывать - как такой случай разрулить....


 
GRF   (2004-02-05 21:19) [20]

Короче, дело труба...


 
Verg   (2004-02-05 21:20) [21]

хех-хе-е! Крепко ты попал на TV...

В DLL-ке Owner в Create у form2 ставь nil, synchronize убирай. т.е. все напрямую закороти пока. Пока, это все и должно заработать...

А потом,... а потом пока не знаю.... времени нет с этим разбираться...


 
Verg   (2004-02-06 09:32) [22]

В общем по-моему придется частично реконструировать механизм синхронизации через оконные сообщения.

DLL-ка

unit DTMain;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

const
WM_THREAD_SYNC = WM_USER + 1;

type
TMethod = procedure of object;

TForm1 = class(TForm)
Label1: TLabel;
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
procedure SyncThread(var Msg : TMessage); message WM_THREAD_SYNC;
public
{ Public declarations }
end;

var
Form1: TForm1;

procedure ShowForm2; stdcall; export;
procedure HideForm2; stdcall; export;

implementation

{$R *.dfm}
const Msgs : array[0..3] of String = ("Раз", "Два", "Три", "Читаем внимательно документацию !");
type
TMyThread = class(TThread)
private
FCaption : String;
FMethod : TMethod;
procedure MySynchronize(M : TMethod);
public

procedure UpdateLabel2;
procedure Execute; override;
end;

procedure ShowForm2;
begin
Form1 := TForm1.Create(nil);
Form1.Show;
TMyThread.Create(False);
end;

procedure HideForm2;
begin
Form1.Free;
Form1:=nil;
end;

procedure TMyThread.MySynchronize(M : TMethod);
begin
if Form1<>nil then
begin
FMethod:=M;
SendMessage(Form1.Handle, WM_THREAD_SYNC, 0, LPARAM(self));
end;
end;

procedure TMyThread.Execute;
var
I : Integer;
begin
I := 0;
while not Terminated do
begin
FCaption := Msgs[I];
I := (I+1) mod 4;
MySynchronize(UpdateLabel2);
Sleep(500);
end;
end;

procedure TMyThread.UpdateLabel2;
begin
if Form1<>nil then
Form1.Label1.Caption := FCaption;
end;

procedure TForm1.SyncThread(var Msg : TMessage);
begin
TMyThread(Msg.LParam).FMethod;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action:=caFree;
Form1:=nil;
end;

end.


Или что-нибудь в таком духе...


 
Digitman   (2004-02-06 09:48) [23]


> Verg © (05.02.04 20:37) [19]


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

но здесь вопрос автором был поставлен принуипиально : поток якобы не стартует ... стартует он ! прекрасно стартует) ... и я намеками на брейкпойнты хотел лишь заставить автора убедиться в этом воочию ..

разумеется, здесь проблемы с synchronize() ...

к сож., нет у меня перед носом реализации метода в Д7

склонен предположить, что хэндл гл.окна хост-приложения все же следует передать в DLL перед 1-м вызовом Synchronize(), назначив его св-ву Application.Handle экз-ра объекта Application, создаваемого в DLL

либо собрать и хост-приложение и DLL c опцией Build With Run-Time Packages - тогда объект Application и там и там будет единственным.


 
Digitman   (2004-02-06 09:54) [24]

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


 
Verg   (2004-02-06 10:03) [25]


> разумеется, здесь проблемы с synchronize() ...


Именно. В D6-7 Synchronize запиывает параметры метода в некий список, являющийся просто переменной раздела implementation модуля classes, и встает в ожидание обработки этого элемента списка. А функция CheckSynchronize, периодически вызываемая из цикла выборки оконных сообщения приложения, проверяет этот список и, если он не пуст производи вызовы синхронизируемых методов классов TThread из этого списка.
Ясно, что в DLL-ке synchronize поступает так же - она тоже записывает в такой же список, но именно "в такой же", а не "в тот же". Ясно, что checksynchronize в главной проге знать ничего не знает о каких-то еще списках синхронизации...
Ну и все, стоит TThread в DLL вызвать Synchronize, он вподает в бесконечное ожидание.


 
Verg   (2004-02-06 10:07) [26]


> к сож., нет у меня перед носом реализации метода в Д7


От D6 могу выслать classes.
Так, для ознокомления...


 
Digitman   (2004-02-06 10:29) [27]


> Verg © (06.02.04 10:03) [25]


отсюда делаем вывод уже, наверно ?
вывод простой (в предположении, что иные нестанд.механизмы синхр-ции не применяются) :

- в Д5 перед выкрутасами со синхронизацией от Борланда передавать хэндл хост-объекта Application в DLL перед первым обращением к методу синхр-ции

- в общем случае (Д5,Д6,Д7) собирать хост-приложение и DLL с устан.опцией BwRTP

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


> [26]


да не стОит, наверно ... при крайней необходимости разверну в 6 секунд ... дистриб у меня иемеется и 6-ки и 7-ки


 
Verg   (2004-02-06 11:06) [28]


> в Д5 перед выкрутасами со синхронизацией от Борланда передавать
> хэндл хост-объекта Application в DLL перед первым обращением
> к методу синхр-ции


Че-то не догоняю. Чем может помочь этот хэндл Host Application в DLL в смысле синхронизации потоков с главным?


 
Digitman   (2004-02-06 11:51) [29]


> Verg © (06.02.04 11:06) [28]


угу... возражение принято

вот глянул сейчас повнимательней у себя в Д5 -действительно, на собственно синхронизацию это не влияет
ThreadWindow - верхнеуровневое окно тек.дисктопа, и цикл выбоки/диспетчеризации сообщений, "крутящийся" в методе Run объекта Application хост-приложения, должен успешно принимать и реагировать на сообщения CM_EXECPROC и CM_DESTROYWINDOW

подтверждающий это фрагмент :

function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
var
Handled: Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
...

и соотв.фр-т хэлпа :

If hWnd is NULL, PeekMessage retrieves messages for any window that belongs to the current thread making the call. (PeekMessage does not retrieve messages for windows that belong to other threads.)

т.е. если ThreadWindow создано в осн.потоке (а здесь, у автора, именно этот случай), то ThreadWndProc() обязательно получит управление, как только осн.поток диспетчеризует упомянутые сообщения


 
Verg   (2004-02-06 12:02) [30]


> т.е. если ThreadWindow создано в осн.потоке (а здесь, у
> автора, именно этот случай), то ThreadWndProc() обязательно
> получит управление, как только осн.поток диспетчеризует
> упомянутые сообщения


Но, у автора "D7" в заголвке subj... А там все по-другому.
Вот я и предложил искусственно "зацепиться" за диспетчерезацию, использовав не ThreadWindow, кторого больше просто нет, а саму же форму, т.к. она создается контекстом осн. код. потока.

> Verg © (06.02.04 09:32) [22]


 
Digitman   (2004-02-06 12:14) [31]


> Verg © (06.02.04 12:02) [30]


нет, подожди ..
ну а единый модуль classes (в случае с BwRTP и там и там) разве не решит проблему просто и без выкрутасов с "искуственностью" ?

imho, вполне должен решить ... список-то становится единым ! доп.поток добавляет в его конец запись, а "разбуженный" осн.поток извлекает из головы списка запись ...


 
Verg   (2004-02-06 12:15) [32]

Да, кстати, теоритически мог бы существовать еще один способ исправить эту ситуацию с синхронизацией в DLL при новой модели.

А именно.

В host приложении мы объявляем процедуру
procedure DllSyncProc(T : TThread; M : TMethod);
При инициализации длл, мы бы передали адрес этой процедуры в длл, чтобы потоки, которые там создаются вызывалии ее для синхронизации вместо штатной Synchronize.
Что делает эта процедура:

procedure DllSyncProc(T : TThread; M : TMethod);
begin
T.Synchronize(M);
end;

Т.о. она установит запрос на синхронизацию метода М в общую очередь(список) запосов.

Все. Казалось бы проблема решена в рамках существующей модели синхронизации.

Но! Synchronize - это из секции protected у TThread.....


 
Verg   (2004-02-06 12:20) [33]


> Digitman © (06.02.04 12:14) [31]
>
> > Verg © (06.02.04 12:02) [30]
>
>
> нет, подожди ..
> ну а единый модуль classes (в случае с BwRTP и там и там)
> разве не решит проблему просто и без выкрутасов с "искуственностью"
> ?


Должен решить.
Если classes становится на самом деле общим.


 
Digitman   (2004-02-06 12:58) [34]


> Verg © (06.02.04 12:15) [32]


согласен, вполне логично


> [33]


и на сем, наверно, закончим прения по сабжу
автор сам примет решение


 
GRF   (2004-02-06 15:43) [35]

to Verg
Попробовал я пример от (06.02.04 09:32) [22] - буковки то появляются, а вот если запустить вычисления в основной форме, то поток замирает.

to Digitman
Насчет пакетов. Насколько я понимаю, то пакеты применяются когда взаимодействие происходит с помощью программ написанных на Delphi(по крайней мере где-то слышал). Если это так, то в моем случае это не подойдет, т.к. вызывающей программой будет Lisp-программа из AutoCAD, а посредником (т.к. Lisp только с серверами автоматизации может работать) ARX-приложение, написанное на VC++.

to All
А вообще большое спасибо за попытку помочь. Хоть почти ничего и не понял (особенно из последних высказываний), но все равно спасибо. Пока как-нибудь без потоков обойдусь.


 
Digitman   (2004-02-06 15:56) [36]


> GRF © (06.02.04 15:43) [35]


> вызывающей программой будет Lisp-программа из AutoCAD


почему бы об этом сразу не сказать !? в этом случае без "выкрутасов" в принципе не обойтись

получается что ты попросту не уважаешь чужое время и траффик...


> почти ничего и не понял


это печально


 
GRF   (2004-02-06 16:13) [37]

to Digitman
Мне не хочется перед Вами оправдываться, но тем не менее, раз пошла такая бодяга, то я это сделаю.
1. Мне кажется, что из диалога со мной очевиден уровень моих знаний.
2. Почему я "сразу не сказал". Наверное я допустил ошибку. Но допустил я ее потому что когда я на разных форумах обращался с подобными вопросами (не именно с этим), то мне либо не могли толком ответить, либо отвечали, но я не понимал. И эта ситуация не зависела от того говорил я об AutoCAd или нет. Поэтому с пустя какое-то время я стал просто забывать об этом. Я понимаю, что это мои проблемы, но сделанного не воротишь. Впредь буду внимательнее.
3. Почему я еще об этом может быть не сказал, да потому что исходя из своего дилетанства я глянул на компонент TAnimate, форма с которым (форма в DLL) работает при вызове из AutoCAD во время любых вычислений и всяких длительных и грузовых AutoCAd-довских действий. Так вот я глянул и подумал, что наверное моя ситуация не зависит от того откуда и как вызывать, раз тот компонент работает.
4. А насчет "трафика" не знаю что Вам сказать. Такое впечатление, что Вы меня в воровстве обвиняете.


 
Digitman   (2004-02-06 16:29) [38]


> Вы меня в воровстве обвиняете


нет. и не думал. сожалею, что ты мою "мысльвслух" неправильно интерпретировал.

теперь - по поводу TAnimate

не знаю как это в Д7, но в Д5, что у меня на столе, в реализации этого класса нет ни намека на исп-е доп.потоков этим классом.


 
GRF   (2004-02-06 16:40) [39]

Я конечно тоже не могу ничего конкретного по поводу TAnimate сказать.
Но кое-что формально могу.
1. В какой-то книге по Delphi я читал, что "компонет TAnimate использует отдельный поток, что позволяет анимации воспроизводиться, а программе выполнять основные действия". Привел хоть не дословно но почти.
2. Я сам это проверял. Хоть в Дельфийских программах, хоть из AutoCAD. Вычисления сами по себе - анимация сама по себе. Но если использовать что-то другое всместо TAnimate, например "раз", "два" и т.д. или другу анимацию (Flash), то ничего не работает.


 
Digitman   (2004-02-06 17:11) [40]

приведи свой код, где ты используешь TAnimate (создаешь/вызываешь методы/разрушаешь) исключительно в доп.код.потоке, при этом род.окном у TAnimate явл-ся окно одной из форм, и при в сем при том эта конструкция якобы успешно работает ...



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

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

Наверх




Память: 0.58 MB
Время: 0.009 c
14-53763
Карелин Артем
2004-01-27 15:19
2004.02.17
Посоветуйте книгу по C# и винде.НЕТ


7-53801
НВ
2003-11-26 14:25
2004.02.17
Как работасть с принтером?


1-53583
VID
2004-02-07 14:00
2004.02.17
Глюк у RichEdit


14-53750
SemFLY
2004-01-27 01:27
2004.02.17
Посоветуйте книжку(и) для изучения ОС семейства UNIX...


14-53738
Думкин
2004-01-28 06:39
2004.02.17
С днем рорждения! 28 января.





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
Английский Французский Немецкий Итальянский Португальский Русский Испанский