از کجا می توانم شرحی از توابع api ویندوز را مشاهده کنم. Windows API چیست؟ شرح نمونه های اولیه تابع در MSDN

FindWindow
GetWindow
GetWindowText
SetWindowText
آیس ویندوز
MoveWindow
IsWindowVisible
فعال کردن پنجره
IsWindowEnabled
WindowFromPoint
پنجره ی نمایش
پنجره بسته
SetWindowPos
GetClassLong
SetClassLong
GetWindowLong
SetWindowLong
GetDesktopWindow
GetParent

تابع FindWindow

تابع FindWindow(className,WindowName: PChar) : HWND;
تابع یک توصیفگر پنجره را برمی گرداند که درخواست را برآورده می کند (0 اگر چنین پنجره ای یافت نشد).

نام کلاس نام کلاسی که برای جستجو در بین تمام پنجره های سیستم استفاده می شود. WindowNameعنوان پنجره

ممکن است یکی از پارامترها برابر با صفر باشد، سپس جستجو با استفاده از پارامتر دیگری انجام می شود.
مثال:

تابع GetWindow

تابع GetWindow (Wnd: HWND؛ Param): HWND
تابع یک توصیفگر پنجره را برمی گرداند که درخواست را برآورده می کند.

Wnd دسته ای به برخی از پنجره های اولیه پارام یکی از مقادیر ثابت زیر را می گیرد: gw_Owner دستگیره به پنجره اجداد برگردانده می شود (0 اگر اجدادی وجود نداشته باشد). gwHWND اول یک دسته را به اولین پنجره (نسبت به Wnd) برمی گرداند. gw_HWNDبعدی یک دسته را به پنجره بعدی برمی‌گرداند (پنجره‌ها بدون تکرار تکرار می‌شوند، یعنی اگر پارامتر Wnd تابع را تغییر نداده باشید، دستگیره‌ها دوباره برگردانده نمی‌شوند) gw_کودک یک دسته را به اولین پنجره فرزند باز می گرداند.

مثال:

تابع GetWindowText

تابع GetWindowText(hWnd: HWND; lpString: PChar; nMaxCount: Integer): Integer;
تابع متن پنجره را برمی گرداند. برای یک فرم، این عنوان، برای یک دکمه - برچسب روی دکمه است.

hWnd توصیفگر پنجره ای که می خواهید متن آن را دریافت کنید. lpString متغیری که نتیجه در آن قرار می گیرد nMaxCount

حداکثر طول متن؛ اگر متن طولانی‌تر باشد، قطع می‌شود.


مثال:

تابع IsWindow

تابع IsWindow(hWnd: HWND): BOOL;اگر پنجره ای با دسته داده شده وجود داشته باشد True، در غیر این صورت False را برمی گرداند.

هوند توصیفگر پنجره مورد نظر

تابع MoveWindow

MoveWindow(hWnd: HWND; X, Y, nWidth, nHeight: Integer; bRepaint: BOOL): BOOL;پنجره را به موقعیت جدیدی منتقل می کند.

hWnd یک دستگیره به سمت پنجره در حال حرکت است. X، Y، nعرض، nارتفاع بر این اساس: مختصات جدید X,Y; عرض، ارتفاع جدید b رنگ آمیزی مجدد یک مقدار بولی که نشان می دهد آیا پنجره دوباره ترسیم می شود یا خیر.

تابع IsWindowVisible

تابع IsWindowVisible(hWnd: HWND): BOOL;
اگر پنجره داده شده قابل مشاهده باشد، True را برمی گرداند.

hWndتوصیفگر پنجره

فعال کردن عملکرد پنجره

تابع EnableWindow(hWnd: HWND; beEnable: BOOL): BOOL;
در دسترس بودن پنجره را تنظیم می کند (اگر به رویدادهای ماوس، صفحه کلید و غیره پاسخ ندهد، پنجره در دسترس نیست). یک آنالوگ در دلفی از ویژگی Enabled of components. EnableWindow اگر همه چیز موفقیت آمیز بود True و در غیر این صورت False را برمی گرداند.

hWndتوصیفگر پنجره فعال شود یک مقدار بولی که نشان می دهد پنجره موجود است یا خیر.


مثال:

تابع IsWindowEnabled

تابع IsWindowEnabled(hWnd: HWND): BOOL;
برمی گرداند برای پنجره داده شده: اگر پنجره موجود باشد درست است و در غیر این صورت False.

hWndتوصیفگر پنجره

تابع WindowFromPoint

WindowFromPoint(نقطه: TPoint): HWND;
یک دستگیره را به پنجره ای که در یک نقطه معین از صفحه قرار دارد برمی گرداند.

نقطه نوع مختصات نقطه صفحه TPoint(تعریف نوع زیر را ببینید)

تابع

نوع TPoint = رکورد x: Longint; y: Longint; پایان;

تابع ShowWindow

تابع ShowWindow(hWnd: HWND; nCmdShow: Integer): BOOL;پنجره را نشان می دهد یا پنهان می کند.

hWnd توصیفگر پنجره مورد نظر nCmdShow ثابتی که تعیین می کند با پنجره چه کاری انجام می شود: SW_HIDE SW_SHOWNORMAL SW_NORMAL SW_SHOWMINIMIZED SW_SHOWMAXIMIZED SW_MAXIMIZE SW_SHOWNOACTIVATE SW_SHOW SW_MINIMIZE SW_SHOWMINNOACTIVE SW_SHOWNA SW_RESTORE SW_SHOWDEFAULT SW_MAX


مثال:

عملکرد بستن پنجره

تابع CloseWindow(hWnd: HWND): BOOL; stdcall;
پنجره را می بندد.

hWnd یک دستگیره به پنجره که باید بسته شود.

SetWindowPos

تابع SetWindowPos(hWnd: HWND؛ hWndInsertAfter: HWND؛ X, Y, cx, cy: Integer؛ uFlags: UINT): BOOL; stdcall;
پنجره را در موقعیت جدیدی قرار می دهد

hWndاپتسالر پنجره hWndInsertAfter توصیفگر پنجره ای که قبل از آن در لیست قرار دارد Z-Orderیک پنجره درج خواهد شد hWnd، یا یکی از ثابت های زیر: HWND_BOTTOM پنجره را در پایین لیست Z-Order قرار دهید HWND_TOP پنجره را در بالای لیست Z-Order قرار دهید X، Y، cx، cy

بر این اساس، افق های جدید. ، راس موقعیت های پنجره ( X، Y) و همچنین عرض جدید
و ارتفاع ( cx، cy)

uFlags یک یا چند (جدا شده یا) ثابت های زیر: SWP_NOSIZE بعد از جابجایی اندازه پنجره را تغییر ندهید ( cx، cyنادیده گرفته می شوند) SWP_NOZORDER موقعیت پنجره را در لیست Z-Order تغییر ندهید SWP_SHOWWINDOW پنجره را بعد از جابجایی قابل مشاهده کنید SWP_HIDEWINDOW پس از حرکت پنجره را مخفی کنید SWP_NOACTIVATE پس از حرکت به پنجره تمرکز نکنید SWP_NOMOVE پنجره را حرکت ندهید (نادیده گرفته شد X، Y)

تابع GetClassLong

تابع GetClassLong(hWnd: HWND; nIndex: Integer): Integer;
این تابع یک عدد صحیح 32 بیتی گرفته شده از یک فیلد خاص در یک رکورد را برمی گرداند TWndClassExپنجره مشخص شده

