الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

دليل المبتديء إلى برمجة ويندوز باستخدام سي

مغلق
بدأه BAZRAMIT في 28 ديسمبر 2002 · 33 رد · 17,154 مشاهدة · في قسم المواضيع الهامة في قسم السي /سي++
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

الدرس الأول: دخول سريع--صندوق الرسائل

إتبع الخطوات التالية من فضلك...

1- قم بتشغيل جهازك.

2- إبدأ برنامج فيجوال سي++

3- إختر الأمر : File--->New

4- تأكد من أنك تقف على الصفحة المعنونة : Projects

5- إختر Win32 Application من الواجهة اليسرى.

6- إملأ اسم المشروع في الحقل النصي المعنون Project Name

7- إنقر OK

8- تأكد من أنك تقف على الخيار An Empty Project

9- مرة أخرى : File--->New

10- تأكد هذه المرة أنك تقف على الصفحة Files

11- عبيء إسم الملف الجديد.

12- إلصق الكود التالي داخله:

#include 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, int nCmdShow)
{
    MessageBox(NULL, "Hello, Bazramit!", "Bazramit", MB_OK);
    return 0;
}

13- من القائمة اختر : Build--->Build [ProjectName].exe

14- إنتظر حتى ينهي البرنامج ما يقوم به.

15- ثم إختر : Build--->Execute [ProjectName].exe

16- استمتع بوقتك.. لقد أصبحت في لمح البصر : مبرمج ويندوز!

*****************

التعليق على الكود:

بالطبع لن أخوض في مسائل جانبية وأقوم بشرح ماهية ويندوز وما إلى ذلك...نحن لنا ما نرى!...

فلنبدأ:

بالطبع تذكر أيام كانت كل مداخل البرامج من خلال الدالة

main()

هل نسيت؟...جيد أنك نسيت..لأننا لن نستخدم هذه الدالة كمدخل تحت منصة ويندوز..ولكن الدالة التي يحب ألا تنساها الآن هي:

WinMain()

وكما تلاحظ من الكود، يقوم عالم ويندوز المرعب بتمرير مجموعة من الـParameters للدالة WinMain عند بدء البنامج..:

أولا..

HINSTANCE hInstance

وهي المعالج الرئيسي Handle للنسخة العاملة من البرنامج في الذاكرة..ستستخدم هذا الـParameter المفيد جدا في حالة تحميل موارد خاصة بالبرنامج و متضمنة

في ملف الـEXE.

ثانيا..

HINSTANCE hPrevInstance

غير مفيد بالنسبة للبرامج تحت ويندوز 95 فما فوق..

ثالثا..

LPSTR lpCmdLine

عندما تقوم بتشغيل برنامج من خلال أمر RUN في أي نسخة ويندوز..بإمكانك بالطبع تمرير خلاصة أو Argument على هيئة نص.

مثلا جرب معي هذا الأمر في نافذة RUN:

"G:Program FilesInternet ExplorerIEXPLORE.EXE" [url="http://www.bazramit.com%5b/php"]]http://www.bazramit.com

في هذه الحالة يمكنك القول بلا فخر أنك مررت Argument قيمتها

http://www.bazramit.com

للبرنامج ليفعل بها ما يحلو له!

هذه الـArgument تستقبلها انت داخل برنامجك من خلال المتغير الممرر لك من ويندوز: lpCmdLine

رابعا..

int nCmdShow

ليس من المهم أن تعرف أهمية هذا المتغير في المرحلة الحالية ولكنه قليلا جدا ما يستخدم.

ملحوظة أخرى على الـParameters أنها تتضمن أنواعا من النتغيرات لأول مرة تصادفها..صحيح؟..

HINSTANCE هو نوع المتغير الذي يحمل Handle للوحدة العاملة لأي برنامج محمل في الذاكرة.

LPSTR يعني Long Pointer to String أو مؤشر إلى نص وهو مساو تماما لـchar * (تذكر أتك تبعث بالـArgument على هيئة نصية).

مفهوم...ولكن يجب أن تعرف أن هذه الأنواع (وغيرها كثير) يستخدم تحت منصة win32 (ويندوز 95 فما فوق) فقط..

وكلا النوعين المشار إليهما يتكون من DWORD أي 32 بٍت. تماما كالـint(تحت ويندوز 95 فما فوق أو UNIX).

يلاحظ أيضا استخدام WINAPI قبل استدعاء WinMain وهي كما يقال مجرد عرف متبع ليس إلا وذلك لتحديد نوع الاستدعاء.

الآن لنلق نظرة وداع على الدالة التي تسببت في إظهار الرسالة:

MessageBox(NULL, "Hello, Bazramit!", "Bazramit", MB_OK);

هذه الدالة تقوم بإخبار ويندوز أن برنامجك يريد أن يظهر رسالة نصها "Hello, Bazramit" و العنوان الئيسي لها "Bazramit" ..

