更新:
由于更新了原帖中的一些信息,我更新了这篇帖子。
您可以尝试以下方法。在我的电脑上,选项 1 大约需要 8.5 秒。对于 50 个文件(每个文件中的文本为 220 个字),选项 2 大约需要 7.2 秒:
添加对 Microsoft.Word 对象库的引用(例如:Microsoft.Word 16.0 对象库)
- 在菜单上,点击项目
- 选择添加参考
- 选择COM
- 选择 Microsoft.Word xx.x 对象库(例如:Microsoft.Word 16.0 对象库)
添加以下 using 语句:
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Word = Microsoft.Office.Interop.Word;
using System.Threading;
using System.Threading.Tasks;
创建一个类来保存创建 Word 文档所需的信息。
注意:以下有两个选项。两个选项都使用“WordFileInfo”类。
WordFileInfo.cs:
public class WordFileInfo
{
public string Filename { get; set; }
public string LetterBody { get; set; }
public WordFileInfo()
{
}
public WordFileInfo(string filename, string letterBody)
{
this.Filename = filename;
this.LetterBody = letterBody;
}
}
在下面的两个选项中,“CreateWordDocument”在两者中完全相同。但是,我已将它包含在每个选项中,因为它是“ClsHelperWord.cs”的一部分。
选项 1:
*注意:此选项同时使用 Task 和 Backgroundworker。如果与 Windows 窗体一起使用,它有助于确保 UI 保持响应。由于使用了Backgroundworker,可能会稍微慢一些。
创建用于从BackgroundWorker线程传回数据的“CurrentState”类。
CurrentState.cs
public class CurrentState
{
public string Status { get; set; } = string.Empty;
public int PercentDone { get; set; } = 0;
}
ClsHelperWord.cs
public class ClsHelperWord : IDisposable
{
//depending on the number of files processed,
//changing this value may increase/decrease performance slightly
private int _maxConcurrentTasks = 25;
//create new instance
private Word.Application _wordApp = new Word.Application();
//store data that needs to be processed
public List<WordFileInfo> WordData { get; set; } = new List<WordFileInfo>();
public ClsHelperWord()
{
}
public ClsHelperWord(List<WordFileInfo> wordData)
{
//set value
this.WordData = wordData;
}
public void Close()
{
Dispose();
}
public void Dispose()
{
if (_wordApp != null)
{
//close Word
object oFalse = false;
_wordApp.Quit(ref oFalse, ref oFalse, ref oFalse);
//release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(_wordApp);
}
}
public void ConvertToWordDocument(System.ComponentModel.BackgroundWorker worker, System.ComponentModel.DoWorkEventArgs e)
{
DateTime lastReportedDateTime = DateTime.MinValue;
double percentDoneDbl = 0.0;
int percentDoneInt = 0;
//create new instance
CurrentState state = new CurrentState();
try
{
Debug.WriteLine("\nConverting to Word doc...");
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(_maxConcurrentTasks))
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < WordData.Count; i++)
{
concurrencySemaphore.Wait();
state.Status = "Processing file " + i + " of " + WordData.Count + "...";
Debug.WriteLine(state.Status);
//-------------------------------
// report progress
//-------------------------------
if (i % 5 == 0)
{
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
//ToDo: Do the following any time you want to
//update the status, progressBar, or any other
//variables you need updated during the
//background operation.
//Reporting progress too often will have a
//significant performance impact
percentDoneDbl = (Convert.ToDouble(i) / Convert.ToDouble(WordData.Count)) * 100.0;
percentDoneInt = Convert.ToInt32(percentDoneDbl);
state.PercentDone = percentDoneInt;
worker.ReportProgress(0, state); //report progress back to form
lastReportedDateTime = DateTime.Now; //update last reported time
}
//-------------------------------
// end of progress reporting
//-------------------------------
//create Word document(s)
var t1 = Task.Run(() =>
{
try
{
CreateWordDocument(WordData[i].Filename, WordData[i].LetterBody);
}
finally
{
concurrencySemaphore.Release();
}
}).ContinueWith(task =>
{
byte[] fBytes = File.ReadAllBytes(WordData[i].Filename);
return fBytes;
});
byte[] bytesRead = (byte[])t1.Result;
//System.Diagnostics.Debug.WriteLine("t1[" + i + "].Length: " + t1.Result.Length);
//add to list
tasks.Add(t1);
}
Task.WaitAll(tasks.ToArray());
//clean up
tasks.Clear();
}
Debug.WriteLine("Status: Complete " + DateTime.Now.ToString("HH:mm:ss"));
if (e.Cancel == true)
{
state.Status = "Status: Cancelled by user.";
}
else
{
state.PercentDone = 100;
state.Status = "Status: Complete.";
}
worker.ReportProgress(0, state); //report progress back to form
lastReportedDateTime = DateTime.Now; //update last reported time
}
finally
{
//don't put any catch statements here so
//the errors will end up in e.Error in
//backgroundWorker1_RunWorkerCompleted.
//Handle the errors in
//backgroundWorker1_RunWorkerCompleted.
}
}
private string CreateWordDocument(string filename, string userData)
{
//*Note: All indices start at 1.
string result = string.Empty;
//set value
object oMissing = System.Reflection.Missing.Value;
object oEndOfDoc = "\\endofdoc"; /* \endofdoc is a predefined bookmark */
Word.Documents documents = null;
Word.Document doc = null;
bool isVisible = false;
try
{
//suppress displaying alerts (such as prompting to overwrite existing file)
_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
//set Word visibility
_wordApp.Visible = isVisible;
//if writing/updating a large amount of data
//disable screen updating by setting value to false
//for better performance.
//re-enable when done writing/updating data, if desired
//_wordApp.ScreenUpdating = false;
if (_wordApp != null)
{
if (File.Exists(filename))
{
//System.Diagnostics.Debug.WriteLine("'" + filename + "' exists. Existing file will be modified.");
//open existing Word document
documents = _wordApp.Documents;
doc = documents.Open(filename, System.Reflection.Missing.Value, false, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, isVisible, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
doc.Activate();
}
else
{
//create new document
doc = _wordApp.Documents.Add();
doc.Activate();
//Debug.WriteLine("Created new document");
}
if (doc == null)
{
Debug.WriteLine("Error: doc is null");
return null;
}
//set text
doc.Content.Text = userData;
if (!_wordApp.ScreenUpdating)
{
//in case screen updating was previously disabled,
//enable screen updating by setting value to true
_wordApp.ScreenUpdating = true;
//refresh screen
//_wordApp.ScreenRefresh();
}
if (!String.IsNullOrEmpty(filename))
{
try
{
//Debug.WriteLine("Saving to '" + filename + "'");
//save the document
//doc.SaveAs(filename, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
doc.SaveAs2(filename, Word.WdSaveFormat.wdFormatXMLDocument, CompatibilityMode: Word.WdCompatibilityMode.wdWord2013);
}//try
catch (Exception ex)
{
string errMsg = "Error: WordWriteDocument - " + ex.Message;
System.Diagnostics.Debug.WriteLine(errMsg);
if (ex.Message.StartsWith("Cannot access read-only document"))
{
System.Windows.Forms.MessageBox.Show(ex.Message + "Please close the Word document, before trying again.", "Error - Saving", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
}
//set value
result = "Success";
}
}
catch (Exception ex)
{
string errMsg = "Error: WordWriteDocument - " + ex.Message;
System.Diagnostics.Debug.WriteLine(errMsg);
}
finally
{
if (doc != null)
{
//close document
doc.Close(Word.WdSaveOptions.wdDoNotSaveChanges, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
//release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc);
}
}
return result;
}
使用方法:
添加以下声明(到 Windows 窗体、UserControl 或 Class)。或者,如果使用 Windows 窗体,则可以从 ToolBox 添加 Backgroundworker。
private BackgroundWorker backgroundWorker1 = new BackgroundWorker();
private ClsHelperWord helperWord = null;
设置 Backgroundworker 属性并订阅事件——这可以放在构造函数中。
//set properties
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
//subscribe to events (add listener)
backgroundWorker1.DoWork += BackgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += BackgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker1_RunWorkerCompleted;
BackgroundWorker1_DoWork
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.ComponentModel.BackgroundWorker worker;
worker = (System.ComponentModel.BackgroundWorker)sender;
ClsHelperWord myClsHelperWord = (ClsHelperWord)e.Argument;
helperWord.ConvertToWordDocument(worker, e);
}
BackgroundWorker1_ProgressChanged
private void BackgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
CurrentState state = (CurrentState)e.UserState;
Debug.WriteLine(state.Status + " " + state.PercentDone.ToString());
}
BackgroundWorker1_RunWorkerCompleted
private void BackgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// Handle the errors here.
// Write error message to console
// and/or a log file.
Debug.WriteLine("Error: " + e.Error.Message);
if (helperWord != null)
{
helperWord.Dispose();
helperWord = null;
}
}
else if (e.Cancelled)
{
Debug.WriteLine("Cancelled by user.");
if (helperWord != null)
{
helperWord.Dispose();
helperWord = null;
}
}
else
{
string finalStatus = "Status: Complete.";
Debug.WriteLine(finalStatus);
//clean up
if (helperWord != null)
{
helperWord.Dispose();
helperWord = null;
}
}
}
我使用以下方法创建一些文件名和文档文本(用于测试)。
创建测试数据
private List<WordFileInfo> CreateTestData(string folderName, string sampleLetterBody)
{
//create data for testing
int numTestFiles = 50; //for testing purposes
List<WordFileInfo> wordData = new List<WordFileInfo>();
for (int i = 0; i < numTestFiles; i++)
{
string filename = System.IO.Path.Combine(folderName, "WordDoc" + (i + 1).ToString() + ".docx");
//add to list
wordData.Add(new WordFileInfo(filename, sampleLetterBody));
//Debug.WriteLine("Filename: '" + filename + "'");
}
return wordData;
}
然后使用以下命令来测试创建 Word 文档:
string folderName = @"C:\Temp";
string sampleLetterBody = "This is some text.";
//get data for testing
List<WordFileInfo> wordData = CreateTestData(folderName, sampleLetterBody);
if (helperWord != null)
{
helperWord.Dispose();
helperWord = null;
}
//create new instance and set property
helperWord = new ClsHelperWord(wordData);
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync(helperWord);
}//if
选项 2:
*注意:如果与 Windows 窗体一起使用,此选项可能会导致 UI 无响应。
ClsHelperWord.cs
public class ClsHelperWord : IDisposable
{
//depending on the number of files processed,
//changing this value may increase/decrease performance slightly
private int _maxConcurrentTasks = 25;
//create new instance
private Word.Application _wordApp = new Word.Application();
//store data that needs to be processed
public List<WordFileInfo> WordData { get; set; } = new List<WordFileInfo>();
public ClsHelperWord()
{
}
public ClsHelperWord(List<WordFileInfo> wordData)
{
//set value
this.WordData = wordData;
}
public void Close()
{
Dispose();
}
public void Dispose()
{
if (_wordApp != null)
{
//close Word
object oFalse = false;
_wordApp.Quit(ref oFalse, ref oFalse, ref oFalse);
//release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(_wordApp);
}
}
public void ConvertToWordDocument()
{
Debug.WriteLine("\nConverting to Word doc...");
using (SemaphoreSlim concurrencySemaphore = new SemaphoreSlim (_maxConcurrentTasks))
{
List<Task> tasks = new List<Task>();
for (int i = 0; i < WordData.Count; i++)
{
concurrencySemaphore.Wait();
var t1 = Task.Run(() =>
{
try
{
CreateWordDocument(WordData[i].Filename, WordData[i].LetterBody);
}
finally
{
concurrencySemaphore.Release();
}
}).ContinueWith(task =>
{
byte[] fBytes = File.ReadAllBytes(WordData[i].Filename);
return fBytes;
});
byte[] bytesRead = (byte[])t1.Result;
//System.Diagnostics.Debug.WriteLine("t1[" + i + "].Length: " + t1.Result.Length);
//add to list
tasks.Add(t1);
}
Task.WaitAll(tasks.ToArray());
//clean up
tasks.Clear();
}
Debug.WriteLine("Status: Complete " + DateTime.Now.ToString("HH:mm:ss"));
}
private string CreateWordDocument(string filename, string userData)
{
//*Note: All indices start at 1.
string result = string.Empty;
//set value
object oMissing = System.Reflection.Missing.Value;
object oEndOfDoc = "\\endofdoc"; /* \endofdoc is a predefined bookmark */
Word.Documents documents = null;
Word.Document doc = null;
bool isVisible = false;
try
{
//suppress displaying alerts (such as prompting to overwrite existing file)
_wordApp.DisplayAlerts = Word.WdAlertLevel.wdAlertsNone;
//set Word visibility
_wordApp.Visible = isVisible;
//if writing/updating a large amount of data
//disable screen updating by setting value to false
//for better performance.
//re-enable when done writing/updating data, if desired
//_wordApp.ScreenUpdating = false;
if (_wordApp != null)
{
if (File.Exists(filename))
{
//System.Diagnostics.Debug.WriteLine("'" + filename + "' exists. Existing file will be modified.");
//open existing Word document
documents = _wordApp.Documents;
doc = documents.Open(filename, System.Reflection.Missing.Value, false, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, isVisible, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
doc.Activate();
}
else
{
//create new document
doc = _wordApp.Documents.Add();
doc.Activate();
//Debug.WriteLine("Created new document");
}
if (doc == null)
{
Debug.WriteLine("Error: doc is null");
return null;
}
//set text
doc.Content.Text = userData;
if (!_wordApp.ScreenUpdating)
{
//in case screen updating was previously disabled,
//enable screen updating by setting value to true
_wordApp.ScreenUpdating = true;
//refresh screen
//_wordApp.ScreenRefresh();
}
if (!String.IsNullOrEmpty(filename))
{
try
{
//Debug.WriteLine("Saving to '" + filename + "'");
//save the document
//doc.SaveAs(filename, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing, ref oMissing);
doc.SaveAs2(filename, Word.WdSaveFormat.wdFormatXMLDocument, CompatibilityMode: Word.WdCompatibilityMode.wdWord2013);
}//try
catch (Exception ex)
{
string errMsg = "Error: WordWriteDocument - " + ex.Message;
System.Diagnostics.Debug.WriteLine(errMsg);
if (ex.Message.StartsWith("Cannot access read-only document"))
{
System.Windows.Forms.MessageBox.Show(ex.Message + "Please close the Word document, before trying again.", "Error - Saving", System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Error);
}
}
}
//set value
result = "Success";
}
}
catch (Exception ex)
{
string errMsg = "Error: WordWriteDocument - " + ex.Message;
System.Diagnostics.Debug.WriteLine(errMsg);
}
finally
{
if (doc != null)
{
//close document
doc.Close(Word.WdSaveOptions.wdDoNotSaveChanges, System.Reflection.Missing.Value, System.Reflection.Missing.Value);
//release all resources
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(doc);
}
}
return result;
}
}
使用方法:
我使用以下方法创建一些文件名和文档文本(用于测试)。
创建测试数据
private List<WordFileInfo> CreateTestData(string folderName, string sampleLetterBody)
{
//create data for testing
int numTestFiles = 50; //for testing purposes
List<WordFileInfo> wordData = new List<WordFileInfo>();
for (int i = 0; i < numTestFiles; i++)
{
string filename = System.IO.Path.Combine(folderName, "WordDoc" + (i + 1).ToString() + ".docx");
//add to list
wordData.Add(new WordFileInfo(filename, sampleLetterBody));
//Debug.WriteLine("Filename: '" + filename + "'");
}
return wordData;
}
然后使用以下命令来测试创建 Word 文档:
string folderName = @"C:\Temp";
string sampleLetterBody = "This is some text.";
//get data for testing
List<WordFileInfo> wordData = CreateTestData(folderName, sampleLetterBody);
using (ClsHelperWord helperWord = new ClsHelperWord(wordData))
{
helperWord.ConvertToWordDocument();
}
以下帖子也可能会有所帮助。