hWndدستگیره پنجره nIndex یک تعریف ثابت که چه چیزی برگردانده خواهد شد. باید یکی از موارد زیر باشد: GCL_MENUNAME اشاره گر را به رشته ای برمی گرداند که حاوی نام منوی کلاس تعریف شده در فایل منبع مرتبط با برخی از برنامه ها است. GCL_HBRBACKGROUND یک دسته (HBRUSH) را به براش پس‌زمینه مرتبط با کلاس برمی‌گرداند GCL_HCURSOR یک دسته (HCURSOR) را به مکان نما مرتبط با کلاس برمی گرداند GCL_HICON یک دسته (HICON) را به نماد مرتبط با کلاس برمی‌گرداند GCL_HMODULE یک دسته را به فرآیند (HMODULE) که کلاس را ثبت کرده است برمی گرداند. GCL_CBWNDEXTRA اندازه حافظه (بر حسب بایت) اختصاص داده شده برای ذخیره داده های اضافی برای THIS WINDOW را برمی گرداند. برای توضیح نحوه استفاده از این حافظه، توضیحات عملکرد را ببینید GCL_CBCLSEXTRA اندازه حافظه (بر حسب بایت) اختصاص داده شده برای ذخیره کلاس اضافی GIVEN را برمی گرداند GCL_WNDPROC آدرس روال پنجره مرتبط با کلاس را برمی گرداند. GCL_STYLE استایل کلاس را برمی‌گرداند (وجود یک سبک خاص با یک عملیات بیتی بررسی می‌شود وبا استفاده از ثابت هایی مانند cs_XXX) GCL_HICONSM

