السلام عليكم ورحمة الله وبركاته
ارجو منكم ان تقبلوني معكم وقد قمت سابقا بالعمل في برمجة الشبكات
وقرأت نصف كتاب الإستاذ فادي
السلام عليكم ورحمة الله وبركاته
ارجو منكم ان تقبلوني معكم وقد قمت سابقا بالعمل في برمجة الشبكات
وقرأت نصف كتاب الإستاذ فادي
طريقة الغاء الخلفية تعتمد على دالة الAPI المتاحة لنا وهي SystemParametersInfo
[color= #009900;][DllImport[color= #009900;]([color= #0000ff;]"user32.dll"[color= #339933;], CharSet [color= #339933;]= CharSet[color= #339933;].Auto[color= #009900;])[color= #009900;]] [color= #000000; font-weight: bold;]public [color= #990000;]static extern int SystemParametersInfo[color= #009900;](int uAction[color= #339933;], int uParam[color= #339933;], string lpvParam[color= #339933;], int fuW inIni[color= #009900;])[color= #339933;];
طبعا استعمال DllImport يستوجب استعمال فضاء الأسماء InteropServices
using [color= #990000;]System[color= #339933;].Runtime[color= #339933;].InteropServices[color= #339933;];
ثم بتمرير الثوابت التالية للدالة :
SystemParametersInfo[color= #009900;](0X14[color= #339933;], [color= #cc66cc;]0[color= #339933;], [color= #0000ff;]"(None)"[color= #339933;], 0x1 [color= #339933;]| 0x2[color= #009900;])[color= #339933;];
بعدها علينا ارسال رسالة تحديث للDesktop باستخدام الدالة SendMessage مع التابع WM_SETTINGCHANGE باستعمال مقبض سطح المكتب
ولاعادة صورة سطح المكتب نقوم بقراءة مكانها من الريجيستري وتمريرها إلى الدالة SystemParametersInfo باستبدال "(None)" باسم الصورة طبعا لاننسى عملية التحديث :)
رائع اخي دريم طريقة ممتازة يبدو أنك بدأت بالسي شارب وهذا شيء ممتاز على فكرة كل مبرمجين الفي بي الذين تعاملت معهم سواء من الطلاب أو المبرمجين حولوا إلى سي شارب فالذي يرى طريقة السي شارب في تنظيم الكود لا يستطيع أن يتخيل نفسه يرجع إلى الفي بي :)
ارحب بك اخي أبو مازن وأتمنى أن تكون عضو فعال معنا وأهلا وسهلا بك :)
بخصوص مقارنة البكسيل لكل صورة هل يوجد أحد لديه فكرة أخرى أو تحسين للفكرة السابقة قبل أن اشرح هذه الجزئية
تم تعديل هذه المشاركة بواسطة فادي عبدالقادر في 27 أغسطس 2008 في 11:26
السي شارب أصبح حتمية خصوصا أنني كنت عندما أبحث شئ على الدوت نت أجده بالسي شارب موثقا بخلاف الفيبي القليل يستعملونه
فقط أردت توضيح فكرتي في مقارنة أجزاء من الصورة طبعا امكانية استعمال المؤشرات قد تسرع العملية لكني كنت أفكر في حصر عملية المقارنة قدر المستطاع (كل كلامي طبعا نظري لم أجربه بعد)
الفكرة هي في تقسيم الصورة إلى عدة صور وعملية المقارنة لا تكون دورية يعني تتم فقط عند:
- تغير تحرك الماوس وعندها لا يتم المقارنة إلا في مساحة معينة موجود فيها مؤشر الماوس أي الصور التي تقع فقط في تلك المساحة
- الاستفادة من رسائل النظام من أجل معرفة تغيير ما على سطح المكتب أو على النوافذ فقط وقتها تتم عملية المقارنة
لا أعرف ربما نظريا ستفيد هذه الطريقة :wink: :wink:
اقتباسيعود ذلك لسببين:الأول: أن تقنية الـRemoting لا تعمل بالأساس إلا من خلال بروتوكول الـTCP
الثاني: لماذا أخترنا تقنية الـRemoting لسبب رأيسي وهو أنها تعتمد على مبدأ الـRequest والـResponse ......
كلامك جواهر أخ فادي
طيب هنا بالنسبة لتقنية ال Remoting ألا يمكن أستعمال منفذين بطريقة UDP واحد للأرسال والآخر للأستقبال
بالطبع نظرا ً لخبرتي القليلة سأدخل ضمن فريق البرمجة والتنفيذ
بالطبع لو قبلتم بي كفرد من الفريق
وعندي سؤال بالنسبة لنقطة اخذ صورة من سطح المكتب وارسالها
وهو جربت التعامل مع جميع ازرار لوحة المفاتيح ولكن الذي لم اعرف كيف اتعامل معه لأن البرنامج لا يحس بالضغط عليه هو PrintScreen وهل يمكن ان اجبر برنامج على تنفيذ امر الضغط على زر معين من لوحة المفاتيح بمعنى ( ان اوهم البرنامج ان المستخدم ضغط على زر printScreen ومن ثم اخذ clipboard.getImage() وارسال هذه الصورة عبر الشبكة )
كما كان لي ملاحظتين في درسك الاول اخ فادي برجاء المرور على الدرس وابداء ردك
بالنسبة للتعامل مع الأزرار
SendKeys.Send("{%}({PRTSC})");لكني لا أفضل ذلك أحسن كود لأخذ سطح المكتب هو :
Rectangle bounds [color= #339933;]= Screen[color= #339933;].PrimaryScreen[color= #339933;].Bounds[color= #339933;];
Bitmap screenshot [color= #339933;]= [color= #000000; font-weight: bold;]new [color= #990000;]System[color= #339933;].Drawing[color= #339933;].Bitmap[color= #009900;](bounds[color= #339933;].Width[color= #339933;], bounds[color= #339933;].Height[color= #339933;],
[color= #990000;]System[color= #339933;].Drawing[color= #339933;].Imaging[color= #339933;].PixelFormat[color= #339933;].Format32bppArgb[color= #009900;])[color= #339933;];
Graphics graph [color= #339933;]= Graphics[color= #339933;].FromImage[color= #009900;](screenshot[color= #009900;])[color= #339933;];
graph[color= #339933;].CopyFromScreen[color= #009900;](bounds[color= #339933;].X[color= #339933;], bounds[color= #339933;].Y[color= #339933;], [color= #cc66cc;]0[color= #339933;], [color= #cc66cc;]0[color= #339933;], bounds[color= #339933;].Size[color= #339933;], CopyPixelOperation[color= #339933;].SourceCopy[color= #009900;])[color= #339933;];
pictureBox1[color= #339933;].Image [color= #339933;]= screenshot[color= #339933;].GetThumbnailImage[color= #009900;](pictureBox1[color= #339933;].Width[color= #339933;], pictureBox1[color= #339933;].Height[color= #339933;], [color= #000000; font-weight: bold;]null[color= #339933;], n
ew IntPtr[color= #009900;]([color= #009900;])[color= #009900;])[color= #339933;];اقتباسSendKeys.Send("{%}({PRTSC})");
عنمدي سؤال وهو في الحالة العادية لا يتم الاحساس بالضغط على زرار PrintScreen
Private Control1_KeyPress(object sender , KeyEventArgs e)
{
if (e.keyCode=keys.PrintScreen)
clipboard.clear();
}
هذا الكود لا يتم تنفيذه ابداً وذلك لعدم احساس الفورم او الكنترول بالضغط على زرار Print Screen
هل هناك طريقة لمنع عملية PrintScreen
اعلم ان السؤال بعيد عن الموضوع ولكن لتتم الفائدة في عملية اخذ نسخة من سطح المكتب
اقتباسالفكرة هي في تقسيم الصورة إلى عدة صور وعملية المقارنة لا تكون دورية يعني تتم فقط عند:- تغير تحرك الماوس وعندها لا يتم المقارنة إلا في مساحة معينة موجود فيها مؤشر الماوس أي الصور التي تقع فقط في تلك المساحة
- الاستفادة من رسائل النظام من أجل معرفة تغيير ما على سطح المكتب أو على النوافذ فقط وقتها تتم عملية المقارنة
لا أعرف ربما نظريا ستفيد هذه الطريقة
هذا صحيح من الناحية النظرية أما م الناحية العملية ففي الـRemoting يتم استدعاء الدالة الموجودة في Class من قبل الـClient وتنفذ هذه الدالة لحظيا على الـServer بمعنى أن المتحكم الأساسي في عملية التقاط الصورة سيكون الـClient وليس الـServer وبالتالي فمن الصعب معرفة رسائل النظام التي تصدر عند حدوث اي حدث يتم من خلال الـServer , واما تقسيم الصورة إلى مجموعة صور وان تتم المقارنة في الصورة التي يؤشر عليها بالماوس فهي ممكنة لكنها غالبا ستحدث مشاكل لأن التغير لن يكون بضرورة بموقع الماوس فقط . الفكرة تحتاج إلى تحليل منطقي اكبر بخصوص عملية المقارنة لصورة كاملة فقد قمت سابقا بكتابة درس لذلك في الـCode Project وتأتي الفكرة اساسا من مقارنة الثلاثة الوان الرئيسية في كل بكسيل (الأحمر,والأخضر, و الأزرق) وفي حالة حدوث اختلافات بالنسبة معينة يتم ارسال الصورة ولتسريع عملية المقارنة قمت بتصغير الصورة إلى 100 في 100 بكسلس وفي هذه الحالة فإن المقارنة ستكون سريعة ولن يأخذ ذلك وقت معالجة كبير , لكن في برمجيات الـRemote Desktop قد تختلف الحاجة وذلك بإرسال البكسلس المختفة فقط بعد اكتشاف تغيرها حيث سيكون لدينا ثلاثة من الـBitmaps واحدة لصورة السابقة وأخرى لصورة الحالية والثالثة يوضع فيها البكسيلس المختلفة ويتم وضع قيمة null في البكسيلس المتشابهة وبعد انتهاء المقارنة يتم ارسال الـBitmap الثالثة وفي طرف الـClient يتم تعبئة الـBitmap الجديدة على الـBitmap الموجودة بحيث يستثني المواقع التي تحتوي على قيمة null ... من يستطيع القيام بالتحويل ذلك إلى كود وبالستعانة بالطريقة الموجودة في الدرس الذي اشرت اليه أو بتحليل الفكرة بشكل منطقي
اقتباسطيب هنا بالنسبة لتقنية ال Remoting ألا يمكن أستعمال منفذين بطريقة UDP واحد للأرسال والآخر للأستقبال
تستطيع عمل منفذين للإرسال والأستقبال من خلال اي بروتوكول المشكلة ليست هنا الـRemoting تقنية تختلف عن مبدأ الـSocket في عملية الإتصال والإرسال فهي تعتمد بشكل رأيسي على بروتوكول الـTCP وتتصل مع الـServer من خلال URL وبذلك فهي تتعدى مشكلة الـNAT بكل سهولة.
اقتباسوهو جربت التعامل مع جميع ازرار لوحة المفاتيح ولكن الذي لم اعرف كيف اتعامل معه لأن البرنامج لا يحس بالضغط عليه هو PrintScreen وهل يمكن ان اجبر برنامج على تنفيذ امر الضغط على زر معين من لوحة المفاتيح بمعنى ( ان اوهم البرنامج ان المستخدم ضغط على زر printScreen ومن ثم اخذ clipboard.getImage() وارسال هذه الصورة عبر الشبكة )
اخي بروف لا ادري ما الهدف من استخدامك للـPrint Screen لكن سنأتي لاحقا على شرح كيفية نقل كافة الأزرار إلتي يتم الضغط عليها بالكي بورد بإذن الله
اقتباسكما كان لي ملاحظتين في درسك الاول اخ فادي برجاء المرور على الدرس وابداء ردك
تمت الإجابة
تم تعديل هذه المشاركة بواسطة فادي عبدالقادر في 28 أغسطس 2008 في 18:46
السلام عليكم ورحمة الله وبركاته
اطلعت على الكلاس ScreenCapture الموجودة في المشروع وقمت بمحاولة الاستفادة منها لتطبيق الفكرة التي تقوم بالمقارنة بين صورتين
وقد عدلت على الكلاس لكي تصبح
class ScreenCapture : System.MarshalByRefObject
{
public Size GetDesktopBitmapSize()
{
return Screen.PrimaryScreen.Bounds.Size;
}
public byte[] GetDesktopBitmapBytes()
{
Rectangle bounds = Screen.PrimaryScreen.Bounds;
Bitmap screenshot = new System.Drawing.Bitmap(bounds.Width, bounds.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
Graphics graph = Graphics.FromImage(screenshot);
graph.CopyFromScreen(bounds.X, bounds.Y, 0, 0, bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream ms = new MemoryStream();
screenshot.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
return ms.GetBuffer();
}
public Image diff_image (Image OrginalImage, Image SecoundImage)
{
Bitmap bt1 = new Bitmap(OrginalImage);
Bitmap bt2 = new Bitmap(SecoundImage);
int size_H = bt1.Size.Height;
int size_W = bt1.Size.Width;
Bitmap bt3 = new Bitmap(size_W, size_H);
Color pixel_image1;
Color pixel_image2;
try
{
for (int x = 0; x != size_W; x++)
{
for (int y = 0; y != size_H; y++)
{
pixel_image1 = bt1.GetPixel(x, y);
pixel_image2 = bt2.GetPixel(x, y);
if (pixel_image1 != pixel_image2)
{
bt3.SetPixel(x, y, pixel_image2);
}
}
}
}
catch (Exception)
{
}
return bt3.GetThumbnailImage(size_W, size_H, null, new IntPtr());
}
}هنا انشئت صورة ثالثة نضع فيها فقط البيكسل الغير متشابهة
وقد طبقت العملية بمقارنة صورتين ونجحت :) لكن طبعا استغرقت وقتا طويلا :(
انا أجرب الآن المؤشرات لتسريع عملية المقارنة مع الunsafe code
لقد أضفت دالة أخرى أسرع لاستخراج التغير بين الصورتين باستخدام المؤشرات
public Image Unsafe_diff_image(Image OrginalImage, Image SecondImage)
{
Bitmap BOrginalImage = new Bitmap(OrginalImage);
Bitmap BSecondImage = new Bitmap(SecondImage);
BitmapData bitmapData1 = BOrginalImage.LockBits(new Rectangle(0, 0,
OrginalImage.Width, OrginalImage.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
BitmapData bitmapData2 = BSecondImage.LockBits(new Rectangle(0, 0,
SecondImage.Width, SecondImage.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
unsafe
{
byte* imagePointer1 = (byte*)bitmapData1.Scan0;
byte* imagePointer2 = (byte*)bitmapData2.Scan0;
for (int i = 0; i < bitmapData1.Height; i++)
{
for (int j = 0; j < bitmapData1.Width; j++)
{
// write the logic implementation here
if ((imagePointer1[0] != imagePointer2[0]) &&
(imagePointer1[1] != imagePointer2[1]) && (imagePointer1[2] != imagePointer2[2]))
{
imagePointer2[0] = imagePointer1[0];
imagePointer2[1] = imagePointer1[1];
imagePointer2[2] = imagePointer1[2];
}
else
{
imagePointer2[0] = 255;
imagePointer2[1] = 255;
imagePointer2[2] = 255;
}
imagePointer2[3] = imagePointer1[3];
imagePointer1 += 4;
imagePointer2 += 4;
}//end for j
imagePointer1 += bitmapData1.Stride -
(bitmapData1.Width * 4);
imagePointer2 += bitmapData1.Stride -
(bitmapData1.Width * 4);
}//end for i
}//end unsafe
BOrginalImage.UnlockBits(bitmapData1);
BSecondImage.UnlockBits(bitmapData2);
return BSecondImage.GetThumbnailImage(SecondImage.Width, SecondImage.Height, null, new IntPtr());;
}الكود غير منظم لكنه مفهوم أظن :)
استعنت بالدرس الموجود هنا لمن يريد شرح أكثر
تم تعديل هذه المشاركة بواسطة DreamNet في 29 أغسطس 2008 في 16:48
عملية تحديث سطح المكتب كنت أتصورها سهلة جدا :)
دالة الApi المستعملة هي SendMessageTimeout
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr SendMessageTimeout(
IntPtr hWnd,
uint Msg,
UIntPtr wParam,
IntPtr lParam,
uint fuFlags,
uint uTimeout,
out uint lpdwResult);لكن هنا يجب الأخذ بعين الاعتبار أن التحديث لا يتم إلا اذا كان هناك تغيير في الاعدادات على سطح المكتب وأسهل شئ يمكننا تغييره في الاعدادات هو حجم الأيقونات (عادة مايكون 32 )
عن طريق الريجيستري يمكنن تغيير هذه القيمة ثم ارسال الأمر كي يتم فعلا التحديث
private void RefreshDesptop()
{
string icon_size_string;
string new_icon_size_string;
//قراءة أبعاد الأيقونات من الريجيستري
icon_size_string = Microsoft.Win32.Registry.CurrentUser.OpenSubKey
(@"Control Panel\Desktop\WindowMetrics").GetValue("Shell Icon Size").ToString();
//كتابة أبعاد جديدة للأيقونات باضفة 1 في الريجيستري
new_icon_size_string = (int.Parse(icon_size_string )+1).ToString();
Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop\WindowMetrics"
,true).SetValue( "Shell Icon Size",new_icon_size_string);
// ارسال أمر التحديث
SendMessageTimeout((System.IntPtr)HWND_BROADCAST,
WM_SETTINGCHANGE,
(System.UIntPtr)SPI_SETNONCLIENTMETRICS,
IntPtr.Zero,
SMTO_ABORTIFHUNG,
1000, out lRes);
// ارجاع القيمة الافتراضية المعدلة من الريجيستري
Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop\WindowMetrics"
, true).SetValue("Shell Icon Size", icon_size_string);
//ارسال أمر التحديث مجددا
SendMessageTimeout((System.IntPtr)HWND_BROADCAST,
WM_SETTINGCHANGE,
(System.UIntPtr)SPI_SETNONCLIENTMETRICS,
IntPtr.Zero,
SMTO_ABORTIFHUNG,
1000, out lRes);
}كي لا أنسى تعريف الثوابت :)
private const int WM_SETTINGCHANGE = 0x001A; private const int HWND_BROADCAST = 0xffff; private const int SPI_SETNONCLIENTMETRICS = 0x002A; private const int SMTO_ABORTIFHUNG = 0x0002; uint lRes;
في الأخير يمكن ارجاع صورة سطح المكتب (بعد انهاء البرنامج ) بقراءتها من الريجيستري واستعمال نفس الدالة SystemParametersInfo التي استعملناها لحذف الخلفية باستبدال None بالمكان المستخرج من الريجيستري
private void RestoreWallpaper()
{
//قراءة مكان الصورة الحالية لسطح المكتب
string WallpaerImage = Microsoft.Win32.Registry.CurrentUser.OpenSubKey
(@"Control Panel\Desktop").GetValue("Wallpaper").ToString();
//استرجاع صورة سطح المكتب
SystemParametersInfo(0X14, 0, WallpaerImage, 0x1);
}تم تعديل هذه المشاركة بواسطة DreamNet في 29 أغسطس 2008 في 17:53
رائع اخي دريم سنستفيد بإذن الله من هذه التعديلات في بناء الـClass الرئيسي للمشروع
سنبدأ المرحلة التالية وهي كتابة الـRemoting Class والذي سيحتوي على الأمور التالية:
1- قراءة احداثيات الـماوس والكي بورد وتطبيقها على الـHost
2-دالة التقاط ونقل الصورة وسوف نستفيد من التعديلات التي اجراها اخي دريم
3- دلة نقل الملفات إلى الـHost
سأحضر بعض المعلومات واضعها اليوم أو غدا بإذن الله
نبدأ اليوم الجانب العملي من المشروع وأول ما سنبدأ به هو تعريف الدوال الرئيسية والتي سيتم استخدامها في عملية التحكم بالـMouse والـKeyboard
أولا دوال التحكم بالـMouse والـKeyboard:
1-دالة ضبط مؤشر الماوس :SetCursorPos وتأخذ إحداثيات
الـ X والـ Y للـ Mouse حيث يتم وضع مؤشر الـMouse على الإحداثيات الممررة ، ولاستخدامها بالدوت نيت سيتم استدعائها من الملف User32.Dll باستخدام الدالة DllImport وكما يلي:
[DllImport("user32.dll")]
private static extern void SetCursorPos(int x, int y);
public void MoveMouse(int x, int y)
{
SetCursorPos(x, y);
}المطلوب في برنامج الـClient هو استدعاء هذه الدالة وتمرير إحداثيات الـMouse التي يتم عمل Hooking لها وعند استدعاء تلك الدالة سيتم تطبيق الإحداثيات الممررة على الـServer بشكل مباشر.
2-دالة إرسال معلومات الأزرار التي يتم الضغط عليها بالماوس أو الكي بورد SendInput: وتأخذ هذه الدالة ثلاثة باروميترات الأول من نوع Integer ويوضع فيه عدد المرات التي سيتم تنفيذها عند استدعاء الدالة والثاني مؤشر على Struct Input يمرر فيه كافة معلومات الإدخال الخاصة بالماوس أو الكي بورد والثالث من نوع Integer يمرر فيه حجم الـInput Struct.
[DllImport("user32.dll")]
private static extern uint SendInput(
uint nInputs, // count of input events
ref INPUT input,
int cbSize // size of structure
);سنقوم بالبداية بإنشاء الـInput Struct وسيتم تعريفه على أساس Explicit Layout بحيث يتم تحديد موقع كل Field Offsetفي الـStruct وسنستخدم هذا الـStruct لتعريف كل من إدخالات الماوس والكي بورد وهي MOUSE_INPUT لإدخال Mouse Buttons Press Values والـKEYBD_INPUT لإدخال Keyboard Button Press Value وتستدعى كل منها باستخدام الـ INPUT Struct وكما يلي:
struct MOUSE_INPUT
{
public uint dx;
public uint dy;
public uint mouseData; // Mouse Button
public uint dwFlags; // Press Or Release Mouse Button
public uint time; // zero by defualt
public uint dwExtraInfo; // any other external data
}
struct KEYBD_INPUT
{
public ushort wVk; // Ketborad Virtual Key
public ushort wScan; // Scan Code
public uint dwFlags; // Press Or Release Keyborad Button
public uint time;
public uint dwExtraInfo;
}
[StructLayout(LayoutKind.Explicit)]
struct INPUT
{
[FieldOffset(0)]
public uint type;
// union
[FieldOffset(4)]
public MOUSE_INPUT mi;
[FieldOffset(4)]
public KEYBD_INPUT ki;
}
يجب أن نمرر في الـType للـInput Struct نوع العملية التي يتم إدخالها وتدل كل قيمة على نوع تلك العملية فمثلا تعني القيمة 0X002 أن العملية هي الضغط على الزر الأيسر للماوس والقيمة 0X004 هي تحرير عملية الضغط ويمكن معرفة كافة هذه القيم من الـMSDN:
private const uint MOUSEEVENTF_MOVE = 0x0001; /* mouse move */ private const uint MOUSEEVENTF_LEFTDOWN = 0x0002; /* left button down */ private const uint MOUSEEVENTF_LEFTUP = 0x0004; /* left button up */ private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; /* right button down */ private const uint MOUSEEVENTF_RIGHTUP = 0x0010; /* right button up */ private const uint MOUSEEVENTF_MIDDLEDOWN = 0x0020; /* middle button down */ private const uint MOUSEEVENTF_MIDDLEUP = 0x0040; /* middle button up */ private const uint MOUSEEVENTF_WHEEL = 0x0800; /* wheel button rolled */ private const uint MOUSEEVENTF_ABSOLUTE = 0x8000; /* absolute move */ private const uint KEYEVENTF_EXTENDEDKEY = 0x0001; private const uint KEYEVENTF_KEYUP = 0x0002; private const uint INPUT_MOUSE = 0; private const uint INPUT_KEYBOARD = 1;
سننشئ دالة باسم PressOrReleaseMouseButton وسنمرر لكل منها مجموعة من المتغيرات حيث سنشتق كائن من الـInput Struct والذي عرفناه في بداية البرنامج وسنمرر له الـ Input Type سواء من الـ Mouse أو الـKeyboard فإذا كان من الـ Mouse نمرر له إحداثيات المؤشر والـKay Button سواء Left أو Right وإحداثيات الموقع الذي تم الضغط عليه X و Y وكما يلي:
public void PressOrReleaseMouseButton(bool Press, bool Left, int X, int Y)
{
INPUT input = new INPUT();
input.type = INPUT_MOUSE;
input.mi.dx = (uint) X;
input.mi.dy = (uint) Y;
input.mi.mouseData = 0;
input.mi.dwFlags = 0;
input.mi.time = 0;
input.mi.dwExtraInfo = 0;
if (Left)
{
input.mi.dwFlags = Press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
}
else
{
input.mi.dwFlags = Press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
}
SendInput(1, ref input, Marshal.SizeOf(input));
}تدل الـPress في الدالة السابقة على استمرار عملية الضغط وتأخذ قيمة True أو False فإذا كانت False فهذا يعني تحرر عملية الضغط ...
وأما فيما يخص الكي بورد فسننشئ دالة باسم PressOrReleaseKeyboradButton وسنمرر لها أربعة باروميترات وهي الـVirtualKeyCode ويوضع فيه الـByte للـButton الذي تم الضغط عليه لمزيد من المعلومات أنظر الرابط http://delphi.about.com/od/adptips2006/qt/vkey2char.htm
والـ ScanCode لمزيد من المعلومات أنظر الرابط http://www.barcodeman.com/altek/mule/scandoc.php
والـKeyDown ويأخذ True أو False لمعرفة الـKey Down من الـKey Up.
والـExtendedKey لوضع أي Extended Key يتم استخدامه كمثال استخدام الـCaps Lock.
public void SendKeystroke(byte VirtualKeyCode, byte ScanCode, bool KeyDown, bool ExtendedKey)
{
INPUT input = new INPUT();
input.type = INPUT_KEYBOARD;
input.ki.wVk = VirtualKeyCode;
input.ki.wScan = ScanCode;
input.ki.dwExtraInfo = 0;
input.ki.time = 0;
if (!KeyDown)
{
input.ki.dwFlags |= KEYEVENTF_KEYUP;
}
if (ExtendedKey)
{
input.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
}
SendInput(1, ref input, Marshal.SizeOf(input));
}تركت بعض الأمور بدون شرح كامل لإستثارة التساؤلات لديكم :) ايضا إذا كان لديكم أي اقتراحات أو تعديلات فيمكنكم كتابتها
السلام عليكم ورحمة الله وبركاته
مادمنا دخلنا الجانب التطبيقي فأظنني سأستبق الأحداث قليلا :)
عملت التطبيق وانا أجربه الآن والكلاس الأساسية التي أستعملها الآن هي بالمرفقات
فيها بعض الأمور أردت أن أناقشها معك
المشكل الذي يواجهي الآن هو التميز بين الصورة الموجودة عند العميل والتي عند الخادم
عملت دالة تقوم بحساب الفارق بين الصورتين لكن لا أعرف كيفية الاستفادة منه
لان العملية هذه تتم في جهة الخادم
والعميل هو الذي يتحكم في قراءة الصورة
الشئ الذي يجب أخذه بعين الاعتبار هو تحويل الاحداثيات الموجودة على الصورة للاحداثيات الحقيقية التي يتم تطبيقها على سطح المكتب
ولذلك يجب قراءة أبعاد الشاشة والتي موجودة في GetDesktopBitmapSize
بعدها يتم حساب معامل التصحيح
Size Remotescreen = obj.GetDesktopBitmapSize(); float factY = Remotescreen.Height / pictureBox1.Height; float factX = Remotescreen.Width / pictureBox1.Width;
بعدها فقط في الحدث MouseMove المتعلق بالصورة نضرب في معامل التصحيح لكل احداثي للحصول على الاحداثيات الصحيحة التي يتم تطبيقها على سطح المكتب
أرجو منك استاذ فادي الاطلاع على الفئة المرفة
حيث قمت باضافة خاصيتين تتحكمان في جودة الصورة الملتقطة لكن لم ألاحظ الفرق عند تطبيقها
بالتوفيق للجميع
تم تعديل هذه المشاركة بواسطة DreamNet في 6 سبتمبر 2008 في 17:51
أشكرك أخي العزيز دريم وبارك الله بك
اقتباسالمشكل الذي يواجهي الآن هو التميز بين الصورة الموجودة عند العميل والتي عند الخادمعملت دالة تقوم بحساب الفارق بين الصورتين لكن لا أعرف كيفية الاستفادة منه
لان العملية هذه تتم في جهة الخادم
والعميل هو الذي يتحكم في قراءة الصورة
التعديل الذي اقترحه هو أن لا يتم استدعاء دالة المقارنة من قبل الـClient وإنما تنفذ مع دالة جلب الصورة في الـServer وتتم عملية المقارنة قبل إرجاع الصورة فمثلا إذا كان هنالك اختلاف بين الصورة السابقة والصورة الحالية يتم إرجاع الصورة أو جزء الاختلاف وإلا يرجع قيمة null وفي طرف الـClient تعرض الصورة التي تم إرجاعها بوضع شرط ان لا تكون قيمة الإرجاع null واما عملية المقارنة فمن المفترض ان يتم عمل Global Variable على الـServer توضع فيه الصورة السابقة ليتم المقارنة بها.
اقتباسالشئ الذي يجب أخذه بعين الاعتبار هو تحويل الاحداثيات الموجودة على الصورة للاحداثيات الحقيقية التي يتم تطبيقها على سطح المكتبولذلك يجب قراءة أبعاد الشاشة والتي موجودة في GetDesktopBitmapSize
بعدها يتم حساب معامل التصحيح
هذا صحيح لكن ممكن أن يتم عرض الصورة على PictureBox بحجمها الطبيعي بدون عمل ستريش لصورة واعتقد ان الـRemote Desktop للويندوز يستخدم نفس الطريقة لكن اقتراحك جميل ويمكن أن يستفاد منه فيما لو تم تصغير النافذة لكن عملية استخراج معامل التصحيح للإحداثيات يحتاج إلى استخراج معادلة دقيقة لوجود عدة مشاكل ستواجهك
واقتراحي كان بدل أن نعمل hook على إحداثيات الماوس من الويندوز يمكن أن نأخذ إحداثيات الماوس من الفورم الذي تعرض عليه الصورة وبذلك تكون عملية استخراج الإحداثيات ادق
اقتباسحيث قمت باضافة خاصيتين تتحكمان في جودة الصورة الملتقطة لكن لم ألاحظ الفرق عند تطبيقها
قمت بتجربتها وهي تعمل بشكل جيد جدا فعند وضع الـQuality على 100% كان حجم الـMemoryStream = 407,858 في حين عند تقليلها إلى 10% اصبح حجمه 49,935 هذا على شاشة بدقة 1280 في 1024 لكن الصورة تصبح رديئة جدا على اي حال إضافة جميلة تشكر عليها :)
قرأت المشاركة وأريد لفت الأنتباه لأمر
اقتباسالشئ الذي يجب أخذه بعين الاعتبار هو تحويل الاحداثيات الموجودة على الصورة للاحداثيات الحقيقية التي يتم تطبيقها على سطح المكتبولذلك يجب قراءة أبعاد الشاشة والتي موجودة في GetDesktopBitmapSize
إذا كان المشروع متعلق بسطح المكتب فقط فنستطيع قراءة أبعاد الشاشة بالطريقة السابقة ولكن لن تفيد في بعض الحالات عند تشغيل البرامج التي تستخدم الدايركت وتصغر دقة الشاشة
حاولت أن أقوم بما ذكرته لكن البرنامج يعلق
عدلت في المكتبة بتعريف متغير byte سميته OldBuff
if ((IsDiff()) | (OldBuff == null))
{
OldBuff = ms.GetBuffer();
return OldBuff;
}
else { return null; }في جهة العميل
if (buffer != null)
{
MemoryStream ms = new MemoryStream(buffer);
pictureBox1.Image = Image.FromStream(ms);
}اقتباسلكن عملية استخراج معامل التصحيح للإحداثيات يحتاج إلى استخراج معادلة دقيقة لوجود عدة مشاكل ستواجهك
ماهي المشاكل التي تقصدها
فقد استعملت الطريقة الثلاثية من اجل حساب المجهول :) وهو الاحداثي الحقيقي
الاحداثي الحقيقي-------------------> ابعاد الشاشة الحقيقية
احداثيات الماوس على الصورة-------------->أبعاد الصورة
فتنتج المعادلة التي تعطينا الاحداثي الحقيقي= (ابعاد الشاشة الحقيقية / أبعاد الصورة) * احداثسات الماوس على الصورة
عبد العزيز اليوسفي كتب:قرأت المشاركة وأريد لفت الأنتباه لأمرإذا كان المشروع متعلق بسطح المكتب فقط فنستطيع قراءة أبعاد الشاشة بالطريقة السابقة ولكن لن تفيد في بعض الحالات عند تشغيل البرامج التي تستخدم الدايركت وتصغر دقة الشاشة
لم أفهم القصد
فالألعاب عند تغيير دقة الشاشة فهي تغير بذلك أبعادها
فمثلا 800*600 هي الأبعاد التي ستغييرها اللعبة للشاشة ولسطح المكتب أيضا
وهذا مانلاحظه عند الخروج من اللعبة حيث ترجع اللعبة الشاشة إلى الوضع الافتراضي
أي إلى الأبعاد الافتراضية
تم تعديل هذه المشاركة بواسطة DreamNet في 9 سبتمبر 2008 في 17:58
كلامك صحيح ولكن بأعتقادي عندما ستقرأ أبعاد شاشة عند تشغيل للعبة تعمل على الدايركت مثلاً 800*600 وكانت دقة سطح المكتب 1024*768 بكسل كمثال ،فستقرأها 1024*768 لأن اللعبة فعلياً لم تقم بتغيير دقة سطح المكتب ولكن أخذ مقطع من المساحة الموجودة ب 1024*768 وتبدأ من يسار أعلى الشاشة إلى 800*600 ،هذا والله أعلم ، يمكنك أختبار ذلك مستقبلاً.اقتباسفالألعاب عند تغيير دقة الشاشة فهي تغير بذلك أبعادهافمثلا 800*600 هي الأبعاد التي ستغييرها اللعبة للشاشة ولسطح المكتب أيضا
وهذا مانلاحظه عند الخروج من اللعبة حيث ترجع اللعبة الشاشة إلى الوضع الافتراضي
أي إلى الأبعاد الافتراضية
أو تستخدم للحصول على ذلك الدايركت نفسه ،فلا أعرف عندكم فعندنا في الفوجول بيسك نستخدم المكتبة dx7vb.dll للحصول على الدقة الصحيحة.
عبد العزيز اليوسفي كتب:كلامك صحيح ولكن بأعتقادي عندما ستقرأ أبعاد شاشة عند تشغيل للعبة تعمل على الدايركت مثلاً 800*600 وكانت دقة سطح المكتب 1024*768 بكسل كمثال ،فستقرأها 1024*768 لأن اللعبة فعلياً لم تقم بتغيير دقة سطح المكتب ولكن أخذ مقطع من المساحة الموجودة ب 1024*768 وتبدأ من يسار أعلى الشاشة إلى 800*600 ،هذا والله أعلم ، يمكنك أختبار ذلك مستقبلاً.أو تستخدم للحصول على ذلك الدايركت نفسه ،فلا أعرف عندكم فعندنا في الفوجول بيسك نستخدم المكتبة dx7vb.dll للحصول على الدقة الصحيحة.
حسب كلامك أخي العزيز فاني لن أستطيع تشغيل لعبة بأبعاد أكبر من أبعاد الشاشة الحالية أو بكلام أدق بأبعاد سطح المكتب
عند تشغيل اللعبة full screen على الديركت اكس أو الOpenGL فهذا يتطلب انشاء نافذة تغطي الشاشة بالأبعاد المختارة
ولكي لا يحدث اختلاف
فان الديركت اكس أو الOpenGL يقومان في الأول بتغيير ابعاد الشاشة بنفس الأبعاد المختارة
ثم بعد الخروج من اللعبة ترجع الأبعاد إلى الوضع الافتراضي
ولا أظنك لم تلاحظ يوما بعد تشغيلك للعبة بابعاد اقل من ابعاد الشاشة الافتراضية تغيير اماكن الأيقونات على سطح المكتب
وهذا دليل على حدوث تغيير لابعاد سطح المكتب أيضا :)
وأيضاً ستلاحظ عند تشغيلك للعبة بابعاد اقل من ابعاد الشاشة الافتراضية فإن أماكن الأيقونات لم تتغير.اقتباسولا أظنك لم تلاحظ يوما بعد تشغيلك للعبة بابعاد اقل من ابعاد الشاشة الافتراضية تغيير اماكن الأيقونات على سطح المكتبوهذا دليل على حدوث تغيير لابعاد سطح المكتب أيضا
على كل حال التجربة أكبر برهان ،أنا عملت الآن برنامج يخزن أبعاد الشاشة كل 1 ثانية وقمت بتشغيله ثم شغلت لعبة بدقة أقل من دقة الشاشة ،وبعد أغلاق اللعبة وجدت أن أبعاد الشاشة لم تتغير ،فما قولك .
حيث أستخدمت الدالة التابعة للفوجول للحصول على الأبعاد وهي كمايلي:
X = Screen.Width \ Screen.TwipsPerPixelX Y = Screen.Height \ Screen.TwipsPerPixelY
ويمكنك تجريب ذلك بطريقتك الخاصة
وأيضاً لكي نبعد الشك بالدالة قمت بتجريب ألتقاط صورة بأبعاد سطح المكتب الحقيقية واللعبة شغالة بأبعاد أقل (يعني على كلامك مغيرة للأبعاد) فنتجت عندي صورة سطح المكتب كاملة وعليها صورة اللعبة بأبعادها الأقل وبدأت من يسار أعلى الشاشة 0 ،0 كما ذكر سابقاً
تم تعديل هذه المشاركة بواسطة عبد العزيز اليوسفي في 10 سبتمبر 2008 في 17:15
:)
لا أعرف أشك في اللعبة الآن
لقد قمت بتجربة كل ماقلت والنتيجة معاكسة لكل ماذكرته بل أكدت لي صحة كلامي بالحرف الواحد
مع العلم أن الكود الذي أدرجته لا يعطي دائما نتائج دقيقة
على الأقل بالنسبة لي
أفضل استعمال دالة الAPI المقترحة لهذا الأمر وهي GetSystemMetrics
[color= #800000;]Private [color= #800000;]Declare [color= #800000;]Function GetSystemMetrics Lib [color= #ff0000;]"user32.dll" [color= #66cc66;](ByVal nIndex [color= #800000;]As [color= #800000;]Long[color= #66cc66;]) [color= #800000;]As [color= #800000;]Long [color= #800000;]Const SM_CXSCREEN = [color= #cc66cc;]0 [color= #800000;]Const SM_CYSCREEN = [color= #cc66cc;]1 x = GetSystemMetrics[color= #66cc66;](SM_CXSCREEN[color= #66cc66;]) y = GetSystemMetrics[color= #66cc66;](SM_CYSCREEN[color= #66cc66;])
لكنك لم تجبني على سؤال:
اذا افترضنا أن الشاشة لا تتغير أبعادها
وكانت 800*600 مثلا وكنت أريد تشغيل لعبة بأبعاد 1024*768
هل سأستطيع؟
لا توجد مساحة كافية المفروض :) لان أبعاد الشاشة 800*600 فقط
كيف يتم ذلك؟
كي نبتعد على البرمجة وليجرب أي أحد ذلك
نقوم بتشغيل أي لعبة في ملء الشاشة ونضغط على زر Print Screen
ثم نفتح برنامج الفوتوشوب عند انشاء صورة جديدة وتعيين الClipboard
ستجد الأبعاد هي أبعاد اللعبة المختارة والمفروض أن تكون هي أبعاد الشاشة ككل
أو لمن لا يملك الفوتوشوب يمكنهم حفظ الصورة على برنامج الرسام ثم في التحقق من خصائص الصورة
سنجدها بكل تأكيد تحمل أبعاد اللعبة وليس كامل الشاشة
ولو كان الأمر صحيحا كما قلت لكانت عملية Print Screen ستعطينا أجزاء من سطح المكتب
طبعا هنا لم نضغط على Print screen+ alt التي تقوم بتصوير النافذة الفعالة فقط :)
أظن أننا تكلمنا كثيرا خارج الموضوع
بالتوفيق
تم تعديل هذه المشاركة بواسطة DreamNet في 10 سبتمبر 2008 في 18:24
أنا لم أقل أن الشاشة لا تتغير أبعادها ولكن قلت سطح المكتب لا يتغير أبعاده في مثالنا هذا (لعبة تعمل على الدايركت) ،فلو كانت أبعاد سطح المكتب أصغر من أبعاد اللعبة وأردت جلب أبعاد سطح المكتب فسترى أن الأبعاد لم تتغير ،وإذا أخذنا صورة لسطح المكتب الأصغر سنجد أن الصورة للعبة أخذت ناقصة من اليمين والأسفل وبنفس أبعاد سطح المكتب .اقتباسلكنك لم تجبني على سؤال: اذا افترضنا أن الشاشة لا تتغير أبعادها وكانت 800*600 مثلا وكنت أريد تشغيل لعبة بأبعاد 1024*768هل سأستطيع؟ لا توجد مساحة كافية المفروض لان أبعاد الشاشة 800*600 فقط كيف يتم ذلك؟
وأنا قلت سابقاً:
لأنني من زمن مرت معي مشكلة بالأبعاد وحللتها بهذه الطريقةاقتباسأو تستخدم للحصول على ذلك الدايركت نفسه ،فلا أعرف عندكم فعندنا في الفوجول بيسك نستخدم المكتبة dx7vb.dll للحصول على الدقة الصحيحة
بالنسبة للدالة GetSystemMetrics سأجربها غداً بعد الأفطار إن شاء الله على جهاز فيه ألعاب .
بالنسبة ل Print Screen فهو يأخذ الصورة لأبعاد الشاشة وليس لسطح المكتب وأنا جربت ذلك
ونحن نتكلم عن معرفة الأبعاد الصحيحة للصورة التي تظهر على الشاشة وليس بألتقاط صورة
وبرأي أن كل هذا النقاش هو من ضمن الموضوع
والله الموفق ..
جربت الدالة GetSystemMetrics وهي تعمل تمام وتعطي أبعاد الشاشة الحقيقية ،وبأستخدام الدالة السابقة ستكون الصورة الملتقطة صحيحة للمشروع.
ولكن الشيىء العجيب أن سطح المكتب لا تتغير أبعاده ،فعند ألتقاط صورة بنفس أبعاد سطح المكتب وهناك لعبة بدقة أصغر فسيظهر سطح المكتب كاملاً وعليه صورة اللعبة بأبعادها الأصغر ،وأعتقد أن هذا ليس بمحور الموضوع وكما قال الأخ DreamNet بأننا خرجنا بذلك عن الموضوع .
والله الموفق ..
السلام عليكم ورحمة الله وبركاته
محاولة مني لتطبيق مااستطعت استيعابه
واجهة برنامج العميل
شاشة الاتصال
سطح المكتب على واجهة البرنامج
الملفات التنفيذية:
الكود سورس:
المتواجدون خلال آخر دقيقتين · يتحدّث كل ٣٠ ثانية
جارٍ التحقق من المتواجدين…