Solved

C# maintain application focus

Posted on 2010-09-22
15
790 Views
Last Modified: 2013-11-07
I have created a basic application with c# and it is set to always be maximized and on top. However i also need it to maintain focus, even if something else runs on the machine and takes focus away.

How might i go about doing this?

Thanks
0
Comment
Question by:ttphil
  • 5
  • 5
  • 2
  • +2
15 Comments
 
LVL 12

Expert Comment

by:jagssidurala
Comment Utility
we have an option called MaintainAutopostback="True" in page directives.
just add this property to page directives.
0
 
LVL 5

Expert Comment

by:roxviper
Comment Utility
0
 
LVL 2

Author Comment

by:ttphil
Comment Utility
Is this only for web apps? Mine is a windows forms app.
I have never used this before but from what i can see it maintains control focus not application focus?
0
 
LVL 7

Expert Comment

by:illusio
Comment Utility

- Override or add a handler to the forms LostFocus()
- In the lostfocus do an API call to NativeMethods.SetForegroundWindow(Me.Handle)

Define the API Call in a seperate class named nativemethods as follows:

Friend NotInheritable Class NativeMethods
    Private Sub New()
    End Sub

     _
    Friend Shared Function SetForeGroundWindow(ByVal hwnd As IntPtr) As Integer
    End Function

End Class
0
 
LVL 2

Author Comment

by:ttphil
Comment Utility
Again this appears to control focus for the control, not for the entire application.

For example, i have a barcode reader putting text into a textbox, if the form looses focus the barcode reader doesnt input the barcode into the textbox - therefore i need to ensure the application maintains focus no matter what else is opened on the machine
0
 
LVL 5

Expert Comment

by:roxviper
Comment Utility
The link is for windows forms(Desktop Application). Please read the entire article
0
 
LVL 8

Expert Comment

by:Subrat (C++ windows/Linux)
Comment Utility
1. Select the form
2. go to properties->Window State(Make it normal) by which when it'll be launched it'll be a normal window. Hope in your case it's set to maximized
3.  Also need to change another property i.e TopMost--------------------------------> make it false/true according to our need.

Hope this may help you.