توجه داشته باشید: در مواردی که تابع یک اشاره گر را برمی گرداند، نوع casting ضروری است (Integer ->

تابع SetClassLong

تابع SetClassLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Integer;تابع تابع جفت. فیلد مورد نیاز را به مقدار مناسب تنظیم می کند.
تابع مقدار قدیمی فیلد را برمی گرداند تا بعداً بتوان آن را اصلاح کرد یا اگر مشکلی پیش آمد صفر را برمی گرداند.

hWndدستگیره پنجره nIndex یکی از ثابت ها GCL_XXXاز تابع بسته به مقدار این فیلد، فیلد مورد نیاز تغییر می کند.

توجه داشته باشید اشاره گرتایپ کردن عدد صحیح.

تابع GetWindowLong

تابع GetWindowLong(hWnd: HWND; nIndex: Integer): Longint;اطلاعات مربوط به یک پنجره را به صورت یک عدد صحیح 32 بیتی برمی گرداند.

hWndدستگیره پنجره nIndex یک تعریف ثابت که چه چیزی برگردانده خواهد شد. باید یکی از موارد زیر باشد:
GWL_WNDPROC آدرس رویه پنجره مرتبط با این پنجره را برمی گرداند. آدرس به دست آمده (پس از کست های نوع مناسب) می تواند در یک تابع استفاده شود CallWindowProc. این مقدار معمولاً در صورتی استفاده می‌شود که بخواهند یک رویه پنجره موجود را با خود جایگزین کنند و برای اینکه عملکرد پنجره را از دست ندهند، معمولاً از آن استفاده می‌کنند. CallWindowProc. GWL_HINSTANCE دسته برنامه مشخص شده هنگام ایجاد پنجره توسط تابع را برمی گرداند CreateWindowEx. GWL_HWNDPARENT دسته (HWND) پنجره والد را برمی گرداند GWL_STYLE سبک پنجره را برمی گرداند. مقادیر سبک خاص با استفاده از یک عملیات بیتی یافت می شود وو ثابت ها WS_XXX GWL_EXSTYLE سبک پنجره توسعه یافته را برمی گرداند. مقادیر سبک خاص با استفاده از یک عملیات بیتی یافت می شود وو ثابت ها WS_EX_XXX GWL_USERDATA عدد صحیح 32 بیتی مرتبط با پنجره را برمی‌گرداند (این آخرین پارامتر در فراخوانی CreateWindow یا CreateWindowEx است) GWL_ID هنگام فراخوانی CreateWindow یا CreateWindowEx، شناسه پنجره را برمی‌گرداند (این ربطی به دسته پنجره ندارد!) که توسط پارامتر hMenu برای پنجره‌های فرزند مشخص شده است.

توجه داشته باشید: در مواردی که تابع یک اشاره گر را برمی گرداند، نوع ریخته گری ضروری است (Integer -> Pointer). شما می توانید این کار را به این صورت انجام دهید:

تابع SetWindowLong

تابع SetWindowLong(hWnd: HWND; nIndex: Integer; dwNewLong: Longint): Longint;
با تابع جفت شد. ویژگی های یک پنجره خاص را تغییر می دهد.
در صورت موفقیت آمیز بودن فراخوانی، این تابع مقدار قدیمی ویژگی را برمی‌گرداند یا در غیر این صورت، آن را تهی می‌کند.

hWndدستگیره پنجره nIndex ثابتی که مشخص می کند کدام ویژگی تغییر خواهد کرد. باید یکی از ثابت ها باشد GWL_XXXاز توضیحات تابع dwNewLong مقدار جدید یک ویژگی که با یک ثابت تعریف می شود nIndex

توجه داشته باشید: هنگام تنظیم فیلدهای اشاره گر، یک نوع Cast مورد نیاز است اشاره گرتایپ کردن عدد صحیح.

تابع GetDesktopWindow

تابع GetDesktopWindow: HWND
تابع یک دسته را به پنجره دسکتاپ برمی گرداند. بدون پارامتر

GetParent

تابع GetParent(hWnd: HWND): HWND;
دسته پنجره والد را برای یک پنجره برمی گرداند hWnd.

hWndدستگیره پنجره

C WinAPI مجموعه اصلی مایکروسافت از رابط های برنامه نویسی (API) موجود در سیستم عامل ها است.نسخه اولیه Win32 API نامیده می شود.

معرفی

C WinAPI یک رابط برنامه نویسی کاربردی است که برای ایجاد برنامه های کاربردی ویندوز استفاده می شود. برای شروع، یک کاربر مبتدی باید Windows SDK را دانلود کند، که قبلا به عنوان Platform SDK شناخته می شد.

حاوی فایل‌های هدر، کتابخانه‌ها، نمونه‌ها، اسناد و ابزارهایی است که برای توسعه برنامه‌ها استفاده می‌شوند. API برای زبان های برنامه نویسی C و C++. این مستقیم ترین راه برای ایجاد برنامه های کاربردی در سیستم عامل شرکت است.

C WinAPI را می توان به چندین بخش تقسیم کرد:

    خدمات اساسی؛

    ایمنی؛

  • رابط کاربری؛

    چند رسانه ای؛

    پوسته ویندوز؛

    خدمات شبکه

خدمات اصلی دسترسی به منابع اولیه را فراهم می کند. اینها شامل توابع C WinAPI، سیستم‌های فایل، دستگاه‌ها، فرآیندها، رشته‌ها، رجیستری و مدیریت خطا هستند. ناحیه امنیتی رابط ها، اشیا و سایر عناصر برنامه نویسی را برای احراز هویت، مجوز، رمزنگاری و سایر وظایف مرتبط با امنیت فراهم می کند. زیرسیستم گرافیک عملکردی را برای خروجی محتوای گرافیکی به نمایشگرها، چاپگرها و سایر دستگاه های خروجی فراهم می کند. رابط کاربری قابلیت هایی را برای ایجاد ویندوز و کنترل ها فراهم می کند.

مؤلفه چند رسانه ای ابزارهایی را برای کار با دستگاه های ویدیویی، صوتی و ورودی فراهم می کند. توابع رابط شل به برنامه‌ها اجازه می‌دهند به توابع ارائه شده توسط پوسته سیستم عامل دسترسی پیدا کنند. خدمات شبکه دسترسی به قابلیت های شبکه ویندوز را فراهم می کند.

اجزاء

هنگام ایجاد WinAPI C، باید قابلیت های اساسی ارائه شده توسط Windows API را در نظر بگیرید که می تواند در هفت دسته سازماندهی شود. بیایید به هر یک از آنها با جزئیات بیشتری نگاه کنیم.

خدمات ضروری دسترسی به منابع اولیه سیستم موجود در ویندوز را فراهم می کند. مثال: سیستم فایل، تجهیزات جانبی، فرآیندها، دسترسی به رجیستری و سیستم مدیریت استثنا. این توابع در فایل‌های kernel.exe، krnl286.exe یا krnl386.exe برای سیستم‌های 16 بیتی و kernel32.dll و advapi32.dll برای سیستم‌های 32 بیتی ذخیره می‌شوند.

رابط کاربری گرافیکی دسترسی به منابع را برای نمایش بر روی مانیتورها، چاپگرها و سایر تجهیزات جانبی فراهم می کند. در gdi.exe در سیستم های 16 بیتی و gdi32.dll در سیستم های 32 بیتی ذخیره می شود.

رابط کاربری مسئول مشاهده و کنترل عناصر اساسی مانند دکمه ها و نوارهای اسکرول، به دست آوردن اطلاعات صفحه کلید و ماوس و عملکردهای مرتبط است. این توابع در user.exe در سیستم های 16 بیتی و user32.dll comctl32.dll در سیستم های 32 بیتی ذخیره می شوند. با شروع XP، کنترل ها در comctl32.dll گروه بندی شدند.

گفتگوهای عمومی - نمایش اطلاعات برای باز کردن و ذخیره فایل ها، انتخاب رنگ ها و فونت ها. در فایل comdlg.dll در سیستم های 16 بیتی و comdlg32.dll در سیستم های 32 بیتی یافت می شود.

Windows Shell جزء WinAPI است که به برنامه‌ها اجازه می‌دهد به عملکردهای ارائه شده توسط پوسته سیستم عامل دسترسی داشته باشند.

خدمات شبکه دسترسی به قابلیت های شبکه ای مختلف سیستم عامل را فراهم می کند. اجزای فرعی آن عبارتند از NetBIOS، Winsock، RPC. در نسخه های قدیمی - NetDDE.

نسخه ها

Win16، Win32 و Win32s مجموعه استانداردی از اجزا هستند که به نرم افزارهای کاربردی اجازه می دهند از عملکردهای سیستم عامل های مختلف خانواده ویندوز استفاده کنند.

Win32، جانشین Win16، در سال 1993 در محصولات خانواده ویندوز 32 بیتی مانند Windows NT، 2000، 95 معرفی شد. این رابط برنامه نویسی توسط سه کتابخانه نرم افزاری Kernel32.dll، User32.dll و GDI32.dll2 پیاده سازی شده است. همان ویژگی های Win32 در همه محصولات ویندوز موجود است و بسته به محصول، استفاده از برخی ویژگی ها ممکن است منجر به خطای سرویس شود.

قابلیت های Win32 شامل ارتباط بین برنامه ها، مدیریت فرآیندها، شبکه های کامپیوتری، فایل ها، چاپگرها، سرورها و پورت های ارتباطی است.

مشخصات

C WinAPI یک ویژگی رابط برنامه نویسی انتزاعی برای سیستم عامل ویندوز است. شامل اعلان توابع، اتحادیه ها، ساختارها، انواع داده ها، ماکروها، ثابت ها و سایر عناصر برنامه نویسی است. WinAPI توسط یک استاد توضیح داده شده و در هدرهای ویندوز C یافت می شود. اجرای رسمی توابع WinAPI در کتابخانه های پیوند پویا (DLL) یافت می شود: به عنوان مثال، kernel32.dll، user32.dll، gdi32.dll یا shell32.dll در دایرکتوری سیستم پیاده سازی های شخص ثالثی از Windows API وجود دارد: مهمترین آنها پروژه Wine و پروژه ReactOS.

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

پلس سی

Pelles C یک برنامه رایگان و بهترین کامپایلر C و محیط توسعه یکپارچه (IDE) برای زبان برنامه نویسی C است. از ویندوز 32 بیتی (x86) و ویندوز 64 بیتی (x64) پشتیبانی می کند. هر دو استاندارد C99 و C11 را اجرا می کند. Pelles C دارای یک ویرایشگر منابع داخلی، بیت مپ، ویرایشگر نماد و مکان نما و ویرایشگر هگز رومیزی است. این توسط توسعه دهنده سوئدی Pelle Orinius توسعه یافته است. کامپایلر به نام نویسنده آن نامگذاری شده است. همراه با SDK است، بنابراین برنامه نویس می تواند بلافاصله بدون نصب بیشتر شروع به ایجاد برنامه های کاربردی کند.

خطای معماری هدف

برای ایجاد برنامه های Windows API، باید افزونه های مایکروسافت را فعال کنید. آنها به طور پیش فرض غیرفعال هستند و باعث می شوند کامپایلر پیغام خطای زیر را صادر کند، که نمونه ای از یک WinAPI شکسته C است: خطای مرگبار #1014: #error: "معماری هدف وجود ندارد" برای فعال کردن برنامه‌های افزودنی مایکروسافت، به تنظیمات پروژه بروید و تب کامپایلر را انتخاب کنید. در این برگه، کادر "Enable Microsoft extensions" را فعال کنید.

MSDN

پورتال مرکزی برای توسعه ویندوز است. این مجموعه عظیمی از مواد مربوط به توسعه برنامه با استفاده از ابزارهای مایکروسافت است. این جامع ترین پایگاه داده به همراه مستندات توسعه برنامه های دسکتاپ و لیستی از API های ویندوز است.

استفاده از DLL در WinAPI C

کتابخانه Common Controls دسترسی به ویژگی های پیشرفته سیستم عامل مانند نوار وضعیت، نوار پیشرفت، نوار ابزار و برگه ها را فراهم می کند. این دستورات در commctrl.dll در سیستم های 16 بیتی و comctl32.dll یافت می شوند و با رابط کاربری گروه بندی می شوند.

DLL یک فرمت فایل کتابخانه پیوند پویا است که برای ذخیره چندین کد و رویه برای برنامه های ویندوز استفاده می شود. فایل های DLL به گونه ای ساخته شده اند که چندین برنامه می توانند همزمان از اطلاعات خود استفاده کنند و به صرفه جویی در حافظه کمک کنند. به کاربر اجازه می دهد تا کدگذاری چندین برنامه را به طور همزمان بدون تغییر آنها ویرایش کند. DLL ها را می توان با استفاده از MSIL Disassembler یا DLL برای Lib 3.00 به استاتیک تبدیل کرد.

WinAPI، به عنوان یک رابط برنامه نویسی کاربردی برای ویندوز، بسیاری از ویژگی های قدرتمند را ارائه می دهد که به شما امکان می دهد برنامه های خود را ایجاد کنید، از پردازش ساده فایل گرفته تا ساخت یک رابط گرافیکی برای برنامه نویسی درایورهای دستگاه های سطح پایین.

قبل از شروع برنامه نویسی در WinAPI، باید محیط کد خود را در ویندوز تنظیم کنید. از آنجایی که توزیع لینوکس نیست، کامپایلر داخلی برای ایجاد برنامه‌ها ندارد. برای کامپایل کردن کد گزینه های زیر را در نظر بگیرید:


یک کیت توسعه برای ویندوز در دسترس است که اسناد و ابزارهایی را برای توسعه دهندگان برای ایجاد نرم افزار با استفاده از API و فناوری های مرتبط فراهم می کند.

مخفف API، Application Programming Interface (API) به سادگی مجموعه ای آماده از توابع است که توسعه دهندگان برنامه می توانند از آن استفاده کنند. به طور کلی، این مفهوم معادل چیزی است که قبلاً بیشتر به آن کتابخانه زیر روال می‌گفتند. با این حال، اغلب API به دسته خاصی از چنین کتابخانه هایی اشاره دارد.

در طول توسعه تقریباً هر برنامه نسبتاً پیچیده (MyApplication) برای کاربر نهایی، مجموعه ای از توابع داخلی خاص تشکیل می شود که برای اجرای این برنامه خاص استفاده می شود که MyApplication API نامیده می شود. اغلب معلوم می شود که این توابع می توانند به طور موثر برای ایجاد برنامه های کاربردی دیگر، از جمله توسط برنامه نویسان دیگر، استفاده شوند. در این صورت، نویسندگان بر اساس استراتژی تبلیغ محصول خود، باید در مورد این سوال تصمیم بگیرند که آیا آنها دسترسی به این مجموعه را برای کاربران خارجی باز می کنند یا خیر؟ اگر پاسخ مثبت باشد، در توضیحات بسته نرم افزاری، به عنوان مزیت آن، این عبارت ظاهر می شود که "بسته شامل مجموعه ای باز از توابع API است."

بنابراین، اغلب یک API به مجموعه ای از توابع اشاره دارد که بخشی از یک برنامه کاربردی هستند، اما برای استفاده در برنامه های دیگر نیز در دسترس هستند. به عنوان مثال، اکسل، علاوه بر رابط کاربری نهایی خود، دارای مجموعه ای از توابع API اکسل است که می توان از آنها به ویژه هنگام ایجاد برنامه های کاربردی با استفاده از VB استفاده کرد.

بر این اساس، Windows API مجموعه ای از توابع است که بخشی از خود سیستم عامل است و در عین حال برای هر برنامه دیگری قابل دسترسی است. و در این رابطه، تشابه با مجموعه وقفه سیستم BIOS/DOS که در واقع یک API DOS است کاملاً موجه است.

تفاوت این است که ترکیب توابع Windows API از یک طرف در مقایسه با DOS بسیار گسترده تر است، از طرف دیگر بسیاری از ابزارهای مدیریت مستقیم منابع رایانه ای را که در گذشته در دسترس برنامه نویسان بود، در بر نمی گیرد. سیستم عامل علاوه بر این، فراخوانی‌های API ویندوز با استفاده از فراخوانی‌های رویه‌ای معمولی انجام می‌شوند و فراخوانی‌های توابع DOS از طریق یک دستورالعمل پردازشگر ویژه به نام Interrupt انجام می‌شوند.

Win16 API و Win32 API

همانطور که می دانید، تغییر از ویندوز 3.x به ویندوز 95 نشان دهنده گذار از معماری سیستم عامل 16 بیتی به 32 بیتی است. در همان زمان، API 16 بیتی ویندوز (Win16 API) با نسخه جدید 32 بیتی (Win32 API) جایگزین شد. در این مورد، فقط باید در نظر داشته باشید که به جز چند استثنا، مجموعه Win32 API برای خانواده‌های Windows 9x و Windows NT یکسان است.

وقتی با Win API آشنا می‌شوید، متوجه می‌شوید که بسیاری از توابع داخلی چیزی بیش از فراخوانی رویه‌های سیستم مربوطه نیستند، بلکه فقط در قالب نحو یک زبان خاص پیاده‌سازی می‌شوند. با در نظر گرفتن این موضوع، نیاز به استفاده از API با گزینه های زیر تعیین می شود:

توابع API که به طور کامل به عنوان توابع داخلی پیاده سازی می شوند. با این حال، گاهی اوقات در این مورد، تغییر به استفاده از API مفید است، زیرا گاهی اوقات می تواند عملکرد را به طور قابل توجهی بهبود بخشد (به ویژه به دلیل عدم تغییر غیر ضروری پارامترهای تصویب شده).

توابع داخلی تنها یک مورد خاص از تابع API مربوطه را پیاده سازی می کنند. این یک گزینه نسبتا رایج است.

تعداد زیادی از توابع API در نسخه فعلی کامپایلرها اصلاً آنالوگ ندارند. به عنوان مثال، شما نمی توانید یک دایرکتوری را با استفاده از VB حذف کنید - برای این کار باید از تابع DeleteDirectory استفاده کنید.

همچنین باید تاکید کرد که برخی از توابع API (سهم آنها در Win API بسیار کم است) به دلیل تعدادی محدودیت زبان، به عنوان مثال، ناتوانی در کار با آدرس های حافظه، نمی توانند از برنامه ها فراخوانی شوند. اما در برخی موارد، تکنیک های برنامه نویسی غیر پیش پا افتاده می تواند کمک کند (به ویژه در مورد همان آدرس ها).

Win APIوکتابخانه پیوند پویا (DLL)

مجموعه Win API در قالب DLL های پویا پیاده سازی شده است.

در این مورد، منظور ما از DLL نسخه سنتی کتابخانه‌های پویا باینری است که برنامه‌ها را با دسترسی مستقیم به رویه‌های لازم - زیر روال‌ها یا توابع (مثل آنچه هنگام فراخوانی رویه‌ها در یک پروژه اتفاق می‌افتد) فراهم می‌کند. چنین کتابخانه هایی را می توان با استفاده از ابزارهای مختلف ایجاد کرد - VC++، Delphi، Fortran، Assembler.

فایل های کتابخانه پویا معمولا دارای پسوند .DLL هستند، اما این ضروری نیست. برای Win16، پسوند EXE اغلب استفاده می‌شد؛ درایورهای دستگاه خارجی با استفاده از .DRV تعیین می‌شوند.

تعیین تعداد دقیق توابع API ویندوز و فایل های حاوی آنها دشوار است (اما همه آنها در فهرست راهنمای سیستم قرار دارند). در این راستا، بهتر است ترکیب کتابخانه هایی که هسته سیستم عامل را تشکیل می دهند و کتابخانه های اصلی با عملکردهای اضافی کلیدی برجسته شود.

کتابخانه های Win32 API هسته سیستم عامل ویندوز 95/98:

KERNEL32.DLL: توابع سطح پایین برای مدیریت حافظه، وظایف و سایر منابع سیستم.

USER32.DLL: این عمدتاً جایی است که توابع کنترل رابط کاربری در آن قرار دارند.

GDI32.DLL: کتابخانه رابط دستگاه گرافیکی - توابع مختلف برای خروجی به دستگاه های خارجی.

COMDLG32.DLL: توابع مربوط به استفاده از جعبه های محاوره ای عمومی.

کتابخانه های اصلی با توابع افزونه:

COMCTL32.DLL: مجموعه ای از کنترل های اضافی ویندوز، از جمله Tree List و Rich Text.

MAPI32.DLL: توابعی برای کار با ایمیل.

NETAPI32.DLL: کنترل ها و توابع شبکه.

ODBC32.DLL: توابع این کتابخانه برای کار با پایگاه داده های مختلف از طریق پروتکل ODBC مورد نیاز است.

WINMM.DLL: عملیات دسترسی به رسانه سیستم.

این مقاله برای کسانی است که مانند من با برنامه نویسی C++ آشنا هستند و به طور اتفاقی یا میل تصمیم به یادگیری WinAPI دارند.
من می خواهم بلافاصله به شما هشدار دهم:
من ادعا نمی کنم که یک گورو C++ یا WinAPI هستم.
من تازه یاد می‌گیرم و می‌خواهم در اینجا چند مثال و نکاتی را ارائه کنم که یادگیری عملکردها و مکانیسم‌های WinAPI را برای من آسان‌تر می‌کند.

در این مقاله، فرض می‌کنم که شما قبلاً به اندازه کافی با C++ آشنا شده‌اید تا بتوانید کلاس‌ها را ایجاد کنید و عملگرهای مختلف را برای آنها بارگذاری کنید، و قبلاً برخی از مکانیسم‌های خود را در کلاس "پنهان" کرده‌اید.

ایجاد و استفاده از کنسول

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

اگر (AllocConsole())
{



std::ios::sync_with_stdio();
}
برای راحتی، من به شما توصیه می کنم آن را در یک تابع بپیچید. مثلا:
void CreateConsole()
{
if (AllocConsole())
{
int hCrt = _open_osfhandle((طولانی)GetStdHandle(STD_OUTPUT_HANDLE)، 4);
*stdout = *(::_fdopen(hCrt، "w"));
::setvbuf(stdout، NULL، _IONBF، 0);
*stderr = *(::_fdopen(hCrt، "w"));
::setvbuf(stderr، NULL، _IONBF، 0);
std::ios::sync_with_stdio();
}

کنسول فراخوانی شده فقط در حالت خروجی کار می کند و مانند برنامه های کنسول کار می کند. اطلاعات خروجی طبق معمول - cout/wcout.
برای اینکه این کد کار کند، باید فایل های زیر را در پروژه قرار دهید:
#عبارتند از
#شامل #شامل
و فضای نام std را در فضای نام جهانی قرار دهید:
با استفاده از namespace std.
البته، اگر نمی‌خواهید این کار را انجام دهید، فقط std:: را به همه موجوداتی که در آن هستند اضافه کنید.

ارث بری اشیا برای خروجی و حساب. عملیات

هنگام ایجاد و مطالعه خود "ویندوزها"، همیشه نیاز داشتم مقداری را به کنسول خروجی بدهم.
مثلا:
اندازه ناحیه مشتری پنجره را با استفاده از تابع GetClientRect دریافت می کنید، جایی که آدرس یک شی ساختار RECT به عنوان پارامتر برای پر کردن این شی با داده ارسال می شود. اگر نیاز به دانستن اندازه ناحیه مشتری دریافتی دارید، می توانید آن را به سادگی در کنسول از قبل متصل شده نمایش دهید

کوت<

اما انجام هر بار این کار (به خصوص اگر اغلب مجبور به انجام چنین کاری هستید) بسیار ناخوشایند است.
اینجاست که ارث به کمک ما می آید.
کلاسی ایجاد کنید که آشکارا از ساختار RECT ارث می برد و عملگر خروجی را بارگذاری می کند<< так, как вам угодно.
مثلا:

کلاس newrect: public RECT
{
عمومی:
دوست ostream& operator<<(ostream &strm,newrect &rect)
{
strm<<"Prtint RECT object:\n";
strm<بازگشت strm;
}
};

اکنون فقط شی را با استفاده از cout/wcout خروجی بگیرید:

کوت<

و همه چیز به شکل مناسبی که نیاز دارید برای شما نمایش داده می شود.
شما می توانید همین کار را با هر اپراتور مورد نیاز خود انجام دهید.
برای مثال، اگر نیاز به مقایسه یا تخصیص ساختارها دارید (به عنوان مثال، RECT یا POINT)، به ترتیب عملگر اضافه بار ==() و عملگر=().
اگر می خواهید کمتر از عملگر را پیاده سازی کنید< что бы быстро сравнивать размеры окна и т.д. перегрузите operator<().
فکر می‌کنم تقریباً با هر ساختاری می‌توانید این کار را انجام دهید و مهم‌ترین چیز این است که همه توابعی که با یک شی ساختار معمولی RECT کار می‌کنند به همان خوبی با وارث آن کار می‌کنند.
و همچنین توصیه می کنم این همه زیبایی را در یک فایل همراه جداگانه قرار دهید و در صورت لزوم از آن استفاده کنید.

کلاس تو

در مورد دیگران نمی دانم، اما از آنجایی که من کاملاً سبز هستم، تصمیم گرفتم برای هر تابعی که مطالعه می کردم یا برای هر فصل / زیرفصل یک کتاب، یک پروژه جدید ایجاد کنم تا همه چیز در قفسه ها باشد و من می توانم هر لحظه برگردم و خاطره ام را از نکات ضروری تازه کنم.
از آنجایی که در WinAPI، حتی برای ایجاد ساده ترین پنجره، باید یک ساختار کلاس را پر کنید، آن را ثبت کنید و یک رویه پنجره بی اهمیت بنویسید، پس از پروژه سوم یا چهارم به یاد آوردم که هنوز در C++ می نویسم.
در نهایت همه چیز را در یک کلاس درس ساده پنهان کردم. دسته پنجره، نام آن، نام کلاس، آدرس رویه پنجره، کلاس پنجره (WNDCLASS) همه در بخش خصوصی کلاس پنهان هستند.
برای به دست آوردن آنها کافی است روش های ساده Get را شرح دهید، به عنوان مثال:
HWND GetHWND()
LPCTSTR GetClsName() و غیره
پر کردن و ثبت کلاس پنجره، ایجاد خود پنجره و نمایش آن در سازنده انجام می شود.
برای راحتی، می‌توانید سازنده را اضافه بارگذاری کنید، و پر کردن و ثبت کلاس پنجره را در یک تابع کلاس خصوصی جداگانه پنهان کنید و آن را در هر یک از سازنده‌ها فراخوانی کنید. راحتی بارگذاری بیش از حد این است که گاهی اوقات من نیاز به ایجاد یک پنجره بسیار ساده دارم و سازنده را با دو پارامتر فراخوانی می کنم - نام پنجره و راهنمای برنامه.
در مواقع دیگر من نیاز به ایجاد یک پنجره با اندازه خاص دارم، نه با رویه پنجره پیش فرض و با سبک خاص دیگر - سازنده را با پارامترهای همراه فراخوانی می کنم.
من این کلاس را در یک فایل include جداگانه تعریف کرده ام که در پوشه include IDE قرار دارد.
یک قالب برای این کلاس:
کلاس BaseWindow
{
WNDCLASSEX_wcex;
TCHAR_className;
TCHAR_windowName;
HWND_hwnd;
bool _WindowCreation();
عمومی:
BaseWindow(LPCTSTR نام پنجره، HINSTANCE hInstance، سبک DWORD، UINT x، UINT y، ارتفاع UINT، عرض UINT).
BaseWindow(LPCTSTR windowName,HINSTANCE hInstance)؛
const HWND GetHWND()const(بازگشت HWND;)
LPCTSTR GetWndName()const(return _windowName;)
};

هنگامی که به طور کامل فکر کنید و چنین کلاسی را بنویسید، زندگی خود را آسان تر خواهید کرد و زمان بیشتری را صرف یادگیری و تقویت مهارت های خود خواهید کرد تا اینکه هر بار همان مطلب را بنویسید. علاوه بر این، فکر می کنم ایجاد چنین کلاسی خودتان و تکمیل آن در صورت لزوم بسیار مفید است.

P.S.

همه چیزهایی که شرح داده شد برای:
پلتفرم - ویندوز 7 32 بیتی
IDE - Visual Studio 2010
شاید این نکات برای برخی باعث خنده و کنایه شود، اما با این حال، همه ما زمانی در کاری مبتدی/کارآموز/جوان بودیم.
لطفا با این پست با درک رفتار کنید. البته انتقاد سازنده قابل استقبال است.

سلب مسئولیت

به نظر می رسد که WinAPI در حال تبدیل شدن به چیزی از گذشته است. مدت زیادی است که تعداد زیادی فریمورک متقابل پلتفرم وجود دارد، ویندوز فقط روی دسکتاپ نیست و خود مایکروسافت از برنامه هایی که از این هیولا در فروشگاه خود استفاده می کنند استقبال نمی کند. علاوه بر این، هزاران مقاله در مورد نحوه ایجاد ویندوز با استفاده از WinAPI، نه تنها در اینجا، بلکه در سراسر اینترنت، در سطحی از پیش دبستانی و بالاتر وجود دارد. کل این فرآیند دیگر حتی به اتم ها تجزیه نمی شود، بلکه به ذرات زیراتمی تبدیل می شود. چه چیزی می تواند ساده تر و واضح تر باشد؟ و من اینجا هستم...

اما همه چیز به این سادگی که به نظر می رسد نیست.

چرا در مورد WinAPI در حال حاضر؟

در یک نقطه، در حالی که در حال مطالعه بر روی یکی از بازی ها در یک بازی بسیار خوب، فکر کردم: به نظر می رسد این امولسیون خوبی است، اما دیباگر چیز ساده ای مانند پیمایش از طریق دکمه های صفحه کلید که در دسترس است ندارد. در هر دیباگر معمولی

من در مورد چه چیزی صحبت می کنم؟ و این هم یک قطعه کد:

Case WM_KEYDOWN: MessageBox(hwndDlg,"Die!","من مردم!",MB_YESNO|MB_ICONINFORMATION)؛ break;
بنابراین، نویسندگان می خواستند پشتیبانی از صفحه کلید را اضافه کنند، اما واقعیت خشن اعماق معماری جعبه های محاوره ای در ویندوز، چنین ابتکاری را به شدت سرکوب کرد. آیا کسی که از شبیه ساز و دیباگر در آن استفاده کرده باشد این پیام را دیده است؟
مشکل چیست؟

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

و، بازگشت به سوال اصلی در مورد WinAPI: بسیاری از پروژه های محبوب، و نه چندان محبوب، در حال حاضر از آن استفاده می کنند، زیرا بسیاری از کارها را نمی‌توان بهتر از استفاده از یک API خالص انجام داد (در اینجا می‌توانید بی‌پایان قیاس‌هایی مانند مقایسه زبان‌های سطح بالا و زبان اسمبلی ارائه دهید، اما اکنون این موضوع نیست). و چه کسی می داند چرا؟ آنها فقط از آن استفاده می کنند و تمام.

در مورد مشکل

جعبه‌های گفتگو کار با رابط کاربری گرافیکی را آسان‌تر می‌کنند و در عین حال فرصت انجام کاری را از ما سلب می‌کنند. به عنوان مثال، پیام‌های WM_KEYDOWN/WM_KEYUP که به رویه پنجره می‌آیند، در اعماق DefDlgProc «خورده» می‌شوند و مواردی مانند پیمایش Tab، پردازش کلیدهای Esc، Enter و غیره را انجام می‌دهند. علاوه بر این، دیالوگ ها نیازی به ایجاد دستی ندارند: به هر حال، ترسیم دکمه ها، لیست ها در ویرایشگر منابع، ساده تر است، CreateDialog/DialogBox را در WinMain فراخوانی کنید و کارتان تمام شد.

دور زدن چنین مشکلات جزئی آسان است. حداقل دو راه کاملا قانونی وجود دارد:

  1. کلاس خود را از طریق RegisterClassEx ایجاد کنید و WM_KEYDOWN را در رویه پردازش کلاس بگیرید و آن را به خود رویه پردازش گفتگو هدایت کنید. بله بله! شما می توانید با کلاس خود دیالوگ ایجاد کنید، و ویرایشگر داخلی VS حتی به شما امکان می دهد نام کلاس را برای گفتگو مشخص کنید. اما چه کسی این را می داند و از آن استفاده می کند؟
    نقطه ضعف واضح است: شما باید یک کلاس دیگر ثبت نام کنید، 1 روش CALLBACK دیگر داشته باشید، که ماهیت آن فقط پخش چند پیام خواهد بود. علاوه بر این، ما نمی دانیم جایی کهآنها را پخش کنید، و شما باید با عصا نرده بکشید.
  2. از مکانیزم شتاب دهنده داخلی استفاده کنید. و ما حتی نیازی به تغییر کد رویه گفتگو نداریم! خوب، شاید یک خط به سوئیچ/حوزه اضافه کنید، اما در زیر در مورد آن بیشتر توضیح دهید.

آموزش ها؟

از گفتن این حرف نمی ترسم همهآموزش ایجاد ویندوز از طریق WinAPI با چنین کد ساده ای شروع می شود و آن را به عنوان یک "حلقه پردازش پیام" نشان می دهد (من جزئیات آماده سازی کلاس پنجره و سایر اتصالات را حذف می کنم):

در حالی که (GetMessage(&msg, nullptr, 0, 0)) ( TranslateMessage(&msg); DispatchMessage(&msg); )
اینجا واقعا ساده است:

  1. GetMessage() پیام بعدی را از صف می گیرد و لحظه کلیدی: در صورت خالی بودن صف، رشته را مسدود می کند.
  2. TranslateMessage() از WM_KEYDOWN/WM_KEYUP پیام‌های WM_CHAR/WM_SYSCHAR را تولید می‌کند (اگر کسی بخواهد ویرایشگر متن خود را بسازد به آن‌ها نیاز است).
  3. DispatchMessage() پیامی را به رویه پنجره ارسال می کند (در صورت وجود).
بیایید با این واقعیت شروع کنیم که استفاده از این کد خطرناک است و در اینجا دلیل آن است. لطفا به پاورقی توجه کنید:
از آنجایی که مقدار بازگشتی می تواند غیر صفر، صفر یا -1 باشد، از کدی مانند زیر اجتناب کنید:
در حالی که (GetMessage(lpMsg، hWnd، 0، 0)) ...
و در زیر نمونه ای از یک حلقه صحیح آورده شده است.

شایان ذکر است که در قالب های VS برای برنامه های Win32 دقیقاً چنین چیزی نوشته شده است اشتباهچرخه و این خیلی ناراحت کننده است. از این گذشته، تعداد کمی از مردم به آنچه خود نویسندگان انجام داده اند می پردازند، زیرا این پیشینی درست است. و کد نادرست به همراه اشکالاتی که یافتن آنها بسیار دشوار است، چند برابر می شود.

پس از این قطعه کد، به طور معمول، داستانی در مورد شتاب دهنده ها دنبال می شود و چند خط جدید اضافه می شود (با در نظر گرفتن نکته در MSDN، پیشنهاد می کنم بلافاصله چرخه صحیح را بنویسید):

HACCEL hAccel = Load Accelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bret = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(msg.hwnd, hAccel, &msg)) ( TranslateMessage(&msg) DispatchMessage(&msg) ;))
این گزینه ای است که من اغلب دیده ام. و او ( تادام) دوباره اشتباه است!