وبالتالي فإن النظام (في أغلب الأحيان) ينزل على رغبتك وينفذ ما طلبت.

إنها واحدة من آلاف الدوال الموجودة بويندوز وتسمى دوال API لتوفر للمبرمج أن يطلب ما يشاء من النظام..

لا تعتقد أنك ستضطر في يوم من الأيام لأن تستذكر آلاف الدوال لتصبح محترفا..على العكس، المهم أن تتعلم كيف تستخدم الـHelp لتخرج منه ما تريد...

خطوة أولى نحو الإحتراف...إذا كنت لا تملك MSDNإنزل هذا الملف المدعو WIN32.hlp من اللنك التالي:

http://212.14.34.87/~devon/down/helpy/winapi32.zip

وابحث عن الدالة MessageBox لتحصل على توقيعها وكيفية إستخدامها...طبعا قد يصعب عليك فهم كل شيء الآن..لكن لا تقلق..أنت قريب جدا!

في الدرس القادم سوف نمضي وقتا ممتعا في تعلم كيفية بناء نافذة Window ونتعرف على أهم مكوناتها.

دمتم بخير

#2

إلى الإمام يا أخي

#3

شكراً اخي على الدرس

أنا لدي كود لم يكتب فيه HINSTANCE بل يوجد فيه HANDLE وهو يقول في Win32 Console Application وليس في Win32 Application هل هذا خطأ ؟

واتمنى لك التوفيق (f)

If you think yourself that you are nobody, you should go somewhere to find yourself

#4

أخي اليك الكود

هذا بعد تعديل بسيط عنما سبق من رد

#include < windows.h >


long FAR PASCAL _export WndProc ( HWND , UINT , UINT , LONG ) ;

int PASCAL WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpszCmdParam , int nCmdShow )

{
	static char szAppName [ ] = "Hello" ;
	HWND	hwnd ;
	MSG		msg  ;
	WNDCLASS	 win ;

	if ( ! hPrevInstance ) 
	{
		win.style 				= CS_HREDRAW | CS_VREDRAW ;
		win.lpfnWndProc			= WndProc ;
		win.cbClsExtra			= 0 ;
		win.cbWndExtra			= 0 ;
		win.hInstance			= hInstance ;
		win.hIcon				= LoadIcon ( NULL , IDI_APPLICATION ) ;
		win.hCursor				= LoadCursor ( NULL , IDC_ARROW ) ;
		win.hbrBackground		= GetStockobject ( WHITE_BRUSH ) ;
		win.lpszMenuName		= NULL ;
		win.lpszClassName		= szAppName ;

		RegisterClass ( &win ) ;
	}

	hwnd = CerateWindow ( szAppName , 
						"اهلا وسهلا" ,
						WS_OVERLAPPEDWINDOW ,
						CW_USEDEFAULT ,
						CW_USEDEFAULT ,
						CW_USEDEFAULT ,
						CW_USEDEFAULT ,
						NULL ,
						NULL ,
						hInstance ,
						NULL ) ;

	ShowWindow ( HWND , nCmdShow ) ;
	UpdateWindow ( hwnd ) ; 

	while ( GetMessage ( &msg , NULL , 0 , 0 ) ) 
	{
		TranslateMessage ( &msg ) ;
		DispatchMessage ( &msg ) ;

	}

	return msg.wParam ;

}

long FAR PASCAL _export WndPort ( HWND hwnd , UINT message , UINT wParam , LONG lParam ) 
{
	HDC				hdc ;
	PAINTSTRUCT		ps ;
	RECT			rect ; 

	switch ( message ) 
	{

	case WM_PAINT :
		hdc = BeginPanit ( hwnd , &ps ) ;
		GetClientRect ( hwnd , &rect ) ;

		DrawText ( hdc , "مرحباً بكم في عالم ويندز" , -1 , &rect , DT_SINGLELEING | DT_CENTER | DT_VCENTER ) ; 

		EndPaint ( hwnd , &ps ) ;

		return 0 ;

	case WM_DESTROY :
			PostQuitMessage ( 0 ) ;
			return 0 ;
	}

	return DefWindowProc ( hwnd , message , wParam , lParam ) ; 

}

واتمنى أن تجدوا لي حلاً واكون شاكر لكم

If you think yourself that you are nobody, you should go somewhere to find yourself

#5

أنا آسف لأنني لم أستخدم سوى فيجوال سي فقط:

هذا الكود سيعمل من خلال فيجوال سي:

#include < windows.h >


LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain ( HINSTANCE hInstance , HINSTANCE hPrevInstance , LPSTR lpszCmdParam , int nCmdShow )

