الشبكة العربية لمطوري الألعاب

مبتدئ  mohamed samir مشاركة 1

السلام عليكم..
قمت بكتابة برنامج بلغة ++C للتعديل على الكتابة في أحد الألعاب من خلال إستخدام Hooking APIs + DLL injection لجعلها عربية و هو يعمل بشكل جيد..ولكن هناك مشكلة غريبة عتدما يعمل على ويندوز XP.
عندما تكون اللعبة في الحالة Window تقوم بإستقبال الحروف العربية الـUnicode التي أرسلها إليها وعرضها بشكل سليم و لكن عندما يقوم اللاعب بتحويل اللعبة إلى الحالة Full Screen لا تقوم اللعبة بإستقبال الحروف الـ Unicode ولكن بدلا منها تقوم بإستقبال الحروف الـ Ansi العادية!
وعندما قمت بعمل بحث على جوجل وجدت أحد الأشخاص عنده نفس المشكلة،
http://www.gamedev.net/community/forums/topic.asp?topic_id=359096
وقام بالرد عليه "عصام بهنسي" من المنتدي. بهذا الرد
***********************************************************
" Here's a suggestion from one of the MVPs.
Quote:Perhaps it's D3D's hooked window proc that's doing this? How about

subclassing the window's wndproc right after the device has gone full screen

(make sure you call the old wndproc instead of DefaultWndProc()).
***********************************************************
قمت بعمل بحث على موقع MSDN عن طريقة Subclassing فوجدت الآتي تحت الفقرة SubClassing a window""
http://msdn.microsoft.com/en-us/library/ms633570%28v=vs.85%29.aspx#associating_proc

ولأني أستخدم Hooking كان لابد من معرفة ما إذا كانت هناك Window جديدة قامت بإنشائها اللعبة أم لا فقمت بإستخدام WH_CBT Hook
لمعرفة متى يتم صنع نافذة جديدة.

المشكلة:
WH_CBT Hook لا يعمل على ويندوز XP بشكل سليم ففي اللحظة التي أحاول فيها قراءة إسم الـ Class الذي تتكون منه الـ Window يحدث Crash
أيضا لا أعتقد أن WH_CBT سيساعدني كثيرا لأن العربي يعمل بشكل سليم في الحالة Window  لكن الحالة Full Screen هي المشكلة لأن الـ Window تتحول إلي Ansi  في الحالة Full Screen فتستقبل الحروف الـ Ansi  فتظهر الحروف مفككة بهذا الشكل "أ ل س ل ا م ع ل ي ك م"
هل هناك حل لهذه المشكلة ؟ أو هل هناك مثال ؟ مع العلم أن البرنامج يعمل بشكل سليم على ويندوز 7 و Vista  X64 و X86
وإصدار الويندوز XP المستخدمة هو Windows XPSP3 Comparative December 2010

3D-Artist/Programmer
http://www.youtube.com/watch?v=DvHvsfrQGyc
http://www.youtube.com/watch?v=PBSeyo9WHwM

خبير مدير وسام البهنسي مشاركة 2

وفي 10/محرم/1432 11:59 ص، أعرب mohamed samir عن رأيه بالموقف كالآتي:

وقام بالرد عليه "عصام بهنسي" من المنتدي. بهذا الرد
***********************************************************
" Here's a suggestion from one of the MVPs.
Quote:Perhaps it's D3D's hooked window proc that's doing this? How about

subclassing the window's wndproc right after the device has gone full screen

(make sure you call the old wndproc instead of DefaultWndProc()).
***********************************************************
قمت بعمل بحث على موقع MSDN عن طريقة Subclassing فوجدت الآتي تحت الفقرة SubClassing a window""
http://msdn.microsoft.com/en-us/library/ms633570%28v=vs.85%29.aspx#associating_proc

نعم، فقط تصحيح بسيط، هو ليس "عصام بهنسي" وإنما "وسام البهنسي" ☺
 
على كل حال، يجب ملاحظة أن دايركت ثري دي يقوم بتسجيل إجراء WndProc الخاص به في نمط الشاشة الكاملة، وذلك كي يقوم ببعض العمليات عند تصغير النافذة وأمور أخرى. أي أن D3D نفسه يقوم بشيء مشابه لما تحاول القيام به (لكن بطريقة أبسط طبعاً لأنه يعمل من داخل البرنامج). كي تعمل فكرتك بنجاح، يجب أن تقوم بتعديلاتك _بعد_ أن يقوم دايركت ثري دي بتسجيل إجراء WndProc الخاص به، وليس قبل ذلك.
 
للأسف يبدو أن دايركت ثري دي لم يأخذ بعين الاعتبار حالة نوافذ يونيكود...
 


