PDA

View Full Version : سوال: ارتباط با کنترل های برنامه های ویندوزی



A_Salimi
پنج شنبه 04 مهر 1387, 15:32 عصر
سوالی هست که مدت نسبتا زیادی ذهن من رو به خودش معطوف کرده و اون این که آیا میشه برنامه ای نوشت که در آن برای مثال فشار دادن یک دکمه فرضا معادل باشد با فشار دادن دکمه play در jetaudio و یا sign in در yahoo messenger و .....

یعنی سوال من این است که آیا میتوان کنترل قسمت های دیگر برنامه های دیگر را در دست گرفت ؟
اگر این کار حالا به هر طریقی ، (احتمالا پیدا کردن هندل ودستگیره ) ممکن است آیا ممکن است که طریقه آن را بیان نمایید و در صورت امکان کدی را قرار دهید ؟

با تشکر

amir_civil
پنج شنبه 04 مهر 1387, 17:15 عصر
LRESULT SendMessage( HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);

BOOL PostMessage( HWND hWnd,
UINT Msg,
WPARAM wParam,
LPARAM lParam
);

HWND FindWindow( LPCTSTR lpClassName,
LPCTSTR lpWindowName
);

A_Salimi
پنج شنبه 04 مهر 1387, 18:56 عصر
من کاربرد این سه تابع رو در msdn نگاه کردم :

تابع SendMessage یک پیغام مشخص را به یک پنجره و یا ویندوز ارسال میکند .و رویه پنجره را برای یک پنجره مشخص فراخوانی میکند وتا هنگامی که رویه پنجره پیغامی را پردازش نکند چیزی باز نمیگرداند.

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

FindWidow هم که اگه اشتباه نکنم برای پیدا کردن هندل یا همون دستگیره پنجره ای استفاده میشه که عنوان (caption) اون رو بلدیم.

حالا ممکنه شما یا یکی از دوستان توضیح جامعتری هم در مورد استفاده و ترتیب استفاده و هم مقدار دهی کردن این توابع بدید ؟ و اگر هم راه ساده تری وجود داره بیان کنید ؟

amir_civil
پنج شنبه 04 مهر 1387, 23:38 عصر
با PostMessage میتونی یه کلید رو فشار بدی در صورتی که هندل اون رو از FindWindow گرفته باشی

A_Salimi
شنبه 06 مهر 1387, 15:00 عصر
البته من فکر میکنم تابع FindWindow نتونه هندل دکمه رو به ما بده چون که اون دکمه یک پنجره فرزند محسوب میشه و در msdn اومده که :


This function does not search child windows


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



HWND hwnd=::FindWindowW(NULL,_T("Yahoo! Messenger with Voice (BETA)"));


پس مجبور شدم از تابع FindWindowEx استفاده کنم :

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

لطفی میکنید که بگید چطور میتونم hwndParent و hwndChildAfter وlpszClass را مقدار دهی کنم ؟و همچنین در تابع PostMessage مقدار Msg که پیغامی هست که باید بفرستیم ،رو چطور باید مقدار دهی کنم ؟در مورد پیغام های اضافی wParam و lParam چطور ؟
با تشکر

Nima_NF
شنبه 06 مهر 1387, 20:54 عصر
دریافت هندل همه کنترل های برنامه به همین سادگی نیست و در اکثر موارد به دلیل طراحی سفارشی برنامه با Skin غیر ممکن است و یا قابل تشخیص نیستند. (همیشه برای تجریه و تحلیل از برنامه ++Spy همراه VS استفاده کنید)
به همین منظور شرکت ها SDK آن برنامه را ارائه می کنند تا برایشان plug-in بنویسید و مثلا همین کاری که شما می خواهید انجام دهید را با یک remote control انجام شود.

JetAudio ، yahoo messenger ، windows media player و امثال آن SDK را برای انجام چنین کارهایی عرضه کرده اند.

در هر صورت توضیحاتی در مورد سوالات شما:

