顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2011年11月13日 星期日

.net 程式進階除錯教學 - 使用WinDbg

        從以前一直研究基金方面的資訊,但由於沒有多於的時間常常觀看,再加上碩士時在我們老師的指導下,我將基金的資訊透過 Data Mining的模式進行分析,但由於Data Mining需要大量的資訊,所以我設計了一個資料搜集器,會從各大網站中進行基金資訊的收集,而這個程式我是從 c# 2.0的時候就開始寫了,功能不斷的增加,原本初期是希望透過排程自動化處理,但是一直以來有一個小問題,由於我的系統中使用了多個執行緒,所以有時候系統會卡住不動,然後就一直停住,而且有時候是不預期的發生,所以這支蟲我也捉了好久,由其是在功能不斷的新增之後,還是沒有改善。



        最近在公司知道有一個好工具可以進行分析,名稱為WinDbg,所以我就拿這個問題來小試身手,我將安裝步驟與解決方法列於下方,再請大家多多參考。

1、下載 WinDbg 工具

1-1. 先至微軟的官方網站下載,此工具分成兩個版本,一個是32位元,一個是64位元,基本上這不是取絕於你的OS為何,而是你要除錯的程式為32位元還是64位元,那要如何分辨呢?最簡單的方式就是透過工作管理員確認即可。



下載網址:
http://msdn.microsoft.com/en-us/windows/hardware/gg463009



2、重現應用程式卡住的情況,然後進行下列的動作,收集dump files。
 
2-1. 確認應用程式執行的是32還是64位元的。

2-2. 啟動一個命令視窗,切換至安裝目錄下:

  •      x32 -> C:\Program Files (x86)\Debugging Tools for Windows (x86)
  •      x64 -> C:\Program Files\Debugging Tools for Windows (x64)

2-3. 由於我的應用程式是Hang的情況,所以請執行下列的指令:

adplus.exe -hang -quiet -pn fund.vshost.exe -o c:\dumps

     參數說明:
  • -hang 執行adplus在Hang的模式。
  • -o 指定輸出路徑
  • -quiet 不跳出快顯視窗
  • -pn 指定應用程式的名稱
     

2-4. 確認在 c:\dumps 的檔案夾下是否有產生dump files

3、設定 WinDbg 的環境

3-1. 開啟 windbg 之後,選擇 [File] -> [Symbol File Path]


3-2. 在開啟的視窗中輸入下列的資訊:         

SRV*c:\websymbols*http://msdl.microsoft.com/download/symbols



4、分析 Dump Files

4-1 開啟 WinDbg


4-2. 載入後,所以的動作皆需透過指令的方式完成,而輸入的地方就在下方 [0:000 >] 的旁邊。
4-3. 輸 [k] 觀察程式中 stack的狀態,輸入後你會發現出現許多的 mscorwks 這也代表您的應用程式是執行在.Net之下。


4-4. 由於我們觀察 .Net的相關記憶體與物件資料,所以需要載入 sos.dll,請在命令視窗中輸入下列的指令。

.loadby sos mscorwks


4-5. 載入後我們 透過下列的指令來確認目前.net 中 stack的情況。

~*e!clrstack


4-6. 透過4-5的指令可能會列出許多的資訊,請仔細觀察後,終於找到卡住的地方,也就是下列紅色圈起來的地方,主要是卡在 [set_page_property],因為這個函式中有一個迴圈會不斷的等待執行緒的回傳,我作了兩個動作,一是初使值的設定,二是等待超過1分鐘就強迫停止這一個執行緒的資料捉取,所以改完後問題就解決了。


PS:在上圖中有一個箭頭由下往上,主要指的是由於這是一個stack的資料結構,所以你可以看出是由 Update_basic() -> external_company() -> set_page_property(),藉以了解執行的順序。


參考連結:
WinDbg Command-Line Options
http://msdn.microsoft.com/en-us/library/ff561306(v=vs.85).aspx
SOS.dll (SOS Debugging Extension)
http://msdn.microsoft.com/en-us/library/bb190764.aspx
Debugging Tools and Symbols: Getting Started
http://msdn.microsoft.com/en-us/windows/hardware/gg462988


關鍵字:C#.NetWinDbgHang DumpDump File



2011年4月30日 星期六

