Service 有关必须使用web浏览器和文件对话框的Windows服务/计划任务的帮助
我想做的事 我正在尝试创建任何类型的解决方案,这些解决方案将每晚在Windows服务器上运行,对网站进行身份验证,检查网站上指示zip文件新版本的新链接,使用新链接(如果存在)下载zip文件,将下载的文件解压缩到服务器上的现有文件夹,使用解压缩的内容(sql脚本等)来构建数据库实例,并记录文本文件中发生的一切 表单应用程序:Sorta工作的部分 我创建了一个Windows窗体应用程序,它使用两个WebBrowser控件、两个线程和几个计时器来完成除夜间运行之外的所有操作。当我登录并运行它时,它作为一个窗体非常有用,但我需要让它(或类似的东西)像服务或计划任务一样自行运行 我的服务尝试 因此,我创建了一个每小时滴答响一次的Windows服务,如果System.DateTime.Now.hour>=22,则会尝试启动Windows窗体应用程序来执行此操作。当该服务尝试启动窗体时,会发生以下错误: 无法实例化ActiveX控件“8856f961-340a-11d0-a96b-00c04fd705a2”,因为当前线程不在单线程单元中 我研究并试图通过在服务程序类的主方法上放置[StatThread]属性,或在一些地方使用类似的代码(包括表单构造函数)来解决这个问题:Service 有关必须使用web浏览器和文件对话框的Windows服务/计划任务的帮助,service,webbrowser-control,scheduled-tasks,Service,Webbrowser Control,Scheduled Tasks,我想做的事 我正在尝试创建任何类型的解决方案,这些解决方案将每晚在Windows服务器上运行,对网站进行身份验证,检查网站上指示zip文件新版本的新链接,使用新链接(如果存在)下载zip文件,将下载的文件解压缩到服务器上的现有文件夹,使用解压缩的内容(sql脚本等)来构建数据库实例,并记录文本文件中发生的一切 表单应用程序:Sorta工作的部分 我创建了一个Windows窗体应用程序,它使用两个WebBrowser控件、两个线程和几个计时器来完成除夜间运行之外的所有操作。当我登录并运行它时,它作
webBrowseThread = new Thread(new ThreadStart(InitializeComponent));
webBrowseThread.SetApartmentState(ApartmentState.STA);
webBrowseThread.Start();
在后一种方法中,表单上的控件(将在InitializeComponent中初始化)没有初始化,我得到空引用异常
我的计划任务尝试
因此,我尝试使用自己的凭据创建一个夜间计划任务,以便在我的开发机器上本地运行表单(只是测试)。它比服务做得更远,但在文件下载对话框中挂起
相关说明:为了发送密钥序列以通过“文件下载”和“文件另存为”对话框,我的表单实际上运行了两个使用WScript.Shell.SendKeys的vbscript文件。好吧,承认这很尴尬,但我尝试了一些不同的方法,包括Win32 API中的SendMessage和在我的表单中引用iWShurantimeLibrary来使用SendKeysC#代码。当我研究如何通过对话框时,Win32 API似乎是推荐的方法,但我无法理解。vbscript文件是我唯一可以工作的东西,但我现在担心这可能是计划任务无法工作的原因
关于我对网络浏览器控件的选择
我已经阅读过System.WebClient类作为WebBrowser控件的替代品,但乍一看,它似乎不具备完成此任务所需的功能。例如,我需要(或我认为我需要)WebBrowser的DocumentCompleted和FileDownload事件,用于处理页面加载、文件下载等延迟。WebClient是否有我没有看到的更多内容?除了WebBrowser之外,是否还有另一个更友好的服务类,可以做到这一点
总之
天啊,这太长了。对不起!如果能有一个更高层次的建议来更好地完成我想做的事情,那会很有帮助,因为我尝试过的方法都不管用
2009年10月22日更新
好吧,我想我更接近了,但我又被卡住了。我应该得到一个大小合适的zip文件,其中包含几个文件,但由我的代码生成的zip文件是空的。以下是我的代码:
// build post request
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(targetHref);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// encoding to use
Encoding enc = Encoding.GetEncoding(1252);
// build post string containing authentication information and add to post request
string poststring = "returnUrl=" + fixCharacters(targetDownloadFileUrl);
poststring += getUsernameAndPasswordString();
poststring += "&login2.x=0&login2.y=0";
// convert to required byte array
byte[] postBytes = enc.GetBytes(poststring);
request.ContentLength = postBytes.Length;
// write post to request
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// get response as stream
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
// writes stream to zip file
FileStream writeStream = new FileStream(fullZipFileName, FileMode.Create, FileAccess.Write);
ReadWriteStream(responseStream, writeStream);
response.Close();
responseStream.Close();
ReadWriteStream的代码如下所示
private void ReadWriteStream(Stream readStream, Stream writeStream)
{
// taken verbatum from http://www.developerfusion.com/code/4669/save-a-stream-to-a-file/
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
post字符串的构建是从我以前运行的forms应用程序中获取的。我比较了两组代码(我的working forms应用程序和此应用程序)在poststring中的结果值,发现它们是相同的
我甚至不知道如何进一步排除故障。有人看到任何明显的原因,这是不工作的吗
结论2009年10月23日
我终于成功了。我必须克服几个重要的障碍。我在网上获得的ReadWriteStream方法代码有一些问题。我不知道为什么,但它对我不起作用。Claudio Lassala中一个叫JB的家伙帮助我想出了这段代码,这段代码对我来说效果更好:
private void WriteResponseStreamToFile(Stream responseStreamToRead, string zipFileFullName)
{
// responseStreamToRead will contain a zip file, write it to a file in
// the target location at zipFileFullName
FileStream fileStreamToWrite = new FileStream(zipFileFullName, FileMode.Create);
int readByte = responseStreamToRead.ReadByte();
while (readByte != -1)
{
fileStreamToWrite.WriteByte((byte)readByte);
readByte = responseStreamToRead.ReadByte();
}
fileStreamToWrite.Flush();
fileStreamToWrite.Close();
}
正如下面威尔所说的,我确实在身份验证方面遇到了问题。下面的代码是解决这个问题的有效方法。插入了一些注释来解决我遇到的关键问题
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest firstRequest = (HttpWebRequest)WebRequest.Create(targetHref);
firstRequest.AllowAutoRedirect = false; // this is critical, without this, NLM redirects and the whole thing breaks
// firstRequest.Proxy = new WebProxy("127.0.0.1", 8888); // not needed for production, but this helped in order to debug the http traffic using Fiddler
firstRequest.Method = "POST";
firstRequest.ContentType = "application/x-www-form-urlencoded";
// build post string containing authentication information and add to post request
StringBuilder poststring = new StringBuilder("returnUrl=" + fixCharacters(targetDownloadFileUrl));
poststring.Append(getUsernameAndPasswordString());
poststring.Append("&login2.x=0&login2.y=0");
// convert to required byte array
byte[] postBytes = Encoding.UTF8.GetBytes(poststring.ToString());
firstRequest.ContentLength = postBytes.Length;
// write post to request
Stream postStream = firstRequest.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length); // Fiddler shows that post and response happen on this line
postStream.Close();
// get response as stream
HttpWebResponse firstResponse = (HttpWebResponse)firstRequest.GetResponse();
// create new request for new location and cookies
HttpWebRequest secondRequest = (HttpWebRequest)WebRequest.Create(firstResponse.GetResponseHeader("location"));
secondRequest.AllowAutoRedirect = false;
secondRequest.Headers.Add(HttpRequestHeader.Cookie, firstResponse.GetResponseHeader("Set-Cookie"));
// get response to second request
HttpWebResponse secondResponse = (HttpWebResponse)secondRequest.GetResponse();
// write stream to zip file
Stream responseStreamToRead = secondResponse.GetResponseStream();
WriteResponseStreamToFile(responseStreamToRead, fullZipFileName);
responseStreamToRead.Close();
sl.logScriptActivity("Downloading update.");
firstResponse.Close();
我想强调的是,在第一个HttpWebRequest实例上将AllowAutoRedirect设置为false对整个工作至关重要。Fiddler显示了未设置时发生的另外两个请求,这破坏了脚本的其余部分。您试图使用UI控件在windows服务中执行某些操作。这永远不会发生工作 您需要做的只是使用和类 您可以转储流的内容,解析文本以查找更新,然后为要下载的文件的URL构造一个新的请求。然后,该响应流将拥有该文件,您可以将其转储到文件系统等
在您怀疑之前,GetResponse将一直阻塞,直到响应返回,并且流将随着数据的接收而阻塞,因此您不必担心在下载所有内容时触发事件。您肯定需要重新思考您的方法(正如您已经开始做的那样)消除基于表单的应用程序方法。您描述的服务需要在没有UI的情况下运行 我不熟悉System.WebClient的详细信息,但因为 提供发送的常用方法 向服务器发送数据和从服务器接收数据 由URI标识的资源 这可能是你的答案。
var request = WebRequest.Create("http://www.google.com");
var response = request.GetResponse();
var stream = response.GetResponseStream();