-در FindWindowEx پارامتر hwndParent همان صفحه یا دیالوگ یا toolbar ای هست که دکمه بر روی آن قرار دارد.
-پارامتر hwndChildAfter هم دفعه اول NULL قرار دهید و دفعات بعد، مقدار برگشتی قبلی از FindWindowEx
lpszClass را یا NULL قرار دهید یا با ++SPY بدست آورید. در صورت استفاده در هر حال باید از قبل بدانید.

مثلا برای PostMessage یا بهتر SendMessage به شکل زیر( که همانطور که توضیح دادم انتظار نداشته باشید حتما جواب دهد):



PostMessage (hwnd, WM_KEYDOWN, VK_SPACE, 1) ;

یا به جای WM_KEYDOWN با توجه به نوع کنترل هر پیام دیگری که فکر می کنید مناسب آن کنترل است.

A_Salimi
شنبه 11 آبان 1387, 22:50 عصر
من تصمیم گرفتم این مسله رو به نحو دیگری انجام بدم اما نتایج عجیبی گرفتم :

برای اینکه در یک دیالگ بتونم تمام هندل های فرزند رو دریافت کنم به این صورت عمل کردم :

هندل پنجره اصلی رو دریافت میکردم(به همون صورتی که در پست قبل گفتم) :



HWND hw=::FindWindowW(NULL,_T("Yahoo! Messenger with Voice (BETA)"));
(
این قسمت را در سازنده گذاشتم و hw را متغیر عمومی تعریف کردم)
زمانی که با spy++ روی پنجره یاهو مکان نما رو حرکت میدادم وقتی روی دکمه ها توقف میکردم نام کلاس اونها Button بود. پس من تصمیم گرفتم که اسم کلاسها رو هم دریافت کنم تا به کلاس Button برسم:



LPWSTR lpStr = new TCHAR[25];
CString myclassname(lpStr);

::GetClassName(hw,lpStr,myclassname.GetLength());

براي اينکه از متد بيخود ()GetBuffer استفاده نکنم از دو خط اول استفاده کردم .

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




HWND hwn=::GetNextWindow(hw,GW_HWNDNEXT);


و باز نام کلاسش رو دريافت ميکردم :




::GetClassName(hwn,lpStr,myclassname.GetLength());



نام رو به یک کادر ویرایشی منتقل میکردم تا تغییرات رو ببینم :




Classname=lpStr;
UpdateData(FALSE);

و سپس هندل جدید رو به hw منتقل میکردم :




hw=hwn;



نام کلاسها رو تا 25 بار تکرار این عمل که برنامه در کادر ویرایش چاپ میکرد را در زیر آورده ام :



1. VttTooltip
2. tooltips_class32
3. MSCTFIME UI
4. IME
5. _WwM
6. tooltips_class32
7. WorkerW
8. tooltips_class32
9. DV2ControlHost
10. tooltips_class32
11. tooltips_class32
12. tooltips_class32
13. tooltips_class32
14. tooltips_class32
15. CiceroUIWndFrame
16. tooltips_class32
17. Shell_TrayWnd
18. tooltips_class32
19. BaseBar
20. VBBubble
21. Auto-Suggest Dropdown
22. tooltips_class32
23. و بعد از 10 بار تکرار مورد 22 این اومد CL RC Engine3 Dummy Winidow
24. CiceroUIWndFrame
25. OfficeTooltip

جالبه که بدونید هیچوقت به کلاس Button نرسیدم (من اونقدر این کار رو ادامه دادم تا برنامه در نهایت با خطا مواجه شد! ).

سوال:

1- به نظر شما روش من چه مشکلی داره ؟ و اصلا چرا به نام کلاس Button نمیرسیدم ؟(اگر من به کلاس Button میرسیدم مطمئن میشدم که هندلش رو هم دارم )

2-اکثر این اسمهایی که برنامه دریافت میکرد هیچوقت در پنجره اسپای روی هیچ کنترلی ظاهر نمیشد .چرا ؟

و یک سوال جدای از بحث :