Crystal Reports與ASP.NET網路送印問題處理與分享

        最近接到一個需求,當使用者希望將原本的報表(Crystal Reports)直接送印到報表主機上的某一台印表機時,該如何進行,基本上程式不困難,但因為這個問題主要由是作業系統造成(Q152451 、 Q184291),所以需進行相關的修正後,程式的寫法也需要調整過才能進行,所以我在這整理一下我的作法並分享出來。

         很多人會問為何不要當報表打開後再直接手動按列印即可,何必要這個功能,但是你想如何有一個單位每天有幾百張報表要印,每一張都要等個10幾秒,那直接送印是不是就可以當掉許多的時間,而目前大部份的使用者在處理這個問題上,大部份都是在客戶端也需要裝上相對的驅動程式,才可以進行直接送印,但是這樣一來在日後部署上就會有問題,而且維護成本也會相對的變重。

設定的步驟如下:

1、先設定IIS的執行身份,這個問題主要是由於當進行網路列印時,Windows並不會指定帳號,所以必須指定固定帳號,並設定印表機的執行身份與權限。
設定伺服器端列印

存取 Machine.config 檔,其預設位置如下:
C:\WINNT\Microsoft.NET\Framework\<VersionNumber>\CONFIG\

<VersionNumber> 是安裝在電腦上的 .NET Framework 的版本。帳戶設定是位於這個檔案的 <ProcessModel> 標記內。帳戶的預設值是:userName="machine" password="AutoGenerate"

請編輯 Machine.config 檔中的組態設定。將預設值變更為:
userName="SYSTEM" password="AutoGenerate"


儲存檔案。
現在就能夠將伺服器上的列印權限賦與本機 SYSTEM 帳戶。

如果印表機是網路印表機,請將此網路印表機公開給本機 SYSTEM 帳戶。
當印表機安裝在電腦上時,這個印表機的設定會儲存在登錄中;而登錄是位於 HKEY_CURRENT_USER 登錄群組 (登錄檔) 之下。IIS 處理序永遠不會使用這個登錄群組,因為它一定是在本機系統帳戶的內容下執行,並且無法以「目前使用者」的身份登入伺服器。根據預設,SYSTEM 帳戶在登錄中並沒有設定任何印表機。有兩篇 Microsoft Knowledge Base 文件,Q152451Q184291 會說明如何將印表機設定公開給 System 帳戶。這兩篇文件都會要求您編輯 Web 伺服器上的登錄,並且將印表機設定從 HKEY_CURRENT_USER 群組複製到 HKEY_USERS/.DEFAULT 群組。

2、設定指定帳號後,就需要透過設定登錄檔的方式指定System帳號的可用印表機有那些。

您可以設定 SYSTEM 帳戶如果要解決這個問題的印表機。 若要設定 SYSTEM 帳戶的印表機,執行下列步驟:
這個方法會要求您修改使用 「 登錄編輯程式 」 登錄。
警告: 不當使用 「 登錄編輯器 」 可能會導致嚴重的全系統的問題,必須重新安裝 Windows 修正它們。 Microsoft 無法保證任何因使用登錄編輯程式所造成的問題可以獲得解決。使用此工具,請自行負擔相關的風險。
確定您目前登入伺服器的使用者具有您想要安裝的印表機。
啟動登錄編輯程式 (Regedit.exe)。
選取下列機碼:
      HKEY_CURRENT_USER
         \Software\Microsoft\Windows NT\Current Version\Devices

按一下 [登錄] 功能表的 [匯出登錄檔案]。
在 [檔案名稱] 文字方塊中,輸入 c:\Devices.reg。
選取下列機碼:
      HKEY_CURRENT_USER
         \Software\Microsoft\Windows NT\Current Version\PrinterPorts

按一下 [登錄] 功能表的 [匯出登錄檔案]。
在 [檔案名稱] 文字方塊中,輸入 c:\PrinterPorts.reg。
選取下列機碼:
      HKEY_CURRENT_USER
         \Software\Microsoft\Windows NT\Current Version\Windows

