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

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

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

أخي المبرمج الذهبى.. عندما تقوم بتشغيل برنامجك من داخل فيجيوال ستوديو, فانك تكون قد أنشأت ملف exe "دون ان تدري" وستجده في مكتبه باسم Debug داخل مكتبة مشروعك..

أرجو ان أكون قد فهمت سؤالك صح.

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

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

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

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

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

أنا اقصد كما سبق من اقتبس هذا الشرح لا يعجبني ( اعتذر اخي العزيز )

لكن الحمد الله ( يقلون الاساتده اقرى السؤال مره مرثين تعرف الجواب ) فهمت ما تقصد بتمام لكن لم ثتبت تماماً في ذاكرتي :D الحفظ مشله عويصه :o:D

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

#28

أريد أن أتعرف على لغة السى++

من حيث الدوال و

class

(f)

#29

أخي * الشبح *

أشكرك على تعليقك على الشرح...

بالفعل..هذه الطريقة، أنا متأكد أنها لن تعجب الكثيرين...

لأنها تعتمد على ايضاحات نصية فقط..من الممكن بالطبع أن تكون أفضل

بالإستعانة برسومات..أو أشكال بيانية..

ولكن، صدقني..أنا أفضل مكان تعلمت فيه برمجة ويندوز..ليس الكتب وإنما

شروحات مبنية بنفس الطريقة تقريبا...مثلا لو كنت تعرف عن شروحات إيكسيليون Iczelion..

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

بالنسبة للحفظ...

أنصحك بألا تحفظ أي شيء!

أعتى المبرمجين في العالم يستخدمون الــHelp

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

الــWin32API help

أو الــMSDN WIN32 SDK

أما ما تعجز عن فهمه..فيما يتعلق بويندوز...لا تستعجله..

هي مراحل نمر بها...والتطور نحو الأفضل هو المنظور دائما....

أشكرك.

#30

mustafa

إذا كنت تعرف اللغة الانجليزية...اطلع على الصفحة التالية:

http://www.tcfb.com/freetechbooks/bookcpp.html

#31

الدرس الرابع:الحاسة البرمجية- نحو معالج نصوص (تكملة 1)

كنا قد تساءلنا في نهاية الدرس السابق عن كيفية معالجة الـBackspace والــEnter.

الحل دوما يكون في يد المبرمج..سأقوم هنا بتقديم حل سهل جدا..ولكن يجب أن تعرف أنه كلما مر الوقت وأنت تطور باستخدام فيجوال سي،

كلما زادت الخيارات أمامك..ولذلك لا تعتبر كل ما أقدمه الآن بأنه أفضل طريقة ممكنة...

الكود التالي يقوم بمعالجة ما ذكرناه..

#include 

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


const char m_ClassName[] = "BazramitClass";
char m_Chars[100][100];
int m_Col[100];
int m_Row;
//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;
	PAINTSTRUCT ps;
	int i;
	RECT rc;
    switch(msg) //
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
	    break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;

	case WM_CHAR:
		switch(wParam)
		{
			case(13):
				m_Row++;
				m_Col[m_Row]=0;
				return 0;
				break;
			case(8) :
				if(m_Col[m_Row]>0)
				{
					m_Col[m_Row]--;
					GetClientRect(hwnd,&rc);
					InvalidateRect(hwnd,&rc,true);
				}
				else if (m_Col[m_Row]==0 && m_Row>0)
				{
					m_Row--;
					GetClientRect(hwnd,&rc);
					InvalidateRect(hwnd,&rc,true);

				}
				return 0;
				break;
			default:
				hdc=GetDC(hwnd);
				m_Chars[m_Row][m_Col[m_Row]]=(char)wParam;
				m_Col[m_Row]++;
				TextOut(hdc,0,m_Row*20,m_Chars[m_Row],m_Col[m_Row]);
				ReleaseDC(hwnd, hdc); 
				return 0 ;
				break;

		}

	case WM_PAINT:

		hdc = BeginPaint (hwnd, &ps) ;
 	    for (i=0;i<=m_Row;i++)
		{
			TextOut(hdc,0,i*20,m_Chars,m_Col);
		}

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

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

جرب الآن كتابة بعض الحروف واستخدم Enter و Backspace.

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

هل حدث تغير كبير عن المرة السابقة؟...

أولا نحن فكرنا في طريقة أخرى لتخزين الحروف المررة لنا من قبل المستخدم...

char m_Chars[100][100];

استخدمنا منظومة ثنائية الفهرس Two Dimensional Array لتخزين الحروف..

السبب في ذلك أننا نريد تخزين كل سطر في منظومة واحدة...

وكلما ضغط المستخدم على "إدخال"..ليبدأ سطرا جديدا..نبدأ في تخزين الحروف في منظومة أخرى..

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

مثال:

