نمایش نتایج 1 تا 6 از 6

نام تاپیک: مقاله : آموزش Win32 API ( برنامه نویسی ویندوز با C )

  1. #1
    کاربر دائمی آواتار www2006
    تاریخ عضویت
    مرداد 1385
    محل زندگی
    Mash <--> Teh
    پست
    187

    مقاله : آموزش Win32 API ( برنامه نویسی ویندوز با C )

    بسمه تعالی

    حرف اول ؛
    اساسی ترین تفاوت دو سیستم عامل Dos و Windows چیست ؟
    جوابش خیلی سخت نیست ، چون اساسا ً همش تفاوته و شباهت بین این دو کمتر پیدا میشه

    حالا که اینطوره ؛
    چرا برنامه هایی که ما با زبان C ( تحت ویندوز ) مینویسیم ، شبیه برنامه های تحت داس میشه ؟ یا اصلا ً یه چیز دیگه : چطوری از زبان C برای نوشتن برنامه های ویندوزی استفاده میکنند ؟ اصلا ً میشه با زبان C پنجره ، دکمه و سایر اجزای یک برنامه تحت ویندوز را ساخت ؟
    جواب این سؤال ها مثبت است ؛ اگر تمام امکانات این زبان ( و البته امکاناتی که خود ویندوز در اختیار ما قرار میدهد ) شناخته بشود همه کار میشه باهاش کرد .. هم می توان برنامه تحت ویندوز نوشت ( مثل خروجی هایی که Delphi یا VS تولید میکنند ) و هم خود ویندوز را ( که البته این دومی خیلی سخته و در اینجا کاری بهش نداریم ! )
    در واقع چیزی که باید بدونیم نحوه ی کار پنجره ها و کنترلهای ویندوز است و اینکه سیستم عامل ویندوز چه امکاناتی در اختیار ما برای این کار قرار داده است .

    کاری که در این سلسله نوشته ها می خوایم انجام بدیم هم در واقع همینه .. امکانات ویندوز برای این مدل برنامه ها رو بیشتر بشناسیم ، با توابع API ی اون بیشتر آشنا بشیم و بتونیم با زبان C یک برنامه با خروجی ای شبیه زبانهای ویژوال بنویسیم .

    انگیزه و هدف
    انگیزه من ( از یادگیری و یعد هم نوشتن این مقاله ) تسلط بیشتر روی جزئیات کار زبانهای ویژوال بوده است .. اینکه وقتی مثلا ً در زبان دلفی روی یک فرم ، یک Button قرار میدهیم چه اتفاقی میفته ؟ و یا دستور SendMessage که برای ارسال پیغام استفاده میشه ، دقیقا ً چگونه عمل میکنه ؟ پیغام کی رو به کی میرسونه !؟
    حقیقتش اگه بخوایم کاری رو که با دلفی و یا C#‎ و یا VB میشه توی نیم ساعت انجام داد با C انجام بدیم ، باید حداقل دوبرابر وقت بگذاریم .. ولی طبییعیه که اگر بیشتر روی جزئیات مسلط باشیم ( که هدف همه برنامه نویسهاست ) ، بیشتر میتونیم مانور بدیم و در واقع هنرنمایی کنیم .

    پیش نیاز
    هیچ نیازی نیست که برنامه نویس حرفه ای C باشید . اگه اصل کار یعنی مفاهیم کلی و سینتکس زبان رو بلد باشید که ( احتمالا ً بلد ) هستید میتونید خوندن این مطلب رو ادامه بدید . اگر هم بلد نیستید با یکی از کتابهایی که در این زمینه ترجمه شده میتونید شروع کنید .
    مفاهیم پیشرفته تر رو بتدریج خواهید آموخت .

    قبل از شروع
    شاید در ابتدا کدها و مطالب گنگ و پیچیده به نظر بیاد .. نترسید و ناامید هم نشید .. ادامه بدید .. تدریجا ً مطلب دستتون میاد .
    در ضمن MSDN رو هم در مواقع لزوم فراموش نکنید ..

    تجربه شخصی
    از کامپایلر Turbo C++‎ version 4.5 برای تست کدهای این مقاله استفاده نکنید که جواب نمیگیرید ..


    نکته مهم
    سورس کدی را که در انتهای هر پست آپلود میشود را حتما ً بگیرید و بخونید و به نمونه کدهای ( بعضا ً ناقص ) داخل متن این نوشته اکتفا نکنید ..

    و دیگر اینکه
    اگر در طول مطالعه سؤالی پیش آمد کرد (!) ، یه کم صبر کنید چون به احتمال 90 درصد سؤال شما در ادامه پاسخ داده خواهد شد ( منظور از ادامه ، ادامه کل مقالات است ) .. البته همانطور که گفتم MSDN رو هم فراموش نکنید .



    لطفا ً برای حفظ پیوستگی مطالب ، سؤالهای پیش آمده ی احتمالی را در تاپیک های مجزای دیگری که ایجاد میکنید ، ارسال کنید .. در این تاپیک به سؤالات جواب داده نمیشود .. از همکاریتون پیشاپیش ممنونم ..



  2. #2
    کاربر دائمی آواتار www2006
    تاریخ عضویت
    مرداد 1385
    محل زندگی
    Mash <--> Teh
    پست
    187

    ساده ترین برنامه Win32

    خوب بریم سر اصل مطلب :

    اگر در این زمینه کاملا ً مبتدی هستید ، برای اینکه تست کنید که اصلا ً قادر به کامپایل برنامه ی ویندوزی هستید یا نه ، از کد زیر استفاده کنید :

    #include <windows.h>

    int WINAPI
    WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR pszCmdLine, int iCmdShow)
    {
    MessageBox(NULL,"Hello World!", "First Win App", MB_OK | MB_ICONEXCLAMATION);
    return 0;
    }
    اگر با پیغام خطا مواجه شدید و برنامه بدرستی کامپایل نشد ، سعی کنید خطای بوجود آمده را اصلاح کنید !
    ( البته مسلما ً این خطا از کد بالا نیست و به احتمال زیاد از کامپایلر شماست همانطور که قبلا ً هم گفتم بعضی از کامپایلر ها مثل Turbo C++‎ 4.5 در اجرای این کدها ناتوان هستند )

    اگر با Warning ای مواجه شدید مبنی براینکه پارامترهای WinMain بدرستی ست نشده اند ، فعلا ً اهمیت ندهید ( البته معمولا ً به Warning ها اهمیت داده نمیشود !)

    اگر برنامه شما بدون مشکل کامپایل شد و پیغام Hello World! را دریافت کردید ، خواندن را ادامه دهید ..

    توضیح کد :

    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance , LPSTR lpCmdLine, int nCmdShow)
    تابع ( )WinMain در ویندوز معادل تابع ( )Main است در Dos و Unix . در واقع اینجا جایی است که برنامه شما شروع به اجرا میکند . پارامترهای آن به شرح زیر هستند :

    HINSTANCE hInstance : هندلی است به ماژول اجرایی برنامه ( همون فایل .exe که درحافظه در حال اجراست )

    HINSTANCE hPrevInstance : برای برنامه های Win32 همیشه Null است .

    LPSTR lpCmdLine : آرگومانهای خط فرمان که بعنوان یک رشته ی منفرد هستند . اینها نام برنامه را شامل نمی شوند .

    Int nCmdShow : یک مقدار Integer که ممکنه به ShowWindow() پاس داده بشه .. بعدا ً بهش میرسیم .

    hInstance برای بارگزاری ریسورس ها ( loading resources ) استفاده میشود و البته هر آنچه که اساس هر ماژول است . یک ماژول میتواند یا یک فایلexe باشد ویا یک فایل dll که در برنامه ی شما لود میشود. برای اکثر برنامه های این نوشته ، فقط یک فایل exe وجود دارد .

    hPrevInstance بعنوان هندلی برای نمونه ی قبلی اجرا شده برنامه شما در Win16 استفاده میشود .. در Win32 از آن استفاده نمیشود.

    فراخوانی مجمع
    WINAPI تعیین کننده فراخوانی همکار است و تحت عنوان _stdcall تعریف میشود .. فعلا ً به معنی و مفهومش کاری نداشته باشید . همین قدر به خاطر بسپارید که در اینجا از آن استفاده شده است .


    انواع داده Win32
    شما خیلی از کلمات کلیدی را برای انواع داده در ویندوز در اختیار خواهید داشت مثلاً : UINT برای Unsigned int ویا LPSTR برای *char و .. . انتخاب اینکه از کدامیک استفاده شود بستگی به خود شما دارد . اگر از استفاده از *char بجای LPSTR راحت هستید ، از همان استفاده کنید .

    فقط یک نکته ی کوچک را فراموش نکنید : پیشوند LP مخفف Long Pointer است . در win32 بخش Long زیاد بکار نمیره و منسوخ شده است .
    نکته ی دیگه اینکه حرف C که در ادامه ی LP میاد ، اول کلمه Constant به معنای ثابت هستش ؛ یعنی LPCSTR نشون دهنده ی یک اشاره گر به یک رشته ی ثابت است و LPSTR یک اشاره گر ثابت نیست و قابل تغییر است .

    پ.ن. فایل ضمیمه شامل :

    1- فرمت Pdf دوبخش اول 2 -سورس برنامه 3- فایل exe

    مربوط به همین بخش میباشد .
    فایل های ضمیمه فایل های ضمیمه

  3. #3
    کاربر دائمی آواتار www2006
    تاریخ عضویت
    مرداد 1385
    محل زندگی
    Mash <--> Teh
    پست
    187

    بخش دوم : ایجاد یک پنجره ساده

    شاید تا حالا به این فکر کرده باشید که " ما چطور میتونیم یک پنجره بسازیم ؟ "
    ساخت یک پنجره و نمایش اون کار زیاد سختی نیست ..

    اول نگاهی داشته باشیم به کدی که برای ما یک پنجره میسازد و بعدش ببینیم که چطور این کد عمل می کنه:

    #include <windows.h>

    const char g_szClassName[] = "myWindowClass";

    // Step 4: the Window Procedure
    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 WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR 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 = g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
    MessageBox(NULL, "Window Registration Failed!", "Error!",
    MB_ICONEXCLAMATION | MB_OK);
    return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
    WS_EX_CLIENTEDGE,
    g_szClassName,
    "The title of my window",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
    NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
    MessageBox(NULL, "Window Creation Failed!", "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;
    }
    شاید پیچیده به نظر بیاد ولی خیلی ساده است .. در واقع این کد ساده ترین نوع پنجره است .. اگر کد بالا را کامپایل و اجرا کنید باید بدون هیچ خطایی اجرا شود. و اما توضیح جزئیات :

    گام اول : ثبت کلاس پنجره
    Window Class اطلاعتی را درباره ی نوع پنجره شما ذخیره میکند . این اطلاعات شامل این موارد است : Window Procedure که پنجره شما را کنترل میکند ، آیکن های کوچک و بزرگ پنجره و رنگ زمینه ی پنجره . پس شما یکبار این کلاس را ثبت کرده و میتوانید چندین پنجره از آن بسازید بدون اینکه بخواهید اطلاعات تکراری وارد کنید .

    const char g_szClassName[] = "myWindowClass";
    متغیر بالا ، نام کلاس پنجره ما را مشخص و ذخیره میکند . ما از آن برای ثبت کلاس پنجره ی خود در سیستم استفاده میکنیم .

    WNDCLASSEX wc;

    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 = g_szClassName;
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
    MessageBox(NULL, "Window Registration Failed!", "Error!",
    MB_ICONEXCLAMATION | MB_OK);
    return 0;
    }
    کد بالا در واقع کدی است که ما برای ثبت کلاس پنجره خود در ()WinMain مینویسیم . ما تک تک اعضای Structure (ساختار) WNDCLASSEX را پر کرده و سپس تابع ()RegisterClassEx را فراخوانی می کنیم .


    توضیح اعضای این Struct

    cbSize : سایز این Struct

    Style : سبک کلاس . نباید این را با (* _Window Styles ( WS اشتباه گرفت .

    lpfnWndProc : اشاره گری به پروسیجر پنجره ( Window Procedure ) برای کلاس پنجره .

    cbClsExtra : فضای اضافه ای که برای این کلاس در نظر گرفته میشود ( معمولا ً صفر قرار داده می شود )

    cbWndExra : فضای اضافه ای که برای هر پنجره ای از این نوع در نظر گرفته میشود ( معمولا ً صفر قرار داده می شود )

    hInstance : هندلی به نمونه برنامه ( همان پارامتری است که بعنوان اولین پارامتر تابع ()WinMain دریافت میکنیم)

    hIcon : آیکن بزرگ که وقتی کاربر دکمه های alt + tab را فشار داد ، مشاهده میکند

    hCursor : شکلکی که وقتی مکان نمای موس روی پنجره قرار میگیرد نشان داده میشود

    hbrBackground : رنگ پس زمینه

    lpszMenuName : نام ریسورس منویی که پنجره های با این کلاس از آن استفاده میکنند

    lpszClassName : نامی که کلاس با آن شناخته می شود

    hIconSm : ایکن کوچکی که در بالا ، سمت چپ پنجره نمایش داده میشود


    اگر احساس غریبی میکنید زیاد نگران نباشید ! قسمتهای مختلفی که میبینید به شکلهای مختلفی در آینده استفاده و توضیح داده خواهند شد . نکته ی مهمی که وجود داره اینه که اصلا ً لازم نیست که اینها رو حفظ کنید ( چونکه نه ساده است و نه عاقلانه ) . هر وقت لازمشون داشتید میتونید مراجعه کنید به یکی از منابع معتبر ( مثل MSDN ) .

    حالا ما تابع ()RegisterClassEx را فراخوانی میکنیم . در صورت بروز خطا ، آن را اعلام کرده و به تابع WinMain باز میگردیم .



    گام دوم : ایجاد پنجره
    وقتی که کلاس پنجره ما ثبت شد ، باید آن را ایجاد کنیم . باید یه نگاهی به پارامتر های ()CreateWindowEx بیاندازید . البته در اینجا مختصرا ً آنها را توضیح میدهم :

    HWND hwnd;

    hwnd = CreateWindowEx(
    WS_EX_CLIENTEDGE,
    g_szClassName,
    "The title of my window",
    WS_OVERLAPPEDWINDOW,
    CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
    NULL, NULL, hInstance, NULL);
    اولین پارامتر ( WS_EX_CLIENTEDGE ) حالت پنجره است؛ در این مثال روی " فرورفتگی حاشیه به سمت داخل ( inner border ) " تنظیم شده است . اگر میخواهید تفاوت آن را با سایر حالتها مقایسه کنید ، آن را روی صفر تنظیم کنید . میتونید بقیه ی حالتها را هم امتحان کنید .

    آرگومان بعدی نام کلاس پنجره ی ماست ( g_szClassName ) . این پارامتر نوع پنجره ای را که ما میخواهیم ساخته شود را به سیستم میگوید ( همان پنجره ای که در گام اول ثبت کردیم ) . حالا میتوانیم عنوان پنجره را که در نوار عنوان ( Title bar ) نمایش داده میشود را مشخص کنیم .

    آرگومان بعدی که ما آن را برابر WS_OVERLAPPEDWINDOW قرار داده ایم ، استایل پنجره است . برای اینکه دقیقا ً بتوانید تفاوت مقادیر مختلف این پارامتر را تشخیص دهید ، مقادیر مختلفی که وجود دارد را امتحان کنید .

    چهار پارامتر بعدی ( CW_USEDEFAULT , CW_USEDEFAULT , 320 , 240 ) مختصات ( X,Y ) که از بالا سمت چپ محاسبه میشود و عرض و ارتفاع پنجره هستند . نکته ای که در اینجا وجود دارد این است که مقدار مینیمم برای X صفر است و با افزایش آن ، پنجره به سمت راست حرکت میکند . همچنین مقدار Y هم با اضافه شدن ، باعث حرکت پنجره به سمت پائین میشود . واحد در اینجا پیکسل است .

    پارامترهای بعدی ( NULL , NULL, g_hInst , NULL ) به ترتیب عبارتند از : هندل والد ( Parent handle ) ، هندل منو ( menu handle ) هندل برنامه نمونه ( application instance handle ) ، و اشاره گری به داده های ایجاد پنجره . در ویندوز ، پنجره های روی صفحه شما ، بصورت سلسله مراتبی از پنجره های Parent و Child ها مرتب میشوند . وقتی شما یک button را روی پنجره ای مشاهده میکنید ، این دکمه یک Child محسوب میشود که پنجره Parent آن است . در این مثال ، هندل Parent ، مقدار Null دارد . دلیل آن هم این است که پنجره ی ما هیچ Parent ای ندارد . در واقع پنجره ی اصلی یا Top Level window است . مقدار هندل منو هم NULL است چون هنوز منویی نداریم . هندل نمونه ( instance handle ) هم برابر با مقداری است که بعنوان اولین پارامتر به WinMain پاس داده میشود . داده های ایجاد ( Creation data ) هم ( که من هنوز از اینها استفاده ای نکرده ام !) ، یکسری اطلاعات اضافه تر را به پنجره ای که ایجاد میشود ارسال میکند .

    سوال متفرقه : این Null چیه که این همه ازش استفاده میشه !؟
    جواب : یک صفر ساده . درواقع در زبان C بصورت ( 0 ( * void ) ) تعریف شده است ، پس برای استفاده با اشاره گرهاست . به همین دلیل اگر شما از آن بجای یک Integer استفاده کنید ، با warning مواجه خواهید شد ( بستگی دارد به کامپایلر شما و تنظیمات آن ) . پس یا از صفر بجای آن استفاده کنید و یا به warning توجه نکنید !

    if(hwnd == NULL)
    {
    MessageBox(NULL, "Window Creation Failed!", "Error!",
    MB_ICONEXCLAMATION | MB_OK);
    return 0;
    }
    بعد از ایجاد پنجره و چک کردن صحت وجود هندل برای آن ، از آخرین پارامتر WinMain برای نمایش پنجره استفاده کرده و سپس آن را آپدیت میکنیم . برای اطمینان از اینکه خودش را دوباره بکشد Redrawn =

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);
    nCmdShow یک پارامتر اختیاری است ؛ شما میتوانید خیلی ساده از SW_SHOWNORMAL استفاده کنید .


    گام سوم : حلقه پیغام ( Message Loop )
    این قسمت قلب برنامه است .

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
    }
    return Msg.wParam;
    تابع ()GetMessage ، یک پیغام از صف پیغامهای برنامه شما برمیدارد . به محض اینکه کاربر موس را حرکت دهد ، کلیدی از کیبورد را فشار دهد ، روی یکی از منوهای ویندوز کلیک کند ویا هر کار دیگری از از این دست ؛ پیغامی توسط سیستم تولید شده و به صف پیغامهای برنامه شما فرستاده میشود . با فراخوانی تابع ()GetMessage ، شما درخواست دریافت پیغام بعدی ( در صورت وجود ) را میدهید و آن را از صف پیغامها حذف میکنید. اگر پیغامی موجود نباشد ، ()GetMessage بلاک می شود . ( اگر با مفهوم بلاک شدن آشنا نیستید : یعنی منتظر یک پیغام می ماند )

    تابع TranslateMessage یک پردازش اضافه روی رخدادهای کیبورد انجام می دهد . مثلا ً تولید پیغام WM_CHAR برای همراهی پیغام WM_KEYDOWN . در نهایت DispatchMessage پیغام رسیده را به پنجره ی مورد نظر میفرستد . این پنجره میتواند پنجره اصلی برنامه ما باشد و یا یک کنترل و یا هر چیز دیگری که در پشت پرده سیستم یا برنامه ی دیگری ایجاد شده است . لازم نیست زیاد نگران باشید .. تنها چیزی که ما لازم است بدانیم این است که پیغامی دریافت میشود و بعد هم ارسال میشود . خود سیستم مراقب سایر وظایف و جزئیات کار است .


    گام چهارم : روال پنجره ( Window Procedure )
    اگر حلقه پیغامها ( گام قبل را ببینید ) قلب برنامه شما باشد ، Window Procedure مغز برنامه شماست . اینجا جایی است که تمام پیغامهای رسیده به پنجره ، پردازش میشوند .

    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;
    }
    برای هر پیغام یکبار این تابع فراخوانی میشود . HWND هندل پنجره ی شماست . همان که پیغام را میپذیرد . تشخیص این مهم است که کدام پنجره باید پیغام را بپذیرد .. ممکن است شما چندین پنجره از یک کلاس داشته باشید که همگی از یک Window Procedure یعنی ()WndProc استفاده کنند . تفاوت فقط توسط پارامتر hwnd مشخص میشود . مثلا ً وقتی از پیغام WM_CLOSE استفاده میشود ، فقط یک پنجره باید بسته شود و در سایر پنجره ها نباید تاثیری داشته باشد .

    WM_CLOSE همان پیغامی است که وقتی کاربر روی علامت ضربدر پنجره (بالای تمام پنجره ها ، سمت راست ) را کلیک کند ، فرستاده می شود ( البته با فشردن Alt + F4 هم این اتفاق می افتد ) . بصورت پیش فرض این پیغام باعث بسته شدن و تخریب ( destroy ) آن می شود ولی در اینجا ما می خواهیم این کار را جداگانه خودمان انجام دهیم ( مثلا ً میخواهیم در صورت تایید کاربر پنجره بسته شود ) .

    وقتی که ما تابع ()DestroyWindow را فراخوانی میکنیم ، سیستم پیغام WM_DESTROY ر ابه پنجره میفرستد با آن را از بین ببرد . در نتیجه تمام فرزندان آن پنجره از بین رفته و در نهایت هم خود پنجره پاک و حافظه ی آن از سیستم پاک می شود . بدلیل اینکه در اینجا ما فقط یک پنجره ساده داریم ، تنها کاری که می خواهیم این است که برنامه ما بسته شود پس ما تابع ()PostQuitMessage را فراخوانی میکنیم . این کار باعث ارسال پیغام WM_QUIT به حلقه پیغامها می شود . ما هیچوقت این پیغام را دریافت نمی کنیم زیرا این پیغام باعث میشود تابع ()GetMessage مقدار False برگرداند ( به کد مربوط به Message Loop دقت کنید ) .

    مقداری که در نهایت بازگردانده میشود ، گاها ً می تواند مفید واقع شود .. مثلا ً اگر بخواهیم برنامه ی دیگری را فراخوانی کنیم ..

    گام پنجم :
    گام پنجمی هم در کار نیست !!
    مطالب فوق در آینده ( ان شاءالله ) بیشتر توضیح داده خواهند شد ...



    پ.ن : مثل قسمت قبل ، فایل ضمیمه شامل این قسمتها است :

    1- فرمت Pdf بخش دوم 2 -سورس برنامه 3- فایل exe
    فایل های ضمیمه فایل های ضمیمه

  4. #4
    کاربر دائمی آواتار www2006
    تاریخ عضویت
    مرداد 1385
    محل زندگی
    Mash <--> Teh
    پست
    187

    مدیریت و اجرای پیغامها ( Handling Messages )

    در قسمت قبل ایجاد یک پنجره را آموختیم . تنها کاری که با این پنجره میتوان کرد این است که آن را Minimize و یا Maximize کرد و یا آن را بست .
    کاری که در این قسمت میخواهیم انجام دهیم این است که با کلیک روی پنجره ، پیغامی نمایش داده شود و این پیغام مسیر نشان دهنده ی مسیر این فایل باشد .

    نکته : این قسمت بسیار به قسمت قبل وابسته است .. پس اگه نتونستید که قبلی رو اجرا کنید ، ادامه دادن رو بیخیال بشید و اول برنامه ی قبلی رو کامپایل و اجرا کنید .


    به کدی که در تابع ( )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:
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
    }

    اگر ما بخواهیم کلیک چپ موس را هندل ( بررسی و اجرا ) کنیم ، باید پیغام WM_LBUTTONDOWN را هندل کنیم ( به همین شکل WM_RBUTTONDOWN, WM_MBUTTONDOW برای کلیک راست و یا میانی موس ) . وقتی که از عبارت هندل کردن یک پیغام حرف میزنیم ، منظور این است که باید کد های مورد نظر را به تابع WndProc اضافه کنیم :

    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
    case WM_LBUTTONDOWN: // <-
    // <- we just added this stuff
    break; // <-
    case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
    case WM_DESTROY:
    PostQuitMessage(0);
    break;
    default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
    }

    ترتیب نوشتن شرطهای دستور Case مسئله مهمی نیست ، فقط گذاشتن break در پایان هر کدام را فراموش نکنید . حالا کدی را که میخواهیم اجرا بشود را به این شکل اضافه میکنیم :

    GetModuleFileName(hInstance, szFileName, MAX_PATH);
    MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);


    با ترکیب کد بالا و قسمتی که دربرنامه مشخص کردیم ، حاصل به این شکل در می آید :

    LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    switch(msg)
    {
    case WM_LBUTTONDOWN:
    // BEGIN NEW CODE
    {
    char szFileName[MAX_PATH];
    HINSTANCE hInstance = GetModuleHandle(NULL);

    GetModuleFileName(hInstance, szFileName, MAX_PATH);
    MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
    }
    // END NEW CODE
    break;
    case WM_CLOSE:
    DestroyWindow(hwnd);
    break;
    case WM_DESTROY:
    PostQuitMessage(0);
    break;
    default:
    return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
    }
    خوب .. اگه الآن کد تکمیل شده خود را کامپایل و اجرا کنید ، پنجره ای شبیه قبلی مشاهده میکنید . با کلیک چپ روی آن پیغامی را مشاهده میکنید که مسیر فایل اجرا شده را نشان میدهد .

    و اما توضیح جزئیات :
    همانطور که متوجه شدید ، داخل دستور Case و در قسمت مربوط به WM_LBUTTONDOWN دو متغیر تعریف کرده ایم . اگر به تابع GetModuleFileName نگاه کنید ، مورد استفاده ی اولین متغیر یعنی hInstance را متوجه خواهید شد . این متغیر در واقع هندلی است به ماژول قابل اجرای نهایی ( فایل .exe ای که در نهایت ساخته میشود ) .
    مقدار دهی این متغیر با استفاده از تابع GetModuleHandle انجام شده است . همانطور که متوجه شده اید اگر NULL بعنوان آرگومان ورودی به این تابع پاس داده شود ، مقداری که برگردانده میشود ، هندل همان فایلی است که این تابع را فراخوانی کرده است و این همان چیزی است که ما در اینجا به آن نیاز داریم . با ترکیب چیزهایی که گفته شد ، به این کد میرسیم :

    HINSTANCE hInstance = GetModuleHandle(NULL);
    دومین پارامتر تابع GetModuleFileName (براساس ارائه شده در مراجع ) اشاره گری است به بافری که مسیر و نام ماژول ( فایل مورد نظر ما ) را قرار است در خود ذخیره کند . نوع آن هم LPSTR است و از آنجایی که LPSTR معادل است با *Char ، ما آرایه را به این شکل تعریف میکنیم :

    char szFileName[MAX_PATH];
    سومین پارامتر یعنی MAX_PATH ماکرو ای است که با الحاق Windows.h به برنامه میتوان از آن استفاده کرد و برابر با ماکزیمم مقداری که مسیر و نام یک فایل در Win32 می تواند داشته باشد تعریف شده است .

    بعد از فراخوانی GetModuleFileName ، بافر ما یعنی szFileName شامل یک رشته ی ناتهی است که نام فایل exe ما است . ما این مقدار را به یک MessageBox پاس میدهیم تا به کاربر نمایش داده شود .

    مثل همیشه فایل ضمیمه شامل :
    1- فرمت Pdf بخش سوم 2-سورس این قسمت 3-فایل exe این قسمت
    می باشد .
    فایل های ضمیمه فایل های ضمیمه

  5. #5
    کاربر دائمی آواتار www2006
    تاریخ عضویت
    مرداد 1385
    محل زندگی
    Mash <--> Teh
    پست
    187

    حلقه پیغام ( Message Loop ) چیست ؟

    یکی از اساسی ترین مواردی که در ویندوز وجود دارد مفهوم حلقه پیغام ( Message Loop ) است . آنچه تا کنون گذشته است بررسی مختصر و جزئی پیغام ها بوده است و الآن وقتشه که کمی عمیقتر وارد این بحث بشیم .

    یک پیغام ( Message ) چیست ؟
    یک پیغام در واقع یک مقدار Integer است . حتما ً تا کنون تعاریفی به این شکل را در برنامه های مختلف دیده اید :

    #define WM_INITDIALOG 0x0110
    #define WM_COMMAND 0x0111

    #define WM_LBUTTONDOWN 0x0201
    از پیغامها ( Messages ) برای برقراری ارتباط بین هر آنچه در ویندوز موجود است ، در پائین ترین سطح ممکن استفاده میشود . اگر میخواهید که یک پنجره یا یک کنترل ( که خود نوعی پنجره است ) در ویندوز ، کار خاصی انجام دهد ، باید پیغامی برای آن ارسال کنید. به همین شکل اگر پنجره دیگری بخواهد که شما ( بعنوان یک پنجره !) کاری را انجام دهید ، منظورش را بصورت یک پیغام ( Message ) برای شما میفرستد . اگر رویدادی اتفاق بیفتد (مثلا ً کاربر کلمه ای را تایپ کند ، موس حرکت کند و یا دکمه ( Button ) ای فشار داده شود ) ، سیستم برای پنجره ها پیغام را ارسال میکند . اگر شما هم یکی از آن پنجره ها باشید ، باید پیغام رسیده را هندل کنید و مطابق با آن عمل کنید .

    هر پیغام در ویندوز حداکثر با دو پارامتر همراه است . wParam و lParam . در اصل wParam 16 بیتی است و lParam 32 بیتی است ولی در win32 هر دو 32 بیتی هستند . لزوما ً تمام پیغام ها از هر دوی این پارامتر ها استفاده نمی کنند . هر پیغام بصورت مجزا از این پیغام ها استفاده میکند . مثلا ً پیغام WM_CLOSE که برای بستن یک پنجره استفاده میشود ، از هیچیک از این پارامترها استفاده ای نمی کند . WM_COMMAND از هر دو استفاده میکند به این شکل :
    wParam شامل دو مقدار است . کلمه باارزشتر ( HIWORD ) حاوی پیغام اخطار و کلمه کم ارزشتر ( Loword ) حاوی Id ی ( شناسه ی) کنترل یا منویی است که پیغام را ارسال کرده است .
    lParam شامل HWND ( هندل پنجره ) کنترلی است که پیغام را ارسال کرده است و یا اینکه مقدار آن NULL است اگر پیغام از طرف هیچ کنترلی ارسال نشده باشد .

    عبارت HIWORD و LOWORD ماکروهایی هستند که توسط خود ویندوز تعریف شده اند به این صورت که برای جدا کردن دو بایت با ارزش و دو بایت کم ارزش یک کلمه 32 بیتی استفاده میشوند .
    دو بایت ( کلمه ) با ارزشتر = 0xFFFF0000
    دو بایت ( کلمه ) کم ارزشتر = 0x0000FFFF
    در win32 یک کلمه ( Word ) 16 بیتی است و DWord مقداری است 32 بیتی .

    برای فرستادن یک پیغام از دو تابع PostMessage و SendMessage می توان استفاده کرد :
    - PostMessage پیغام را داخل صف پیغام ها ( Message Queue ) قرار میدهد و بلافاصله برمیگردد . به این معنا که وقتی شما PostMessage را فراخوانی می کنید و این تابع اجرا می شود ، ممکن است که ارسال پیغام شما انجام شده باشد و یا هنوز در حال انجام شدن باشد.
    - تفاوت SendMessage با قبلی هم در این است که که SendMessage تا وقتی که پیغام ارسال نشود برنمیگردد . در واقع این یکی پیغام را مستقیما ً به پنجره مورد نظر میفرستد .

    اگر ما بخواهیم که پنجره ای را ببندیم ، از PostMessage می توان به این صورت استفاده کرد :


    PostMessage(hwnd, WM_CLOSE, 0, 0);
    این کد دقیقا ً مثل این عمل میکند که شما روی دکمه ضربدری که بالا سمت چپ هر پنجره قرار دارد کلیک کنید .
    همانطور که قبلا ً هم گفته شد ، دو پارامتر wParam و lParam برای WM_CLOSE استفاده نمیشوند و مقدار آنها صفر است .

    صف پیغامها ( Message Queue ) چیست ؟
    فرض کنید که که ما مشغول پاسخگویی به پیغام WM_PAINT هستیم و در این هنگام کاربر کلمه ای را با استفاده از کیبورد تایپ میکند . به نظر شما چه اتفاقی می افتد؟
    آیا باید در کار ترسیم صفحه وقفه ای وارد شود و به سراغ کیبورد برویم و یااینکه تایپ کاربر را نادیده بگیریم ؟ همانطور که ( احتمالا ً ) میدانید هر دوی اینها غلط است . راه حلی که در اینگونه موارد استفاده میشود استفاده از صف پیغام است به این شکل که وقتی پیغامی ارسال میشود ، به انتهای صف اضافه شده و به محض اینکه نوبت بررسی و اجرای آنها برسد از صف حذف میشوند . با این کار هیچ پیغامی بدون بررسی حذف نمی شود .

    حلقه پیغام ( Message Loop ) چیست؟

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
    WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
    fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
    }

    1- حلقه پیغام ، تابع GetMessages را فراخوانی میکند و به این ترتیب پیغامی از صف پیغامها خوانده میشود . اگر صف پیغام خالی باشد ، برنامه شما ( پروسس برنامه شما ) بلاک میشود .
    2- وقتی رویدادی ( event ) اتفاق می افتد که باعث افزوده شدن پیغامی به صف پیغامها می- شود ( مثلا ً یک کلیک موس ) ، تابع GetMessages مقداری مثبت برمیگرداند که نشان دهنده وجود پیغامی برای بررسی است . در ادامه ساختار ( Struct ) Msg که ما به آن پاس داده ایم با مقادیر مناسب پر می شود . اگر مقدار برگردانده شده صفر باشد یعنی با پیغام WM_QUIT مواجه شده ایم و اگر مقدار منفی برگردانده شود یعنی خطایی اتفاق افتاده است .
    3- پیغام دریافتی ( از طریق متغیر Msg ) را به تابع TranslateMessage پاس میدهیم تا یکسری پردازشهای اضافی روی پیغام انجام شود .
    4- سپس تابع DispatchMessage را فراخوانی میکنیم . کاری که این تابع انجام میدهد به این ترتیب است : پیغام را میگیرد ، بررسی می کند که مربوط به کدام پنجره است و سپس بدنبال Window Procedure مربوط به آن پنجره می گردد . سپس آنرا فراخوانی میکند و هندل پنجره ، پیغام ، wParam و lParam را بعنوان پارامتر برای آن ارسال میکند .
    5- در Window Procedure ( همانگونه که در قسمت قبلی عملا ً انجام دادید ) ، شما پیغام و پارامترهای مربوط به آن را بررسی می کنید و هرگونه که بخواهید میتوانید با آن برخورد کنید (!) اگر شما پیغامی را هندل نکنید ، تابع DefWindowProc برای آن فراخوانی می شود و عملیاتی که خود ویندوز بصورت پیش فرض در نظر گرفته است ، برای آن انجام میگیرد ( اغلب هم هیچ کاری انجام نمی شود !)
    6- هنگامی که کار شما و Window Procedure و DispatchMessage تمام شد ، به ابتدای حلقه برمیگردیم .

    درک این مطلب خیلی مهمه که Window Procedure خود به خود و بدون دلیل فراخوانی نمیشود بلکه توسط خود شما و بصورت غیر مستقیم توسط DispatchMessage فراخوانی میشود .


    همانطور که میبینید برنامه شما اکثر زمان خود را صرف اجرای حلقه پیغام میکند ، جایی که شما پیغامهای خود را میفرستید و اجرا هم می شوند .
    برای خروج از برنامه هم کافیست که تابع GetMessage مقدار False ( یا صفر ) برگرداند . در اینصورت از حلقه خارج شده و به تابع WinMain باز می گردیم . این دقیقا ً کاری است که PostMessage انجام می دهد ، یعنی با قرار دادن مقدار WM_QUIT در صف پیغامها باعث میشود که تابع GetMessage مقدار صفر برگرداند .

    اصلی ترین هدف این قسمت ایجاد درک بهتر و کاملتر از پیغامها و نحوه کار آنها در ویندوز بوده است . امیدوارم که به این هدف رسیده باشه (!) .. البته در آینده باز هم خواهم نوشت ( انشاءالله )

    پ.ن:فایل ضمیمه ی این بخش فقط شامل یک Pdf است . همین ..

    فایل های ضمیمه فایل های ضمیمه

  6. #6

    نقل قول: مقاله : آموزش Win32 API ( برنامه نویسی ویندوز با C )

    بقیه آن هم ترجمه می شود؟

    نوشته ها بدون عنوان منبع ترجمه
    http://www.winprog.org/tutorial/
    است

