Service 有关必须使用web浏览器和文件对话框的Windows服务/计划任务的帮助

Service 有关必须使用web浏览器和文件对话框的Windows服务/计划任务的帮助,service,webbrowser-control,scheduled-tasks,Service,Webbrowser Control,Scheduled Tasks,我想做的事 我正在尝试创建任何类型的解决方案,这些解决方案将每晚在Windows服务器上运行,对网站进行身份验证,检查网站上指示zip文件新版本的新链接,使用新链接(如果存在)下载zip文件,将下载的文件解压缩到服务器上的现有文件夹,使用解压缩的内容(sql脚本等)来构建数据库实例,并记录文本文件中发生的一切 表单应用程序:Sorta工作的部分 我创建了一个Windows窗体应用程序,它使用两个WebBrowser控件、两个线程和几个计时器来完成除夜间运行之外的所有操作。当我登录并运行它时,它作

我想做的事

我正在尝试创建任何类型的解决方案,这些解决方案将每晚在Windows服务器上运行,对网站进行身份验证,检查网站上指示zip文件新版本的新链接,使用新链接(如果存在)下载zip文件,将下载的文件解压缩到服务器上的现有文件夹,使用解压缩的内容(sql脚本等)来构建数据库实例,并记录文本文件中发生的一切

表单应用程序:Sorta工作的部分

我创建了一个Windows窗体应用程序,它使用两个WebBrowser控件、两个线程和几个计时器来完成除夜间运行之外的所有操作。当我登录并运行它时,它作为一个窗体非常有用,但我需要让它(或类似的东西)像服务或计划任务一样自行运行

我的服务尝试

因此,我创建了一个每小时滴答响一次的Windows服务,如果System.DateTime.Now.hour>=22,则会尝试启动Windows窗体应用程序来执行此操作。当该服务尝试启动窗体时,会发生以下错误:

无法实例化ActiveX控件“8856f961-340a-11d0-a96b-00c04fd705a2”,因为当前线程不在单线程单元中

我研究并试图通过在服务程序类的主方法上放置[StatThread]属性,或在一些地方使用类似的代码(包括表单构造函数)来解决这个问题:

        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();