ANTICHAT.XYZ    VIDEO.ANTICHAT.XYZ    НОВЫЕ СООБЩЕНИЯ    ФОРУМ  
Баннер 1   Баннер 2
Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей. Здесь обсуждаются безопасность, программирование, технологии и многое другое. Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
Вернуться   Форум АНТИЧАТ > Программирование > С/С++, C#, Delphi, .NET, Asm
   
Ответ
 
Опции темы Поиск в этой теме Опции просмотра

Кодерские tips and tricks
  #1  
Старый 16.11.2006, 15:48
Аватар для TaNkist
TaNkist
Участник форума
Регистрация: 06.04.2006
Сообщений: 257
Провел на форуме:
367179

Репутация: 76
По умолчанию Кодерские tips and tricks

Кодерские tips’n’tricks

В данной теме делимся программерскими трюками. Это могут быть советы по оптимизации, нестандартные приемы, интересные (но не слишком большие) куски кода. Не стоит превращать тему в свалку исходников, выбирайте наиболее интересные и полезные участки кода. Желательно откомметировать трюк, чтобы всем было понятно.
Delphi
Оптимизация

[Передача аргументов] Никогда не передавай функции в качестве параметра структуру, лучше передавать указатель на нее
Код:
//Ни в коем случае не делай так.
Procedure code (Data:TStructure);
//Правильный вариант
Procedure code (PData:PStructure); //гда PStructure = ^TStrucrure
Старайся передавать своим функциям не более трех параметров, т.к. они передаются через регистры (по соглашению fastcall, принятом по умолчанию в Delphi), а все остальные через стек.
[Функции - инварианты] Довольно распространенная ошибка программистов – присутствие функций - инвариантов в цикле.
Код:
//Неоптимизированный вариант
While i<= lstrlen(str) do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
Очевидно, что длина str не меняется, но компилятор считает, что все, что передается по ссылке сожжет быть изменено, и lstrlen вычисляется много раз. Оптимизированный вариант выглядит так.
Код:
//Так лучше
N:=lstrlen(str);
While i<= n do
Begin
X:=x+ord(str[i]);
Inc(i);
End;
[Экономия памяти] Когда класс располагается в памяти, то между полями появляются пустые ячейки памяти. Это происходит потому, что Delphi, оптимизируя код, каждое поле располагает от предыдущего со сдвигом в 4 байта.
Код:
// Неоптимизированный вариант
TMyClass = class
  private
    field1: boolean;//1 байт
    field2: longint; //4 байт
    field3: char; //1 байт
    field4: string; //4 байт
    field5: byte; //1 байт
    field6: integer; //4 байт
    field7: byte; //1 байт
  public
    procedure code;
end;
Реально этот экземпляр класса будет занимать 28 байт. Если мы изменим порядок полей, то сможем добиться уменьшения размера до 16 байт. В нашем примере после field1 размером 1 байт идет field2 размером 4 байта, значит, мы теряем 3 байта на выравнивание. Если же размер field2 не превышал 3 байт, то Delphi не стал бы выравнивать, а поместил бы это поле сразу после первого.
Код:
// Оптимизированный вариант
TMyClass = class
  private
    field1: boolean; //1 байт
    field3: char; //1 байт
    field5: byte; //1 байт
    field7: byte; //1 байт
   field2: longint; //4 байт
    field4: string; //4 байт
    field6: integer; //4 байт
  public
    procedure code;
end;
[Компиляция без RTL (Run Time Library)] Как известно минимальный размер скомпилированной в Delphi программы с настройками по умолчанию равен 13,5 Кб. Виной тому принудительно подключаемая Delphi RTL, в которой реализованы некоторые возможности языка Delphi.
Для уменьшения размера скомпилированных прог исправим модели System.pas и SysInit.pas, удалив все «лишнее». Затем перекомпилируем их и полученные dcu-файлы поместим в папку с прогой.
Минимальный System.pas
Код:
unit System;

interface

procedure _HandleFinally;