ابتدا در مورد آنچه تغییر کرده است (سپس در مورد مشکلات این کد):

در خط اول، جدولی از کلیدها از منابع بارگیری می شود؛ با فشار دادن، یک پیام WM_COMMAND با شناسه فرمان مربوطه ایجاد می شود.

در واقع، TranslateAccelerator این کار را انجام می دهد: اگر WM_KEYDOWN و کد کلید موجود در این لیست را ببیند، سپس (دوباره نکته کلیدی) یک پیام WM_COMMAND (MAKEWPARAM(id, 1)) تولید می کند و آن را به مورد مربوطه ارسال می کند. دسته پنجره مشخص شده در آرگومان اول، رویه پردازش.

از جمله آخر فکر کنم مشخص شد مشکل کد قبلی چی بوده.
اجازه دهید توضیح بدهم: GetMessage پیام‌ها را برای همه اشیاء از نوع "پنجره" (که شامل کودکان: دکمه‌ها، لیست‌ها و غیره می‌شود) می‌گیرد و TranslateAccelerator WM_COMMAND تولید شده را به کجا ارسال می‌کند؟ صحیح: بازگشت به دکمه/لیست و غیره. اما ما WM_COMMAND را در رویه خود پردازش می کنیم، به این معنی که ما علاقه مند به دریافت آن در آن هستیم.

