‏הצגת רשומות עם תוויות c#. הצג את כל הרשומות
‏הצגת רשומות עם תוויות c#. הצג את כל הרשומות

יום שני, 2 ביוני 2014

Remote Control Application - ScreenShot




עד עכשיו נגעתי בסדרה בכלי שליטה מתוכנתים בצד הלקוח, השלב הבא הוא לכידת התמונה בצד השרת באמצעות הספריה הקלאסית GDI, זה לא המנגנון היעיל ביותר אבל מספיק מתאים בשביל המשימה, לכידת המסך צריכה לעמוד בזמנים יחסית טובים על מנת לשמור על חווית משתמש, לא פחות חשוב שניהול הזיכרון צריך להיות מתוחזק כמו שצריך למרות שמדובר ב .Net על מנת למנוע זליגת זיכרון.

בנוסף לקובץ user32.dll שהשתמשנו בעבר נעבוד עם קובץ נוסף GDI.dll שמאפשר לנו לגשת למשאבים הגרפיים במחשב ע"י מספר פונקציות תחילה מקבלים את המצביע עבור הרכיב הפיזי בעזרת הפונקציה CreateDC לאחר מכן יוצרים Buffer משותף עם הפונקציה CreateCompatibleDC, השלב הבא להצהיר על Pointer ל Bitmap של Win32Api עם הפונקציה CreateCompatibleBitmap , מחברים בין ה Bitmap ל Buffer וקוראים לפונקציה BitBlt שאוספת את הפריים מהרכיב הפיזי ומעתיקה אותו ל Buffer ומשם ממשיכים הלאה.