type
 TGUID = record
  D1: LongWord;
  D2: Word;
  D3: Word;
  D4: array [0..7] of Byte;
 end;

 PInitContext = ^TInitContext;
 TInitContext = record
    OuterContext:   PInitContext;   
    ExcFrame:       Pointer;          
    InitTable:      pointer;     
    InitCount:      Integer;          
    Module:         pointer;       
    DLLSaveEBP:     Pointer; 
    DLLSaveEBX:     Pointer; 
    DLLSaveESI:     Pointer;   
    DLLSaveEDI:     Pointer;   
    ExitProcessTLS: procedure;     
    DLLInitState:   Byte;             
 end;

implementation

procedure _HandleFinally;
asm
end;

end.
Минимальный SysInit.pas
Код:
unit SysInit;

interface
procedure _InitExe;
procedure _halt0;
procedure _InitLib(Context: PInitContext);

var
  ModuleIsLib: Boolean;        
  TlsIndex: Integer = -1;       
  TlsLast: Byte; 

const
  PtrToNil: Pointer = nil;   

implementation

procedure _InitLib(Context: PInitContext);
asm
end;

procedure _InitExe;
asm
end;

procedure _halt0;
asm
end;
end.
Компиляция
Код:
Dcc32.exe – Q System.pas SysInit.pas –M –Y –Z -$D- -O
Некоторые трюки

[Обмен] Обмен значений между двумя переменными без привлечения третьей.
Код:
x:=x xor y;
y:=y xor x;
x:=x xor y;
 
Ответить с цитированием

  #2  
Старый 16.11.2006, 16:04
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


Отправить сообщение для _Great_ с помощью ICQ
По умолчанию

