// Retrieves a string when the reference to a string is embedded as a field
        // within a structure. The lParam to the message is a reference to the struct
        // 
        // The parameters are: a pointer the struct and its size.
        //                     a pointer to the pointer to the string to retrieve.
        //                     the max size for the string.
        // Param "hwnd" the Window Handle
        // Param "uMsg" the Windows Message
        // Param "wParam" the Windows wParam
        // Param "lParam" a pointer to a struct allocated on the stack
        // Param "cbSize" the size of the structure
        // Param "pszText" the address of a pointer to a string located with the structure referenced by the lParam
        // Param "maxLength" the size of the string
        // Param "remoteBitness" the bitness of the pointer contained within pszText
        internal static unsafe string GetTextWithinStructureRemoteBitness(
            IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr lParam, int cbSize,
            IntPtr pszText, int maxLength, ProcessorTypes remoteBitness, bool ignoreSendResult)
        {
            using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd))
            {
                if (hProcess.IsInvalid)
                {
                    // assume that the hwnd was bad
                    throw new ElementNotAvailableException();
                }

                using (RemoteMemoryBlock rmem = new RemoteMemoryBlock(cbSize + (maxLength + 1) * sizeof(char), hProcess))
                {
                    // Allocate the space for the object as well as the string
                    // Ensure proper allocation
                    if (rmem.IsInvalid)
                    {
                        return "";
                    }

                    // Force the string to be zero terminated

                    IntPtr remoteTextArea = new IntPtr((byte*)rmem.Address.ToPointer() + cbSize);
                    if (remoteBitness == ProcessorTypes.Processor32Bit)
                    {
                        // When the structure will be sent to a 32-bit process,
                        // pszText points to an int, not an IntPtr.
                        // remoteTextArea should be a 32-bit address.
                        System.Diagnostics.Debug.Assert(remoteTextArea.ToInt32() == remoteTextArea.ToInt64());
                        *(int*)((byte*)pszText.ToPointer()) = rmem.Address.ToInt32() + cbSize;
                    }
                    else
                    {
                        *(IntPtr*)((byte*)pszText.ToPointer()) = remoteTextArea;
                    }

                    // Copy the struct to the remote process...
                    rmem.WriteTo(lParam, new IntPtr(cbSize));

                    // Send the message...
                    IntPtr result = Misc.ProxySendMessage(hwnd, uMsg, wParam, rmem.Address);

                    // Nothing, early exit
                    if (!ignoreSendResult && result == IntPtr.Zero)
                    {
                        return "";
                    }

                    // Allocate the buffer for the string
                    char[] achRes = new char[maxLength + 1];

                    // Force the result string not to go past maxLength
                    achRes[maxLength] = '\0';
                    fixed (char* pchRes = achRes)
                    {
                        // Read the string from the common 
                        rmem.ReadFrom(new IntPtr((byte*)rmem.Address.ToPointer() + cbSize), new IntPtr(pchRes), new IntPtr(maxLength * sizeof(char)));

                        // Construct the returned string with an explicit length to avoid
                        // a string with an over-allocated buffer, as would occur
                        // if we simply use new string(achRes).
                        int length = 0;
                        for (; achRes[length] != 0 && length < maxLength; length++)
                        {
                        }
                        return new string(achRes, 0, length);
                    }
                }
            }
        }
        // Main method.  It simply copies an unmamaged buffer to the remote process, sends the message, and then
        // copies the remote buffer back to the local unmanaged buffer.
        internal static int XSendGetIndex(IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr ptrStructure, int cbSize)
        {
            using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd))
            {
                if (hProcess.IsInvalid)
                {
                    // assume that the hwnd was bad
                    throw new ElementNotAvailableException();
                }

                using (RemoteMemoryBlock rmem = new RemoteMemoryBlock(cbSize, hProcess))
                {
                    if (rmem.IsInvalid)
                    {
                        throw new OutOfMemoryException();
                    }

                    // Copy the struct to the remote process...
                    rmem.WriteTo(ptrStructure, new IntPtr(cbSize));

                    // Send the message...
                    int res = Misc.ProxySendMessageInt(hwnd, uMsg, wParam, rmem.Address);

                    // Copy returned struct back to local process...
                    rmem.ReadFrom(ptrStructure, new IntPtr(cbSize));

                    return res;
                }
            }
        }
        // Main method.  It simply copies an unmamaged buffer to the remote process, sends the message, and then
        // copies the remote buffer back to the local unmanaged buffer.
        internal static bool XSend (IntPtr hwnd, int uMsg, IntPtr ptrStructure1, IntPtr ptrStructure2, int cbSize1, int cbSize2, ErrorValue errorCode)
        {
            using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd))
            {
                if (hProcess.IsInvalid)
                {
                    // assume that the hwnd was bad
                    throw new ElementNotAvailableException();
                }

                using (RemoteMemoryBlock rmem1 = new RemoteMemoryBlock(cbSize1, hProcess))
                {
                    // Ensure proper allocation
                    if (rmem1.IsInvalid)
                    {
                        return false;
                    }

                    using (RemoteMemoryBlock rmem2 = new RemoteMemoryBlock(cbSize2, hProcess))
                    {
                        // Ensure proper allocation
                        if (rmem2.IsInvalid)
                        {
                            return false;
                        }

                        // Copy the struct to the remote process...
                        rmem1.WriteTo(ptrStructure1, new IntPtr(cbSize1));
                        rmem2.WriteTo(ptrStructure2, new IntPtr(cbSize2));

                        // Send the message...
                        IntPtr res = Misc.ProxySendMessage(hwnd, uMsg, rmem1.Address, rmem2.Address);

                        // check the result
                        if ((errorCode != ErrorValue.NoCheck) && ((errorCode == ErrorValue.Zero && res == IntPtr.Zero) || (errorCode == ErrorValue.NotZero && res != IntPtr.Zero)))
                        {
                            return false;
                        }

                        // Copy returned struct back to local process...
                        rmem1.ReadFrom(ptrStructure1, new IntPtr(cbSize1));
                        rmem2.ReadFrom(ptrStructure2, new IntPtr(cbSize2));
                    }
                }
            }

            return true;
        }
        // Helper override to handle common scenario of having a string lParam
        // Handles bi-directional string marshaling
        internal static bool XSend (IntPtr hwnd, int uMsg, IntPtr wParam, ref string str, int maxLength)
        {
            using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd))
            {
                if (hProcess.IsInvalid)
                {
                    // assume that the hwnd was bad
                    throw new ElementNotAvailableException();
                }

                using (RemoteMemoryBlock rmem = new RemoteMemoryBlock(maxLength * sizeof(char), hProcess))
                {
                    if (rmem.IsInvalid)
                    {
                        return false;
                    }

                    // Send the message...
                    if (Misc.ProxySendMessage(hwnd, uMsg, wParam, rmem.Address) == IntPtr.Zero)
                    {
                        return false;
                    }

                    // Read the string from the remote buffer
                    return rmem.ReadString(out str, maxLength);
                }
            }
        }
        // A generic GetItemText that sends a message to a control then reads memory out of the controls process.
        internal static string GetItemText(IntPtr hwnd, int msg, int index, int textLen)
        {
            if (textLen <= 0)
            {
                return "";
            }

            using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd))
            {
                if (hProcess.IsInvalid)
                {
                    // assume that the hwnd was bad
                    throw new ElementNotAvailableException();
                }

                using (RemoteMemoryBlock rmem = new RemoteMemoryBlock((textLen + 1) * sizeof(char), hProcess))
                {
                    // Allocate memory for UNICODE string.
                    if (rmem.IsInvalid)
                    {
                        return "";
                    }

                    if (Misc.ProxySendMessage(hwnd, msg, new IntPtr(index), rmem.Address) != IntPtr.Zero)
                    {
                        string itemText;

                        // Read the string from the remote buffer
                        if (rmem.ReadString(out itemText, (textLen + 1)))
                        {
                            return itemText;
                        }
                    }
                }
            }

            return "";

            //ProcessorTypes localBitness;
            //ProcessorTypes remoteBitness;
            //GetProcessTypes(hwnd, out localBitness, out remoteBitness);

            //if (localBitness == remoteBitness)
            //{
            //    return "";
            //}
            //else if (remoteBitness == ProcessorTypes.Processor32Bit)
            //{
            //ToDo: Convert from 64-bit to 32-bit
            //    return "";
            //}
            //else if (remoteBitness == ProcessorTypes.Processor64Bit)
            //{
            //ToDo: Convert from 32-bit to 64-bit
            //    return "";
            //}
            //return "";
        }
        //------------------------------------------------------
        //
        //  Generic Control Methods that support cross process / cross bitness
        //
        //------------------------------------------------------

        #region Generic Control Methods

        // A generic GetItemRect that sends a message to a control then reads memory out of the controls process.
        internal static Rect GetItemRect(IntPtr hwnd, int msg, int index)
        {
            using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd))
            {
                if (hProcess.IsInvalid)
                {
                    // assume that the hwnd was bad
                    throw new ElementNotAvailableException();
                }

                NativeMethods.Win32Rect rectW32 = NativeMethods.Win32Rect.Empty;
                int cMem = Marshal.SizeOf(rectW32.GetType());

                using (RemoteMemoryBlock remoteMem = new RemoteMemoryBlock(cMem, hProcess))
                {
                    // Check if RemoteMmeoryBlock.Allocate returns null.
                    if (remoteMem.IsInvalid)
                    {
                        return Rect.Empty;
                    }

                    unsafe
                    {
                        IntPtr localRectStart = new IntPtr(&rectW32.left);

                        remoteMem.WriteTo(localRectStart, new IntPtr(cMem));

                        if (Misc.ProxySendMessageInt(hwnd, msg, new IntPtr(index), remoteMem.Address) != 0)
                        {                            
                            remoteMem.ReadFrom(localRectStart, new IntPtr(cMem));
                            Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref rectW32, 2);                            

                            // Errors in RTL handling are sometimes caused by a failure to clearly define 
                            // class responsibility for normalizing the rectangles.  If a class provides a 
                            // normalized rectangle that is subsequently normalized by a caller, Win32Rect 
                            // will convert the boundaries to EmptyRect.  This is caused by the test in 
                            // Win32Rect.IsEmpty, which returns true if (left >= right || top >= bottom).  
                            // For various reasons some controls provide normalized rectangles and some 
                            // controls do not, so the calling class needs to selectively normalize based 
                            // on known behaviors, version information, or other criteria.  
                            // CommonXSendMessage is an intermediary, consumed by multiple classes, 
                            // and normalizing here introduces some unwarranted complexity into the 
                            // responsibility contract.
                            return rectW32.ToRect(false);
                        }
                    }
                }

                return Rect.Empty;
            }

            //ProcessorTypes localBitness;
            //ProcessorTypes remoteBitness;
            //GetProcessTypes(hwnd, out localBitness, out remoteBitness);

            //if (localBitness == remoteBitness)
            //{
            //    return Rect.Empty;
            //}
            //else if (remoteBitness == ProcessorTypes.Processor32Bit)
            //{
                //ToDo: Convert from 64-bit to 32-bit
            //    return Rect.Empty;
            //}
            //else if (remoteBitness == ProcessorTypes.Processor64Bit)
            //{
                //ToDo: Convert from 32-bit to 64-bit
            //    return Rect.Empty;
            //}
            //return Rect.Empty;
        }