/// <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;
        }
        private void StreamIn(Stream data, int flags) {
            // clear out the selection only if we are replacing all the text
            //
            if ((flags & RichTextBoxConstants.SFF_SELECTION) == 0) {
                NativeMethods.CHARRANGE cr = new NativeMethods.CHARRANGE();
                UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, cr);
            }

            try {
                editStream = data;
                Debug.Assert(data != null, "StreamIn passed a null stream");

                // If SF_RTF is requested then check for the RTF tag at the start
                // of the file.  We don't load if the tag is not there
                // 
                if ((flags & RichTextBoxConstants.SF_RTF) != 0) {
                    long streamStart = editStream.Position;
                    byte[] bytes = new byte[SZ_RTF_TAG.Length];
                    editStream.Read(bytes, (int)streamStart, SZ_RTF_TAG.Length);
                    string str = Encoding.Default.GetString(bytes);
                    if (!SZ_RTF_TAG.Equals(str))
                        throw new ArgumentException(SR.GetString(SR.InvalidFileFormat));

                    // put us back at the start of the file
                    editStream.Position = streamStart;
                }

                int cookieVal = 0;
                // set up structure to do stream operation
                NativeMethods.EDITSTREAM es = new NativeMethods.EDITSTREAM();
                if ((flags & RichTextBoxConstants.SF_UNICODE) != 0) {
                    cookieVal = INPUT | UNICODE;
                }
                else {
                    cookieVal = INPUT | ANSI;
                }
                if ((flags & RichTextBoxConstants.SF_RTF) != 0) {
                    cookieVal |= RTF;
                }
                else {
                    cookieVal |= TEXTLF;
                }
                es.dwCookie = (IntPtr) cookieVal;
                es.pfnCallback = new NativeMethods.EditStreamCallback(this.EditStreamProc);

                // gives us TextBox compatible behavior, programatic text change shouldn't
                // be limited...
                //
                SendMessage(RichTextBoxConstants.EM_EXLIMITTEXT, 0, Int32.MaxValue);


        
                // go get the text for the control
                //Weird hack needed for 64-bit
                if (IntPtr.Size == 8) { 
                    NativeMethods.EDITSTREAM64 es64 = ConvertToEDITSTREAM64(es);
                    UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es64);

                    //Assign back dwError value
                    es.dwError = GetErrorValue64(es64);
                }
                else {
                    UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_STREAMIN, flags, es);
                }
          
                UpdateMaxLength();

                // If we failed to load because of protected
                // text then return protect event was fired so no
                // exception is required for the the error
                if (GetProtectedError())
                    return;

                if (es.dwError != 0)
                    throw new InvalidOperationException(SR.GetString(SR.LoadTextError));

                // set the modify tag on the control
                SendMessage(NativeMethods.EM_SETMODIFY, -1, 0);

                // EM_GETLINECOUNT will cause the RichTextBoxConstants to recalculate its line indexes
                SendMessage(NativeMethods.EM_GETLINECOUNT, 0, 0);
                
                
            }
            finally {
                // release any storage space held.
                editStream = null;
            }
        }
        /// <include file='doc\RichTextBox.uex' path='docs/doc[@for="RichTextBox.Find3"]/*' />
        /// <devdoc>
        ///     Searches the text in a RichTextBox control for a given string.
        /// </devdoc>
        public int Find(string str, int start, int end, RichTextBoxFinds options) {

            int textLen = TextLength;

            if (str == null)
                throw new ArgumentNullException("str");
            if (start < 0 || start > textLen)
                throw new ArgumentOutOfRangeException("start", SR.GetString(SR.InvalidBoundArgument, "start", start, 0, textLen));
            if (end < -1)
                throw new ArgumentOutOfRangeException("end", SR.GetString(SR.RichTextFindEndInvalid, end));

            bool selectWord = true;
            NativeMethods.FINDTEXT ft = new NativeMethods.FINDTEXT();
            ft.chrg = new NativeMethods.CHARRANGE();

            // set up the default values for the FINDTEXT structure, that is
            // the given string and the whole range of the text stream
            ft.lpstrText = str;
            if (end == -1) {
                end = textLen;
            }

            if (start > end) {
                throw new ArgumentException(SR.GetString(SR.RichTextFindEndInvalid, end));
            }

            if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) {
                // normal
                //
                ft.chrg.cpMin = start;
                ft.chrg.cpMax = end;
            }
            else {
                // reverse
                //
                ft.chrg.cpMin = end;
                ft.chrg.cpMax = start;
            }

            // force complete search if we ended up with a zero length search
            if (ft.chrg.cpMin == ft.chrg.cpMax) {
                if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) {
                    ft.chrg.cpMin = 0;
                    ft.chrg.cpMax = -1;
                }
                else {
                    ft.chrg.cpMin = textLen;
                    ft.chrg.cpMax = 0;
                }
            }

            // set up the options for the search
            int findOptions = 0;
            if ((options & RichTextBoxFinds.WholeWord) == RichTextBoxFinds.WholeWord)
                findOptions |= RichTextBoxConstants.FR_WHOLEWORD;
            if ((options & RichTextBoxFinds.MatchCase) == RichTextBoxFinds.MatchCase)
                findOptions |= RichTextBoxConstants.FR_MATCHCASE;
            if ((options & RichTextBoxFinds.NoHighlight) == RichTextBoxFinds.NoHighlight)
                selectWord = false;
            if ((options & RichTextBoxFinds.Reverse) != RichTextBoxFinds.Reverse) {
                // The default for RichEdit 2.0 is to search in reverse
                findOptions |= RichTextBoxConstants.FR_DOWN;
            }

            // Perform the find, will return ubyte position
            int position;

            position = (int)UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_FINDTEXT, findOptions, ft);
            

            // if we didn't find anything, or we don't have to select what was found,
            // we're done
            if (position != -1 && selectWord) {
                // Select the string found, this is done in ubyte units
                NativeMethods.CHARRANGE chrg = new NativeMethods.CHARRANGE();
                chrg.cpMin = position;
                //Look for kashidas in the string.  A kashida is an arabic visual justification character 
                //that's not semantically meaningful.  Searching for ABC might find AB_C (where A,B, and C 
                //represent Arabic characters and _ represents a kashida).  We should highlight the text
                //including the kashida (VSWhidbey 94809).
                char kashida = (char)0x640;
                string text = this.Text;
                string foundString = text.Substring(position, str.Length);
                int startIndex = foundString.IndexOf(kashida);
                if (startIndex == -1)
                {
                    //No kashida in the string
                    chrg.cpMax = position + str.Length;
                }
                else
                {
                    //There's at least one kashida
                    int searchingCursor; //index into search string
                    int foundCursor; //index into Text
                    for (searchingCursor = startIndex, foundCursor = position + startIndex; searchingCursor < str.Length;
                        searchingCursor++, foundCursor++)
                    {
                        while (text[foundCursor] == kashida && str[searchingCursor] != kashida)
                        {
                            foundCursor++;
                        }
                    }
                    chrg.cpMax = foundCursor;
                }

                UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), RichTextBoxConstants.EM_EXSETSEL, 0, chrg);
                SendMessage(NativeMethods.EM_SCROLLCARET, 0, 0);

            }

            return position;
        }