Оптимизация выходного файла в Microsoft Visual C++.
Параметры линкера для уменьшения размера:
- объединение секций
Код:
/MERGE:.data=.text /MERGE:.rdata=.text
- можно еще снизить выравнивание, например, задать 512 байт или 32 байта
Код:
/ALIGN:32
или
Код:
/ALIGN:512
соответственно.
- смена точки входа
Код:
/ENTRY:main
или
Код:
/ENTRY:WinMain
для консольного и виндового приложения соответственно. Уменьшает размер, т.к. тогда линкер не пихает в экзешник код стартовой функции mainCRTStartup (WinMainCRTStartup). Только надо помнить, что параметры main (WinMain) в этом случае не будут нести никакого смысла, аргументы командной строки придется получать явно через API GetCommandLine().
- еще можно убрать нафиг CRT или линковать ее динамически:
Код:
/NODEFAULTLIB msvcrt.lib
С учетом всех рекомендаций:
Код:
#include <windows.h>
#pragma comment(linker, "/NODEFAULTLIB /MERGE:.data=.text /MERGE:.rdata=.text /ALIGN:512 /ENTRY:WinMain")

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
	MessageBox(0, "Hello, World!", "Tiny application", MB_ICONINFORMATION);
	return 0;
}
Результат - имеет хелловорлд размером 1к, состоящим из заголовка экзешника и одной секции.
Дизассемблерный листинг точки входа не содержит ни одного лишнего байта:
Код:
00400230 >/$ 55             PUSH EBP
00400231  |. 8BEC           MOV EBP,ESP
00400233  |. 6A 40          PUSH 40                                  ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL
00400235  |. 68 08024000    PUSH tiny.00400208                       ; |Title = "Tiny application"
0040023A  |. 68 1C024000    PUSH tiny.0040021C                       ; |Text = "Hello, World!"
0040023F  |. 6A 00          PUSH 0                                   ; |hOwner = NULL
00400241  |. FF15 00024000  CALL DWORD PTR DS:[<&USER32.MessageBoxA>>; \MessageBoxA
00400247  |. 33C0           XOR EAX,EAX
00400249  |. 5D             POP EBP
0040024A  \. C2 1000        RETN 10
UPD: Можно снизить выравнивание до 16-и байт, притворившись, что мы собираем драйвер:
/ALIGN:16 /DRIVER
это нужно вписать в настройки проекта, в #pragma comment(linker) это не прокатит

Последний раз редактировалось _Great_; 08.12.2006 в 14:28..
 
Ответить с цитированием

  #3  
Старый 18.11.2006, 21:28
Аватар для TaNkist
TaNkist
Участник форума
Регистрация: 06.04.2006
Сообщений: 257
Провел на форуме:
367179

Репутация: 76
По умолчанию

Delphi
Чтобы во время выполнения циклов, не происходил эффект зависания, нужно в теле цикла вставить
Код:
Application.ProcessMessages;
Эффект зависания происходит из-за того, что Windows ждет пока накопятся задания в очереди, а не выполняет их сразу. Application.ProcessMessages заставляет винду выполнить все задачи, которые накопились в данный момент.
 
Ответить с цитированием

  #4  
Старый 19.11.2006, 10:24
Аватар для W!z@rD
W!z@rD
Reservists Of Antichat - Level 6
Регистрация: 12.02.2006
Сообщений: 891
Провел на форуме:
1892597

Репутация: 836


Отправить сообщение для W!z@rD с помощью ICQ
По умолчанию

в VCL (delphi) есть "перехват" закрытия формы
OnCloseQuery (где canclose boolean типа который и дает "разрешение" на закрытие формы)...
__________________
*********************************
*Я не волшебник ٩(๏̯͡๏)۶, только учусь...*
*********************************
Программы на заказ
Times to fly...

Последний раз редактировалось W!z@rD; 16.10.2009 в 07:56..
 
Ответить с цитированием

  #5  
Старый 22.11.2006, 14:14
Аватар для TaNkist
TaNkist
Участник форума
Регистрация: 06.04.2006
Сообщений: 257
Провел на форуме:
367179

Репутация: 76
По умолчанию

Экстремально маленький Hello Word! на Delphi.

Собирать исполняемый файл линкером от Microsoft. К сожалению, майкрософтовкий линкер понимает только COFF и Intel OMF, наотрез отказываясь работать с Borland OMF. Delphi же, начиная с третьей версии, перешла с формата Intel OMF на Borland OMF. Поэтому компилировать придется компилятором от Delphi 3.
Пример минимального HelloWord.
Код:
Unit HelloWord;
Interface
Procedure Start;
Implementation
Function MessageBoxA(hWnd:cardinal; IpText, IpCaption:Pchar; uType:Cardinal): Integer; stdcall; external ‘user32.dll’ name ‘_MessageBoxA@16’;
Procedure Start;
Begin
	MessageBoxA(0,’Hello word!’,nil,0);
End;
End.
Ти модуля Unit нужен для того, чтобы компилятор сгенерировал в объектном файле символьные имена объявленных процедур.
Компилируем:
Dcc32.exe –JP -$A-,B-,C-,D-,G-,H-,I-,J-,L-,M-,O+,P-,Q-,R-,T-,U-,V-,W+,X+,Y- HelloWord.pas
Открываем файл HelloWord.obj в HEX-редакторе и смотрим во что превратилась наша точка входа. Допустим Start$wwrv. Теперь выполняем сборку
Link.exe /ALIGN:32 /FORCE:UNRESOLVED /SUBSYSTEM:WINDOWS /ENTRY:Start$wwrv HelloWord.obj user32.lib /out:Hello.exe
В результате имеем файл размером 832 байта.
 
Ответить с цитированием

  #6  
Старый 25.11.2006, 22:08
Аватар для W!z@rD
W!z@rD
Reservists Of Antichat - Level 6
Регистрация: 12.02.2006
Сообщений: 891
Провел на форуме:
1892597

Репутация: 836


Отправить сообщение для W!z@rD с помощью ICQ
По умолчанию

в библиотеках (касается Delphi) если передаешь string данные от лучше заюзать переменную типа ShortString либо добавить ShareMem в раздел Uses... Имхо 1 лучше...
а если явно передаваемые данные string (например Edit1.Text), преобразуй ShortString(Edit1.Text);
таким образом можно пребразовывать не только ShortString, но допустим и из string в PChar=> pchar(edit1.text)
__________________
*********************************
*Я не волшебник ٩(๏̯͡๏)۶, только учусь...*
*********************************
Программы на заказ
Times to fly...
 
Ответить с цитированием

  #7  
Старый 08.12.2006, 13:43
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


Отправить сообщение для _Great_ с помощью ICQ
По умолчанию

Вот нашел несколько полезных советов для M$ Visual Studio.
Кому интересно - оригинал тут
Я приведу собственный перевод с моими комментариями.

1. Учим Visual Studio раскрывать структуры и классы в отладчике
Редактируем файл \Program Files\Microsoft Visual Studio\Common\MSDev98\Bin. Его формат можно определить методом тыка, посмотрев уже существующие строки. (Там все не так уж и сложно. В начале файла есть немаленький комментарий про синтаксис. Насколько я понял, просто вместо древовидной структуры объекта или структуры будет показываться одно значение. Например, для CString - m_pchData - Прим. Great).

2. Добавляем слова для подсветки.
Если создать файл usertype.dat в каталоге \Program Files\Microsoft Visual Studio\Common\MSDev98\Bin со словами по одному на строчку, то эти слова студия будет выделять синим цветом как ключевые. (Я уже добавил туда main и WinMain для удобства - Прим. Great).

3. Как использовать расширение *.cc для C++
Нужно произвести следующие изменения в реестре:
Цитата:
HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0 \Text Editor\Tabs/Language Settings\C/C++
FileExtensions=cpp;cxx;c;h;hxx;hpp;inl;tlh;tli;rc; rc2;cc;cp

HKEY_USERS\S-1-5-21-1219703950-274334628-1532313055-1335\Software\Microsoft\DevStudio\6.0\Build System\Components\Platforms\Win32 (x86)\Tools\32-bit C/C++ Compiler for 80x86
Input_Spec=*.c;*.cpp;*.cxx,*.cc,*.cp

HKEY_USERS\S-1-5-21-1219703950-274334628-1532313055-1335\Software\Microsoft\DevStudio\6.0\Build System\Components\Tools\<Component 0x3>
Input_Spec=*.c;*.cpp;*.cxx;*.cc;*.cp
и добавить флаг /Tp к флагам компилятора в настройках проекта

4. Как закрепить меню.
В Visual Studio панель меню перемещаемая (об этом говорят две полосы слева). Если это мешает, это можно убрать, выставив галочку "Use screen reader compatible menus" в диалоговом окне Tools -> Options -> закладка Worspace

5. Напоминания при компиляции
Если у тебя плохо с памятью и тебе сложно запомнить, например, что вот тот кусок кода в финальной версии желательно убрать или что в этом коде содержится некритичная проблема, можно сделать себе напоминание:
Код:
#define Stringize( L ) #L
#define MakeString( M, L ) M(L)
#define $Line MakeString( Stringize, __LINE__ )
#define Reminder __FILE__ "(" $Line ") : Reminder: "

использовать:
#pragma message(Reminder "Fix me!")
В результате компилятор сгенерирует при компиляции сообщение:
Цитата:
Compiling...
seh.cpp
H:\Progs\seh\seh.cpp(163) : Reminder: Fix me!
Linking...

seh.exe - 0 error(s), 0 warning(s)
6. Как сделать вручную точку останова
Просто поставить код
Код:
__asm int 3;
При нажатии на F5, когда выполнение дойдет до этого места, процессор сгенерирует исключение EXCEPTION_BREAKPOINT, отладчик его поймает и выведет сообщение - User breakpoint called from code at 0x...

7. Отладочные значения в различных областях памяти программы
0xCDCDCDCD Память выделена в куче, не инициализирована
0xDDDDDDDD Память, выделенная в куче, уже освобождена
0xFDFDFDFD Заполнитель NoMansLand записывается у границ участка памяти для контроля распространенного типа ошибки выхода за границы массива и переполнения. После затирании это указатели при освобождлении этой памяти free/delete выдаст диалоговое окно: DAMAGE: after ТИП block (#номер) at 0xадрес., где ТИП - обычно Normal - тип освобождаемого участка, номер - номер блока.
Пример неправильного кода:
Код:
	char* a = new char[2];
	strcpy(a, "aaaaaaaaaaaaaaaaaaaaa");
	delete a;
Во время выполнения delete отладочная сборка программы выдаст окно "DAMAGE: after Normal block (#55) at 0x00430030".
0xCCCCCCCC Выделено в стеке, не инициализировано

8. Предопределенные псевдопеременные во время отладки
В окно Watch можно добавить следующие "переменные":
@err - последняя ошибка (GetLastError)
@tib - адрес Thread Information Block потока
@clk - время выполнения программы (мс)

9. Просмотр указателей как массивов
Обычно, если добавить в окно Watch переменную типа char*, она покажется как строка, а не как массив. Чтобы посмотреть отдельные элементы массива (например, чтобы узнать ASCII-код символа из этой строки), надо добавить в Watch выражение переменная,длина, где переменная - переменная указательного типа, длина - за массив какой длины отладчик ее должен считать.
Например, пусть объявлен char* str;
Выражение str,10 нам покажет всю строку + отдельно как массив ее первые 10 символов (в массиве - индексы 0-9).
Выражение ((char*)&main),100 покажет нам первые 100 байт машинного кода функции main

10. Вызов программных функций во время отладки
Если у тебя есть функция, например,
Код:
int function()
{
  return 100*2;
}
а тебе хочется посмотреть, что бы она возвратила во время отладки, если бы стоял ее вызов в коде - нет проблем! Просто добавляем в окно Watch выражение function() и сразу там появится значение 200

11. Именование потоков при отладке
Следущий код содержит функцию, позвляющую назвать поток с указанным ID'ом своим именем
Код:
#define MS_VC_EXCEPTION 0x406d1388
typedef struct tagTHREADNAME_INFO
{
	DWORD dwType; // must be 0x1000
	LPCSTR szName; // pointer to name (in same addr space)
	DWORD dwThreadID; // thread ID (-1 caller thread)
	DWORD dwFlags; // reserved for future use, most be zero
} THREADNAME_INFO;
void SetThreadName(DWORD dwThreadID, LPCTSTR szThreadName)
{
	THREADNAME_INFO info;
	info.dwType = 0x1000;
	info.szName = szThreadName;
	info.dwThreadID = dwThreadID;
	info.dwFlags = 0;
	__try 
	{
		RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info);
	}
	__except (EXCEPTION_CONTINUE_EXECUTION)
	{
	}
}
Имя потока появится в окне Debug->Threads.

Последний раз редактировалось _Great_; 08.12.2006 в 14:11..
 
Ответить с цитированием

  #8  
Старый 05.01.2007, 12:55
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


Отправить сообщение для _Great_ с помощью ICQ
По умолчанию

Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог GetModuleHandle, но для др. адресного пространства).
Может, кому понадобится. Писал я по просьбе протеуса ну и соотв. там мои норкоманские идеи
Итак, че же для этого я сделал.
Ищем первый поток процесса. С помощью GetThreadSelectorEntry получаем запись LDT по селектору FS = 0x3b. Открываем Thread Environment Block, из него Process Environment Block, из него - структуру данных загрузчика (все это через ReadProcessMemory), потом проходимся по списку модулей.