captureControl.cs

       //global stream
        MemoryStream stream;

        //isRunning flag
        public bool isRunning = false;

        int width = 0;
        int height = 0;

        //capture signature
        [DllImport("GDI32.dll")]
        public static extern bool BitBlt(
            int hdcDest, int nXDest, int nYDest,
               int nWidth, int nHeight, int hdcSrc,
            int nXSrc, int nYSrc, int dwRop);

        //create compatible bitmap for device context
        [DllImport("GDI32.dll")]
        public static extern int CreateCompatibleBitmap
            (int hdc, int nWidth, int nHeight);

        //create compatible device context for the device
        [DllImport("GDI32.dll")]
        public static extern int CreateCompatibleDC(int hdc);

        //delete driver context
        [DllImport("GDI32.dll")]
        public static extern bool DeleteDC(int hdc);

        //delete native bitmap type
        [DllImport("GDI32.dll")]
        public static extern bool DeleteObject(int hObject);

        //create device context
        [DllImport("gdi32.dll")]
        static extern int CreateDC(string lpszDriver,
            string lpszDevice,string lpszOutput,
            IntPtr lpInitData);

        //get device information
        [DllImport("GDI32.dll")]
        public static extern int GetDeviceCaps(int hdc, int nIndex);

        //add object to the device context
        [DllImport("GDI32.dll")]
        public static extern int SelectObject(int hdc, int hgdiobj);

        //cursor object win32 api structure
        [StructLayout(LayoutKind.Sequential)]
        private struct CURSORINFO
        {
            public Int32 cbSize;
            public Int32 flags;
            public IntPtr hCursor;
            public POINTAPI ptScreenPos;
        }

        //point type win32 structure
        [StructLayout(LayoutKind.Sequential)]
        private struct POINTAPI
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct ICONINFO
        {
            public bool fIcon;
            public Int32 xHotspot;
            public Int32 yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }

        ////for more information visit
        ////http://msdn.microsoft.com/en-us/library/windows/desktop/dd145130(v=vs.85).aspx
        public enum TernaryRasterOperations : uint
        {
            /// <summary>dest = source</summary>
            SRCCOPY = 0x00CC0020,
            /// <summary>dest = source OR dest</summary>
            SRCPAINT = 0x00EE0086,
            /// <summary>dest = source AND dest</summary>
            SRCAND = 0x008800C6,
            /// <summary>dest = source XOR dest</summary>
            SRCINVERT = 0x00660046,
            /// <summary>dest = source AND (NOT dest)</summary>
            SRCERASE = 0x00440328,
            /// <summary>dest = (NOT source)</summary>
            NOTSRCCOPY = 0x00330008,
            /// <summary>dest = (NOT src) AND (NOT dest)</summary>
            NOTSRCERASE = 0x001100A6,
            /// <summary>dest = (source AND pattern)</summary>
            MERGECOPY = 0x00C000CA,
            /// <summary>dest = (NOT source) OR dest</summary>
            MERGEPAINT = 0x00BB0226,
            /// <summary>dest = pattern</summary>
            PATCOPY = 0x00F00021,
            /// <summary>dest = DPSnoo</summary>
            PATPAINT = 0x00FB0A09,
            /// <summary>dest = pattern XOR dest</summary>
            PATINVERT = 0x005A0049,
            /// <summary>dest = (NOT dest)</summary>
            DSTINVERT = 0x00550009,
            /// <summary>dest = BLACK</summary>
            BLACKNESS = 0x00000042,
            /// <summary>dest = WHITE</summary>
            WHITENESS = 0x00FF0062
        }


        //for more info visit
        //http://msdn.microsoft.com/en-us/library/windows/desktop/ms648381(v=vs.85).aspx
        private const Int32 CURSOR_SHOWING = 0x1;
        private const Int32 CURSOR_SUPPRESSED = 0x2;

        //getting cursor info signature
        [DllImport("user32.dll")]
        private static extern bool GetCursorInfo(out CURSORINFO pci);

        //getting cursor info signature
        [DllImport("user32.dll", EntryPoint = "GetIconInfo")]
        public static extern bool GetIconInfo(IntPtr hIcon, out ICONINFO piconinfo);

        //getting icon buffer signature
        [DllImport("user32.dll", EntryPoint = "CopyIcon")]
        public static extern IntPtr CopyIcon(IntPtr hIcon);

        //return handle to the desktop window
        [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
        public static extern IntPtr GetDesktopWindow();

        //getting device context
        [DllImport("user32.dll", EntryPoint = "GetDC")]
        public static extern IntPtr GetDC(IntPtr ptr);


        /// <summary>
        /// initialize settings 
        /// </summary>
        /// <param name="captureWidth">the width to capture</param>
        /// <param name="captureHeight">the height to cature</param>
        public captureControl(int captureWidth, int captureHeight)
        {
            width = captureWidth;
            height = captureHeight;
        }

        /// <summary>
        /// capture screen shot
        /// </summary>
        /// <returns>return stream</returns>
        public MemoryStream makeScreenShot()
        {
            //process flag to true
            isRunning = true;

            //create stream for return
            stream = new MemoryStream();

            //X draw position - cursor
            int x = 0;
            //Y draw position - cursor
            int y = 0;

            //create device context for Display device
            int hdcSrc = CreateDC("Display", null, null, IntPtr.Zero);

            //make it compatible.
            int hdcDest = CreateCompatibleDC(hdcSrc);
            int hBitmap = CreateCompatibleBitmap(hdcSrc,
                Screen.PrimaryScreen.Bounds.Width,
                Screen.PrimaryScreen.Bounds.Height);

            //add object to device context
            SelectObject(hdcDest, hBitmap);

            //make bit transfer from device
            BitBlt(hdcDest, 0, 0, Screen.PrimaryScreen.Bounds.Width,
                Screen.PrimaryScreen.Bounds.Height, hdcSrc, 0, 0,
                             (int)TernaryRasterOperations.SRCCOPY);

            //get image from native bitmap pointer
            Image imf = Image.FromHbitmap(new IntPtr(hBitmap));

            //start graphics from image
            Graphics g = Graphics.FromImage(imf);

            //get cursor bitmap and position
            Bitmap f = getCursorIcon(out x, out y);

            //draw the cursor the image by position
            if (f != null)
                g.DrawImage(f, x, y);

            //encoding parameters
            EncoderParameters encoderParameters = new EncoderParameters(1);
            encoderParameters.Param[0] =
                new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 70L);

            //save image as stream
            imf.Save(stream, GetEncoder(ImageFormat.Jpeg), encoderParameters);

            //remove device context
            DeleteDC(hdcSrc);
            //remove device context
            DeleteDC(hdcDest);
            //remove native bitmap
            DeleteObject(hBitmap);

            //resize the image
            stream = resizeImage(Image.FromStream(stream), new Size(width, height));

            //process flag to false
            isRunning = false;

            return stream;
        }

        /// <summary>
        /// getting the cursor image
        /// </summary>
        /// <param name="x">cursor x position</param>
        /// <param name="y">cursor y poistion</param>
        /// <returns>the cursor bitmap</returns>
        private Bitmap getCursorIcon(out int x, out int y)
        {
            Rectangle bounds = Screen.PrimaryScreen.Bounds;
            CURSORINFO pci;
            pci.cbSize = Marshal.SizeOf(typeof(CURSORINFO));

            x = 0;
            y = 0;


            if (GetCursorInfo(out pci))
            {
                x = pci.ptScreenPos.x - bounds.X;
                y = pci.ptScreenPos.y - bounds.Y;

                IntPtr hicon = CopyIcon(pci.hCursor);

                ICONINFO iconInfo;
                GetIconInfo(hicon, out iconInfo);

                Bitmap maskCursor = Bitmap.FromHbitmap(iconInfo.hbmMask);

                // if the size of the icon is double
                //so is in inverted mode
                if (maskCursor.Height == maskCursor.Width * 2)
                {
                    x = pci.ptScreenPos.x - bounds.X;
                    y = pci.ptScreenPos.y - bounds.Y - 8;

                    //create new bitmap for cursor
                    Bitmap resultBitmap = new Bitmap(maskCursor.Width, maskCursor.Height);

                    //get the desktop
                    Graphics desktopGraphics = Graphics.FromHwnd(GetDesktopWindow());
                    //get it's device context
                    IntPtr desktopHdc = desktopGraphics.GetHdc();

                    //make it compatible
                    int maskHdc = CreateCompatibleDC(desktopHdc.ToInt32());

                    //set object between context and the cursor
                    int oldPtr = SelectObject(maskHdc, (int)maskCursor.GetHbitmap());

                    //start graphics from image
                    Graphics resultGraphics = Graphics.FromImage(resultBitmap);

                    //get device context
                    IntPtr resultHdc = resultGraphics.GetHdc();

                    //combines image bit depends the Ternary Raster Operations
                    //first (0 - 32) pixels draw normaly, (32 - 64) as inverted
                    BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
                        0, 32, (int)TernaryRasterOperations.SRCCOPY);
               
                    BitBlt(resultHdc.ToInt32(), 0, 0, 32, 32, maskHdc,
                        0, 0, (int)TernaryRasterOperations.SRCINVERT);

                    //release device context
                    resultGraphics.ReleaseHdc(resultHdc);
                    //dispose grahics
                    resultGraphics.Dispose();

                    //make white pixels transparent
                    resultBitmap.MakeTransparent(Color.White);

                    return resultBitmap;

                }
                else
                {
                    //get bitmap from pointer
                    Bitmap colorCursor = Bitmap.FromHbitmap(iconInfo.hbmColor);

                    //make black pixels transparent
                    colorCursor.MakeTransparent(Color.Black);

                    return colorCursor;
                }

            }

            return null;
        }

       /// <summary>
       /// resize the capture image
       /// </summary>
       /// <param name="imgToResize">instance of the image</param>
       /// <param name="size">the new size</param>
       /// <returns>new image stream</returns>
        private MemoryStream resizeImage(Image imgToResize, Size size)
        {
            MemoryStream resizeStream = new MemoryStream();
            int sourceWidth = imgToResize.Width;
            int sourceHeight = imgToResize.Height;

            float nPercent = 0;
            float nPercentW = 0;
            float nPercentH = 0;

            nPercentW = ((float)size.Width / (float)sourceWidth);
            nPercentH = ((float)size.Height / (float)sourceHeight);

            if (nPercentH < nPercentW)
                nPercent = nPercentH;
            else
                nPercent = nPercentW;

            int destWidth = (int)(sourceWidth * nPercent);
            int destHeight = (int)(sourceHeight * nPercent);

            resizeStream = new MemoryStream();

            Bitmap b = new Bitmap(destWidth, destHeight);
            Graphics g = Graphics.FromImage((Image)b);
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            g.DrawImage(imgToResize, 0, 0, destWidth, destHeight);
            g.Dispose();

            b.Save(resizeStream, ImageFormat.Jpeg);


            return resizeStream;
        }

        /// <summary>
        /// get the jpg codec
        /// </summary>
        /// <param name="format">by Imageformat</param>
        /// <returns></returns>
        private ImageCodecInfo GetEncoder(ImageFormat format)
        {

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
    }


