السلام عليكم و رحمة الله و بركاته,
في سالف العصر و الزمان, كان حجم Turbo Pascal المترجم و الـ IDE في نفس الوقت لا يتعدى 40 كيلوبايت! و للعجب فـ uTorrent لا يتعدى حجمه 650 كيلوبايت, و مع هذا فإنه يوفر وظائف لا تعد و لا تحصى في مجاله. كيف استطاع هؤلاء كتابة برامج بهذا الحجم الصغير؟ لو تكلمنا عن Windows حصرياً, فإن الفكرة كلها تكمن في معرفة على ماذا يعتمد ملفنا التنفيذي --> و بالطبع التخلص من تلك الاعتمادية. بالطبع, قليل من اللعب في الـ Linker لتخصيص الملف التنفيذي حتى آخر قطرة! سنأخذ عهداً على أنفسنا أن لا نفتح Hex editor و لا أن ننادي أي مبرمج x86. سنعمل فقط بلغة C و سنستخدم مترجم Microsoft و الأدوات الملحقة به!
بعد ليلة سهر مليئة بالتجارب, وصلت إلى التالي: برنامج يعرض نافذة فارغة بالشكل التالي:
برنامجنا المكتوب بلغة C على الشكل التالي:
#include "smallexe.h"
LPCTSTR class_name = TEXT("smallexe");
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch(msg){
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI win_main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow){
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = NULL;
wc.lpszClassName = class_name;
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if(!RegisterClassEx(&wc)){
MessageBox(NULL, TEXT("Window Registration Failed!"), TEXT("Error!"),
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
class_name,
TEXT("Small EXE"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
NULL, NULL, hInstance, NULL);
if(hwnd == NULL){
MessageBox(NULL, TEXT("Window Creation Failed!"), TEXT("Error!"),
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while(GetMessage(&Msg, NULL, 0, 0) > 0){
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}للوصول إلى أصغر حجم, كان لابد من التخلي عن المكتبات القياسية و بالطبع التي تعتمد على الـ C Runtime Library. لذلك, لا يمكننا افتراض أي شي, و كان لابد من كتابة الدالة الأولى في البرنامج يدوياً و التي ستنادي win_main. كل ما في الأمر أنني قمت بكتابة entry function في smallexe.h و هذه الدالة هي أول دالة سوف تنادى في برنامجنا!
#define UNICODE
#include <windows.h>
int WINAPI win_main(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow);
void custom_main(){
HMODULE hInstance = GetModuleHandle(NULL);
HINSTANCE hPrevInstance = NULL;
LPTSTR lpCmdLine = GetCommandLine();
STARTUPINFO startup_info;
int nCmdShow;
int return_value;
GetStartupInfo(&startup_info);
nCmdShow = startup_info.wShowWindow;
return_value = win_main(hInstance, NULL, lpCmdLine, nCmdShow);
ExitProcess(return_value);
}ماذا بعد؟ سنحتاج إلى ترجمة و ربط البرنامج بحيث لا نعتمد على أي مكتبة خارجية(بالطبع سنستدعي دوال Windows من خلال الربط الديناميكي!). قمت بكتابة batch لتبسيط المهمة!
@ECHO off
set compiler_options= /O2 /Ox /Os
cl /nologo /c %compiler_options% %*
set merged_sections= /MERGE:.rdata=.data /MERGE:.text=.data
set disabled_warnings= /ignore:4254 /ignore:4108
set exe_obj= *.obj
set basic_libs= kernel32.lib gdi32.lib user32.lib
set additional_libs= advapi32.lib comdlg32.lib comctl32.lib shell32.lib
link /nologo /ENTRY:custom_main /NODEFAULTLIB /SUBSYSTEM:WINDOWS /ALIGN:4 ^
%merged_sections% %disabled_warnings% %exe_obj% %basic_libs% %additional_libs%
@ECHO onو النتيجة؟ ملف تنفيذي بحجم 1688 bytes, نعم كما ترى كيلو و نصف من البايتات الجميلة!
ستجد في المرفقات, المشروع كاملاً. و لتجربته لديك, لابد أن يتوفر Visual Studio, بحيث تقوم بفتح الـ Visual Studio Command Prompt:
Start > All Programs > Microsoft Visual Studio 2010 > Visual Studio Tools > Visual Studio Command Prompt
ثم قم بتمرير اسم الملف الذي تريد ترجمته للـ batch file, كما في مثالنا التالي:
c:\test>build window.c
سينتج الملف التنفيذي مباشرة!
لمن يحب المشاركة, اطرح طريقة لإنتاج ملفات تنفيذية صغيرة جداً بلغة البرمجة و البيئة التي تفضلها! بالتاكيد, سأكون أكثر من سعيد لو التفت الأسمبلاوية لهذا الموضوع و طرحوا ما لديهم, فلاقدرة لي على مجابهتهم :lol: و أرحب بأي استفسار حول أي نقطة في الطريقة.
تحياتي,
============