Код:
	const DWORD FS = 0x3B;
	DWORD ownerProcessId = 38056;

	// Open process
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, ownerProcessId);
	if(!hProcess)
		return printf("Cannot open process\n");

	// Find thread
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
	THREADENTRY32 te = {sizeof(te)};
	Thread32First(hSnapshot, &te);
	DWORD threadId = 0;
	do
	{
		if(te.th32OwnerProcessID == ownerProcessId)
			threadId = te.th32ThreadID;
	}
	while(Thread32Next(hSnapshot, &te));
	if(!threadId)
		return printf("No threads for specified process id\n");

	// Get FS base
	HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, threadId);
	if(!hThread)
		return printf("Cannot open thread\n");

	LDT_ENTRY fs_entry;
	GetThreadSelectorEntry(hThread, FS, &fs_entry);

	DWORD fs_base = fs_entry.BaseLow | (fs_entry.HighWord.Bytes.BaseMid<<16) | (fs_entry.HighWord.Bytes.BaseHi<<24);

	// Read modules list
	TEB teb;
	DWORD read;
	if(!ReadProcessMemory(hProcess, (LPCVOID) fs_base, &teb, sizeof(teb), &read))
		return printf("Can't read process memory for TEB, %d bytes read\n", read);

	PEB peb;
	if(!ReadProcessMemory(hProcess, (LPCVOID) teb.Peb, &peb, sizeof(peb), &read))
		return printf("Can't read process memory for PEB, %d bytes read\n", read);

	PEB_LDR_DATA pld;
	if(!ReadProcessMemory(hProcess, (LPCVOID) peb.LoaderData, &pld, sizeof(pld), &read))
		return printf("Can't read process memory for PEB_LDR_DATA, %d bytes read\n", read);

	LDR_MODULE entry;
	if(!ReadProcessMemory(hProcess, (LPCVOID) pld.InInitializationOrderModuleList.Flink, &entry, sizeof(entry), &read))
		return printf("Can't read process memory for LDR_MODULE, %d bytes read\n", read);

	WORD wbuffer[20];
    char buffer[1024];

    // Walk the list
    do
    {
        // Print the name
		if(!ReadProcessMemory(hProcess, (LPCVOID) entry.BaseDllName.Buffer, &wbuffer, sizeof(wbuffer), &read))
			return printf("Can't read process memory for wbuffer in loop, %d bytes read\n", read);
		
        WideCharToMultiByte(CP_ACP, 0, wbuffer, -1, buffer, sizeof(buffer), 0, 0);
        printf("%s\t[0x%08x]\n", buffer, entry.BaseAddress);

        // Next
		if(!ReadProcessMemory(hProcess, (LPCVOID) entry.ModuleList.Flink, &entry, sizeof(entry), &read))
			return printf("Can't read process memory for LDR_MODULE in loop, %d bytes read\n", read);
    }
    while(entry.ModuleList.Flink != pld.InInitializationOrderModuleList.Flink);

	CloseHandle(hThread);
	CloseHandle(hProcess);
	CloseHandle(hSnapshot);
 