ברגע הקריאה לפונקציה makeScreenShot מתחילה שרשרת של תהליכים, תחילה לוכדים את התמונה כפי שהסברתי בהתחלה, אבל לכידת המסך לא אוספת את סמן העכבר ולכן יש לקרוא למספר פונקציות נוספות על מנת למצוא אותו ולצייר אותו ידנית אבל גם זה לא פשוט כמו שזה נשמע, למצביעים בעכבר (Cursor) יש 2 מצבים הגודל שלהם בדר"כ הוא  32 פיקסלים אבל לפעמים הוא משתנה ל 64 פיקסלים בגלל הרקע שמתחת, נעזר במספר פונקציות ומבנים מ user32.dll על מנת לטפל במצבים השונים, בסיום התהליך לוקחים את הפריים, מורידים לו את האיכות ,מקטינים ומחזרים אותו להמשך טיפול.

בדומה לשאר המאמרים גם כאן נבנה תוכנית שנועדה לבדיקת התהליך, התוכנית מפעילה את הפונקציה makeScreenShot בעזרת שעון שדוגם (אם אין תהליך דגימה שכבר רץ) כל 10 ms, שומרים את הפריים כ Jpg על מנת להקטין נפחים וטוענים את ה Stream ל PictureBox שמדמה את הלקוח המרוחק.


מסך בתוך מסך בתוך מסך...

captureForm.cs

        captureControl c;
        int frameCounter = 0;

        public captureForm()
        {
            InitializeComponent();
        }

        private void captureForm_Load(object sender, EventArgs e)
        {
            //create new instance for capture control
            c = new captureControl(1200, 600);
            //fit image to picturebox
            pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
        }

        /// <summary>
        /// running every 10ms
        /// </summary>
        private void timer1_Tick(object sender, EventArgs e)
        {
            //getting screenshot as stream
            if (c.isRunning)
                return;

            try
            {
                //watch for checking performance
                Stopwatch watch = new Stopwatch();

                watch.Start();
                MemoryStream st = c.makeScreenShot();
                watch.Stop();

                //loaded to the pictureBox
                pictureBox1.Image = Image.FromStream(st);

                //change title and calculate the frame size
                frameCounter = frameCounter + 1;
                this.Text = "captureScreen Frame Counter "
                    + frameCounter.ToString() + " Frame Size: "
                    + (st.Length / 1024).ToString() + " KB width:"
                    + pictureBox1.Image.Size.Width.ToString() + " px Height: "
                    + pictureBox1.Image.Size.Height.ToString() +
                    " px Capture Time: " + watch.ElapsedMilliseconds + " ms";

                //dispose the stream, avoid memory leak
                st.Dispose();
                //call the garbage collector
                GC.Collect();

            }
            catch (Exception ex)
            {
                c.isRunning = false;
            }
        }
    }
ביצועים

לכידת התמונה יחד עם ציור סמן העכבר לוקחת בסביבות 150 ms ברזולוציה 1920X1080 עם מעבד 7 Icore (יחסית ישן) מה שנותן בין 6 ל 7 פריימים בשניה, זה לא הכי אידיאלי אבל אם מורידים את הרזולוציה משפרים את המהירות בצורה משמעותית, מבדיקה נוספת שביצעתי על מחשב נייד עם Icore 7 (גם הוא יחסית ישן) לכידת תמונה ברזולוציה 1366X768 לוקחת בסביבות 110 ms.

סיכום