{
    static char szAppName [ ] = "Hello" ;
    HWND    hwnd ;
    MSG        msg  ;
    WNDCLASS     win ;

    if ( ! hPrevInstance ) 
    {
        win.style                 = CS_HREDRAW | CS_VREDRAW ;
        win.lpfnWndProc   = WndProc ;
        win.cbClsExtra            = 0 ;
        win.cbWndExtra            = 0 ;
        win.hInstance            = hInstance ;
        win.hIcon                = LoadIcon ( NULL , IDI_APPLICATION ) ;
        win.hCursor                = LoadCursor ( NULL , IDC_ARROW ) ;
        win.hbrBackground        = (HBRUSH) GetStockObject (WHITE_BRUSH) ;
        win.lpszMenuName        = NULL ;
        win.lpszClassName        = szAppName ;

        RegisterClass ( &win ) ;
    }

   hwnd = CreateWindow ( szAppName , 
                        "ÇåáÇ æÓåáÇ" ,
                        WS_OVERLAPPEDWINDOW ,
                        CW_USEDEFAULT ,
                        CW_USEDEFAULT ,
                        CW_USEDEFAULT ,
                        CW_USEDEFAULT ,
                        NULL ,
                        NULL ,
                        hInstance ,
                        NULL ) ;


    ShowWindow ( hwnd , nCmdShow ) ;
    UpdateWindow ( hwnd ) ; 

    while ( GetMessage ( &msg , NULL , 0 , 0 ) ) 
    {
        TranslateMessage ( &msg ) ;
        DispatchMessage ( &msg ) ;

    }

    return msg.wParam ;

}

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    HDC                hdc ;
    PAINTSTRUCT        ps ;
    RECT            rect ; 

    switch ( message ) 
    {

    case WM_PAINT :
        hdc = BeginPaint (hwnd, &ps) ;
        GetClientRect ( hwnd , &rect ) ;

        DrawText ( hdc , "ãÑÍÈÇð Èßã Ýí ÚÇáã æíäÏÒ" , -1 , &rect , DT_CENTER | DT_VCENTER ) ; 

        EndPaint ( hwnd , &ps ) ;

        return 0 ;

    case WM_DESTROY :
            PostQuitMessage ( 0 ) ;
            return 0 ;
    }

    return DefWindowProc ( hwnd , message , wParam , lParam ) ; 

}
#6

على فكرة.. Win32 Console Appliaction مخصصة للبرامج المكتوبة كي تعمل

تحت الــConsole Window الموجودة بويندوز والشبيهة بدوس..

لكي تكتب تطبيقا لويندوز تختار Win32 Application. حت تستطيع عرض النوافذ.

وكذلك Console تحتاج إلى دالة main وليس WinMain.

#7

شكراً أخي أنا ايضاً استخدم الفجوال سي لكن أنا لا اعرف كيف شغله من قبل

لدي مشكله هوه لا يوجد فيه خطأ عدما اريد تشغيله يرسل لي رسالة خطأ

اليك الرساله :

Compiling...
Win32 Application.cpp
Linking...
LIBCD.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Debug/Win32 Application.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

Win32 Application.exe - 2 error(s), 0 warning(s)

هذه الرساله لم اجد اليها حل

شكرك مره أخرى

If you think yourself that you are nobody, you should go somewhere to find yourself

#8

أخي الشيخ..أنت قد اخترت مشروعا من نوع Win32 Console Application

وهو يتطلب دالة main كمدخل للبرنامج وليس WinMain كما هي الحال في

الـويندوز..

إبدأ مشروعا جديدا Win32 Application ثم أضف له الملف الذي قمت بكتابته

وسيعمل إن شاء الله.

#9

قررت أن أنشر الدرس الثاني الآن لأن الدرس القادم قد يتأخر قليلا..وأرجو مخلصا أن تكون هذه الشروحات بسيطة وخالية من التهويل.

الدرس الثاني:أغلق الباب وافتح النافذة: نافذة عامة

1- إبدأ مشروعا جديدا Win32 Application.

2- أضف ملفا جديدا إلى مشروعك.

3- ألصق الكود التالي داخله:

include 

//WndProc signature
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//My Window Class name
const char m_ClassName[] = "BazramitClass";

//Main Program Entry
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Fill Window Class Structure Members
    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 = m_ClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
	//Register What you just filled
    RegisterClassEx(&wc);

    //Create the window from the defined class
    hwnd = CreateWindowEx(
        NULL,
        m_ClassName,
        "Bazramit Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL);

	//Display The Window on desktop
    ShowWindow(hwnd, nCmdShow);
	//Refresh the client area
    UpdateWindow(hwnd);

    //Enter Message Loop
    while(GetMessage(&Msg, NULL, 0, 0))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}


//The WndProc  Which Processes Messages 
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:
			//Call The Default Handler
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

4- Build المشروع ثم Executeٌــه.

تعليق على الكود:

قد يبدو الكود المرفق أعلاه غريبا بعض الشيء للوهلة الأولى..ولكنه في الحقيقة أسهل مما تتصور. إنها مراحل دائما ما تتكرر بنفس الطريقة في معظم