المستخدم يدخل:

Hello I am new to Visual C

ثم يضغط على الزر Enter ثم يكمل:

Now, I am starting anew line

عنها يكون شكل المنظومة m_Chars كالتالي:

m_Chars[0]="Hello I am new to Visual C"
m_Chars[1]="Now, I am starting anew line"

وإذا كان بالإمكان الدخول إلى الفهرس الثاني في المنظومة الثنائية سنجد فيها:

m_Chars[0][0]='H'
m_Chars[0][1]='e'
m_Chars[0][2]='l'
m_Chars[0][3]='l'
m_Chars[0][4]='o'
m_Chars[0][5]=' ' 
....
.....
......

وبذلك يكون المفهرس (index) الأول عنوانا لرقم السطر، والمفهرس الثاني عنوانا لرقم العمود.

هذا كود افتراضي يبين كيفية عرض كل الأسطر المخزنة (أرجو ألا تكون قد نسيت فائدة الدالة TextOut):

for (i=0;i<100;i++)
{
TextOut(hdc,0,0,m_Chars,100);
}

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

int m_Row;

بالطبع سنقوم بزيادة m_Row كلما ضغط المستخدم على Enter.

وبما أن كل سطر سيخزن عدد مختلف من الأعمدة(عدد الأحرف لكل سطر) وأقصى حد من عدد الأسطر هو 100:

int m_Col[100];

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

m_Col[m_Row]++;

إذن دعنا الآن نراجع التصميم الذي أعددناه لمعالجة الرسالة WM_CHAR :

1-كلما ضغط المستخدم على Enter نقوم بزيادة عدد الأسطر m_Row.

2-كلما ضغط المستخدم على Backspace نقوم بإنـــقـــاص عدد الأعمدة (الحروف) في السطر الحالي وبذلك لا يتم عرض الحرف

السابق.

3-كلما ضغط المستخدم على أي حرف آخر نقوم بـــزيـــادة عدد الأعمدة (الحروف) في السطر الحالي ونخزن الحرف في m_Chars.

قبل أن ننتقل إلى تحليل الكود في WndProc دعونا نمضي بعض الوقت مع ضيف جديد.

لكل نافذة يوجد مساحة تخضع للرسم والمعالجة تسمى Client Area Rectangle. دعنا نسميها مربع العمليات...

مثلا مربع العمليات في Internet Explorer هو المساحة التي يتم عليها رسم محتويات صفحات الويب المختلفة.

ومثلا مربع العمليات في MS WORD هو المساحة التي يتم عليها كتابة النصوص..

إذن Client Area Rectangle هو المربع الذي نرسم عليه جميع المحتويلت للنافذة...أي أن هذا المربع يشمل النافذة كلها ما عدا

الإطار، وقضيب العنوان (Title Bar)، والمزلاجان ScrollBars الرأسي والأفقي....

لكي نحصل على مربع العمليات الخاص بأي نافذة نستخدم الدالة: GetClientRect.

ولم لا نلق نظرة على التوقيع الخاص بهذه الدالة لنعرف كيفية استخدامها:

BOOL GetClientRect(

    HWND hWnd,	// handle of window
    LPRECT lpRect 	// address of structure for client coordinates
   );

واضح!...نمرر للدالة قيمة المعالج الخاص بالنافذة..HWND

وكذلك عنوان لمتغير من نوع:LPRECT .

LPRECT في حد ذاته هو مؤشر لبنية Structure من نوع RECT...وهذا هو تعريفها:

typedef struct _RECT {    // rc  
    LONG left; 
    LONG top; 
    LONG right; 
    LONG bottom; 
} RECT;

كالترجمة الحرفية للكلمة RECT..مربــــع..له الخصائص التالية:

left المكان الذي سيبدأ عنده المربع على المحور الأفقي النافذة...صفر 0 تعني أن المربع سيبدأ من حدود الـClient Area اليسرى.

top المكان الذي سيبدأ عنده المربع على المحور الرأسي...صفر 0 تعني أن المربع سيبدأ من حدود الـClient Area العليا.

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

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

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

ولكن إذا أردنا ملأ هذه البنية بالقيم الخاصة بكل الـClientArea، نستخدم الدالة GetClientRect لتملأه لنا.

وفي الوقت الذي نريد أن نعيد رسم محتويات المربع بامكاننا استخدام الدالة: InvalidateRect.

BOOL InvalidateRect(

    HWND hWnd,	// handle of window with changed update region  
    CONST RECT *lpRect,	// address of rectangle coordinates 
    BOOL bErase	// erase-background flag 
   );

نمرر للدالة قيمة عنوان البنية RECT.

الدالة InvalidateRect تقوم في الواقع بإعادة رسم محتويات أي مربع نمرره لها...