按一下 [登錄] 功能表的 [匯出登錄檔案]。
在 [檔案名稱] 文字方塊中,輸入 c:\Windows.reg。
從 [開始] 按鈕選取 [執行]。在執行] 對話方塊中鍵入 「 記事本 」 Devices.reg,「 記事本 」 開啟 Devices.reg。
文字 HKEY_CURRENT_USER 取代 HKEY_USERS\.DEFAULT
儲存檔案。然後匯入登錄連按兩下檔案,在 [檔案總管] 中。
對 PrinterPorts.reg 和 Windows.reg 重複執行修改後再匯入的步驟即可。
這些步驟僅適用於本機印表機。

若要以便 IIS 使用 SYSTEM 帳戶來列舉網路印表機請依照下列步驟執行。
附註網路服務帳戶下執行處理程序,明確的權限到新建立的登錄需要。
請確定您目前登入伺服器以使用者身分已安裝的所需的網路印表機。
啟動 「 登錄編輯程式 」。
按一下下列機碼:
HKEY_CURRENT_USER\Printers\Connections
在 [登錄] 功能表上按一下 [匯出登錄檔案]。
在 [檔案名稱] 方塊中,輸入 c:\printconns.reg。
在 「 記事本 」 開啟 printconns.reg 檔案,請按一下 [開始]、 按一下 [執行]、 在 [開啟] 方塊輸入 記事本 printconns.reg,然後按一下 [[確定]]。
取代文字 HKEY_USERS\.DEFAULT HKEY_CURRENT_USER 文字。
儲存檔案。
將檔案匯入到登錄,按兩下在 [檔案總管] 中的檔案。
重新啟動列印多工緩衝處理器 」 服務。


3、當瞬間的列印排程過多時,可能也會出現相同的錯誤訊息,所以可以透過修改登錄檔的方式進行調整,並將原本預設值從75調整放大,藉以減少列印排程超過設定值而造成錯誤發生。

HKEY_LOCAL_MACHINE\SOFTWARE\Business Objects \Suite 11.x\Report Application Server\InprocServer\PrintJobLimit

4、設定System帳號可以存取相關的印表機。




5、當修改完上述的設定值之後,在程式進行直接列印的部份也需要進行調整,不可用ReportDocument.PrintOptions.PrinterName的方式來指定,必須用PrinterSettings的方式來進行印表機的設定,C#程式碼如下:

舊有寫法:

rptDoc.PrintOptions.PrinterName = printerSettings.PrinterName;


新的寫法:

PrinterSettings printerSettings = new PrinterSettings();
PageSettings pageSettings = new PageSettings();
rd.PrintOptions.CopyTo(printerSettings, pageSettings);
printerSettings.PrinterName = PrinterName;
printerSettings.PrintRange = PrintRange.AllPages;
printerSettings.Collate = false;
printerSettings.Copies = 1;
pageSettings.PrinterSettings = printerSettings;
rd.PrintToPrinter(printerSettings, pageSettings, false);


參考網址:
1、http://msdn.microsoft.com/zh-tw/library/aa290045(v=vs.71).aspx
2、http://support.microsoft.com/kb/184291

關鍵字:Q152451 、 Q184291、Crystal Reports、網路送印、IIS、Server Print、PrintJobLimit、C#

2011年4月19日 星期二

如何使用 T-SQL 與 ADO.Net 操作 SQL Server 2008 - Filestream的功能(內附原始碼)

        由於上次推出 [啟用與設定SQL Server 2008 新功能 - FileStream] 的介紹後,本篇我再來說明一下如何利用,T-SQL與ADO.NET來使用FileStream的新功能。同樣的我先把上一期介紹過的Table Schema貼上來給大家參考,然後再利用T-SQL的方式來進行介紹。

Table Schema:

CREATE TABLE [dbo].[cary_files](
[sn] [uniqueidentifier] ROWGUIDCOL NOT NULL UNIQUE,
[systemid] [int] NOT NULL,
[filedata] [varbinary](max) FILESTREAM NULL
) ON [PRIMARY]
GO;


我先利用Insert的指令寫入資料表中,先嘗試利用空值(Null)寫入FileStream的欄位中。

INSERT INTO [cary_db].[dbo].[cary_files]
VALUES (newid (), 1, NULL);

再將空字串('')寫入FileStream的欄位中。

INSERT INTO [cary_db].[dbo].[cary_files]
VALUES (newid (), 2,
CAST ('' as varbinary(max)));