تاپیک های مشابه

  1. نوشتن GUI با Win32 یا MFC یا ...
    نوشته شده توسط lord_akinak در بخش برنامه نویسی با MFC و ++Visual C
    پاسخ: 10
    آخرین پست: سه شنبه 21 آذر 1396, 08:57 صبح
  2. Win32 هنوز نفس میکشه !
    نوشته شده توسط توسعه نویس در بخش برنامه نویسی با زبان C و ++C
    پاسخ: 7
    آخرین پست: یک شنبه 25 آذر 1386, 03:41 صبح
  3. تشخیص win32
    نوشته شده توسط رهنورد2 در بخش برنامه نویسی با زبان C و ++C
    پاسخ: 0
    آخرین پست: جمعه 12 آبان 1385, 16:52 عصر
  4. کامپایل کامل یک dot net app به win32 app
    نوشته شده توسط Mrs.Net در بخش C#‎‎
    پاسخ: 1
    آخرین پست: جمعه 07 مهر 1385, 14:54 عصر
  5. تبدیل یه کامپوننت VCL به Win32 DLL یا NET
    نوشته شده توسط sh در بخش کامپوننت های سایر شرکت ها، و توسعه کامپوننت
    پاسخ: 4
    آخرین پست: جمعه 04 فروردین 1385, 23:22 عصر

قوانین ایجاد تاپیک در تالار

  • شما نمی توانید تاپیک جدید ایجاد کنید
  • شما نمی توانید به تاپیک ها پاسخ دهید
  • شما نمی توانید ضمیمه ارسال کنید
  • شما نمی توانید پاسخ هایتان را ویرایش کنید
  •