واضح است که TranslateAccelerator باید برای پنجره ایجاد شده ما فراخوانی شود:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = Load Accelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bret = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) ( TranslateMessage(&msg; DispatchMessage(&msg);) )
و اکنون همه چیز خوب و شگفت انگیز به نظر می رسد: ما همه چیز را با جزئیات تجزیه و تحلیل کرده ایم و همه چیز باید به خوبی کار کند.

و باز هم نه :-) تا زمانی که دقیقاً یک پنجره داشته باشیم - این پنجره به درستی کار می کند. به محض ظاهر شدن یک پنجره جدید بدون حالت (دیالوگ)، تمام کلیدهایی که در آن فشار داده می شوند به WM_COMMAND ترجمه می شوند و به کجا ارسال می شوند؟ و دوباره به درستی: در پنجره اصلی ما.

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

IsDialogMessage

با قضاوت بر اساس نام این تابع، ممکن است فکر کنید که به دلایلی تعیین می کند که آیا یک پیام داده شده به دیالوگ تعلق دارد یا خیر. اما اول از همه، چرا باید این را بدانیم؟ و دوم اینکه با این اطلاعات باید چیکار کنیم؟

در واقع، کمی بیشتر از چیزی که از نامش پیداست، انجام می دهد. برای مثال:

  • با استفاده از دکمه‌های Tab/Shift+Tab/بالا/پایین/راست/چپ در میان کنترل‌های کودک حرکت می‌کند. به علاوه یک چیز دیگر، اما این برای ما کافی است
  • با فشردن ESC WM_COMMAND(IDCANCEL) تولید می شود
  • با فشردن Enter، WM_COMMAND(IDOK) تولید می کند یا به طور پیش فرض روی دکمه فعلی کلیک می کند.
  • دکمه های پیش فرض را تغییر می دهد (قاب چنین دکمه هایی کمی روشن تر از بقیه است)
  • خوب، و چیزهای مختلف دیگری که کار با دیالوگ را برای کاربر آسان تر می کند
