今天下午我一直在工作,试图使用后台工作人员读取位于我的程序集中的一个相当大的xml文件。它一直运行得很好,直到我决定重命名后台工作人员的对象名。在我构建解决方案后,它告诉我它是成功的。在运行并测试了我的程序后,我注意到后台工作人员的DoWork Event根本拒绝启动。当我将下面的代码添加到Backgroundworker的RunWorkerCompleted事件中时,我得到的只是一个消息框,其中包含一个巨大的"没有"。MessageBox.Show(e.Result + " " + e.Error);
将其重新命名为原来的名称也无济于事。所有的代码都是我自己写的,所以这里没有涉及第三方应用程序。
这是我用来设置后台工作人员工作的代码
private volatile List<string> listItems = new List<string>();
private BackgroundWorker _populateListItems = new BackgroundWorker();
private string currentConnection;
#endregion
public frmSettings()
{
InitializeComponent();
//I will be able to track how far the list is populated with the following command.
_populateListItems.WorkerReportsProgress = true;
//This will fire if there is an error in the xml file.
_populateListItems.WorkerSupportsCancellation = true;
//Assign the job to be done for the backgroundworker
_populateListItems.DoWork +=new DoWorkEventHandler(_populateListItems_DoWork);
_populateListItems.ProgressChanged += new ProgressChangedEventHandler(_populateListItems_ProgressChanged);
//When the worker is finished, the following event will fire: All UI controls will be updated.
_populateListItems.RunWorkerCompleted += new RunWorkerCompletedEventHandler(_populateListItems_RunWorkerCompleted);
_populateListItems.RunWorkerAsync();
}
void _populateListItems_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
prgProgress.Value = e.ProgressPercentage;
lblProgress.Text = e.ProgressPercentage.ToString() + "%";
}
private void _populateListItems_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//The operation is completed/Cancelled/Erroneous. The list will now be populated with the available information.
MessageBox.Show(e.Result + " " + e.Error);
foreach (string item in listItems)
{
liConnections.Items.Add(item);
}
//This shows the user which connection is currently used.
lblCurrentSelection.Text = currentConnection;
}
private void _populateListItems_DoWork(object sender, DoWorkEventArgs e)
{
try
{
//The following lines of code are the variables that are going to be used.
// xReadSettings will make the XmlReader xRead Ignore whitespaces and comments, if any.
// assemblyExecuted will get information from which assembly gets Executed.
// filename Will get the filename of the settings.xml file that is going to be used.
// SettingsStream will open the stream for reading and writing, using the GetManifestResourceStream() method from the currently executed assembly.
XmlReaderSettings xReadSettings = new XmlReaderSettings();
Assembly assemblyExecuted = Assembly.GetExecutingAssembly();
string filename = String.Format("{0}.Settings.xml", assemblyExecuted.GetName().Name);
Stream settingsStream = assemblyExecuted.GetManifestResourceStream(filename);
xReadSettings.IgnoreComments = true;
xReadSettings.IgnoreWhitespace = true;
//Finally the xmlReader object is created using the settingstream, and its settings.
//While the stream reads, it checks whether the nodes accessed are elements of the xml file.
//if it is an element that is accessed, then we check whether the element which is accessed is a connection string to the database
//if it is a connectionstring to the database, we will check if the connection has a name.
//if it has a name, we get the name attribute, and add it to our list. The list is volatile, so it will be up to date, because this
//background thread is updating it.
//To Update the progress of the backgroundworker, we need to know the amount of elements in the XML File. Xpath will be used for this.
XmlDocument xdoc = new XmlDocument();
xdoc.Load(settingsStream);
XmlNodeList nodes = xdoc.SelectNodes("*"); //Xpath - select all.
int totalElementsToRead = nodes.Count;
int totalElementsRead = 0;
using (XmlReader xRead = XmlReader.Create(settingsStream, xReadSettings))
{
while (xRead.Read())
{
if (xRead.NodeType == XmlNodeType.Element)
{
if (xRead.Name == "ConnectionString")
{
if (xRead.HasAttributes)
{
string attribute = xRead.GetAttribute("name").ToString();
listItems.Add(attribute);
}
}
if (xRead.Name == "CurrentConnection")
{
xRead.Read(); //gets the value of <CurrentConnection>
currentConnection = xRead.Value.Trim();
}
totalElementsRead++;
_populateListItems.ReportProgress(totalElementsRead / totalElementsToRead * 100);
}
}
}
}
catch
{
_populateListItems.CancelAsync();
}
}
请原谅背后的评论中的理论。我正在尽我所能解释它。
不过,我的问题是,有人能看到这哪里出了问题吗?为什么活动突然不启动?它应该用我的xml文件中的项目填满一个列表(未动,在重命名之前正在工作(。通过调试运行一个步骤也证明它跳过了我的doWork事件处理程序。
我认为问题是您从构造函数调用
RunWorkerAsync
,而ProgressChanged
由于表单还不可见而失败。尝试将对RunWorkerAsync
的调用移动到窗体的Show
事件处理程序
好的,所以问题是DoWork
事件处理程序中的一个异常,它被try..catch
块吞噬。
总结您的代码问题:
-
的
try..catch
块占用所有异常并使调试变得困难 - 从表单构造函数内部调用
RunWorkerAsync
- 在没有正确同步/锁定的情况下从工作线程访问UI线程对象
- 从
DoWork
事件处理程序内部调用CancelAsync