وعندما نمرر المربع الذي تم ملؤه بواسطة GetClientRect، فإن InvalidateRect تتسبب في إعادة رسم جميع

محتويات النافذة...وبالتالي كأننا نقوم بإرسال رسالة WM_PAINT للنافذة.

شكرا!

المرحلة الحاية أيضا تقتضي أن ننعش الذاكرة فيما يتعلق بالدالة TextOut إحدى الدوال المسؤلة عن إخراج النصوص، وهذا تعريفها

مرة أخرى:

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 
   );

نحتاج لتشغيل هذه الدالة...أن نحدد المعالج Handle الخاص بسطح النافذة...أو ما يسمى HDC...ونمرره في الباراميتر الأول.

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

وفي الباراميتر الثالث..نمرر المكان على المحور الأفقي..

في الرابع..نمرر عنوان النص String الذي نود كتابته...لاحظ أننا من الممكن أن نمرر منظومة من الحروف Array of Chars في هذا

الباراميتر..

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

الآن نظرة شاخصة إلى الكود الخاص بمعالجة ضغطات المستخدم على لوحة المفاتيح:

	case WM_CHAR:
		switch(wParam)//
		{
			//Enter في حالة الضغط على
			case(13):
				m_Row++;			//زيادة عدد الأسطر
				m_Col[m_Row]=0;		//أعد قيمة الأحرف المخزنة في السطر 

الجديد إلى صفر
				return 0;		
				break;			
			// Backspace في حالة الضغط على 
			case(8) :
				if(m_Col[m_Row]>0)	//لا بد ان يكون عدد الأحرف في السطر أكبر من صفر 
				{
					m_Col[m_Row]--;	//في هذه الحالة أنقص عدد الأحرف 

وبالتالي يلغى الحرف السابق
					GetClientRect(hwnd,&rc);		//إحصل عبى 

قيمة مربع الرسم الذي نظهر عليه الحروف في النافذة
					InvalidateRect(hwnd,&rc,true);	//قم بإعادة رسم المنطقة أو 

المربع الذي تظهر عليه الحروف في النافذة
				}
				else if (m_Col[m_Row]==0 && m_Row>0)//أما إذا كان عدد الحروف 

في السطر مساو للصفر


		//وفي نفس الوقت يكون عدد الأسطر أكبر من الصفر	
				{
					m_Row--;						

//قم بإلغاء السطر الحالي وعدسطرا إلى الوراء
					GetClientRect(hwnd,&rc);		//أيضا 

أعطني قيمة مربع العمليات
					InvalidateRect(hwnd,&rc,true);	//أرغم البرنامج على إعادة 

رسم المربع وإخراج الحروف عند السطر الجديد

				}
				return 0;
				break;
			// في حالة الضغط على أي مفتاح آخر في لوحة المفاتيح
			default:
				hdc=GetDC(hwnd);						

	//لو سمحت، أعطني قيمة معالج الأسطح الخاص بالنافذة
				m_Chars[m_Row][m_Col[m_Row]]=(char)wParam;	// 

m_Charsخزن الحرف الجديدفي المتغير


				//m_Row في السطر رقم


				//m_Col[m_Row]والعمود (الحرف) رقم 
				m_Col[m_Row]++;						

		// قم بزيادة رقم المفهرس الذي يحتفظ بعدد الحروف في السطر الحالي
				TextOut(hdc,0,m_Row*20,m_Chars[m_Row],m_Col[m_Row]);	

//أخرج كل الحروف في المكان المحدد
				ReleaseDC(hwnd, hdc);	//انتهينا
				return 0 ;
				break;

		}

هذه ترجمة مفصلة لطريقة عمل الكود:

عندما يضغط المستخدم علىEnter يقوم البرنامج بالتالي:

-زيادة عدد الأسطر

-تنصيب عدد الأعمدة في هذا السطر الجديد على أنها صفر..حيث تزداد هذه القيمة كلما ضغط المستخدم على حرف جديد

كما تقل كلما ضغط المستخدم على Backspace.

			//Enter في حالة الضغط على
			case(13):
				m_Row++;			//زيادة عدد الأسطر
				m_Col[m_Row]=0;		//أعد قيمة الأحرف المخزنة في السطر 

الجديد إلى صفر
				return 0;		
				break;

وعندما يضغط المستخدم على Backspace:

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

نقوم برسم محتويات النافذة مرة أخرى (بواسطة InvalidateRect)..لا يظهر الحرف الأخير..

-إذا كان السطر الحالي أكبر من صفر، في الوقت الذي يكون عدد الأحرف في السطر صفر....

يقوم البرنامج بإلغاء السطر الحالي بالكامل...

بمعنى: إذا ضغط المستخدم على Enter نقوم بإضافة سطر جديد...ولكن إذا قام المستخدم بعد ذلك مباشرة

