/// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.CharRangeToString"]/*' />
        /// <devdoc>
        ///     Converts a CHARRANGE to a string. Note: The behavior of this is dependent on the current window
        ///     class name being used. We have to create a CharBuffer of the type of RichTextBox DLL we're using,
        ///     not based on the SystemCharWidth.
        /// </devdoc>
        /// <internalonly/>
        private string CharRangeToString(NativeMethods.CHARRANGE c) {
            NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE();
            txrg.chrg = c;
            Debug.Assert((c.cpMax-c.cpMin)>0, "CHARRANGE was null or negative - can't do it!");

            //Windows bug: 64-bit windows returns a bad range for us.  VSWhidbey 504502.  
            //Putting in a hack to avoid an unhandled exception.
            if (c.cpMax > Text.Length || c.cpMax-c.cpMin <= 0) {
                return string.Empty;
            }

            int characters = (c.cpMax-c.cpMin) + 1; // +1 for null termination
            UnsafeNativeMethods.CharBuffer charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(characters);
            IntPtr unmanagedBuffer = charBuffer.AllocCoTaskMem();
            if (unmanagedBuffer == IntPtr.Zero)
                throw new OutOfMemoryException(SR.GetString(SR.OutOfMemory));

            txrg.lpstrText = unmanagedBuffer;
            int len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg);
            Debug.Assert(len != 0, "CHARRANGE from RichTextBox was bad! - impossible?");
            charBuffer.PutCoTaskMem(unmanagedBuffer);
            if (txrg.lpstrText != IntPtr.Zero)
                Marshal.FreeCoTaskMem(unmanagedBuffer);

            string result = charBuffer.GetString();
            return result;
        }
        /// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find6"]/*' />
        /// <devdoc>
        ///     Searches the text in a RichTextBox control for the given characters.
        /// </devdoc>
        public int Find(char[] characterSet, int start, int end) {
            // Code used to support ability to search backwards and negate character sets.
            // The API no longer supports this, but in case we change our mind, I'm leaving
            // the ability in here.
            bool forward = true;
            bool negate = false;

            int textLength = TextLength;

            if (characterSet == null)
                throw new ArgumentNullException("characterSet");
            if (start < 0 || start > textLength)
                throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLength));
            if (end < start && end != -1)
                throw new ArgumentOutOfRangeException("end", SR.GetString(SR.InvalidLowBoundArgumentEx, "end", end, "start"));

            // Don't do anything if we get nothing to look for
            if (characterSet.Length == 0)
                return -1;

            int textLen = SafeNativeMethods.GetWindowTextLength(new HandleRef(this, Handle));
            if (start == end) {
                start = 0;
                end = textLen;
            }
            if (end == -1) {
                end = textLen;
            }

            NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE(); // The range of characters we have searched
            chrg.cpMax = chrg.cpMin = start;

            // Use the TEXTRANGE to move our text buffer forward
            // or backwards within the main text
            NativeMethods.TEXTRANGE txrg = new NativeMethods.TEXTRANGE(); // Characters we have slurped into memory in order to search
            txrg.chrg = new NativeMethods.CHARRANGE();

            txrg.chrg.cpMin = chrg.cpMin;
            txrg.chrg.cpMax = chrg.cpMax;
            UnsafeNativeMethods.CharBuffer charBuffer;
            charBuffer = UnsafeNativeMethods.CharBuffer.CreateBuffer(CHAR_BUFFER_LEN + 1);
            
            txrg.lpstrText = charBuffer.AllocCoTaskMem();
            if (txrg.lpstrText == IntPtr.Zero)
                throw new OutOfMemoryException();

            try {
                bool done = false;

                // We want to loop as long as it takes.  This loop will grab a
                // chunk of text out from the control as directed by txrg.chrg;
                while (!done) {
                    if (forward) {
                        // Move forward by starting at the end of the
                        // previous text window and extending by the
                        // size of our buffer
                        txrg.chrg.cpMin = chrg.cpMax;
                        txrg.chrg.cpMax += CHAR_BUFFER_LEN;
                    }
                    else {
                        // Move backwards by anchoring at the start
                        // of the previous buffer window, and backing
                        // up by the desired size of our buffer
                        txrg.chrg.cpMax = chrg.cpMin;
                        txrg.chrg.cpMin -= CHAR_BUFFER_LEN;

                        // We need to keep our request within the
                        // lower bound of zero
                        if (txrg.chrg.cpMin < 0)
                            txrg.chrg.cpMin = 0;
                    }

                    if (end != -1)
                        txrg.chrg.cpMax = Math.Min(txrg.chrg.cpMax, end);

                    // go get the text in this range, if we didn't get any text then punt
                    int len;
                    len = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_GETTEXTRANGE, 0, txrg);
                    if (len == 0) {
                        chrg.cpMax = chrg.cpMin = -1; // Hit end of control without finding what we wanted
                        break;
                    }

                    // get the data from RichTextBoxConstants into a string for us to use.
                    charBuffer.PutCoTaskMem(txrg.lpstrText);
                    string str = charBuffer.GetString();
                    
                    // Loop through our text
                    if (forward) {
                        // Start at the begining of the buffer
                        for (int x = 0; x < len; x++) {
                            // Is it in char set?
                            bool found = GetCharInCharSet(str[x], characterSet, negate);

                            if (found) {
                                done = true;
                                break;
                            }

                            // Advance the buffer
                            chrg.cpMax++;
                        }
                    }
                    else { // Span reverse.
                        int x = len;
                        while (x-- != 0) {
                            // Is it in char set?
                            bool found = GetCharInCharSet(str[x], characterSet, negate);

                            if (found) {
                                done = true;
                                break;
                            }

                            // Bring the selection back while keeping it anchored
                            chrg.cpMin--;
                        }
                    }
                }
            }
            finally {
                // release the resources we got for our GETTEXTRANGE operation.
                if (txrg.lpstrText != IntPtr.Zero) Marshal.FreeCoTaskMem(txrg.lpstrText);
            }

            int index = (forward) ? chrg.cpMax : chrg.cpMin;
            return index;
        }