في 10/محرم/1432 11:59 ص، قال mohamed samir بهدوء وتؤدة:

ولأني أستخدم Hooking كان لابد من معرفة ما إذا كانت هناك Window جديدة قامت بإنشائها اللعبة أم لا فقمت بإستخدام WH_CBT Hook
لمعرفة متى يتم صنع نافذة جديدة.

في الحالة الاعتيادية لا يقوم البرنامج بإعادة إنشاء النافذة عندما يريد الانتقال لنمط الشاشة الكاملة، وإنما يتم إعادة جهاز دايركت ثري دي (IDirect3DDevice9::Reset)، أو إعادة إنشاؤه من الصفر (Release ثم CreateDevice). النافذة تبقى نفسها، لكن دايركت ثري دي يسجل إجراء WndProc الخاص به فوق الإجراء الأصلي.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

مبتدئ  mohamed samir مشاركة 3

شكرا على الرد وسام..
إذا أنا الآن أحتاج DirectX Hook  عند قيام الـ DirectX بالتحويل إلى Full Screen عند Device.fullscreen
ثم عمل subclass من النافذة الحالية و نداء الإجراء WndProc الخاص بي بعد إجراء الـ DirectX
لست متأكدا من كيفية فعل هذا..هل تستطيع إعطائي مثال صغير ؟

3D-Artist/Programmer
http://www.youtube.com/watch?v=DvHvsfrQGyc
http://www.youtube.com/watch?v=PBSeyo9WHwM

خبير مدير وسام البهنسي مشاركة 4

وفي 10/محرم/1432 03:21 م، قال mohamed samir متحمساً:

إذا أنا الآن أحتاج DirectX Hook  عند قيام الـ DirectX بالتحويل إلى Full Screen عند Device.fullscreen

لا أعتقد أنه توجد طريقة باستخدام الـ hook للتنصت على أحداث دايركت ثري دي، لكن... يوجد حل شرير للغاية لكنه مضمون. الحل هو كتابة مكتبة ديناميكية تحمل بدلاً من مكتبة D3D9.DLL. هذه المكتبة تقوم بإعادة تعريف كافة إجراءات IDirect3DDevice9 لما تود فعله. مثلاً، تستطيع استقبال النداءات للإجراء IDirect3DDevice9::Reset أو IDirect3D9::CreateDevice ومن ثم فعل ما يحلو لك، وأخيراً إعادة نداء الإجراءات الأصلية من مكتبة D3D9.DLL الحقيقية.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

مبتدئ  mohamed samir مشاركة 5

السلام عليكم..شكرا على الرد.
بكلمة "مكتبة" هل تقصد "Interface" أو Library" ؟
وهل عند مثال لما تقول ؟
شكرا مقدما

3D-Artist/Programmer
http://www.youtube.com/watch?v=DvHvsfrQGyc
http://www.youtube.com/watch?v=PBSeyo9WHwM

خبير مدير وسام البهنسي مشاركة 6

في 13/محرم/1432 10:00 ص، غمغم mohamed samir باستغراب قائلاً:

بكلمة "مكتبة" هل تقصد "Interface" أو Library" ؟

نعم أقصد بها "library"، أما كلمة "interface" فلا يمكن لها أن تقابل "مكتبة" سواء لغوياً أو اصطلاحياً...



بتاريخ 13/محرم/1432 10:00 ص، قطب mohamed samir حاجبيه بشدة وهو يقول:

وهل عند مثال لما تقول ؟

ألقِ نظرة على هذا الموضوع (أيضاً من محمد حجاج): 
 
http://www.gamedev.net/community/forums/topic.asp?topic_id=359794

وسام البهنسي
مبرمج في إنفيديا وإنفريمز

مبتدئ  mohamed samir مشاركة 7

شكرا أخ وسام على المساعدة، أنا بالفعل معي هذا المثال و سأقوم بالنظر إليه في القريب بإذن الله..
شكرا مرة أخرى.

3D-Artist/Programmer
http://www.youtube.com/watch?v=DvHvsfrQGyc
http://www.youtube.com/watch?v=PBSeyo9WHwM

مبتدئ  حسام زكريا مشاركة 8

السلام عليكم :

ضمن الرابط أدناه هناك شرح بسيط عن كيفية نداء الإجراء WndProc الخاص بك ثم نداء الإجراء الأصلي للنافذة ضمن الفقرة subclassing a window أرجو أن يكون الرابط مفيداً لكم

http://msdn.microsoft.com/en-us/library/ms633570(v=vs.85).aspx

التوابع الرئيسية التي يتم استخدامها:
1-  SetWindowLong لاستبدال الاجراء WndProc القديم بالاجراء الحديث الخاص بك والذي يرد بدوره (التابع  SetWindowLong) القيمة القديمة لمؤشر WndProc القديم الخاص بالنافذة
2- CallWindowProc لاستدعاء الاجراء WndProc القديم ضمن الاجراء الحديث الذي قمت بكتابته

