[ Уменьшение размера программ написанных на C++ ]

В этой статье я хочу затронуть одну уже вдоль и поперек изъезженную тему – уменьшение размера
приложений написанных на C++. В частности я создаю эту тему для того, чтобы поделиться с вами
найденными мною функциями, заменяющими стандартные RTL-функции C++ при разработке на чистом
WinAPI. Также надеюсь, что вы пополните этот список, а так же по возможности оптимизируете
существующий. Ну и, конечно же, надеюсь, что статья окажется полезной новичкам.

И так допустим, вы написали или хотите написать, какую-либо программу на C++ при этом вы хотите,
чтобы она «весила» как можно меньше, и не потребляла много ресурсов. Если вы уже написали программу,
и у вас нет желания переписывать код на WinAPI можете просто добавить следующие строчки, которые
уменьшат размер программы, в начало кода:

#pragma optimize("gsy",on)
#pragma comment(linker,"/RELEASE")
#pragma comment(linker,"/MERGE:.rdata=.text /MERGE:.data=.text")
#pragma comment(linker, "/SECTION:.text,EWR")
#pragma comment(linker,"/FILEALIGN:512")

Либо эти же строчки можно вынести в отдельный заголовочный файл (.h), а затем подключить к своему
проекту через #include. Теперь немного расскажу, что означают эти строки.

#pragma optimize("gsy",on) – позволим компилятору самому оптимизировать код:
g - global optimizations (глобальная оптимизация), s - favor small code (оптимизировать в пользу
уменьшения кода, при этом может незначительно снизиться быстродействие), y - no frame pointers.

#pragma comment(linker,"/RELEASE") – компилируем релиз версию, чтобы не было никакой отладочной
информации, увеличивающей размер.

#pragma comment(linker,"/MERGE:.rdata=.text /MERGE:.data=.text") – объединяем секции, код
уменьшается за счет того, что теперь нет свободного пространства между секциями.

#pragma comment(linker, "/SECTION:.text,EWR") – выставляем необходимые права на секцию
.text: Execute (выполнение), Write (запись), Read (чтение).

#pragma comment(linker,"/FILEALIGN:512") – уменьшаем величину выравнивания секций до 512 байт,
по умолчанию 4 Кб. При размере 4 Кб приложение загружается немного быстрее.

Еще более оптимизировать приложение можно, отключив библиотеку времени исполнения
(Run-Time Library, RTL). При этом все стандартные функции, такие как strcmp, strlen, memcpy,
CopyMemory, malloc, new и другие, станут не доступны. Их можно заменить на схожие функции Win API,
а некоторые написать самому.
И так если вы решили писать на WinAPI, то добавьте к предыдущим строчкам еще эти:

#pragma comment(linker,"/NODEFAULTLIB")
#pragma comment(linker,"/ENTRY:main")
#pragma comment(linker,"/SUBSYSTEM:windows")

#pragma comment(linker,"/NODEFAULTLIB") – подключает к приложению только те библиотеки, которые
указаны явно.

#pragma comment(linker,"/ENTRY:main") - отключает RTL и переназначает точку входа.

#pragma comment(linker,"/SUBSYSTEM:windows") – нужно, чтобы линкер определил систему

Теперь функция main не передает Instance и командную строку, но их можно получить, используя
функции WinAPI - GetModuleHandle() и GetCommandLine(). Функции malloc, free можно заменить на
HeapAlloc, HeapFree. Функциям strlen, strcpy, strcmp соответствуют схожие по названию, но с
буквой "l" вначале – lstrlen, lstrcpy, lstrcmp.

Теперь то, для чего собственно это все и затевалось – замены стандартным RTL-функциям, а также
другие полезные функции:

static void WINAPI ZeroMemory(LPVOID lpMem, DWORD dwSize)
{
    _asm
    {
        mov edi,[lpMem]
        mov ecx,[dwSize]
        xor eax,eax
        rep stosb
    }
}


static void WINAPI MemCpy(LPVOID lpMem, LPVOID lpMem2, DWORD dwLen)
{
    _asm
    {
        mov        edi,[lpMem]
        mov        esi,[lpMem2]
        mov        ecx,[dwLen]
        rep        movsb
    }
}


static void WINAPI MemSet(void *szBuffer, DWORD dwLen, DWORD dwSym)
{
    _asm
    {
        pushad
        mov        edi,[szBuffer]
        mov        ecx,[dwLen]
        mov        eax,[dwSym]
        rep        stosb
        popad
    }

}


// Функция ищет подстроку Str2 в строке Str1

int InStr(char* Str1, char* Str2)
{
    int cStr1=0,cStr2=0,i;

    while (Str1[cStr1]) cStr1++; // считаем размер
    while (Str2[cStr2]) cStr2++; // строк, можно и через lstrlen

    if ((!cStr1)||(!cStr1)||(cStr1<cStr2)) return 0;

    for (i=0; i<(cStr1-cStr2+1); i++) // search Str2 in string Str1
        if(CompareString(LOCALE_USER_DEFAULT,0,Str1+i,cStr2,Str2,cStr2)==2) return i+1;

    return 0;
}


// Функция заменяет в строке Src подстроку Str1 на Str2

char* Replace(char* Src,char* Str1, char* Str2)
{
    int        cStr1=0,cStr2=0,i;
    char*    buffer;

    buffer = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,lstrlen(Src)*2);

    while (Str1[cStr1]) cStr1++; // считаем размер
    while (Str2[cStr2]) cStr2++; // строк, можно и через lstrlen

    i = InStr(&Src[0],&Str1[0]);
    while(i != 0)
    {
        lstrcpyn(&buffer[lstrlen(buffer)],&Src[0],i);
        lstrcat(&buffer[0],&Str2[0]);
        lstrcpy(&Src[0],&Src[i+cStr1-1]);
        i = InStr(&Src[0],&Str1[0]);
    }
    lstrcat(&buffer[0],&Src[0]);

    lstrcpy(&Src[0],&buffer[0]);
    HeapFree(GetProcessHeap(),HEAP_NO_SERIALIZE,buffer);
    return Src;
}


// Функция определяющая размер файла

long FileLen(LPCSTR lpszFileName)
{
    WIN32_FIND_DATA wfd;
    long            len;

    HANDLE hFindFile = FindFirstFile(lpszFileName,&wfd);
    if (hFindFile == INVALID_HANDLE_VALUE)
        len = 0;
    else
    {
        len = wfd.nFileSizeLow;
        FindClose(hFindFile);
    }
    return len;
}


(с) Geograph