اگر ما هندل دکمه رو از طریق اسپای بدست بیاریم آیا این هندل فقط برای همین کامپیوتر صادق است و یا این هندل روی سیستمهای دیگر هم جواب میدهد ؟

با تشکر .

Nima_NF
یک شنبه 12 آبان 1387, 15:08 عصر
1- تابع FindWindow کنترل های فرزند یا همان child را جستجو نمی کند، شما وقتی که هندل پنجره مورد نظر خودتان را گرفتید سپس باید از FindWindowEx استفاده کنید که تمامی فرزند ها را نیز دریافت کنید چون پارامتر hwndChildAfter را در آن دارید.
مثلا پنجره اصلی yahoo والد پنجره های کوچک دیگر مثل پنجره log in است، پس اگر می خواهید از پنجره اصلی به آن برسید باید دو مرحله به پیش روید،

یعنی مثلا برای پنجره اولیه log in به این شکل:
ابتدا با FindWindow پنجره اصلی را می یابید، سپس با FindWindowEx پنجره والد که ممکن است نام X داسته باشد را می یابید و سپس باز هم با FindWindowEx پنجره فرزند فرزند پنجره اصلی (child of child) را بیابید که با نام YLoginWnd است و همین طور فرزند های دیگر پنجره، که button هم یک فرزند (child) است.



2-اکثر این اسمهایی که برنامه دریافت می کرد هیچوقت در پنجره اسپای روی هیچ کنترلی ظاهر نمیشد .چرا ؟در بالا هم توضیح دادم، بسیاری از موارد قابل شناسایی نیستند چون آن ها سفارشی ساخته شده اند، یعنی یک کنترل شامل چندین چیز به ظاهر شبیه کنترل های معمولی هستند و خواص تعریف شده خاص خود را دارند.
سایر موارد نیز لزوما یک کنترل قابل مشاهده نیستند، بلکه کلا برای برنامه ساخته شده اند که اعمال خاصی در بخش های مختلف انجام دهند، البته نه در همان پنجره ای که ظاهرا مشاهده می کند.

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


اگر ما هندل دکمه رو از طریق اسپای بدست بیاریم آیا این هندل فقط برای همین کامپیوتر صادق است و یا این هندل روی سیستمهای دیگر هم جواب میدهد ؟خیر، حتی در سیستم شما هم شاید جواب ندهد.
چیزهایی که معمولا ثابت هستند همان (class name) نام کلاس ها و (caption) عناوین کنترل یا یا پنجره ها هستند که می توانید از آن ها استفاده کنید. که البته آن هم به شرط آن است که همان نسخه نرم افزار باشد.

نکته پایانی:
احتیاج نیست که خودتان برنامه آن را بنویسید تا پنجره ها را لیست کند، در spy++ پنجره اصلی را بیابید و سپس در پنجره window properties در قسمت windows، اگر بر روی لینک های آبی کلیک کنید پنجره یا فرزند بعدی به شما داده خواهد شد.
موفق باشید

A_Salimi
یک شنبه 12 آبان 1387, 23:00 عصر
آقای نیک فطرت خیلی متشکرم . به همون روشی که گفتید به نتیجه رسیدم و ریموت من کار کرد و تونستم دکمه sign in رو از طریق فرم خودم فشار بدم .

فقط چند تا نکته رو خاطر نشان میکنم :

در این خط باید به جای والد مینوشتید فرزند .(درست میگم ؟)


ابتدا با FindWindow پنجره اصلی را می یابید، سپس با FindWindowEx پنجره والد که ممکن است نام X داسته باشد را می یابید

در پارامتر دوم FindWindowEx از NULL استفاده کردم به این دلیل در MSDN :

If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.

در تابع SendMessage هم در پارامتر سوم به جای VK_SPASE از VK_RETURN استفاده کردم.

Nima_NF
دوشنبه 13 آبان 1387, 02:08 صبح
بله منظور همان فرزند بود که اشتباها نوشتم والد.