Husam Zakaria
Game Programmer

مبتدئ  mohamed samir مشاركة 9

شكرا أخ حسام على الرد..بالفعل قرأت هذه المقالة وانا في مراحل البحث..
وأعتقد أن المشكلة هي أنني كنت أتمنى أن يكون الموضوع أبسط من ذلك.. فعملية الكتابة العربية وحدها كانت صعبة ولم أكن أريد كتابة الكثير من الأسطر لجعلها تعمل على ويندوز XP لأنها بالفعل تعمل على فيزتا و 7 في الحالتين Window و Full screen.
الأن دعنى أراجع معك و مع الأخ وسام ما توصلت إليه أو ما فهمته حتى الآن:
أولا: يجب علي عمل Library تحمل بدلا من الـlibrary الأصلية التي تستخدمها اللعبة، وبهذه الـlibrary أستطيع أن أنادي الـFunctions الأصلية التي تستخدمها اللعبة قبل عمل هذه الـlibrary وأيضا أستطيع أن أعرف الوقت الذي تقوم فيه اللعبة بالتحويل إلى الحالة Full Screen.
ثانيا:عند الحالة Full Screen (أعتقد) تقوم الـLibrary  التي قمت بعملها بإستقبال HWND للنافذة الحالية.
ثالثا: أقوم بإستخدام الإجراء SetWindowLong وأقوم بإرسال الـHWND للنافذة الحالية إليه (الذي قمت بالحصول عليه من الـlibrary التي قمت بعملها -أتمنى ذلك-) و أرسل إلى الإجراء أيضا الإجراء الذي أريد تنفيذه.
رابعا: في نهاية الإجراء الخاص بي..أقوم بنداء الإجراء الأصلي للنافذة مرة أخرى.

من فضلكم قوموا بتصحيحي إذا كنت مخطئا، و شكرا لكم جميعا.

3D-Artist/Programmer
http://www.youtube.com/watch?v=DvHvsfrQGyc
http://www.youtube.com/watch?v=PBSeyo9WHwM

خبير مدير وسام البهنسي مشاركة 10

في 21/محرم/1432 07:46 ص، قال mohamed samir بهدوء وتؤدة:

أولا: يجب علي عمل Library تحمل بدلا من الـlibrary الأصلية التي تستخدمها اللعبة، وبهذه الـlibrary أستطيع أن أنادي الـFunctions الأصلية التي تستخدمها اللعبة قبل عمل هذه الـlibrary وأيضا أستطيع أن أعرف الوقت الذي تقوم فيه اللعبة بالتحويل إلى الحالة Full Screen.

بالضبط. كلام دقيق.
 


في 21/محرم/1432 07:46 ص، عقد mohamed samir حاجبيه بتفكير وقال:

ثانيا:عند الحالة Full Screen (أعتقد) تقوم الـLibrary  التي قمت بعملها بإستقبال HWND للنافذة الحالية.

صحيح. تستطيع برامج دايركت ثري دي 9 أن تسلك فقط إحدى طريقين للانتقال لنمط الشاشة الكاملة. إما أثناء إنشاء جهاز الرسم IDirect3D9::CreateDevice أو أثناء إعادة ضبط الجهاز IDirect3DDevice9::Reset. وفي كلا الحالتين ستستقبل HWND النافذة المسؤولة عن العرض. 


وفي 21/محرم/1432 07:46 ص، قال mohamed samir متحمساً:

ثالثا: أقوم بإستخدام الإجراء SetWindowLong وأقوم بإرسال الـHWND للنافذة الحالية إليه (الذي قمت بالحصول عليه من الـlibrary التي قمت بعملها -أتمنى ذلك-) و أرسل إلى الإجراء أيضا الإجراء الذي أريد تنفيذه.
رابعا: في نهاية الإجراء الخاص بي..أقوم بنداء الإجراء الأصلي للنافذة مرة أخرى.

هنا خطأ. فلو قمتَ بنداء الإجراء الأصلي بعد أن تعدل الـ WndProc فإن دايركت ثري دي سيضع نفسه بدلاً من إجرائك وبالتالي تعود للـ ANSI بدلاً من الـ Unicode. الترتيب الصحيح هو: 
عند استقبال أي من النداءين CreateDevice أو Reset، فإنك أولاً تمرر النداء للإجراء الأصلي، ثم تقوم فوراً بنداء SetWindowLong لوضع الـ WndProc الخاص بك _بعد_ ذلك الذي وضعه دايركت ثري دي.

وسام البهنسي
مبرمج في إنفيديا وإنفريمز