I found a lot of information on the Internet and did not find much support to return to the current reading position. I used the C # callback function. Then, to make it easy to use it, I simply wrapped up nothing technical, so that Daniel flew over directly [csharp] using System; using System. collections. generic; using System. text; using DotNetSpeech; using System. threading; using System. IO; namespace SpVoiceDemo {class SpVoiceUtil {SpVoice voice = new DotNetSpeech. spVoiceClass (); public delegate void CallBack (bool B, int InputWordPosition, int InputWordLength); // <summary> /// Read text /// </summary> /// <param name = "str"> text to be read </param> /// <param name = "CallBack"> CallBack address </param> /// <returns> return bool </returns> public bool Speak (string str, callBack) {int n = voice. speak (str, SpeechVoiceSpeakFlags. SVSFlagsAsync); Thread thread Thread = new thread (new ParameterizedThreadStart (Call); Thread. isBackground = true; thread. start (Object) CallBack); return! (N! = 1 );} /// <summary> /// callBack function thread subroutine /// </summary> /// <param name = "callBack"> </param> private void Call (Object callBack) {int InputWordLength = 0; // partial _ read length int InputWordPosition = 0; // partial _ read position CallBack = (callBack) CallBack; while (int) voice. status. runningState! = 1) {if (InputWordPosition! = Voice. Status. InputWordPosition | InputWordLength! = Voice. status. inputWordLength) {InputWordPosition = voice. status. inputWordPosition; InputWordLength = voice. status. inputWordLength; // CallBack (false, InputWordPosition, InputWordLength) ;}} CallBack (true, InputWordPosition, InputWordLength );} /// <summary> /// obtain the voice library /// </summary> /// <returns> List <string> </returns> public List <string> getDescription () {List <string> list = new List <string> (); DotNetSpeech. ISpeechObjectTokens obj = voice. getVoices (); int count = obj. count; // obtain the total number of voice libraries for (int I = 0; I <count; I ++) {string desc = obj. item (I ). getDescription (); // traverses the voice database list. add (desc);} return list ;} /// <summary> /// set the current language library // </summary> /// <returns> bool </returns> public bool setDescription (string name) {List <string> list = new List <string> (); DotNetSpeech. ISpeechObjectTokens obj = voice. getVoices (); int count = obj. count; // obtain the total number of voice libraries bool result = false; for (int I = 0; I <count; I ++) {string desc = obj. item (I ). getDescription (); // traverses the voice database if (desc. equals (name) {voice. voice = obj. item (I); result = true;} return result ;} /// <summary> /// set the language speed /// </summary> /// <param name = "n"> </param> public void setRate (int n) {voice. rate = n ;} /// <summary> /// set the sound size /// </summary> /// <param name = "n"> </param> public void setVolume (int n) {voice. volume = n ;}/// <summary> /// Pause /// </summary> public void Pause () {voice. pause () ;}/// <summary> /// continue /// </summary> public void Resume () {voice. resume () ;}//< summary> /// Stop /// </summary> public void Stop () {voice. speak (string. empty, SpeechVoiceSpeakFlags. SVSFPurgeBeforeSpeak );} /// <summary> /// output WAV // </summary> /// <param name = "path"> Save path </param> /// <param name = "str"> text to be converted </param> // <returns> </returns> public bool WreiteToWAV (string path, string str, SpeechAudioFormatType SpAudioType) {SpeechStreamFileMode SpFileMode = SpeechStreamFileMode. SSFMCreateForWrite; SpFileStream = new SpFileStream (); SpeechVoiceSpeakFlags SpFlags = SpeechVoiceSpeakFlags. SVSFlagsAsync; SpAudioFormat SpAudio = new DotNetSpeech. spAudioFormat (); SpAudio. type = SpAudioType; SpFileStream. format = SpAudio; SpFileStream. open (path, SpFileMode, false); voice. audioOutputStream = SpFileStream; voice. speak (str, SpFlags); voice. waitUntilDone (Timeout. infinite); SpFileStream. close (); return File. exists (path );}}}