If working on ASP.net then see in the property--------------------> AutoPostBack(bydefault it's false, make it true)
http://www.5min.com/Video/How-to-Use-Auto-Postback-Events-in-ASPNET-80725185
0
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 8

Expert Comment

by:Subrat (C++ windows/Linux)
Comment Utility
sorry for the late
0
 
LVL 2

Author Comment

by:ttphil
Comment Utility
Roxviper, sorry my response was to illusio, but i have just been looking at the link you sent and I will try a few of the suggestions that are in there. Will let you know how i get on.
0
 
LVL 7

Expert Comment

by:illusio
Comment Utility
Oki - I must admit. It has been a while since I solved this.

It's butt-ugly but it does the trick and it's about the only thing you can get working in "managed" code for as far as I know...

Add the code below to your form and it'll work...
Protected Overrides Sub OnActivated(ByVal e As System.EventArgs)
        lstQueryResult.Items.Add("activate")
        If _activeCtrl IsNot Nothing Then
            lstQueryResult.Items.Add("refocus")
            _activeCtrl.Focus()
            _activeCtrl = Nothing
        End If
    End Sub

    Private _activeCtrl As Control = Nothing
    Private _timer As System.Threading.Timer = Nothing
    Protected Overrides Sub OnDeactivate(ByVal e As System.EventArgs)
        _activeCtrl = Me.ActiveControl
        lstQueryResult.Items.Add("deactivate")
        _timer = New System.Threading.Timer(New Threading.TimerCallback(AddressOf ActivateThis), Nothing, 1, Threading.Timeout.Infinite)
    End Sub

    Private Delegate Sub ActivateThisDelegate(ByVal state As Object)
    Private Sub ActivateThis(ByVal state As Object)
        If Me.InvokeRequired Then
            Me.Invoke(New ActivateThisDelegate(AddressOf ActivateThis), state)
        ElseIf Not Me.IsDisposed Then
            NativeMethods.SetWindowPos(Me.Handle, NativeMethods.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.ShowWindow Or SetWindowPosFlags.IgnoreMove Or SetWindowPosFlags.IgnoreResize)
            NativeMethods.SetForegroundWindow(Me.Handle)
        End If
    End Sub

Open in new window

0
 
LVL 7

Expert Comment

by:illusio
Comment Utility
Lol - typed in VB.NET.... mistakingly though the question was for VB.NET
If needed I can provide you with the C# Part andalso - I forgot some classes in the snippet - they are in VB.NET again.
Friend NotInheritable Class NativeMethods
    Private Sub New()
    End Sub
    <DllImport("user32.dll")> _
    Friend Shared Function SetForegroundWindow(ByVal hwnd As IntPtr) As Integer
    End Function

    Public Shared ReadOnly HWND_BOTTOM As New IntPtr(1)
    Public Shared ReadOnly HWND_NOTOPMOST As New IntPtr(-2)
    Public Shared ReadOnly HWND_TOP As New IntPtr(0)
    Public Shared ReadOnly HWND_TOPMOST As New IntPtr(-1)

    <DllImport("user32.dll")> _
    Friend Shared Function SetWindowPos( _
        ByVal hwnd As IntPtr, _
        ByVal hWndInsertAfter As IntPtr, _
        ByVal X As Integer, _
        ByVal Y As Integer, _
        ByVal cx As Integer, _
        ByVal cy As Integer, _
        ByVal uFlags As SetWindowPosFlags) As Integer
    End Function
End Class

<Flags()> _
<CLSCompliant(False)> _
Public Enum SetWindowPosFlags As UInteger
    ''' <summary>If the calling thread and the thread that owns the window are attached to different input queues, 
    ''' the system posts the request to the thread that owns the window. This prevents the calling thread from 
    ''' blocking its execution while other threads process the request.</summary>
    ''' <remarks>SWP_ASYNCWINDOWPOS</remarks>
    SynchronousWindowPosition = &H4000
    ''' <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
    ''' <remarks>SWP_DEFERERASE</remarks>
    DeferErase = &H2000
    ''' <summary>Draws a frame (defined in the window's class description) around the window.</summary>
    ''' <remarks>SWP_DRAWFRAME</remarks>
    DrawFrame = &H20
    ''' <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to 
    ''' the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE 
    ''' is sent only when the window's size is being changed.</summary>
    ''' <remarks>SWP_FRAMECHANGED</remarks>
    FrameChanged = &H20
    ''' <summary>Hides the window.</summary>
    ''' <remarks>SWP_HIDEWINDOW</remarks>
    HideWindow = &H80
    ''' <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the 
    ''' top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter 
    ''' parameter).</summary>
    ''' <remarks>SWP_NOACTIVATE</remarks>
    DoNotActivate = &H10
    ''' <summary>Discards the entire contents of the client area. If this flag is not specified, the valid 
    ''' contents of the client area are saved and copied back into the client area after the window is sized or 
    ''' repositioned.</summary>
    ''' <remarks>SWP_NOCOPYBITS</remarks>
    DoNotCopyBits = &H100
    ''' <summary>Retains the current position (ignores X and Y parameters).</summary>
    ''' <remarks>SWP_NOMOVE</remarks>
    IgnoreMove = &H2
    ''' <summary>Does not change the owner window's position in the Z order.</summary>
    ''' <remarks>SWP_NOOWNERZORDER</remarks>
    DoNotChangeOwnerZOrder = &H200
    ''' <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to 
    ''' the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent 
    ''' window uncovered as a result of the window being moved. When this flag is set, the application must 
    ''' explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
    ''' <remarks>SWP_NOREDRAW</remarks>
    DoNotRedraw = &H8
    ''' <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
    ''' <remarks>SWP_NOREPOSITION</remarks>
    DoNotReposition = &H200
    ''' <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
    ''' <remarks>SWP_NOSENDCHANGING</remarks>
    DoNotSendChangingEvent = &H400
    ''' <summary>Retains the current size (ignores the cx and cy parameters).</summary>
    ''' <remarks>SWP_NOSIZE</remarks>
    IgnoreResize = &H1
    ''' <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
    ''' <remarks>SWP_NOZORDER</remarks>
    IgnoreZOrder = &H4
    ''' <summary>Displays the window.</summary>
    ''' <remarks>SWP_SHOWWINDOW</remarks>
    ShowWindow = &H40
End Enum

Open in new window

0
 
LVL 2

Author Comment

by:ttphil
Comment Utility
Ive tried the options in the link - they can bring the app to front etc but dont give it focus, i really need my form to receive all keyboard inputs.
0
 
LVL 2

Author Comment

by:ttphil
Comment Utility
Illusio - im afraid im not familiar enough with vb to get it to work in c#, if you have it in c# it would be spot on. Thanks
0
 
LVL 7

Accepted Solution

by:
illusio earned 500 total points
Comment Utility
Post redo - seems it didn't get through...

Translated!

Have fun (and yes - it stays butt ugly - even in C#)
// This goes in a seperate file
// remember to using System.Runtime.InteropServices;
    public sealed static class NativeMethods
    {
        [DllImport("user32.dll")]
        internal extern static int SetForegroundWindow(IntPtr hwnd);

        public static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
        public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        public static readonly IntPtr HWND_TOP = new IntPtr(0);
        public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);

        [DllImport("user32.dll")]
        internal extern static int SetWindowPos(
            IntPtr hwnd,
            IntPtr hWndInsertAfter,
            int X,
            int Y,
            int cx,
            int cy,
            SetWindowPosFlags uFlags);
    }

    [Flags]
    [CLSCompliant(false)]
    public enum SetWindowPosFlags : uint
    {
        /// <summary>If the calling thread and the thread that owns the window are attached to different input queues, 
        /// the system posts the request to the thread that owns the window. This prevents the calling thread from 
        /// blocking its execution while other threads process the request.</summary>
        /// <remarks>SWP_ASYNCWINDOWPOS</remarks>
        SynchronousWindowPosition = 0x4000,
        /// <summary>Prevents generation of the WM_SYNCPAINT message.</summary>
        /// <remarks>SWP_DEFERERASE</remarks>
        DeferErase = 0x2000,
        /// <summary>Draws a frame (defined in the window's class description) around the window.</summary>
        /// <remarks>SWP_DRAWFRAME</remarks>
        DrawFrame = 0x20,
        /// <summary>Applies new frame styles set using the SetWindowLong function. Sends a WM_NCCALCSIZE message to 
        /// the window, even if the window's size is not being changed. If this flag is not specified, WM_NCCALCSIZE 
        /// is sent only when the window's size is being changed.</summary>
        /// <remarks>SWP_FRAMECHANGED</remarks>
        FrameChanged = 0x20,
        /// <summary>Hides the window.</summary>
        /// <remarks>SWP_HIDEWINDOW</remarks>
        HideWindow = 0x80,
        /// <summary>Does not activate the window. If this flag is not set, the window is activated and moved to the 
        /// top of either the topmost or non-topmost group (depending on the setting of the hWndInsertAfter 
        /// parameter).</summary>
        /// <remarks>SWP_NOACTIVATE</remarks>
        DoNotActivate = 0x10,
        /// <summary>Discards the entire contents of the client area. If this flag is not specified, the valid 
        /// contents of the client area are saved and copied back into the client area after the window is sized or 
        /// repositioned.</summary>
        /// <remarks>SWP_NOCOPYBITS</remarks>
        DoNotCopyBits = 0x100,
        /// <summary>Retains the current position (ignores X and Y parameters).</summary>
        /// <remarks>SWP_NOMOVE</remarks>
        IgnoreMove = 0x2,
        /// <summary>Does not change the owner window's position in the Z order.</summary>
        /// <remarks>SWP_NOOWNERZORDER</remarks>
        DoNotChangeOwnerZOrder = 0x200,
        /// <summary>Does not redraw changes. If this flag is set, no repainting of any kind occurs. This applies to 
        /// the client area, the nonclient area (including the title bar and scroll bars), and any part of the parent 
        /// window uncovered as a result of the window being moved. When this flag is set, the application must 
        /// explicitly invalidate or redraw any parts of the window and parent window that need redrawing.</summary>
        /// <remarks>SWP_NOREDRAW</remarks>
        DoNotRedraw = 0x8,
        /// <summary>Same as the SWP_NOOWNERZORDER flag.</summary>
        /// <remarks>SWP_NOREPOSITION</remarks>
        DoNotReposition = 0x200,
        /// <summary>Prevents the window from receiving the WM_WINDOWPOSCHANGING message.</summary>
        /// <remarks>SWP_NOSENDCHANGING</remarks>
        DoNotSendChangingEvent = 0x400,
        /// <summary>Retains the current size (ignores the cx and cy parameters).</summary>
        /// <remarks>SWP_NOSIZE</remarks>
        IgnoreResize = 0x1,
        /// <summary>Retains the current Z order (ignores the hWndInsertAfter parameter).</summary>
        /// <remarks>SWP_NOZORDER</remarks>
        IgnoreZOrder = 0x4,
        /// <summary>Displays the window.</summary>
        /// <remarks>SWP_SHOWWINDOW</remarks>
        ShowWindow = 0x40
    }

// This goes in your Form

        private Control _activeControl = null;
        private System.Threading.Timer _timer;
        protected override void OnDeactivate(EventArgs e)
        {
            _activeControl = this.ActiveControl;
            _timer = new System.Threading.Timer(new System.Threading.TimerCallback(ActivateThis), null, 1, System.Threading.Timeout.Infinite);
        }

        private delegate void  ActivateThisDelegate(object state);
        private void ActivateThis(object state)
        {
            if (this.InvokeRequired)
                this.Invoke(new ActivateThisDelegate(ActivateThis), state);
            else if (!this.IsDisposed)
            {
                NativeMethods.SetWindowPos(this.Handle, NativeMethods.HWND_TOP, 0, 0, 0, 0, SetWindowPosFlags.ShowWindow | SetWindowPosFlags.IgnoreMove | SetWindowPosFlags.IgnoreResize);
                NativeMethods.SetForegroundWindow(this.Handle);
            }
        }

        protected override void OnActivated(EventArgs e)
        {
            if (_activeControl != null)
            {
                _activeControl.Focus();
                _activeControl = null;
            }
        }

Open in new window

0
 
LVL 7

Expert Comment

by:illusio
Comment Utility
I have an even more elegant solution - not completely refined though - if you test you'll see why... (you'll have to keep track of the shift - altgr - ctrl states to translate to the correct keys)

On the form you set the topmost = true (will keep your form always in front of all other windows) (prevent the minimalize might also be handy). When the form is shown you do the following:
- keep track of the active control (or force one)
- hook to the global keyevents
- insert the pressed keys in the active control

When you are done - you unhook the keyboard logger and close your form.
Included a keylogger (if you google it you will find this code for WPF - adapted it a little)
/// <summary>
    /// Listens keyboard globally.
    /// </summary>
    public class KeyboardListener : IDisposable
    {
        /// <summary>
        /// Creates global keyboard listener.
        /// </summary>
        public KeyboardListener()
        {
            // We have to store the HookCallback, so that it is not garbage collected runtime
            hookedLowLevelKeyboardProc = (InterceptKeys.LowLevelKeyboardProc)LowLevelKeyboardProc;

            // Set the hook
            hookId = InterceptKeys.SetHook(hookedLowLevelKeyboardProc);

            // Assign the asynchronous callback event
            hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
        }

        /// <summary>
        /// Destroys global keyboard listener.
        /// </summary>
        ~KeyboardListener()
        {
            Dispose();
        }

        /// <summary>
        /// Fired when any of the keys is pressed down.
        /// </summary>
        public event RawKeyEventHandler KeyDown;

        /// <summary>
        /// Fired when any of the keys is released.
        /// </summary>
        public event RawKeyEventHandler KeyUp;

        #region Inner workings
        /// <summary>
        /// Hook ID
        /// </summary>
        private IntPtr hookId = IntPtr.Zero;

        /// <summary>
        /// Asynchronous callback hook.
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode);

        /// <summary>
        /// Actual callback hook.
        /// 
        /// <remarks>Calls asynchronously the asyncCallback.</remarks>
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
                if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYUP)
                    hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent) wParam.ToUInt32(), Marshal.ReadInt32(lParam), null, null);

            return InterceptKeys.CallNextHookEx(hookId, nCode, wParam, lParam);
        }

        /// <summary>
        /// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
        /// </summary>
        private KeyboardCallbackAsync hookedKeyboardCallbackAsync;

        /// <summary>
        /// Contains the hooked callback in runtime.
        /// </summary>
        private InterceptKeys.LowLevelKeyboardProc hookedLowLevelKeyboardProc;

        /// <summary>
        /// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
        /// </summary>
        /// <param name="keyEvent">Keyboard event</param>
        /// <param name="vkCode">VKCode</param>
        void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode)
        {
            switch (keyEvent)
            {
                // KeyDown events
                case InterceptKeys.KeyEvent.WM_KEYDOWN:
                    if (KeyDown != null)
                        KeyDown(this, new RawKeyEventArgs(vkCode, false));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
                    if (KeyDown != null)
                        KeyDown(this, new RawKeyEventArgs(vkCode, true));
                    break;

                // KeyUp events
                case InterceptKeys.KeyEvent.WM_KEYUP:
                    if (KeyUp != null)
                        KeyUp(this, new RawKeyEventArgs(vkCode, false));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYUP:
                    if (KeyUp != null)
                        KeyUp(this, new RawKeyEventArgs(vkCode, true));
                    break;

                default:
                    break;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Disposes the hook.
        /// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
        /// </summary>
        public void Dispose()
        {
            InterceptKeys.UnhookWindowsHookEx(hookId);
        }

        #endregion
    }
    /// <summary>
    /// Raw KeyEvent arguments.
    /// </summary>
    public class RawKeyEventArgs : EventArgs
    {
        /// <summary>
        /// VKCode of the key.
        /// </summary>
        public int VKCode;

        /// <summary>
        /// Key of the key.
        /// </summary>
        public Char Char;

        /// <summary>
        /// Is the hitted key system key.
        /// </summary>
        public bool IsSysKey;

        /// <summary>
        /// Create raw keyevent arguments.
        /// </summary>
        /// <param name="VKCode"></param>
        /// <param name="isSysKey"></param>
        public RawKeyEventArgs(int VKCode, bool isSysKey)
        {
            this.VKCode = VKCode;
            this.IsSysKey = isSysKey;
            uint nonvirtual = InterceptKeys.MapVirtualKey((uint)VKCode, 2);
            if (nonvirtual >> 31 != 0x1)
                this.Char = Convert.ToChar(nonvirtual);
            else
                this.Char = default(Char);
        }
    }

    /// <summary>
    /// Raw keyevent handler.
    /// </summary>
    /// <param name="sender">sender</param>
    /// <param name="args">raw keyevent arguments</param>
    public delegate void RawKeyEventHandler(object sender, RawKeyEventArgs args);

    #region WINAPI Helper class
    /// <summary>
    /// Winapi Key interception helper class.
    /// </summary>
    internal static class InterceptKeys
    {
        public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);
        public static int WH_KEYBOARD_LL = 13;

        public enum KeyEvent : int {    
            WM_KEYDOWN = 256,
            WM_KEYUP = 257,
            WM_SYSKEYUP = 261,
            WM_SYSKEYDOWN = 260
        }

        public static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
                    GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, UIntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern uint MapVirtualKey(uint uCode, uint uMapType);
    }
    #endregion

Open in new window

0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

More often than not, we developers are confronted with a need: a need to make some kind of magic happen via code. Whether it is for a client, for the boss, or for our own personal projects, the need must be satisfied. Most of the time, the Framework…
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
This video gives you a great overview about bandwidth monitoring with SNMP and WMI with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're looking for how to monitor bandwidth using netflow or packet s…

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

16 Experts available now in Live!

Get 1:1 Help Now