چه چیزی به ما می دهد؟ اول، لازم نیست به پیمایش درون پنجره فکر کنیم. به هر حال هر کاری برای ما انجام خواهند داد. به هر حال، ناوبری Tab را می توان با افزودن سبک WS_EX_CONTROLPARENT به پنجره اصلی ما انجام داد، اما این کار ناشیانه است و چندان کاربردی نیست..

ثانیاً، زندگی ما را در مورد سایر نکات ذکر شده در لیست (و حتی کمی بیشتر) آسان تر می کند.

به طور کلی در جایی در اعماق ویندوز برای اطمینان از کار استفاده می شود جعبه های محاوره ای مودال، و به برنامه نویسان داده می شود تا آن را برای گفتگوهای بدون حالت فراخوانی کنند. با این حال، ما می توانیم از آن در هر جایی استفاده کنیم:

اگرچه تابع IsDialogMessage برای جعبه‌های محاوره‌ای بدون حالت در نظر گرفته شده است، اما می‌توانید آن را با هر پنجره‌ای که حاوی کنترل‌ها است استفاده کنید و پنجره‌ها را قادر می‌سازد تا همان صفحه‌کلیدی را که در یک کادر محاوره‌ای استفاده می‌شود، ارائه دهند.
آن ها حالا اگر حلقه را به این صورت طراحی کنیم:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = Load Accelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bret = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) ( if (!IsDialogMessage(hMainWnd, &msg) ( TranslateMessage(&msg); DispatchMessage(&msg); ) )
سپس پنجره ما ناوبری خواهد داشت، مانند گفتگوی بومی ویندوز. اما الان دو تا عیب داریم:

  1. این کد نیز فقط به خوبی کار خواهد کرد با یک پنجره (غیر مدال).;
  2. با دریافت تمام مزایای ناوبری گفتگو، ما از جذابیت های پیام های WM_KEYDOWN/WM_KEYUP (فقط برای خود پنجره و نه برای کنترل های کودک) محروم هستیم.
و در این مرحله، به طور کلی، تمام آموزش ها به پایان می رسد و سوالات شروع می شود: چگونه رویدادهای صفحه کلید را در یک گفتگوی استاندارد winapi مدیریت کنیم؟
این اولین لینک در گوگل است، اما باور کنید: هزاران مورد وجود دارد. در مورد راه حل های پیشنهادی (بهترین آنها ایجاد کلاس گفتگوی خود است، که در بالا در مورد آن نوشتم، قبل از کلاس بندی و RegisterHotKey. در جایی حتی "بهترین" راه را دیدم: از Windows Hooks استفاده کنید).

وقت آن است که در مورد آنچه در آموزش ها و پاسخ ها نیست صحبت کنیم.

به عنوان یک قاعده (به عنوان یک قاعده! ​​اگر کسی بیشتر می خواهد، پس می توانید کلاس خود را برای دیالوگ ها ثبت کنید و مانند این کار کنید. و اگر کسی به این موضوع علاقه دارد، می توانم این را به مقاله اضافه کنم) وقتی می خواهد WM_KEYDOWN می خواهد یک کلیک روی کلیدی را پردازش کنید که بدون توجه به کنترل انتخاب شده در پنجره عملکردی را انجام می دهد - i.e. برخی از عملکردهای کلی برای همه این گفتگوی خاص. و اگر چنین است، پس چرا از فرصت های غنی که خود WinAPI به ما ارائه می دهد استفاده نکنید: Translate Accelerator.

همه جا استفاده می شود دقیقا یکیجدول شتاب دهنده و فقط برای پنجره اصلی. خوب، واقعاً: یک چرخه حلقه GetMessage وجود دارد، به این معنی که یک جدول وجود دارد. دیگر کجا باید بروند؟

در واقع، GetMessage-loops می تواند باشد تو در تو. بیایید دوباره به توضیحات PostQuitMessage نگاه کنیم:

تابع PostQuitMessage یک پیام WM_QUIT را به صف پیام رشته ارسال می کند و فوراً برمی گردد؛ این تابع به سادگی به سیستم نشان می دهد که موضوع در زمانی در آینده درخواست خروج از آن را دارد.
و GetMessage:
اگر تابع پیام WM_QUIT را بازیابی کند، مقدار بازگشتی صفر است.
بنابراین، اگر در رویه پنجره، PostQuitMessage را فراخوانی کنیم، حلقه GetMessage خارج می شود. چه مفهومی داره؟

ما می توانیم برای هر کس غیر معین ویندوز در برنامه ما حلقه مشابه خود را ایجاد می کند. در این مورد، DialogBoxParam برای ما مناسب نیست، زیرا چرخه خودش را می‌چرخاند و ما نمی‌توانیم روی آن تأثیر بگذاریم. با این حال، اگر یک گفتگو از طریق CreateDialogBoxParam یا یک پنجره از طریق CreateWindow ایجاد کنیم، می‌توانیم یک حلقه دیگر بچرخانیم. در همان زمان، در هر کسدر چنین پنجره و دیالوگی، باید PostQuitMessage را فراخوانی کنیم:

HWND hMainWnd = CreateWindow(...); HACCEL hAccel = Load Accelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR)); BOOL bret = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!TranslateAccelerator(hMainWnd, hAccel, &msg)) ( if (!IsDialogMessage(hMainWnd, &msg) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) // .... LRESULT CALLBACK WndProc(HWND hwnd، UINT umsg، WPARAM wparam، LPARAM lparam) (سوئیچ(umsg) (مورد WM_MYWNDATEHD hInstance، MAKEINTRESOURCE(IDD_MYDIALOG)، hwnd، MyDialogBoxProc)؛ HACCEL hAccel = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCELERATOR_FOR_MY_DIALOG))؛ BOOL fRwnStable=drownEnd0; EnableWindow(hwnd, F ALSE)؛ // غیر فعال کردن پنجره والد از آنجایی که پنجره محاوره‌ای مودال است در حالی که (bRet = GetMessage(&msg, nullptr, 0, 0)) (اگر (-1 == bRet) شکسته شود؛ اگر (!TranslateAccelerator(hDlg, hAccel, &msg)) (اگر (!IsDialogMessage( hDlg , &msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) ) EnableWindow(hwnd, fSavedEnabledState)؛ // فعال کردن پنجره والد. دیالوگ بسته شد شکست؛ ) ) ) INT_PTRgProCALLBACKHU,My WPARAM wparam، LPARAM lparam) ( switch(umsg) ( case WM_CLOSE: ( // EndDialog(hwnd, 0); --این کار را نکن! // EndDialog فقط برای دیالوگ های Modal معتبر است که با DialogBox(Param) DestroyWindow(hwnd) ایجاد شده است. زنگ تفريح؛ ) case WM_DESTROY: ( PostQuitMessage(0); break; ) // .... ) return 0; )
لطفا توجه داشته باشید: اکنون برای هر پنجره جدید در برنامه ما می توانیم به پردازش اضافه کنیم خودجدول شتاب دهنده WM_QUIT GetMessage را از حلقه برای گفتگو ربوده و حلقه بیرونی حتی آن را نمی بیند. چرا این اتفاق می افتد؟

واقعیت این است که حلقه بیرونی در تماس با DispatchMessage، که رویه ما را فراخوانی می‌کند، متوقف شد. داخلیحلقه GetMessage با همان DispatchMessage. یک تماس تو در تو کلاسیک (در این مورد DispatchMessage). بنابراین، حلقه بیرونی WM_QUIT را دریافت نمی کند و در این مرحله خاتمه نمی یابد. همه چیز به آرامی کار خواهد کرد.

اما این هم معایبی دارد:
هر حلقه از این قبیل پیام ها را پردازش می کند فقط برای پنجره "شما".. ما اینجا از دیگران خبر نداریم. این بدان معنی است که اگر چرخه دیگری در جایی ظاهر شود، تمام پنجره های دیگر پردازش لازم از پیام های خود را توسط جفت TranslateAccelerator/IsDialogMessage دریافت نمی کنند.

خوب، وقت آن است که همه این نظرات را در نظر بگیریم و در نهایت پردازش صحیح همه پیام ها را از تمام پنجره های برنامه خود بنویسیم. من می خواهم توجه داشته باشم که در زیر ما مورد را برای یک موضوع در نظر می گیریم. زیرا هر رشته دارای صف پیام های خاص خود است، سپس برای هر رشته باید ساختارهای خود را ایجاد کنید. این کار با تغییرات بسیار کم اهمیت در کد انجام می شود.

ما آن را به زیبایی انجام می دهیم

زیرا فرمول صحیح مسئله نیمی از راه حل است، سپس ابتدا باید این مشکل را به درستی مطرح کنید.

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

ثانیاً، اگر جدول شتاب دهنده برای پنجره مشخص نشده باشد، چیزی برای پخش وجود ندارد؛ ما به سادگی پیام را به IsDialogMessage ارسال می کنیم.

بیایید یک std::map ساده بسازیم که توصیفگر پنجره را به توصیفگر جدول شتاب دهنده نگاشت می کند. مثل این:

Std:: map l_mAccelTable;
و همانطور که ویندوز ایجاد می شود، پنجره های جدیدی را با یک توصیفگر به جدول مورد علاقه خود (یا صفر، اگر چنین پردازشی لازم نیست) به آن اضافه می کنیم.

BOOL AddAccelerators(HWND hWnd, HACCEL hAccel) ( if (IsWindow(hWnd)) ( l_mAccelTable[hWnd ] = hAccel; return TRUE; ) return FALSE; ) BOOL AddAccelerators(HWND AddAccelerators(HWNDAccelerator(hWnd,hWnd) نویسندگان ( hInstance, accel)); ) BOOL AddAccelerators(HWND hWnd, int accel) ( return AddAccelerators(hWnd, MAKEINTRESOURCE(accel)); ) BOOL AddAccelerators(HWND hWnd) ( بازگشت AddAccelerators(hWnd, HAULLCCEL);
خب پس از بستن پنجره، آن را حذف کنید. مثل این:

Void DelAccel(HWND hWnd) ( std::map ::iterator me = l_mAccelTable.find(hWnd); if (me != l_mAccelTable.end()) ( if (me->second) (DestroyAcceleratorTable(me->second); ) l_mAccelTable.erase(me); ) )
اکنون، همانطور که یک گفتگو/پنجره جدید ایجاد می کنیم، با AddAccelerators (hNewDialog، IDR_MY_ACCEL_TABLE) تماس بگیرید. نحوه بستن: DelAccel (hNewDialog).

ما لیستی از ویندوزها با توصیفگرهای لازم داریم. بیایید حلقه اصلی پردازش پیام خود را کمی تغییر دهیم:

// ... HWND hMainWnd = CreateWindow(...); Add Accelerators (hMainWnd، IDR_ACCELERATOR)؛ BOOL bret = 0; while (bRet = GetMessage(&msg, nullptr, 0, 0)) ( if (-1 == bRet) break; if (!HandleAccelArray(GetActiveWindow(), msg)) ( TranslateMessage(&msg); DispatchMessage(&msg); ) ) // ...
خیلی بهتر! در HandleAccelArray چیست و چرا GetActiveWindow() در آنجا وجود دارد؟

کمی تئوری:

دو تابع وجود دارد که یک دسته را به پنجره فعال باز می گرداند: GetForegroundWindow و GetActiveWindow. تفاوت بین اول و دوم کاملاً به وضوح در توضیح دوم توضیح داده شده است:

مقدار بازگشتی، دستگیره پنجره فعال متصل به صف پیام رشته تماس است. در غیر این صورت، مقدار بازگشتی NULL است.
اگر اولی یک دسته را به هر پنجره ای در سیستم برگرداند، دومی فقط برمی گردد که از صف پیام رشته ما استفاده می کند. زیرا ما فقط به پنجره های رشته خود علاقه مند هستیم (و بنابراین آنهایی که در صف پیام ما قرار می گیرند)، بنابراین دومی را می گیریم.

بنابراین، HandleAccelArray، با هدایت توصیفگر ارسال شده به آن برای پنجره فعال، همین پنجره را در نقشه ما جستجو می کند، و اگر وجود دارد، این پیام را برای پخش به TranslateAccelerator می فرستد، و سپس (اگر اولین مورد آن را ندید سمت راست) به IsDialogMessage. اگر دومی پیام را پردازش نکرد، ما FALSE را برمی‌گردانیم تا از رویه استاندارد TranslateMessage/DispatchMessage عبور کنیم.

به نظر می رسد که:

BOOL HandleAccelWindow(std::map ::const_iterator mh، MSG و msg) (const HWND & hWnd = mh-> first; const HACCEL & hAccel = mh->second; if (!TranslateAccelerator(hWnd، hAccel، &msg)) (// پیام elerator برای TranslateAcc نیست. آن را با IsDialogMessage امتحان کنید اگر (!IsDialogMessage(hWnd, &msg)) ( // بنابراین، موارد پیش‌فرض را FALSE برگردانید؛ ) ) // خوب، پیام ترجمه شده است. بگویید به حلقه پیام، برای دریافت پیام بعدی TRUE; ) BOOL HandleAccelArray (HWND hActive، MSG & msg) (اگر (!hActive) FALSE را برگرداند؛ // پنجره فعالی وجود ندارد. کاری برای انجام دادن std::map وجود ندارد ::const_iterator mh = l_mAccelTable.find(hActive); if (mh != l_mAccelTable.end()) ( // متوجه شدم! سعی کنید این پیام را برای پنجره فعال ترجمه کنید return HandleAccelWindow(mh, msg); ) return FALSE; )
اکنون هر پنجره فرزند حق دارد جدول شتاب دهنده مورد علاقه خود را اضافه کند و با آرامش WM_COMMAND را با کد لازم بگیرد و پردازش کند.

و یک خط دیگر در کد کنترل کننده WM_COMMAND چطور؟

توضیحات در TranslateAccelerator به شرح زیر است:
برای متمایز کردن پیامی که این تابع ارسال می کند از پیام های ارسال شده توسط منوها یا کنترل ها، کلمه مرتبه بالای پارامتر wParam پیام WM_COMMAND یا WM_SYSCOMMAND حاوی مقدار 1 است.
معمولاً کد پردازش WM_COMMAND به شکل زیر است:

سوئیچ (HIWORD(wParam)) (مورد BN_CLICKED: // دستور از دکمه‌ها/منوها (سوئیچ(LOWORD(wParam)) (مورد IDC_BUTTON1: DoButton1Stuff(); break؛ مورد IDC_BUTTON2: DoButton2Stuff(); break; // ... ) زنگ تفريح؛ ) )
حالا می توانید اینگونه بنویسید:

سوئیچ(HIWORD(wParam)) ( مورد 1: // مورد شتاب دهنده BN_CLICKED: // فرمان از دکمه ها/منوها ( سوئیچ(LOWORD(wParam)) (مورد IDC_BUTTON1: DoButton1Stuff(); شکست؛ مورد IDC_BUTTON2: DoButton break2Stuff(); ؛ // ... ) زنگ تفريح؛ ) )
و اکنون، بازگشت به همان fceux، اضافه کردن فقط یک خطدر کد پردازش دستورات از دکمه ها، آنچه را که می خواهیم دریافت می کنیم: دیباگر را از صفحه کلید کنترل کنید. کافی است یک بسته بندی کوچک در اطراف حلقه پیام اصلی و یک جدول شتاب دهنده جدید با موارد منطبق VK_KEY => IDC_DEBUGGER_BUTTON اضافه کنید.

P.S.: تعداد کمی از مردم می دانند، اما شما می توانید جدول شتاب دهنده خود را ایجاد کنید و اکنون آن را در پرواز اعمال کنید.

P.P.S.: چون DialogBox/DialogBoxParam حلقه خود را می‌چرخاند، سپس هنگام فراخوانی گفتگو از طریق آنها، شتاب‌دهنده‌ها کار نمی‌کنند و حلقه (یا حلقه‌های) ما "بیکار" خواهند بود.

P.P.P.S.: پس از فراخوانی HandleAccelWindow، نقشه l_mAccelTable ممکن است تغییر کند، زیرا TranslateAccelerator یا IsDialogMessage با DispatchMessage تماس می گیرد و در آنجا ممکن است با AddAccelerators یا DelAccel در گرداننده های خود مواجه شویم! بنابراین بهتر است بعد از این عملکرد به آن دست نزنید.

می توانید کد را احساس کنید. کد تولید شده از الگوی استاندارد MS VS 2017 به عنوان پایه در نظر گرفته شد.

برچسب ها: اضافه کردن برچسب