2009-10-07 31 views
0

我正在嘗試做幫助Windows服務/計劃任務必須使用一個網絡瀏覽器和文件對話框

我試圖創建任何類型的解決方案,將在夜間運行Windows服務器,對網站進行身份驗證,在網站上檢查網頁上的新鏈接,以指示新版本的zip文件,使用新鏈接(如果存在)下載壓縮文件,將下載的文件解壓縮到服務器,使用解壓後的內容(sql腳本等)構建數據庫實例,並記錄發生在文本文件中的所有內容。

窗體應用程序:那八九不離十工程

我創建一個Windows的部分形成,它使用一對夫婦的WebBrowser控件,一對夫婦線程,和幾個定時器做一切,除了夜間運行的應用程序。當我登錄並運行它時,它非常適合作爲表單使用,但我需要將它(或類似的東西)作爲一個服務或計劃任務運行。

我的服務嘗試

所以,我創建了一個Windows服務蜱每一個小時,如果System.DateTime.Now.Hour> = 22,嘗試啓動Windows窗體應用做的事情。當服務嘗試啓動窗體時,會發生此錯誤:

無法實例化ActiveX控件'8856f961-340a-11d0-a96b-00c04fd705a2',因爲當前線程不在單線程單元中。

,我研究並試圖通過放置該[STAThread]屬性上服務的程序類的主要方法還是使用像這樣的代碼在一些地方,包括窗體構造函數來解決:

 webBrowseThread = new Thread(new ThreadStart(InitializeComponent)); 
     webBrowseThread.SetApartmentState(ApartmentState.STA); 
     webBrowseThread.Start(); 

我無法得到任何一種工作方法。在後一種方法中,表單上的控件(將在IntializeComponent中初始化)不會被初始化,而是我得到空引用異常。

我的任務計劃嘗試

所以,我試圖用我自己的憑據到我的開發機器上本地運行的形式創造一個夜間計劃任務(只是測試)。它比服務得到的更遠,但掛在文件下載對話框中。

相關注意事項:要發送鍵序列通過文件下載來獲取和文件另存爲對話框,我的表實際運行一對夫婦使用WScript.Shell.SendKeys的VBScript文件。好吧,這是尷尬承認,但我嘗試了一些不同的東西,包括Win32 API中的SendMessage和引用IWshRuntimeLibrary在我的C#代碼中使用SendKeys。當我研究如何通過對話框時,Win32 API似乎是推薦的方法,但我無法弄清楚。 vbscript文件是我能工作的唯一東西,但我現在擔心這可能是計劃任務無法工作的原因。

關於我的WebBrowser控件的選擇

我已閱讀有關System.WebClient類來替代WebBrowser控件,而且一目瞭然,它看起來並不像它有什麼,我需要完成這件事。例如,我需要(或者我認爲我需要)Web​​Browser的DocumentCompleted和FileDownload事件來處理頁面加載,文件下載等方面的延遲。WebClient還有更多我沒有看到的事情嗎?除了WebBrowser以外,是否還有另外一個更友好的服務並且可以做到這一點?

總結

吉茲,這是長。抱歉!這將有助於甚至有一個更好的方式來做我想做的事情的高層次的建議,因爲我嘗試過的東西都沒有奏效。

更新09年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(); 
} 

後字符串的建設是從我以前的形式的應用程序,採取的工作。我比較了兩組代碼(我的工作表單應用程序和這一個)的poststring中的結果值,它們是相同的。

我甚至不確定如何排除故障。任何人都可以看到明顯的原因,爲什麼這不起作用?

結論09年10月23日

我終於有了這個工作。我必須克服一些重要的障礙。我在線上的ReadWriteStream方法代碼遇到了一些問題。我不知道爲什麼,但它不適合我。在克勞迪奧Lassala的Virtual Brown Bag meeting叫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(); 

我想強調的是,AllowAutoRedirect設置爲false,第一個HttpWebRequest的實例是整個事情的關鍵工作。當沒有設置時,提琴手顯示了兩個額外的請求,並且破壞了腳本的其餘部分。

+0

代碼看起來不錯。我會抓住一小撮Fiddler http://www.fiddlertool.com/並觀看您的要求。如果這不起作用,我會創建一個小型的網絡應用程序並與之對抗,以便我可以看到兩端發生了什麼。 – Will 2009-10-22 17:48:41

+0

另一個想法 - 您的請求可能會遇到登錄和退出問題。同樣,通過查看請求可以讓您比較從瀏覽器和代碼中進行此操作。我懷疑您需要登錄,獲取您的身份驗證cookie,並在嘗試下載zip文件時將該cookie發回。從小提琴手開始,比較你的要求。 – Will 2009-10-22 17:51:48

+0

太棒了。感謝您更新您的問題,並提供有關這些內容的詳細信息。我可能會回來偷你的一些代碼... – Will 2009-10-23 20:35:10

回答

1

您正在嘗試使用UI控件在Windows服務中執行某些操作。這將無法工作。

你需要做的只是使用WebRequestWebResponsedownload the contents of the webpage.

var request = WebRequest.Create("http://www.google.com"); 
var response = request.GetResponse(); 
var stream = response.GetResponseStream(); 

你可以轉儲流的內容是什麼,分析查找更新的文本,然後構造了一個新的請求您要下載的文件的URL。然後,該響應流將具有該文件,您可以將其轉儲到文件系統等。

在您想知道之前,GetResponse會阻塞,直到響應返回,並且數據流將被阻塞,因此您無需擔心在下載完所有事件後觸發事件。

+0

謝謝,威爾。我正在努力使用您所建議的方法,並且迄今爲止都非常棒。一旦我完成並工作,我會回覆任何額外的值得注意的事項。 – Marvin 2009-10-21 18:57:48

+0

我有更新。我再次被卡住了。我編輯了主帖,因爲評論太多了。 – Marvin 2009-10-22 16:44:05

+0

我終於弄明白了,並且更新了我原來的帖子以反映。謝謝,威爾,感謝您的幫助。 – Marvin 2009-10-23 19:45:46

0

您絕對需要重新考慮您的方法(如您已經開始這樣做),以消除基於表單的應用程序方法。您所描述的服務需要在沒有UI的情況下運行。

我不熟悉System.WebClient的細節,但由於它

提供了從由URI標識的 資源發送 數據和接收數據的常用方法,

它可能會是你的答案。 乍一看,WebClient.DownloadFile(...)或WebClient.DownloadFileAsync(...)將會做你所需要的。

0

我唯一可以補充的是,一旦你刮過了你的屏幕並擁有你想要下載的文件的完全限定名稱,你可以將它傳遞給Windows/DOS命令'get'來獲取文件通過HTTP。如果需要,您還可以編寫命令行FTP客戶端腳本。我在Windows中嘗試過這樣的事情已經很長時間了,但我認爲你已經快到了。一旦你提取了正確的文件,建立一個批處理文件來完成其他任務應該非常容易。如果你更喜歡Unix,Google的「unix services for windows」只是關注他們開始運行的服務(DHCP等)。有一些很好的實用工具可以讓你把dos當成一個類Unix的shell(ls -l,grep等)。最後,你可以嘗試另一種語言,比如Perl或者Python,但是我不認爲這是你的建議尋找。 :)