Ответить с цитированием

  #9  
Старый 16.02.2007, 16:15
Аватар для Cr4sh
Cr4sh
Познающий
Регистрация: 25.08.2005
Сообщений: 57
Провел на форуме:
216363

Репутация: 76
Отправить сообщение для Cr4sh с помощью ICQ
По умолчанию

Цитата:
Сообщение от _Great_  
Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог GetModuleHandle, но для др. адресного пространства).
уж0с... вот то же самое, но без изврата =/
Код:
DWORD RemoteGetModuleHandle(char *name, DWORD dwPid)
{
	MODULEENTRY32 m = {sizeof(m)};
         DWORD dwRet = 0;

	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);
	if (hSnap == INVALID_HANDLE_VALUE) 
		return NULL; 

	if (!Module32First(hSnap, &m)) 
		return NULL;
	do {
		if (!lstrcmpi(m.szModule, name)) 
                  { 
			dwRet = m.modBaseAddr;
                           break;
                  }
	} while (Module32Next(hSnap, &m));

	CloseHandle(hSnap);
	
	return dwRet;
}
 
Ответить с цитированием

  #10  
Старый 16.02.2007, 18:38
Аватар для _Great_
_Great_
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме:
5339610

Репутация: 4360


Отправить сообщение для _Great_ с помощью ICQ
По умолчанию

Cr4sh, нуууууу ))) так не прикольно ) Прикольно вручную анализировать ))
Хотя изобретать велосипед, конечно, тоже не стоит )
 
Ответить с цитированием
Ответ



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
10 tips & tricks for C/C++ windows programming with Visual C++ 6.0 Dracula4ever Forum for discussion of ANTICHAT 0 30.05.2006 17:11



Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
 


Быстрый переход




ANTICHAT.XYZ