לכידת מסך היא הרכיב האחרון בפאזל לפני שמתחילים לחבר את הכל, אבל חשוב לי להוסיף שקיימות עוד דרכים שאיתן ניתן לבצע את המשימה כמו:

  • DirectX - לכידת מסך גם מחוץ ל Desktop לדוגמה במשחקים.
  • Windows Media Encoder - מתאים בעיקר ללכידת מסך כוידאו.
  • Mirror Display Driver - הדרך הטובה ביותר אבל מאוד מסובכת, נמצאת בשימוש ב UltraVnc, לכל מי שרוצה לדעת עוד יש דוגמה טובה ב Wdk גרסת 7.600 אולי עוד אחזור לזה בהמשך.

  • עוד תמונה ועוד תמונה...



    יום שבת, 24 במאי 2014

    Remote Control Application - Keyboard



    במאמר הראשון ראינו כיצד ניתן לדמות תנועה של עכבר בעזרת תוכנה בלבד, השלב הבא הוא להשתלט על המקלדת וכך להשלים את מנגנוני השליטה של הלקוח על השרת, נבנה מחלקה יעודית שדרכה נזריק את המידע כאירועים של לחיצה במקלדת,נתחבר לפונקציה חיצונית נוספת בקובץ  user32.dll ונעביר בה את המצבים של המקשים.


    keyboardControl.cs

      public class keyboardControl
        {
            //key event signature
            [DllImport("user32.dll")]
            public static extern void keybd_event(byte bVk, byte bScan,
                uint dwFlags, uint dwExtraInfo);

            //keys:up, down, left, right
            //for more keys visit
            //http://msdn.microsoft.com/en-us/library/dd375731%28v=vs.85%29.aspx
            //const int VK_UP = 0x26; 
            //const int VK_DOWN = 0x28;
            //const int VK_LEFT = 0x25;
            //const int VK_RIGHT = 0x27;
            //...

            //set key status
            const uint KEYEVENTF_KEYUP = 0x0002;
            const uint KEYEVENTF_EXTENDEDKEY = 0x0001;
          

            /// <summary>
            /// set the key as press
            /// </summary>
            /// <param name="key">the number as ascii</param>
            public void press(int key)
            {
                //make key press
                keybd_event((byte)key, 0, 
                    KEYEVENTF_EXTENDEDKEY | 0, 0);

            }

            /// <summary>
            /// release the press key
            /// </summary>
            /// <param name="key">the number as ascii</param>
            public  void release(int key)
            {
                //make the key release
                keybd_event((byte)key, 0, 
                    KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0);
            }
        }

    תלחצו על Send וישר תעברו ל Notepad


    תוכנית הבדיקה עבור המנגנון מכילה תיבת טקסט וכפתור כאשר נלחץ עליו היו לנו 5 שניות למצוא את הטופס שאליו אנחנו רוצים להזריק את המחרוזת, בדומה לבקשה שמגיעה מהלקוח לשרת, חשוב מאוד להשים לב שאנחנו מחקים את המקלדת לחלוטין כלומר במקרה שנרצה לרשום את האות a נצטרך להעביר את הערך 0x41 שהוא הערך לאות A בטבלת  Ascii במקרה שנרצה להעביר את האות  A  נצטרך ללחוץ על Shift לרשום את הערך 0x41 ואח"כ לשחרר את ה Shift כפי שניתן לראות בדוגמה:

    KeyboardForm.cs

     public partial class keyboardForm : Form
        {
            public keyboardForm()
            {
                InitializeComponent();
            }

            keyboardControl d;
            private void keyboardForm_Load(object sender, EventArgs e)
            {
                d = new keyboardControl();
            }

            /// <summary>
            /// start inject the message to keyboard
            /// </summary>
            private void btn_send_Click(object sender, EventArgs e)
            {

                if (txb_message.Text != "")
                {
                    //wait 5 second this is enougth time to find application and inject it
                    Thread.Sleep(5000);
                    send();

                    txb_message.Text = "";
                }
            }

            /// <summary>
            /// send the message as key press
            /// </summary>
            private void send()
            {
                string message = txb_message.Text;

                for (int i = 0; i < message.Length; i++)
                {
                    //set shift key on
                    if (char.IsUpper(message[i]))
                        d.press(0x10);

                    //set the keys
                    d.press(char.ToUpper(message[i]));
                    d.release(char.ToUpper(message[i]));

                    //release shift key
                    if (char.IsUpper(message[i]))
                        d.release(0x10);

                }
            }
        }


    סיכום

    עד עכשיו טיפלנו בשליטה של הלקוח על החומרה, השלב הבא הוא לבצע לכידה של המסך בשרת ולהעביר למשתמש מרוחק.

    קצת שליטה לא תזיק...

    Remote Control Application - Mouse




    תוכנות לשליטה מרחוק הן כלי חיוני בניהול מחשבים, זה כלי תמיכה מעולה ולגיטמי ברוב המקרים אבל יש מקרים שזה עלול להיות כלי מסוכן שיכול לעלות לנו ביוקר, בסדרת המאמרים הקרובה אתמקד כיצד לבנות תוכנית שתאפשר לנו לשלוט במחשבים מבוססי Windows מרחוק, התוכנית מחולקת ל 2 חלקים כיאה לעבודה עם Sockets אבל לפני שנתחיל לכתוב את צד השרת והלקוח קודם נגדיר על איזה מנגנונים נאפשר שליטה.

    המנגנון הראשון הוא העכבר, אנחנו רוצים להזיז וללחוץ עליו מרחוק, נגדיר מחלקה שדרכה ניגשים למשאבים של העכבר, בעזרת קריאה לפונקציה mouse_event נוכל לדמות אירועים של כפתורים, נוסיף פונקציות נוספות שדרכן נזיז את העכבר לנקודה במסך ונקבל את מיקום הסמן.

     mouseControl.cs

        public class mouseControl
        {
            /// <summary>
            /// mouse virtual buttons
            /// </summary>
            public enum mouseButtons
            {
                LEFT,
                RIGHT
            }

            //mouse event signature
            [DllImport("user32.dll", CharSet = CharSet.Auto, 
                CallingConvention = CallingConvention.StdCall)]

            public static extern void mouse_event(long dwFlags, long dx,
                long dy, long cButtons, long dwExtraInfo);

            //mouse event types, for more events visit this link
            //http://msdn.microsoft.com/en-us/library/windows/desktop/ms646260(v=vs.85).aspx
            private const int MOUSEEVENTF_LEFTDOWN = 0x02;
            private const int MOUSEEVENTF_LEFTUP = 0x04;
            private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
            private const int MOUSEEVENTF_RIGHTUP = 0x10;

            /// <summary>
            /// raise mouse click event
            /// </summary>
            /// <param name="btn">left / right buttons</param>
            public void mouseClick(mouseButtons btn)
            {
                switch (btn)
                {
                    case mouseButtons.LEFT:
                        mouse_event(MOUSEEVENTF_LEFTDOWN | 
                            MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
                        break;
                    case mouseButtons.RIGHT:
                        mouse_event(MOUSEEVENTF_RIGHTDOWN |
                            MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
                        break;
                    default:
                        break;
                }
            }

            /// <summary>
            /// move the mouse to new location
            /// </summary>
            /// <param name="x">x position</param>
            /// <param name="y">y position</param>
            public void mouseMove(int x, int y)
            {
                Cursor.Position = new Point(x, y); 
            }

            /// <summary>
            /// get the cursor location on screen
            /// </summary>
            /// <returns>cursor location</returns>
            public Point screenLocation()
            {
                return Cursor.Position;
            }

        }


    על מנת שנוכל לבדוק את המחלקה נבנה תוכנית מאוד פשוטה, מדובר על טופס שמכיל מספר כפתורים ושדות שדרכם נשלוט בעכבר, בנוסף נגדיר שעון שדוגם כל 100ms את המיקום הנוכחי של העכבר ע"ג המסך, המטרה של התוכנית היא קודם כל לבדוק את הקוד מקומית ואח"כ מרחוק כפי שנראה במאמרים הבאים.




       mouseForm.cs

        public partial class mouseForm : Form
        {
            mouseControl c;

            public mouseForm()
            {
                InitializeComponent();
            }

            private void mouseForm_Load(object sender, EventArgs e)
            {
                txb_x.Text = "0";
                txb_y.Text = "0";
                c = new mouseControl();
            }

            /// <summary>
            /// timer for polling the location every 100ms
            /// </summary>
            private void timer1_Tick(object sender, EventArgs e)
            {
                Point p = c.screenLocation();

                lbl_x.Text = "X:" + p.X.ToString();
                lbl_y.Text = "Y:" + p.Y.ToString();
            }

            /// <summary>
            /// getting the location of the cursor
            /// </summary>
            private void setLocation()
            {
                int x = 0, y = 0;

                int.TryParse(txb_x.Text, out x);
                int.TryParse(txb_y.Text, out y);

                c.mouseMove(x, y);
            }

            /// <summary>
            /// move the cursor by x and y
            /// </summary>
            private void btn_move_Click(object sender, EventArgs e)
            {
                //set the cursor location
                setLocation();
            }

            /// <summary>
            /// move the cursor and make double click on the left button
            /// </summary>
            private void btn_left_Click(object sender, EventArgs e)
            {
                //set the cursor location
                setLocation();

                //single click
                c.mouseClick(mouseControl.mouseButtons.LEFT);
                //add other request for double click
                c.mouseClick(mouseControl.mouseButtons.LEFT);
            }

            /// <summary>
            /// move the cursor and make double click on right button
            /// </summary>
            private void btn_right_Click(object sender, EventArgs e)
            {
                //set the cursor location
                setLocation();

                //single click
                c.mouseClick(mouseControl.mouseButtons.RIGHT);
                //add other request for double click
                c.mouseClick(mouseControl.mouseButtons.RIGHT);
            }
        }


    סיכום

    אז אחרי שיש שליטה על העכבר נעבור למנגנונים נוספים כמו המקלדת ולכידת מסך אח"כ נכתוב את צד השרת ואת צד הלקוח ונשלב בהם את המחלקות שבנינו יחד עם אובייקט מרכזי שיעבור בניהם.

    תחילת של דלת סתרים....

    יום רביעי, 12 בפברואר 2014

    Managed / UnManaged Code




    לאורך הבלוג נזרק המושג Managed Code מספר פעמים ולא יותר מידי הרחבתי עליו , אבל מסתתר מאחוריו פואנטה שכדאי להכיר ולכן החלטתי לכתוב מאמר קצרצר שיסביר את הנושא למרות שכבר עברתי עליו בצורה כזאת או אחרת בבלוג, החיבור בין 2 שיטות ריצה שונות מאפשר יצירת תוכניות מורכבות שמאפשרות ניצול של 2 פלטפורמות שונות זו מזו, לדוגמה האפשרות לכתוב שכבה שעובדת מול Win32 API שלא ניתן לגשת אליה באופן חופשי מ .Net כפי שכבר הראתי בעבר ב Personal Firewall.


    Managed Code

    סביבת .Net מוגדרת כ Manage Code ומזכירה בהתנהגות שלה את Java בכך שב 2 הסביבות נוצר חדר נקי שמאפשר לקוד לרוץ ללא הפרעות, במקרה של .Net אומנם נוצר קובץ EXE בסוף התהליך, אך כאשר מפעילים אותו הוא מפעיל מערכת שלמה מאחורי הקלעים על מנת שיוכל לרוץ, כאשר הקובץ נוצר, הוא נשמר בשפת בניים הנקראת IL ועובר למנגנון CLR ומשם ל JIT כפי שכתבתי במאמר Managed .Net RootKit, לכן זה לא באמת קוד מכונה, הוא נהפך לקוד מכונה תוך כדי ריצה.

    UnManaged Code

    זה קוד שנהפך לקוד מכונה ברגע שמבצעים את ה Compile ומאותו רגע הוא יכול לרוץ ולכן מהיר יותר, בדר"כ נכתוב אותו ב ++C/ C ונשתמש ב Compilers ותיקים בתחום, לא קיים חדר נקי ולכן התוכניות עלולות לגרום נזק למערכת הפעלה ישירות, בדרך כלל נשתמש בו כאשר נרצה לדבר ישירות עם ה Win32API או לשפר ביצועים, השפה שנהנת מכל העולמות היא ++C שמאפשרת כתיבת קוד משולב.

    שימו לב! - ב Managed Code קיים מנגנון ה Garbage Collector שמשחרר זיכרון באופן עצמי גם אם לא שחררתם אותו בעצמכם לעומת זאת ב UnManaged Code הוא לא קיים ואחריות שחרור הזיכרון היא על המפתחים בלבד.

    בדוגמה הבאה אני ידגים כיצד לחבר בין אפליקציית .Net עם פונקציות שנכתבו ב  ++C/ C, תחילה נתמקד ב UnManaged Code שאותו אני אכתוב ב ++C בעזרת Visual Studio.

    dllctest.h

    //in C++ we can have functions with the same name,
    //the compiler create unique name for each function, 
    //in C is not possible to have different functions with the same name, 
    //the extern "C" tell the compiler to keep the name as is like in C
    //be aware from functions with the same name!.
    extern "C" {
    //exported function signature,
    //__declspec tell to compiler to expose the function
    __declspec(dllexport)   char *  getString(void);
    __declspec(dllexport)   void  setString(char * data);
    }


    dllctest.cpp

    //dllctest.cpp : Defines the exported functions for the DLL application.
    #include "stdafx.h"
    #include "dllctest.h"

    static char * sdata;

    //tell to compiler someone else going to use this method
    extern char * getString(void)
    {
    return sdata;
    }

    //tell to compiler someone else going to use this method
    extern void setString(char * data)
    {
    sdata = data;
    }

    יש מספר מילים שמורות שאומרות ל Compiler שמדובר על פונקציות שמשהו אחר (חיצוני) הולך להשתמש בהם, ומאפשרות לחשוף אותן החוצה, בגלל שהקוד עצמו  ב ++C חשוב להגיד ל Compiler שישמור על השמות של הפונקציות כמו שהם בדומה להתנהגות של C כי אחרת הוא ממציא להן שמות בעצמו.

    שימו לב! - חשוב מאוד שהשמות של הפונקציות לא יחזרו על עצמן, ב C זה בלתי אפשרי אבל ב ++C זה אפשרי ועלולה להיות התנגשות בין הפונקציות.

    dllctestcs.cs

    class Program
    {
            //call to external function from dll 
            //using the same signature from the dll
            [DllImport(@"C:\inetpub\wwwroot\dllctest\Debug\dllctest.dll", CharSet = CharSet.Unicode)]
            extern static void setString(string data);

            [DllImport(@"C:\inetpub\wwwroot\dllctest\Debug\dllctest.dll",CharSet=CharSet.Unicode)]
            extern static string getString();

            static void Main(string[] args)
            {
                setString("HI");

                Console.Write(getString());

                Console.ReadLine();
            }
        }
    על מנת לדבר עם ה DLL שיצרנו נשתמש ב (Platform Invocation Services (PInvoke , מנגנון שמגיע עם .Net שמאפשר לטעון DLL של UnManaged Code תוך כדי ריצה, במקרה שלנו אנחנו מעבירים מחרוזות לכן חשוב להגדיר את סוג הקידוד.

    סיכום

    תחשבו לכם איזה יתרון אדיר יש בשילוב בין 2 פלטפורמות שונות שמאפשרות לנו לגעת בכל העולמות, לצורך העניין ב .Net יש כלים נהדרים לעבודה על אתרי אינטרנט, בניית ממשקים, DB וכו' ואילו ב C נוכל לשפר ביצועים ולהגיע למקומות ש .Net לא יכול.

    השילוב הראוי...

    יום שני, 27 בינואר 2014

    C# Reflection Guide



    אחד התחומים המעניינים בפיתוח, האפשרות לתוכנית ללמוד על עצמה תוך כדי ריצה מאפשרת לפתח תוכניות מיוחדות במיוחד, האמת שנושא ה Reflection ישן והוא קיים עוד מתחילת ה Framework הראשון אבל אף פעם לא התייחסתי אליו כי בדרך כלל אין בו צורך, לרוב אנחנו יודעים מה אנחנו הולכים לפתח אבל קיימים מקרים מסויימים בהם יש חור שחור ואין לנו מספיק מידע ולכן עלינו לשמור על קוד גנרי ככל האפשר.

    Reflection

    יכולת של תוכנית לזהות במהלך הריצה את האובייקטים שרצים בה, לנתח אותם ולהפעיל בהם פונקציות באופן דינמי לפי סוג האובייקט או על פי קונבנציה קבועה, במאמר זה אני יעבור על נושא ה Reflection בסביבת .Net אבל היכולת הזאת נתמכת גם בסביבות נוספות כמו JavaScript, C++,Phyton וכו', הרעיון המרכזי הוא להפוך את התוכנית ל"שקופה" ושתוכל להתמודד עם אובייקטים שונים מבלי לעבור Compile כל פעם מחדש.

    לצורך הדוגמה בניתי תוכנית והיא מאפשרת למשתמשים ליצור עבורה Plugins מיוחדים, מהרגע שהכנסתי את התכונה הזאת אני לא יודע מה המשתמש יעשה (האמת שזה לא כלכך צריך לעניין אותי), ואני לא יכול לעשות Compile לתוכנית המקורית כל פעם מחדש כי זה יהיה מגוחך, כאן נכנס ה Reflection לתמונה שבעזרתו אני יכול להגדיר קונבנציה עבור טעינת Plugin כחלק מה SDK של התוכנית, משתמש הקצה מייצר אובייקט שמכיל בתוכו קריאות לפונקציות שנקבעו מראש שמטרתם לחבר בין התוכנית לקוד שהמשתמש יצר, ברגע שמופעלת הפונקציה הקוד של המשתמש רץ מבלי שהיה חלק מהתוכנית עצמה כפי שניתן לראות בקוד הבא:

    public class example
        {
            //private string
            private string msg0 = "this is exmaple of private string!";

            //public string
            public string msg1 = "this is exmaple of public string!";

            //constructor
            public example()
            {

            }

            //public function for invoke return string
            public string returnPrivateMessage()
            {
                return msg0;
            }

            //public function for invoke getting external 
            //parameter and return string
            public string returnEditMessage(string msg)
            {
                return "you write: " + msg;
            }
        }


    המשתמש עוטף את המחלקה כ DLL ומעתיק אותה לתיקיית Plugin בתוכנית,התוכנית טוענת אותו לזיכרון ומחפשת את הפונקציה שנקבעה מראש על מנת להפעיל אותה ובעצם מעבירה את הריצה ל DLL מבלי לדעת מה בדיוק עומד לקרות שם.


    class Program
        {
            static void Main(string[] args)
            {
                //load assmbly dynamically
                Assembly assemblyInstance = Assembly.LoadFrom(@"C:\\RefDLL.dll");

                Type[] types = assemblyInstance.GetTypes();

                for (int i = 0; i < types.Length; i++)
                {
                    //search for specific type
                    if (types[i].Name.ToLower() == "example")
                    {
                        MethodInfo[] methods = types[i].GetMethods();
                        if (methods != null)
                        {
                            for (int z = 0; z < methods.Length; z++)
                            {
                                //search for specific method
                                if (methods[z].Name == "returnPrivateMessage")
                                {
                                    //create instance for dynamic object
                                    object classInstance = Activator.CreateInstance(types[i], null);

                                    //execute method and gettig string
                                    object res = methods[z].Invoke(classInstance, new object[0]);

                                }

                                if (methods[z].Name == "returnEditMessage")
                                {
                                    //create parameters array for function,
                                    //you can get the types of the arguments using
                                    //ParameterInfo[] parameters = methods[z].GetParameters();
                                    object[] parametersArray = new object[] { "my information!" };
                                    object classInstance = Activator.CreateInstance(types[i], null);

                                    object res = methods[z].Invoke(classInstance, parametersArray);
                                }
                            }
                        }
                    }
                }

            }
        }



    ראינו כיצד ניתן להפוך את התוכנית למשהו גנרי אבל מצד שני הקוד שלנו חשוף וכל אחד יכול להפעיל אותו, הצגתי בעבר כיצד ניתן לעשות Disassembly לקבצי DLL שנוצרו ב .Net וה Reflection לוקח חלק חשוב בתהליך, לסיום החלטתי שזו הזדמנות טובה להראות את היכולות של ה Reflection בעזרת כלי קטן שמאפשר לנתח קבצי DLL בצורה נוחה ומהירה, בדומה לדוגמה הקודמת התוכנית מקבלת נתיב ל DLL ופורסת אותו, היא מראה את הפונקציות, המשתנים והאירועים של האובייקטים בקובץ.

    DLLReflector


    סיכום:

    אז כמו שכבר אמרתי בהתחלה בדרך כלל לא נמצא ל Reflection שימוש אלא במקרים מאוד מסויימים, ההתעסקות בניתוח האובייקט עלולה להפוך את התוכנית למסורבלת ואיטית ולכן כדאי לחשוב פעמיים לפני שבוחרים בדרך הזאת, אבל אין ספק שזה לוקח את האינטליגנציה של התוכנה צעד אחד קדימה.

    קוד להורדה:


    סקיינט?



    יום שבת, 15 ביוני 2013

    Android BlueTooth Guide



    במאמר האחרון בנושא ראינו כיצד ניתן להשתמש ב GPS שבמכשיר לצרכי האפליקציה שלנו אבל קיימים עוד רכיבים מגניבים שצריך לעבור עליהם, אחד הדברים שסקרנו אותי הוא כיצד אני יכול לשלוט בעזרת הסמארטפון או הטאבלט על מכשירים אחרים והחלטתי להתמקד ב BlueTooth כי כמעט כל מכשיר תומך בתקשורת הזאת והיא נחשבת לזולה ואמינה, ה"שן הכחולה" מבוססת על גלי רדיו קצרים בתדר 2.4 Mhz שעד טווח של מאה מטר ובשנת 1999 הוגדרה כתקן וקיבלה המון שיפורים עם השנים.


    android.bluetooth

    המפתחים של Android כתבו מרחב שמות של מחלקות שמאפשר לנו להשתמש ב Bluetooth ולבצע פעולות רבות כמו חיפוש מכשירים, אירוח משתמשים או התחברות למארח, ההתממשקות מבוססת על כתובת ה MAC במכשיר ועל פרמטר ייחודי של האפליקציה, על מנת שנוכל לתקשר ב BlueTooth יש לעבוד ב Thread נפרד ולתת מספר הרשאות בקובץ Manifest  כפי שניתן לראות בדוגמה:

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

    המחלקות העיקריות הן:

    רכיב ה BlueTooth שעל המכשיר

    מכשיר חיצוני 

    אירוח מכשירים חיצוניים

    הקישור בין המכשירים


    Android

    הדוגמה הראשונה הופכת את המכשיר שלנו לשרת שממתין למכשירים חיצוניים שהתחברו אליו ובעצם אנחנו במצב האזנה אינסופי בעזרת AsyncTask שמאפשר לנו לעשות את התהליך ב Thread נפרד מבלי לחסום את האפליקציה.

    private class BTListener extends AsyncTask<String, String, String> {

    private BluetoothServerSocket mmServerSocket;
    private OutputStream mmOutStream;
    private InputStream mmInStream;
    private final String NAME = "BlueTooth Test Application";
    private UUID MY_UUID;
    public String message = "";

    //getting application uid and start to listen
    public BTListener(String CODE) {
    MY_UUID = UUID.fromString(CODE);

    //getting device adapter
    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter
    .getDefaultAdapter();
    if (mBluetoothAdapter == null) {
    // no bluetooth adapter found.
    return;
    }

    BluetoothServerSocket tmp = null;
    try {
    tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(
    NAME, MY_UUID);
    } catch (IOException e) {
    }

    mmServerSocket = tmp;
    }

    // create listener to start serving the client.
    private void executeListener() {

    BluetoothSocket socket = null;
    message = "";

    while (true) {

    try {
    // enter to blocking mode until connection create, or timeout
    socket = mmServerSocket.accept();

    // getting the sockets
    mmInStream = socket.getInputStream();
    mmOutStream = socket.getOutputStream();

    int bytesRead = 0;

    int bufferSize = 1024;
    byte[] buffer = new byte[1024];

    message = "";
    // get the message from client and update the gui.
    while (true) {

    bytesRead = mmInStream.read(buffer);

    if (bytesRead != -1) {
    while ((bytesRead == bufferSize)
    && (buffer[bufferSize] != 0)) {
    message = message
    + new String(buffer, 0, bytesRead);
    bytesRead = mmInStream.read(buffer);
    }
    // update the gui from another thread.
    publishProgress(new String(buffer, 0, bytesRead));

    }
    }
    } catch (IOException e) {
    break;
    } finally {
    if (socket != null) {
    try {
    mmServerSocket.close();
    } catch (IOException e) {
    break;
    }
    }
    }
    }

    }

    // write message to the outputstream
    public void write(String msg) {

    byte[] buffer = msg.getBytes();
    try {
    mmOutStream.write(buffer);

    } catch (IOException e) {
    Log.e(TAG, "Exception during write", e);
    }
    }

    @Override
    protected void onPostExecute(String result) {
    super.onPostExecute(result);

    }

    @Override
    protected void onProgressUpdate(String... values) {
    super.onProgressUpdate(values);
    // sending information to function in the activity thread
    setLblMessage(values[0]);
    }

    @Override
    protected String doInBackground(String... params) {
    executeListener();
    return null;
    }

    }
    }

    על מנת להשלים את התמונה אנחנו צריכים מכשיר שיתחבר ל Android בהתחלה חשבתי להתחבר עם מכשיר Android נוסף, האמת שזו הזדמנות לשלב פלטפורמה נוספת כמו מחשב פיסי רגיל ובעזרת .Net לכתוב את הקוד , אבל העסק לא בא בחבילה אחת כמו ב Android וצריך להשתמש בספריות צד שלישי, החלטתי להשתמש עבור הדוגמה ב 32Feet.net שהיא מאוד נוחה וכמובן חינמית, המחלקה הזו יוצרת חיבור עם המכשיר שלנו ובעצם פותחת את ה Socket שמאפשר למכשירים לתקשר אחד עם השני, בדומה ל Android גם פה ניכנס למצב האזנה אינסופי ובעזרת BackgroundWorker נעשה זאת ב Thread חיצוני. 



    .Net

    מקבלים את רשימת המכשירים שמארחים בעזרת הפונקציה getAdapter ,יוצרים אובייקט חדש מסוג BTDesktopCLient ומעמיסים עליו את המכשיר המתאים יחד עם המפתח היחודי של האפליקציה.

    using InTheHand.Net.Sockets;

     public class BTDesktopCLient
        {
            //return message to gui.
            public delegate void _delegateReadHandler(string msg);
            public event _delegateReadHandler readHandler;

            private const int MSGSIZE = 1024;

            BluetoothClient _client;
            BluetoothDeviceInfo _peer;
            BackgroundWorker _worker;
            Stream _peerStream;
            Guid _uid;
            private BluetoothAddress bluetoothAddress;
            private Guid _gu;

            public static BluetoothDeviceInfo[] getAdapter()
            {
                BluetoothClient tmp = new BluetoothClient();
                return tmp.DiscoverDevices();
            }

            //getting the device with application unique key
            public BTDesktopCLient(BluetoothDeviceInfo peer, Guid uid)
            {
                _uid = uid;
                _peer = peer;
                _worker = new BackgroundWorker();
                _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
             
                connect();
                _worker.RunWorkerAsync();
            }

            //make the first connection with the device 
            //sending the unique application UID.
            public void connect()
            {
                try
                {
                    _client = new BluetoothClient();
                    BluetoothEndPoint ep = new BluetoothEndPoint(_peer.DeviceAddress, _uid);
                    _client.Connect(ep);
                    _peerStream = _client.GetStream();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

            //write to the socket
            public void Write(string msg)
            {
                if (!_client.Connected)
                    return;
                try
                {

                    byte[] msgBuff = Encoding.ASCII.GetBytes(msg);
                    _peerStream.Write(msgBuff, 0, msgBuff.Length);
                    _peerStream.Flush();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

            void _worker_DoWork(object sender, DoWorkEventArgs e)
            {
                //entering infinity loop
                looper();
            }

            void looper()
            {
                //starting listening to the connected socket for incoming stream
                while (true)
                {
                    byte[] buff = new byte[MSGSIZE];
                    int bytesRead = 0;

                    bytesRead = _peerStream.Read(buff, 0, buff.Length);
                    while (bytesRead != 0)
                    {
                        readHandler(Encoding.ASCII.GetString(buff).Trim());
                        buff = new byte[MSGSIZE];
                        bytesRead = _peerStream.Read(buff, 0, buff.Length);
                    }
                 
                }
            }
        }

    יופי, אז עד עכשיו ראינו כיצד Android מארח חיבורים חיצוניים כמו מחשב הפיסי הרגיל שיש לנו בבית השלב הבא הוא להפוך את מחשב הפיסי למארח של משתמש Android  אם נשים לב לקוד נראה ש BTListener מקבל רק את המפתח היחודי של האפליקציה ולעומת זאת ה BTDesktopCLient מקבל מפתח ו Device שאליו הוא מעוניין להתחבר נהפוך את התהליך בעזרת מחלקות נוספות.

    Android

    עכשיו על ה Android לחפש מכשירים וליזום את החיבור, ההבדל העיקרי הוא שיש לבצע Discovering בדומה ל BTDesktopClient ב .Net, על מנת שנוכל למצוא את המכשיר המארח ואת זה עושים בעזרת פונקציה פשוטה כמו בדוגמה הבאה:

    private void discovery() {
    mBluetoothAdapter.startDiscovery();
    Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
    .getBondedDevices();

    if (pairedDevices.size() > 0) {
    for (BluetoothDevice device : pairedDevices) {
    Log.d(TAG, device.getName());
    //send the wanted device to BTClient
    }
    }
    }


    לאחר שמצאנו את המכשיר  נייצר אובייקט מסוג BTClient ונעביר אליו את ה Device ואת המפתח הייחודי של האפליקציה גם הפעם נצטרך להעזר ב AsyncTask על מנת לרוץ ברקע.

    private class BTClient extends AsyncTask<String, String, String> {
                    private final String TAG = null;
    private BluetoothSocket mmSocket;
    private BluetoothDevice mmDevice;
    private BluetoothAdapter mBluetoothAdapter;
    private InputStream mmInStream;
    private OutputStream mmOutStream;
    private UUID mmUUID;

    //getting the device with application unique key
    public BTClient(BluetoothDevice device, String CODE) {
    mmUUID = UUID.fromString(CODE);
    mmDevice = device;
    BluetoothSocket tmp = null;

    //getting the your own device adpater
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

    //try to create a socket
    try {
    tmp = device.createRfcommSocketToServiceRecord(mmUUID);
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    mmSocket = tmp;

    }

    @Override
    protected String doInBackground(String... params) {

    //if our adapter on discovering, cancel it
    if(mBluetoothAdapter.isDiscovering())
    mBluetoothAdapter.cancelDiscovery();
    try {
    //make the connection and collect the streams
    mmSocket.connect();
    mmInStream = mmSocket.getInputStream();
    mmOutStream = mmSocket.getOutputStream();
    } catch (IOException e) {
    e.printStackTrace();
    }

    byte[] buffer = new byte[1024];
    int bytes;
    String message = "";

    // keep listening to the InputStream while connected
    while (true) {
    try {
    // read from the InputStream
    bytes = mmInStream.read(buffer);
    message = message + new String(buffer, 0, bytes);

    publishProgress(new String(buffer, 0, bytes));
    mmSocket.getInputStream();

    } catch (IOException e) {
    Log.e(TAG, "disconnected", e);
    break;
    }
    }

    return null;
    }

    //write message to the outputstream
    public void write(String msg) {

    byte[] buffer = msg.getBytes();
    try {
    mmOutStream.write(buffer);
    } catch (IOException e) {
    Log.e(TAG, "Exception during write", e);
    }
    }

    @Override
    protected void onPostExecute(String result) {
    super.onPostExecute(result);
    }

    @Override
    protected void onProgressUpdate(String... values) {
    super.onProgressUpdate(values);
    //sending information to function in the activity thread
    setLblMessage(values[0]);
    }

    }


    .Net

    בדומה ל Android גם פה נאזין במצב אינסופי על המפתח הייחודי ובעזרת BackgroundWorker ,נמתין עד שמשתמש יבקש להתחבר ונפתח Socket מולו.

    using InTheHand.Net.Sockets;

    public class BTDesktopListener
        {
            // return message to gui.
            public delegate void _delegateReadHandler(string msg);
            public event _delegateReadHandler readHandler;

            private const int MSGSIZE = 1024;

            Guid _uid;
            BluetoothListener _listener;
            BluetoothClient _client;
            Stream _peerStream;

            BackgroundWorker _worker;

            // getting application uid and start to listen
            public BTDesktopListener(Guid uid)
            {
                _uid = uid;
                _worker = new BackgroundWorker();
                _worker.DoWork += new DoWorkEventHandler(_worker_DoWork);
                _worker.RunWorkerAsync();

            }


            // create listener to start serving the client.
            void _worker_DoWork(object sender, DoWorkEventArgs e)
            {
               //entering infinity loop
                looper();
            }

            void looper()
            {
                _listener = new BluetoothListener(_uid);
                _listener.Start();

                while (true)
                {
                    // enter to blocking mode until connection create, or timeout
                    _client = _listener.AcceptBluetoothClient();
                    _peerStream = _client.GetStream();
                    byte[] buff = new byte[MSGSIZE];
                    int bytesRead = 0;

                    // getting the socket
                    bytesRead = _peerStream.Read(buff, 0, buff.Length);
                    while (bytesRead != 0)
                    {
                        // get the message from client and update the gui.
                        readHandler(Encoding.ASCII.GetString(buff).Trim());
                        buff = new byte[MSGSIZE];
                        bytesRead = _peerStream.Read(buff, 0, buff.Length);
                    }
                }
            }

            //write to the socket
            public void Write(string msg)
            {
                if (!_client.Connected)
                    return;

                try
                {
                    byte[] msgBuff = Encoding.ASCII.GetBytes(msg);
                    _peerStream.Write(msgBuff, 0, msgBuff.Length);
                    _peerStream.Flush();
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

        }

    כיצד זה נראה ב Android:


    כיצד זה נראה ב PC:


    סיכום:

    ראינו את התהליך משני צדדי המטבע ועכשיו ניתן לשלב בין הפלטפורמות או לעבוד על כל פלטפורמה בנפרד, המאמר הזה הוא נקודת ציון להמשך שבו נשלב את ה Android עם מערכות משובצות מחשב אחרות ונוכל לשלוט בהן מרחוק.

    קבצי מקור:

    טיפולי שיניים משהו?