Antichat снова доступен.
Форум Antichat (Античат) возвращается и снова открыт для пользователей.
Здесь обсуждаются безопасность, программирование, технологии и многое другое.
Сообщество снова собирается вместе.
Новый адрес: forum.antichat.xyz
 |
|
Кодерские tips and tricks |

16.11.2006, 15:48
|
|
Участник форума
Регистрация: 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;
|
|
|

16.11.2006, 16:04
|
|
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме: 5339610
Репутация:
4360
|
|
Оптимизация выходного файла в Microsoft Visual C++.
Параметры линкера для уменьшения размера:
- объединение секций
Код:
/MERGE:.data=.text /MERGE:.rdata=.text
- можно еще снизить выравнивание, например, задать 512 байт или 32 байта
или соответственно.
- смена точки входа
или для консольного и виндового приложения соответственно. Уменьшает размер, т.к. тогда линкер не пихает в экзешник код стартовой функции 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..
|
|
|

18.11.2006, 21:28
|
|
Участник форума
Регистрация: 06.04.2006
Сообщений: 257
Провел на форуме: 367179
Репутация:
76
|
|
Delphi
Чтобы во время выполнения циклов, не происходил эффект зависания, нужно в теле цикла вставить
Код:
Application.ProcessMessages;
Эффект зависания происходит из-за того, что Windows ждет пока накопятся задания в очереди, а не выполняет их сразу. Application.ProcessMessages заставляет винду выполнить все задачи, которые накопились в данный момент.
|
|
|

19.11.2006, 10:24
|
|
Reservists Of Antichat - Level 6
Регистрация: 12.02.2006
Сообщений: 891
Провел на форуме: 1892597
Репутация:
836
|
|
в VCL (delphi) есть "перехват" закрытия формы
OnCloseQuery (где canclose boolean типа который и дает "разрешение" на закрытие формы)...
__________________
*********************************
*Я не волшебник ٩(๏̯͡๏)۶, только учусь...*
*********************************
Программы на заказ
Times to fly...
Последний раз редактировалось W!z@rD; 16.10.2009 в 07:56..
|
|
|

22.11.2006, 14:14
|
|
Участник форума
Регистрация: 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 байта.
|
|
|

25.11.2006, 22:08
|
|
Reservists Of Antichat - Level 6
Регистрация: 12.02.2006
Сообщений: 891
Провел на форуме: 1892597
Репутация:
836
|
|
в библиотеках (касается Delphi) если передаешь string данные от лучше заюзать переменную типа ShortString либо добавить ShareMem в раздел Uses... Имхо 1 лучше...
а если явно передаваемые данные string (например Edit1.Text), преобразуй ShortString(Edit1.Text);
таким образом можно пребразовывать не только ShortString, но допустим и из string в PChar=> pchar(edit1.text)
__________________
*********************************
*Я не волшебник ٩(๏̯͡๏)۶, только учусь...*
*********************************
Программы на заказ
Times to fly...
|
|
|

08.12.2006, 13:43
|
|
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме: 5339610
Репутация:
4360
|
|
Вот нашел несколько полезных советов для 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. Как сделать вручную точку останова
Просто поставить код
При нажатии на 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..
|
|
|

05.01.2007, 12:55
|
|
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме: 5339610
Репутация:
4360
|
|
Не совсем в тему, но все же. Поиск базы длл по ее имени в адресном пространстве другого процесса (аналог 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);
|
|
|

16.02.2007, 16:15
|
|
Познающий
Регистрация: 25.08.2005
Сообщений: 57
Провел на форуме: 216363
Репутация:
76
|
|
Сообщение от _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;
}
|
|
|

16.02.2007, 18:38
|
|
Флудер
Регистрация: 27.12.2005
Сообщений: 2,372
Провел на форуме: 5339610
Репутация:
4360
|
|
Cr4sh, нуууууу ))) так не прикольно  ) Прикольно вручную анализировать ))
Хотя изобретать велосипед, конечно, тоже не стоит )
|
|
|
|
 |
|
|
Здесь присутствуют: 1 (пользователей: 0 , гостей: 1)
|
|
|
|