最後再將文字內容('test data')整個寫入到FileStream的欄位中。

INSERT INTO [cary_db].[dbo].[cary_files]
VALUES (newid (), 3,
CAST ('test data' as varbinary(max)));

查詢結果:

SELECT [sn] ,[systemid] ,[filedata]
FROM [cary_db].[dbo].[cary_files]







寫入的資料如果只是純文字的話,可以透過下列的語法進行轉型,即可得知欄位的內容。

SELECT [sn] ,[systemid] ,cast([filedata] as varchar(100)) ac
FROM [cary_db].[dbo].[cary_files]








如果想要針對FileStream的欄位進行修改時,同樣地可以使用Update來進行更新,此步驟主要將原本的 'test data' 更改為 'Cary test data'。

UPDATE [cary_db].[dbo].[cary_files]
SET [filedata] = CAST('Cary test data' as varbinary(max))
WHERE [SerialNumber] = 3;


刪除的話,使用Delete即可。

DELETE [cary_db].[dbo].[cary_files]
WHERE SerialNumber = 1;


 以上的介紹主要透過T-SQL的語法來操作FIleStream,當然你可以包含在一個交易中,確保文件與資料的同步化,因為在以往如果只有文件的路徑存在資料庫,異動分成兩個程序時,如果有一邊失敗,容易造成資訊與文件不同步的情況,所以存放在FileStream算是一個不錯的選擇。

接下來我再透過ADO.Net的方式來操作剛剛建立的FIleStream,另外我再介紹如何將一個圖檔寫入FileStream後,再讀取出來。

範例:











在介紹前先說明一下程式中主要會使用到兩個Function,我先列出在下面進行說明,然後程式會從FileStream的欄位中,讀取出資料,並載入右邊空白的Picture的物件中,而寫入FileStream的程式碼也是大致相同,差別在 FileAccess.Read中要改用FileAccess.Write,然後在寫入資料庫中即可,另外我也有介紹如何利用程式將FileStream中的文字轉出,詳細的部份再請參考程式碼。


  • PathName:會將路徑當做 Token 傳給 BLOB。應用程式會使用此 Token 來取得 Win32 控制代碼,並在 BLOB 資料上運作。
  • GET_FILESTREAM_TRANSACTION_CONTEXT():會傳回代表工作階段之目前交易的 Token。應用程式會使用此 Token 將 FILESTREAM 檔案系統資料流作業繫結至此交易。


FileStream to Image:

using (SqlConnection connection = new SqlConnection(ConnectionString))
{
    connection.Open();
    SqlCommand command = new SqlCommand("", connection);

    SqlTransaction tran = connection.BeginTransaction(
       System.Data.IsolationLevel.ReadCommitted);
    command.Transaction = tran;

    command.CommandText =
          " SELECT [filedata].PathName(), "
        + " GET_FILESTREAM_TRANSACTION_CONTEXT () from [cary_db].[dbo].[cary_files]"
        + " WHERE systemid = 2";
    using (SqlDataReader reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            // Get the pointer for the file
            string path = reader.GetString(0);
            byte[] transactionContext = reader.GetSqlBytes(1).Buffer;

            // Create the SqlFileStream
            SqlFileStream fileStream = new SqlFileStream(path,
                                    (byte[])reader.GetValue(1),
                                        FileAccess.Read,
                                        FileOptions.SequentialScan, 0);

            pic_target.Image = Image.FromStream(fileStream);

            // Read the contents as bytes and write them to the console                      
            fileStream.Close();
        }
    }
    tran.Commit();
}


FileStream to String:

SqlFileStream fileStream = new SqlFileStream(path,
(byte[])reader.GetValue(1),
FileAccess.Read,
FileOptions.SequentialScan, 0);

byte[] contents = new byte[fileStream.Length];
fileStream.Read(contents, 0, (int)fileStream.Length);
string results = System.Text.Encoding.ASCII.GetString(contents);


原始下載:
http://blogger-files.googlecode.com/files/TestFormApplication.rar

參考網址:
http://technet.microsoft.com/zh-tw/library/cc645962.aspx
http://msdn.microsoft.com/zh-tw/library/cc716724.aspx#Y700
http://msdn.microsoft.com/zh-tw/library/bb933877(v=SQL.100).aspx