بالضغط على Backspace، نقوم بإلغاء السطر...

			// Backspace في حالة الضغط على 
			case(8) :
				if(m_Col[m_Row]>0)	//لا بد ان يكون عدد الأحرف في السطر أكبر من صفر 
				{
					m_Col[m_Row]--;	//في هذه الحالة أنقص عدد الأحرف 

وبالتالي يلغى الحرف السابق
					GetClientRect(hwnd,&rc);		//إحصل عبى 

قيمة مربع الرسم الذي نظهر عليه الحروف في النافذة
					InvalidateRect(hwnd,&rc,true);	//قم بإعادة رسم المنطقة أو 

المربع الذي تظهر عليه الحروف في النافذة
				}
				else if (m_Col[m_Row]==0 && m_Row>0)//أما إذا كان عدد الحروف 

في السطر مساو للصفر


		//وفي نفس الوقت يكون عدد الأسطر أكبر من الصفر	
				{
					m_Row--;						

//قم بإلغاء السطر الحالي وعدسطرا إلى الوراء
					GetClientRect(hwnd,&rc);		//أيضا 

أعطني قيمة مربع العمليات
					InvalidateRect(hwnd,&rc,true);	//أرغم البرنامج على إعادة 

رسم المربع وإخراج الحروف عند السطر الجديد

				}
				return 0;
				break;

أما عندما يضغط المستخدم على أي حرف آخر:

-نخزن الحرف...

-نقوم بزيادة عدد الأحرف للسطر الحالي..

-نرسم جميع الحروف في السطر الحالي بما فيها الحرف الجديد...

			// في حالة الضغط على أي مفتاح آخر في لوحة المفاتيح
			default:
				hdc=GetDC(hwnd);						

	//لو سمحت، أعطني قيمة معالج الأسطح الخاص بالنافذة
				m_Chars[m_Row][m_Col[m_Row]]=(char)wParam;	// 

m_Charsخزن الحرف الجديدفي المتغير


				//m_Row في السطر رقم


				//m_Col[m_Row]والعمود (الحرف) رقم 
				m_Col[m_Row]++;						

		// قم بزيادة رقم المفهرس الذي يحتفظ بعدد الحروف في السطر الحالي
				TextOut(hdc,0,m_Row*20,m_Chars[m_Row],m_Col[m_Row]);	

//أخرج كل الحروف في المكان المحدد
				ReleaseDC(hwnd, hdc);	//انتهينا
				return 0 ;
				break;

ولكن هناك ملاحظة على مكان إخراج الحروف في الدالة: TextOut::

TextOut(hdc,0,m_Row*20,m_Chars[m_Row],m_Col[m_Row]);

المكان اللأفقي لإخراج أي سطر يكون 0.

ولكن المكان العمودي يختلف حسب رقم السطر...m_Row*20

أي أننا سنترك 20 وحدة قياسية بين كل سطر وآخر..

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

في المكان الصحيح....

تصور مثلا أن طول الحرف عموديا هو X وأننا استطعنا الحصول عليه...

في هذه الحالة تكون الطريقة المناسبة لإظهار السطور:

TextOut(hdc,0, (m_Row * X) + 2 ,m_Chars[m_Row],m_Col[m_Row]);

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

ملحوظة أخرى...

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

بإمكاننا إعادة رسم المنطقة المتضررة فقط..وذلك بتعيين مربع يشمل الحرف الملغي فقط....

على العموم..هو ليس بالأمر اليسير...ولكن لا شيء لا يمكن عمله باستخدام Visual C.

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

التي تصل للبرنامج في حالة تغيير وضع النافذة...أو حجمها..أو عندما نستدعي الدالة InvalidateRect...أو أي حالة أخرى

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

	case WM_PAINT:

		hdc = BeginPaint (hwnd, &ps) ;
 	    for (i=0;i<=m_Row;i++)
		{
			TextOut(hdc,0,i*20,m_Chars,m_Col);
		}

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

نقوم بإعادة إخراج كل السطرو المخزنة في m_Chars

أعرف أن هذا الدرس قد يثير العديد من التساؤلات حول التكنيك المتبع...

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

إذا كانت لك أي ملاحظات حول الكود...من فضلك..لا تتردد في السؤال....

#32

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

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

#33

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

#34

أخي BAZRAMIT بارك الله فيك .. هل تستطيع جمعها في درس أو عدة دروس في ملف HTML .. حتى نضعها في قسم الدروس .. وأكون لك من الشاكرين ..

بالنسبة للكود اكتب الكود حول العنصر التالي :

<CODE language=cpp>

   put the code here

</CODE>

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

Coding on the Cloud and for the Cloud!

My Blog

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

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

عدد الزوار حالياً

المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية

—الإجمالي—أعضاء مسجّلون—زوار بدون تسجيل

جارٍ التحقق من المتواجدين…