برامج ويندوز.

المرحلة الأولى: ملء بنية WNDCLASSEXWNDCLASSEX هو نوع من الـStructures أو البنى تستخدم لتخزين البيانات الخاصة بالنافذة وتسمى هذه البنية Window Class .

دعونا نخصص مساحة مستحقة للكود الخاص بملء هذه البنية:

في البداية نقوم بتعريف بنية جديدة من نوع WNDCALSSEX داخل الدالة WinMain.

WNDCLASSEX wc;

ثم نملأ هذه البنية كالتالي:

   //Fill Window Class Structure Members
    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 = m_ClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

أعضاء هذه البنية كما يتضح من الكود أعلاه هم:

cbSize ويحمل حجم هذه البنية، نستخدم الدالة sizeof للقيام بعملية حساب الحجم.

style ويحدد ملامح البنية التي ستستخدمها نافذتنا الرئيسية فيما بعد

lpfnWndProc ونملؤه بعنوان الدالة التي ستقوم بمتابعة عمل النافذة التي ستستخدم هذه البنية، في هذه الحالة قررنا أن نستخدم الدالة WndProc.

cbClsExtra ويحدد عدد البايتات التي الزيادة التي نود أن نضيفها إلى البنية.(غير مهم بالنسبة لأغراضنا الحالية)

cbWndExtra ويحدد عدد البايتات الزيادة التي نحتاجها في النافذة نفسها (لاحظ أن النافذة ليست هي بنية WNDCLASSEX،

النافذة هي من تستخدم البنية كما)

hInstance ويملأ بقيمة الرابط Instance الممرر من ويندوز إلى WinMain.

hIcon ويملأ برابط (Handle) الأيقونة التي ستستخدمها النافذة. نستخدم لملء هذه الخلية دالة LoadIcon.

hCursor نفس الشيء ولكن بالنسبة إلى المشيرة Cursor.

hbrBackground لون الخلفية التي ستظهر بها النافذة التي ستستخدم هذه البنية.

lpszMenuName إذا كان هناك قائمة نريد أن نصلها بانافذة، من الممكن إضافة اسمها هنا.

lpszClassName إسم الـبنية (أي نص نختاره، في حالتنا أنا اخترت النص "BazramitClass" وخزنته في المتغير m_ClassName)

hIconSm هنا نضع قيمة رابط الأيقونة الصغرى. هناك عدة أحجام من الأيقونات، ويعرضها النظام في مكانها المناسب، عندما نختار NULL قيمة

لهذه الخلية فإن النظام يقوم بالبحث عن الأيقونة بنفسه. (من الممكن أن يتضمن رابط الأيقونة الأساسية للنافذة عدة أحجام من الأيقونات)

بعد كل هذا الشرح..يظل السؤال عالقا..هل يجب أن أفهم كل هذا الهراء؟..والجواب، طبعا لا ( في الوقت الحالي)..مع تقدمك في القدرة على التطوير

بلغة سي، فإن كثيرا من الأمور الغامضة ستنجلي بإذن الله..

المهم الآن أن تعي أهمية هذه البنية بالنسبة للنافذة لأنها تحمل البيانات الأساسية التي تحتاجها الأخيرة. وستقوم دوما بملء البنية بنفس الطريقة

تقريبا..

المرحلة الثانية: إنشاء النافذة

بعد ذلك نقوم باستخدام الدالة CreateWindowEx لإنشاء نافذتنا ذات الطلة البهية:

    //Create the window from the defined class
    hwnd = CreateWindowEx(
        NULL,
        m_ClassName,
        "Bazramit Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL););

ولكي نأخذ قسطا أكبر من المعرفة، دعنا نعرض توقيع الدالة المأخوذ رأسا من WIN32.hlp:

HWND CreateWindowEx(

    DWORD dwExStyle,	// extended window style
    LPCTSTR lpClassName,	// pointer to registered class name
    LPCTSTR lpWindowName,	// pointer to window name
    DWORD dwStyle,	// window style
    int x,	// horizontal position of window
    int y,	// vertical position of window
    int nWidth,	// window width
    int nHeight,	// window height
    HWND hWndParent,	// handle to parent or owner window
    HMENU hMenu,	// handle to menu, or child-window identifier
    HINSTANCE hInstance,	// handle to application instance
    LPVOID lpParam 	// pointer to window-creation data
   );

وهذا شرح لكل Parameter على حدة:

dwExStyle نستخدمه كي نخصص ملامح إضافية للنافذة، نحن لا نريد مواصفات إضافية لذلك نمرر NULL هنا.

lpClassName أيضا نمرر اسم البنية التي نود أن نستخدمها m_ClassName لأن كل نافذة لابد أن ترتكز على بنية من نوع WNDCLASSEX.

lpWindowName نمرر هنا الإسم الذي سيظهر كعنوان للنافذة (الاسم المكتوب على الشريط الذي يحتوي على أزرار التصغير والتكبير والإغلاق).

dwStyle هنا يمكنك أن تختار مظهر النافذة (ستايل) من خلال تمرير عدد من الثوابت المعرفة خصيصا لهذا الغرض. في حالتنا استخدمنا أكثر

الستايلات شيوعا وهو WS_OVERLAPPEDWINDOW الذي يتيح عدة خصائص منها إظهار الأزرار تصغير وتكبير وإغلاق..إذا جربت

أن تمرر NULL إلى هذا الباراميتر، فإن النافذة لن تظهر الأزرار المذكورة.

X,Y المكان الذي ستظهر فيه النافذة.. يمكنك أن تدع للنظام أن يقرر المكان المناسب بأن تمرر القيمة الثابتة:CW_USEDEFAULT

nWidth, nHeight العرض والطول، أيضا استخدمنا CW_USEDEFAULT.

hWndParent إن كانت هناك نافذة أم لنافذتنا..نمرر قيمة الهاندل أو الرابط للنافذة الأم في هذا الباراميتر. في حالتنا لا يوجد لأنها النافذة الأم بحد

ذاتها ولذلك مررنا NULL

hMenu قيمة الرابط المخصص للقائمة (Menu)، نافذتنا لا يوجد بها menu ولذلك مررنا NULL.

hInstance قيمة رابط الوحدة في الحافظة الممرر إلينا من ويندوز في الدالة WinMain.

lpParam أي مؤشر لقيم أو وحدات بنيوية إضافية نريد زيادتها، في الغالب سيكون NULL.

نقطة أخيرة قبل الانتقال إلى المرحلة التالية، الدالة CreateWindowEX تعيد إلينا قيمة الرابط من نوع HWND

الذي نستخدمه كثيرا عندما نستدعي دوال API.

بعد أن نقوم بإنشاء النافذة، يجب أن نظهرها باستخدام الدالة ShowWindow

ثم نقوم بطلب "إنعاش" مناطق النافذة ورسمها بواسطة UpdateWindow

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

المرحلة الثالثة: دخول الحلقة التكرارية لتلقي الرسائلهنا يكمن قلب عمل النافذة:

   while(GetMessage(&Msg, NULL, 0, 0))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

الدالة GetMessage تقوم باستقبال كل الرسائل Messages المبعوثة إلى النافذة..الرسائل المبعوثة هي بالضرورة

مثل الضغط على لوحة المفاتيح أو تحريك الماوس أو تغيير حجم النافذة..إلخ.وحتى تتحول النافذة إلى تطبيق حقيقي

يجب أن تتلقف الأخيرة الرسائل و تقوم يمعالجتها..

ومع وجود برامج عديدة ممكن أن تعمل على نظام ويندوز في وقت واحد..فإن النظام يقوم بتجميع الرسائل فيما يسمى

طابور الرسائل أو Message Queue ويرسل بها حسب ترتيب دخولها الطابور إلى كل نافذة صدرت عنها.

وبالتالي فإن نافذتنا يجب أن تتلقف هذه الرسائل كمثيلاتها من النوافذ، وإلا أصبحت خبرا منسيا، كلما بعث لها المستخدم

برسالة عبر النظام لا ترد ولا تستجيب، شكل جميل بلا وظيفة أو يسمونه جمال الروح.

من هنا كان استخدام الدالة GetMessage مهما في داخل حلقة تكرارية لتقوم باستمرار بتفقد بريدها من النظام.

أما الدالة TranslateMessage فتقوم بتحويل كبسات لوحة المفاتيح إلى رسائل يمكن تقبلها ومعالجتها.

وتكمن أهمية DispatchMessage في أنها تقوم بتمرير الرسالة إلى الدالة WndProc لتقوم بمعلجتها.

المرحلة الرابعة: متخذ الإجراءات WndProc

حسبما بينا في شرح الخانات المختلفة لـWNDCLASSEX..كان أحد الخانات هو عنوان الدالة التي يلقى على عاتقها

إتخاذ الإجراءات ومعالجة الرسائل. لقد أصبح عرفا منذ زمن في أوساط مبرمجي فيجوال سي، أن يسموا هذه الدالة:WndPro،

لك حرية الاختيار أن تلتزم بالعرف أولا..ولكن يجب أن تراعي أن يكون توقيع الدالة على الشكل التالي:

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

الباراميتر الأول هو هاندل النافذة من نوع HWND، والثاني هو رقم الرسالة من نوع UINT. والثالث والرابع نوعي

WPARAM, LPARAM ويحتويان على بيانات خاصة مع كل رسالة.

في داخل الــWndProc وجدنا الكود التالي:

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:
			//Call The Default Handler
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

كما نلحظ، تقوم الدالة بعملية تنقيح للرسائل باستخدام switch-case ففي كل مرة تستقبل فيها النافذة رسالة جديدة..يتم تفعيل الدالة

وبالتالي تفعيل الجزء الخاص بمعالجة الرسالة..

كمبرمج سيكون أغلب عملك في هذه المنطقة..حيث إن اهتمامك الأساسي سينصب على التعامل مع عدد من الرسائل التي يطلقها النظام

حسب استخدام البرنامج. مثلا الرسالة WM_CLOSE تطلق عندما يقوم المستخدم بطلب الخروج من البرنامج.

في الكود أعلاه تقوم الدالة في حالة تلقيها للرسالة WM_CLOSE باستدعاء الدالة DestroyWindow، التي بدورها تتسبب في وضع

رسالة WM_DESTROY إفي طابور الرسائل بالنظام..ومن ثم يقوم النظام بترشيحها إلى الحلقة التكرارية التي تستقبل الرسائل في

WinMainداخل النافذة، وبدورها ترسل الدالة WinMain بالرسالة إلى WndProc الذي يستقبل الرسالة ويتعرف عليها فيستدعي الدالة

PostQuitMessage التي تتسبب في الخروج من البرنامج...

من الممكن جدا أن كثير من الأمور لم تتضح حتى الآن..لكن المهم قبل أن ننتقل إلى درس آخر أن تفهم أساسيات المراحل المختلفة

المشروحة في هذه الحصة...وعلى العموم فإن أهم المراحل وأكثرها استهلاكا للوقت مرحلة معالجة واستقبال الرسائل لأن الكود فيما يسبقها

لا يختلف كثيرا بين برنامج وآخر.

#10

ممتاز ....

اشكرك على هذه المشاركة ...

تقبل تحياتي ...

#11

شكراً لك أخي و الى الامام

If you think yourself that you are nobody, you should go somewhere to find yourself

#12

مشكوووووووورين على المجهود (gift)

تحياتي (f)

#13

رااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااااائع

أرجو المزيد ولكن ضمن برنامج كامل

#14

السلام عليكم

مشكور وإلى مزيد من التقدم ( بإذن الله )

" وما توفيقى إلا بالله عليه توكلت وإليه أنيب "
-------------------------------------------------
قل : " لا حول ولا قوة إلا بالله "
-------------------------------------------------

#15

الحق يقال منذ أن بدأت في هذا المنتدى لم أستفد كما استفدت من هذا الموضوع فأرجو المتابعة على نفس المنوال

#16

المزيد .......... بارك الله فيك

#17

أولا أشكر كل من اهتم بهذه الشروحات..

وأتمنى من كل قلبي أن تكون هذه الدروس مفيدة لجميع المهتمين

بالدخول إلى هذا العالم الرحب.

ولكني أتأسف لكم أيضا بسبب عدم وجود مواعيد ثابتة لتقديم الدروس

راجيا أن تقدروا ظروف العمل...وأتمنى لكم جميعا التوفيق.

الدرس الثالث: نحو معالج نصوص

أنشيء مشروعا جديدا وأضف ملفا جديدا تلصق به الكود التالي:

#include 

//WndProc signature
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

//My Window Class name
const char m_ClassName[] = "BazramitClass";
char m_Chars[100];
 int m_Col;
//Main Program Entry
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    //Fill Window Class Structure Members
    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 = m_ClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
	//Register What you just filled
    RegisterClassEx(&wc);

    //Create the window from the defined class
    hwnd = CreateWindowEx(
        NULL,
        m_ClassName,
        "Bazramit Window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL);

	//Display The Window on desktop
    ShowWindow(hwnd, nCmdShow);
	//Refresh the client area
    UpdateWindow(hwnd);

    //Enter Message Loop
    while(GetMessage(&Msg, NULL, 0, 0))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}


//The WndProc  Which Processes Messages 
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;

    switch(msg) //
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
	    break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;

	case WM_CHAR:
	    hdc=GetDC(hwnd);
	    m_Chars[m_Col]=(char)wParam;
	    m_Col++;
	    TextOut(hdc,0,0,m_Chars,m_Col);
	    ReleaseDC(hwnd, hdc); 
	    break;	

	default:
			//Call The Default Handler
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

نفذ البرنامج، اكتب بعض الجمل، ولاحظ النتيجة..

ــــــــــــــــــــــــــــــ

تعليق على الكود:

ما نود فعله في هذا البرنامج..إخراج الحروف التي يدخلها المستخدم..

كما ترى..لم يتغير الكود كثيرا هذه المرة..

أضفنا بعض المتغيرات في البداية قبل WinMain:

char m_Chars[100];
int m_Col;

m_Chars سنضيف فيه كل الأحرف التي يضغط عليها المستخدم لنخرجها له فيما بعد...لاحظ أن الحد الأقصى للحروف

في هذا البرنامج 100.

و m_Col نضع فيه عدد الحروف التي أدخلها حتى اللحظة..

كذلك أضفنا متغيرا من نوع HDC جديد نراه لأول مرة داخل حدود WndProc:

HDC hdc;

HDC هو نوع يستخدم بواسطة ويندوز لكي يحدد السطح الذي سيخرج إليه الحروف أو حتى الرسومات...

وهو اختصار لــHandle to a Device Context.

لكل نافذة أيا كان نوعها Device Context خاص بها..وعندما نخرج رسوما إلى هذه النافذة، نحتاج إلى Device Context لكي يعرف

النظام أي نافذة نريد أن نرسل بخرجنا إليها.

كذلك استخدمنا الدالة TextOut داخل WndProc وهي إحدى أشهر الدوال المستخدمة لإخراج النصوص.

الآن لنراجع ما تغير داخل WndProc:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;

    switch(msg) //
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
	    break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;

	case WM_CHAR:
	    hdc=GetDC(hwnd);
	    m_Chars[m_Col]=(char)wParam;
	    m_Col++;
	    TextOut(hdc,0,0,m_Chars,m_Col);
	    ReleaseDC(hwnd, hdc); 
	    break;	

	default:
			//Call The Default Handler
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

لأننا نريد أن نتعامل مع كبسات لوحة المفاتيح المتولدة عن المستخدم، فإننا قمنا بمعالجة الرسالة:WM_CHAR..

عندما يبعث النظام لنا بهذه الرسالة فإنه يخبرنا أن المستخدم قد ضغط على أحد المفاتيح ويمرر لنا قيمة الــASCII code

الخاص بالمفتاح في الباراميتر: wParam.

في هذه الحالة يقوم برنامجنا بتلقف الرسالة وعمل الآتي:

	    	case WM_CHAR:
	    hdc=GetDC(hwnd);
	    m_Chars[m_Col]=(char)wParam;
	    m_Col++;
	    TextOut(hdc,0,0,m_Chars,m_Col);
	    ReleaseDC(hwnd, hdc); 
	    break;

بما أننا سنستخدم الدالة TextOut في إخراج النصوص..لا بد أن نحصل على الباراميترات التي تحتاجها لكي يعمل

توقيع TextOut من داخل Win32.hlp هو:

BOOL TextOut(

    HDC hdc,	// handle of device context 
    int nXStart,	// x-coordinate of starting position  
    int nYStart,	// y-coordinate of starting position  
    LPCTSTR lpString,	// address of string 
    int cbString 	// number of characters in string 
   );

hdc هو قيمة الرابط إلى الـDevice Context للنافذة التي نود إخراج النص إليها، ويمكن الحصول عليه من خلال الدالة GetDC

nXStart المكان على المحور الأفقي للنافذة الذي يبدأ عنده النص.

nYStart المكان على المحور العمودي للنافذة الذي يبدأ عنده النص.

lpString قيمة pointer يشير إلى مكان النص في الذاكرة.

cbString عدد الأحرف التي نود إخراجها..

وكما قلنا في السابق..البرنامج يخزن الأحرف لحد 100 حرف في المتفير من نوع Array of chars المسمى m_Chars.

كما أننا سوف نزيد قيمة المتغير m_Col في كل مرة نستقبل فيها الرسالة وهذا المتغير يكون بمثابة العدّاد الذي يخزن

عدد الأحرف التي أدخلها المستخدم.

ولكي نحصل على قيمة الـDevice Context فإننا نستخدم GetDC التي نمرر إليها قيمة رابط النافذة hwnd.

hdc=GetDC(hwnd);

ولكن يجب أن نضع في اعتبارنا أنه من المهم أن نعفي الـ Device Context مباشرة بعد أن ننهي عملنا (بإخراج النص)

بواسطة الدالة:

ReleaseDC(hwnd, hdc);

أعتقد أن الأمور أصبحت الآن واضحة تماما...

TextOut(hdc,0,0,m_Chars,m_Col);

تخرج كل الحروف المخزنة على نافذتنا في المكان المحدد (0,0).

طيب...عظيم، ولكن جرب معي الأتي في البرنامج..

قم بكتابة أي نص على الشاشة..ثم قم بتصغيرها...

أرجعها إلى الواجهة الآن...ماذا ترى؟..

لقد انمحى النص الذي أدخلته في السابق...وعندما تبدأ في الكتابة مرة أخرى..يعود النص إلى ما كان عليه...

السبب فيما حدث، أننا قمن في البرنامج يمعالجة الرسالة WM_CHAR..فإخراج النصوص يتم عندما يضغط

المستخدم على لوحة المفاتيح ...

وعندما نقوم يتصغير النافذة ثم نعيدها مرة أخرى يبعث النظام برسالة WM_PAINT التي للأسف لم نضعها

في حسباننا في البداية...

الرسالة WM_PAINT دائما ما سنحتاجها عند إعادة إنتاج محتويات الشاشة..ويرسلها لنا النطام في حالة أن

النافذة تغير حجمها أو أصبحت في خلفية نافذة أخرى مثلا...

هذه هي النسخة المعدلة من WndProc التي تعالج هذه السقطة:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
    switch(msg) //
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
	    break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;

	case WM_CHAR:
	    hdc=GetDC(hwnd);
	    m_Chars[m_Col]=(char)wParam;
	    m_Col++;
	    TextOut(hdc,0,0,m_Chars,m_Col);
	    ReleaseDC(hwnd, hdc); 
		return 0 ;
	    break;	
	case WM_PAINT:
	    hdc = BeginPaint (hwnd, &ps) ;
 	    TextOut(hdc,0,0,m_Chars,m_Col);
	    EndPaint (hwnd, &ps) ;
	    return 0 ;
	    break;

	default:
			//Call The Default Handler
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

كما اتضح تلقفنا هذه المرة الرسالة WM_PAINT..

هناك طريقة للحصول على الــHDC لا تستخدم إلا في داخل هذه الرسالة:

case WM_PAINT:

	// 
		hdc = BeginPaint (hwnd, &ps) ;

		// أجر كل عمليات الرسم هنا

		//   
		EndPaint (hwnd, &ps) ;
	    return 0 ;
	    break;

والتفسير:

-إبدأ عملية رسم المحتويات بالحصول على قيمة HDC بواسطة BeginPaint (لا نحتاج إلى GetDC)

2-أجر كل عمليات الرسم المطلوبة

3-أنهي العملية بواسطة EndPaint (لا نحتاج إلى ReleaseDC).

الدالتان BeginPaint و EndPaint يجب أن نمرر لها متغيرا من نوع PAINTSTRUCT وهي بنية تملؤها لنا الدالة بالمعلومات المهمة

المتعلقة بخصائص منطقة الرسم Client Area داخل النافذة.

بهذا ينتهي الدرس الثالث...

أريد منك حتى الدرس المقبل أن تفكر في الأمور التالية:

1-كيف نستطيع معالجة كبسات المستخدم على BackSpace.؟

2-كيف لنا أن نعالج كبسات المستخدم على Enter لإضافة سطر جديد.؟

#18

thanks for a lesson but i connot read your lesson now

(f) thanks

If you think yourself that you are nobody, you should go somewhere to find yourself

#19

ما شاء الله... بارك الله فيك أخي BAZRAMIT على هذه الدروس القيّمة...

لكن لدي سؤال: هل تخطط لبناء دروسك على موضوع win API أم انك تريد ان تكمل بـ MFC ذلك لأنني بدأت سلسلة, كنت أود الاكمال. وأنا خططت للسلسلة ان تكون عن MFC فلكي لا يحدث تكرار ارجو إخباري عن أي موضوع ستكتب (Win API أم MFC )....

وتحياتي.

#20

أخي إسماعيل ابراهيم،

لا أخي أنا كما يقولون Anti-MFC ، ولم أستخدمها من قبل...

ولذلك لن يكون هناك تعارض...

أرجوك أن تكمل دروسك، فهي مفيدة لي أنا أيضا(yess)

تحياتي لك

#21

شكراً أخي على الدرس فعلاً جميل واتمنى منك المزيد قد قرات الدرس

بس للاسف في الدرس دى ولي رح من دروسك تواجهني مشاكل ( قصدي شي ما فهمته عدل )

واذا استطعت ان تشرح الره الجايه بشرح ممل جداً جداً

If you think yourself that you are nobody, you should go somewhere to find yourself

#22

* الشبح *

في الحقيقة هذه أول مرة أكتب فيها شروحات...

ومن المفيد جدا أن أسمع رأيك..وما الذي لم تفهمه..

من الصعب جدا أن يكتب الواحد درسا جيدا...ولهذا أتمنى أن تخبرني

عن الأشياء التي ترى أنها غير مكتملة..

#23

والله يا BAZRAMIT انك (gift) من الله

ايوة كذا الدروس والا بلاش

#24

اخي العزيز :

تسلم علي مجهودك وعلي هذا اليرنامج

بس لي طلب كيف اعمل للبرنامج امتداد EXE.

اي اشغل البرنامج بدون compilier

ولك جزيل الشكر

#25

أخي المبرمج الذهبى، أنا لم أفهم قصدك جيدا،

ولكن.. إذا أردت أن تحول البرنامج المكتوب بلغة سي إلى exe

لابد أن تحوله عن طريق compiler للغة سي...زد على ذلك أن برامج ويندوز

تتطلب مواصفات معينة للcompiler"، ولذلك أنا أستخدم

Visual C++ (Visual Studio) compiler.....

لذلك بلا شك تحتاج إلى compiler لكي تجمع exe file.

هذا الموضوع مغلق.

مواضيع مشابهة