October 2008 - 文章

【茶包射手專欄】SQL 2005 SP2安裝失敗

最近跟SQL的Hotfix很有緣,話說為了因應KB956391會造成SSRS無法列印報表的問題,跟同事逐一為手上的SQL及SSRS主機上Patch。

不過,其中一台SQL升級時卻遇到SP2無法安裝的情況,依據提示,查看了C:\Program Files\Microsoft SQL Server\90\Setup Bootstrap\LOG\Hotfix下的幾個Log檔案:

Summary.txt中出現:

Product                   : Database Services (MSSQLSERVER)
Product Version (Previous): 1399
Product Version (Final)   :
Status                    : Failure
Log File                  : C:\Program Files\Microsoft SQL Server\90\Setup Bootstrap\LOG\Hotfix\SQL9_Hotfix_KB921896_sqlrun_sql.msp.log
Error Number              : 29528
Error Description         : MSP Error: 29528  The setup has encountered an unexpected error while Installing performance counters. The error is: 當檔案已存在時,無法建立該檔案。(英文原文為Cannot create a file when that file already exists)

Hotfix.log中看到

10/31/2008 13:02:39.738 Registry: Opened registry key "Software\Policies\Microsoft\Windows\Installer"
10/31/2008 13:02:39.738 Registry: Cannot read registry key value "Debug"
10/31/2008 13:10:17.525 MSP Error: 29528  The setup has encountered an unexpected error while Installing performance counters. The error is: 當檔案已存在時,無法建立該檔案。

用13:10:17當索引去查SQL9_Hotfix_KB921896_sqlrun_sql.msp.log:

<Func Name='LaunchFunction'>
Function=Do_sqlPerfmon2
<Func Name='GetCAContext'>
<EndFunc Name='GetCAContext' Return='T' GetLastError='203'>
Doing Action: Do_sqlPerfmon2
PerfTime Start: Do_sqlPerfmon2 : Fri Oct 31 13:10:16 2008
<Func Name='Do_sqlPerfmon2'>
<EndFunc Name='Do_sqlPerfmon2' Return='183' GetLastError='183'>
PerfTime Stop: Do_sqlPerfmon2 : Fri Oct 31 13:10:16 2008
Gathering darwin properties for failure handling.
MSI (s) (04!D8) [13:10:17:478]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:478]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:525]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:525]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:525]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:525]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:525]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:525]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:525]: Note: 1: 2262 2: Error 3: -2147287038
Error Code: 183
MSI (s) (04!D8) [13:10:17:541]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:541]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:572]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:588]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:588]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:588]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:588]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:588]: Transforming table Error.

MSI (s) (04!D8) [13:10:17:588]: Note: 1: 2262 2: Error 3: -2147287038
MSI (s) (04!D8) [13:10:17:588]: Product: Microsoft SQL Server 2005 -- Error 29528. The setup has encountered an unexpected error while Installing performance counters. The error is: 當檔案已存在時,無法建立該檔案。

Error 29528. The setup has encountered an unexpected error while Installing performance counters. The error is: 當檔案已存在時,無法建立該檔案。

由這些線索看來,應與Performance Counter安裝失敗有關,但是哪一個Counter安裝失敗,並無進一步的訊息。我再去找了Event Log,看到了以下兩則訊息:

13:10:16 LoadPerf Error 3009
為 SQLSERVERAGENT 安裝效能計數器字串時失敗。 錯誤碼為記錄資料的 DWORD 0。

13:10:17 MsInstaller Error 10005
Product: Microsoft SQL Server 2005 -- Error 29528. The setup has encountered an unexpected error while Installing performance counters. The error is: 當檔案已存在時,無法建立該檔案。

看起來SQL Agent Performance Counter的嫌疑很大,於是我切到C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Binn,下了以下指令移除幾個可疑的Performance Counter:

lodctr /R:perf-MSSQLSERVERsqlagtctr.ini
(我還一併移除了sqlagtctr.ini, sqlctr.ini, perf-MSSQLSERVERsqlctr.ini,但以上的我覺得最可疑)

再試一次,SP2安裝成功! 運氣真好~~~

KB-三探Windows Update後SSRS不能列印問題

上回貼出Windows Update導致SSRS不能列印問題後,有些網友成功了,但似乎有些網友依循文章的解法SP2->GDR2依然不能解決問題,裝完後RSClientPrint.cab仍未更新。由於手邊沒有可以驗證的同樣情境,我想深究也無從嘗試。

今天同事在更新一台SQL時,同樣的情境上演了。GDR2安裝、移除、再安裝了四五次,RSClientPrint.cab還是文風不動,一直保持2007/2/10的版本。

這真是天賜的良機呀!讓這麼刁鑽的問題在我眼前重現,著實令人激動不已... (遇到難題還這麼High,我應該是萬中選一的賤骨頭吧?)

幾經嘗試,我找出一條可以解決的路徑:

安裝SP2,但不要安裝GDR2(KB933097,如果有裝請移除),接著安裝KB954606,這樣RSClientPrint.cab就會更新到2008/08/05的版本了。

先前更新失敗的朋友可以嘗試看看,成功或失敗也回報讓我知一下,謝謝!

【2008-11-04更新】有使用Report Viewer的朋友,請記得還要加裝Microsoft Report Viewer Redistributable 2005 Service Pack 1,ReportViewer所用的RSClientPrint版本才會更新。感謝Jeniffer, Brian補充。

【茶包射手專欄】WSS敲不完的帳號密碼

新裝了一台WSS主機,建了一些每天申報伺服器運作狀況的清單應用,交給User測試,卻發現User在使用時,輸入網域帳號密碼登入網頁後,操作幾下就會一直跳出帳號密碼登入對話框,即使輸入正確的帳號密碼也不管用,但有時連按十幾下又會成功。換了隔壁同事的主機也是如此,User被這種靈異現象搞到痛不欲生,但說也奇怪,用我的機器測試,卻總是一路順暢。

被這個詭異現象糾纏了一兩個星期,每天早上都要陪著User按個數十下登入確定鈕,等它老爺心情好給放行,不勝其擾。偏偏使用者用網域身份登入其他網站又都無異狀,問題出在哪,我毫無頭緒...

今天痛下決心要把鬼抓出來,由IIS Log看來,使用者似乎沒有通過IIS認證,用Process Monitor檢查,證實了這一點。試著連上_themes下的某個JPG,在不斷跳出登入視窗的當下,並沒有存取圖檔的記錄,顯示問題在IIS層次而非ASP.NET層次,而Request一開始就死在驗證失敗,未到後面的階段。

IIS Log上很難進一步了解身份認證過程的細節,我想到了另一件神奇兵刃--Fiddler 2。比對正常Client與異常Client的存取過程,我發現了一件有趣的事實: 正常Client透過NTLM進行驗證,而異常Client則用的是Kerberos(二者觀察上的差異可以參考這裡) 。

啊! 會出問題的Client機器均有加入WSS所在的AD Domain,換言之,Client與IIS位處於同一個Domain中;而我的機器則隸屬另一個Domain,大概就是如此造成Kerberos vs NTLM的差別。

問題既已分明,那就強迫用NTLM好了,我Google到這篇文章。由文中的細節,我推測Kerberos會失敗可能與WSS並非用Domain Account身份執行有關,但因為一些因素,暫不打算改變WSS的執行身份,於是我用以下的方法停用了Kerberos:

C:\Inetpub\AdminScripts>cscript adsutil.vbs GET W3SVC/1/Root/NTAuthenticationPro
viders

Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

NTAuthenticationProviders       : (STRING) "Negotiate,NTLM"

C:\Inetpub\AdminScripts>cscript adsutil.vbs SET W3SVC/1/Root/NTAuthenticationPro
viders NTLM

Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

NTAuthenticationProviders       : (STRING) "NTLM"

終於從惱人的登入對話框地獄解脫了...

【茶包射手專欄】被CSS排擠的IE

幫忙看了一個問題,某個大家已經檢視無誤的網頁,在某位同事檢視時,發現所有的CSS格式設定都不見了,表格底色、字型都變成IE的預設值,感覺上是整個CSS失效。

先用Fiddler2檢視,確認CSS、JS等都下載正常,沒有發生HTTP 404下載檔案失敗的問題。為什麼大家看到的網頁都正常,只有這台機器被排擠呢?

遇到這種"眾人皆好我獨爛"的金包銀情境,當務之急不是怨天尤人,而該反躬自省: 我的機器跟別人有什麼不一樣?? 比對的重點,OS/IE版本、網路設定都要留意。我發現到最大的不同點,這台機器的IE是IE6,而其他檢視正常的機器是IE7。

這時我腦中快速閃過幾個畫面... IE6與IE7在處理Encoding的原則上是有差異的,過去有遇在IE6檢視OK,IE7會出鎚的情境,也吃過js Encoding不對產生錯誤的悶虧,把兩件事串在一起: 該不會是因為CSS用了BIG5編碼?

更進一步喚醒記憶,我找回這篇完全相同問題的文章,只是當時沒有IE7混搭IE6出來攪局。

檢查了一下,果然,CSS被存成了BIG5編碼,而我們的網頁都是以UTF-8為核心。看來IE7可以自動彈性調適,IE6卻會硬用網頁的Encoding去解讀CSS。

改存UTF-8後,天下太平!

【緊急呼叫】請立刻進行Windows Update!!!

微軟在10/23號公佈了一個遍及Windows 2000, XP, 2003, 2008,甚至Windows 7 Pre-Beta的資安漏洞,其影響層面頗大。只要主機開機有接上網路,就算不收信、不逛網站、不做任何事都可能中槍倒地。雖然不是0Day攻擊(指漏洞揭露的當天,就有壞人"同步推出"利用該漏洞進行攻擊的病毒/蠕蟲),但在隔日已經傳出已有利用該弱點進行攻擊的木馬間諜程式TSPY_GIMMIV.A,會利用該漏洞竊取密碼及其他機密後上傳至特定網站。

這個漏洞十分具有威脅性,建議各位立即檢查手邊所有的Windows,馬上進行Windows Update並重新開機,以減少被攻擊的可能性。若未能及時防堵,有可能重演幾年前疾風病毒(其實是蠕蟲)發作,搞垮整個公司網路的慘劇,千萬要小心提防。

KB-再探Windows Update後SSRS不能列印問題

【2008-10-30更新】依本文做法仍無法更新RSClientPrint.cab的朋友,請參考我的第三帖藥方。 

自從貼出安裝KB956391後SSRS不能列印問題後,陸續又接獲同事及網友的回報,才慢慢釐清這個事件的始未...

SSRS不能網頁列印的原因源於RSClient Print這顆元件被ActiveX KillBit封殺,而封殺的理由是基於安全考量,RSClientPrint Class存在一個GDI+漏洞,讓駭客可以透過惡意的BMP發動溢位攻擊。換句話說,KB956391為了修復GDI+弱點,就透過Kill Bit判了舊版RSClientPrint元件的死刑,安裝後SSRS不能列印是By Design,而不是Bug!

了解前因後果後,下一步就是解決問題。

微軟提供的解法是裝SQL 2005 SP2 GDR(有人稱GDR2)。我安裝GDR2後,ReportServer顯示9.00.3152,但C:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\bin\RSClientPrint.cab仍是2007/02/10的舊版,想當然爾,其中是{FA91DF8D-53AB-455D-AB20-F2F023E498D3}這顆被封殺的舊元件,問題仍未解決。

不過看到不少人分享,幾乎可以確認安裝GDR2會更新RSClientPrint.cab,換版後問題就排除,我假設我的GDR2安裝並未成功,因此未成功置換RSClientPrint。於是,我決定反安裝GDR2再重新安裝,接著就看到C:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\bin\RSClientPrint.cab變成美妙的2008/8/5的版本,Browser連上時會提示安裝新版元件,很自然地,列印功能就OK了。

以下對這個問題做個總結:

  1. Client安裝KB956391後就無法用SSRS的網頁報表列印是正常現象,理由是SSRS的舊版RSClientPrint元件有安全漏洞,不建議大家繼續使用。
  2. 由於舊版元件不安全,我們便需要更新SSRS Server,使其改用新版RSClientPrint。
    最簡單的更新方法是安裝GDR2:(要先裝SQL 2005 SP2)
    SQL 2005 SP2 GDR(General Distribution Release) KB933097
    http://support.microsoft.com/kb/933097/
    有一些GDR2安裝的注意事項,也一併列出作為參考:
    (請注意版號,可由http:// yourweb /ReportServer頁面上取得)
    http://www.microsoft.com/taiwan/sql/sp2_gdr_install.mspx
    http://blogs.msdn.com/psssql/archive/2007/04/06/post-sql-server-2005-service-pack-2-sp2-fixes-explained.aspx
  3. 安裝後確定C:\Program Files\Microsoft SQL Server\MSSQL.2\Reporting Services\ReportServer\bin\RSClientPrint.cab的檔案日期是2008/8/5,表示更新完成。此時Client再連上SSRS會提示安裝元件,安裝完成應該就可以列印。
  4. 如果一時無法裝好GDR2,又急著列印怎麼辦?
    事實上,只需先將HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{FA91DF8D-53AB-455D-AB20-F2F023E498D3}\Compatibility Flags刪除即可。如果嫌用RegEdit改太麻煩,可以將以下內容存成FixRSClientPrint.reg,點兩下執行亦可解除封殺救急。待更新SSRS後,請務必將其還原回"Compatibility Flags"=dword:00000400(或重新安裝一次KB956391)。
    Windows Registry Editor Version 5.00

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{FA91DF8D-53AB-455D-AB20-F2F023E498D3}]
    "Compatibility Flags"=-

    ** 但還是要提醒大家,這個元件已被證實有漏洞,強制開放使用可以救急,但也意味著要承擔風險,儘速更新SSRS才是上策!!
  5. SQL 2000 Reporting Service也可能有類似問題,有人回報裝了以下Hotfix即可解決。
    SQL 2000 Reporting Service SP2 Hotfix KB954609
    http://www.microsoft.com/downloads/details.aspx?displaylang=zh-tw&FamilyID=5f9e7f78-7439-414b-a9dc-a779b89427db
TIPS-Alter Assembly's Owner

Attach或Retore SQL資料庫到別台,必須要先Drop User才能重設User權限,這個我很早就有經驗

這次的問題有點不一樣,在Drop User時出現以下訊息:

The database principal owns an assembly and cannot be dropped

用SQL Server Management Studio檢查,果然有一個SQLCLR Stored Procedure的Owner不是dbo,而是這個我要Drop的User。我想起來,之前在Deploy SQLCLR Procedure時,連線字串用了這個User帳號而沒用sa,但Assembly似乎不能用sp_changeobjectowner更換Owner,MSDN文件上也闡明了未來將不再支援sp_changeobjectowner這個老SP,將以ALTER AUTHORIZATION、ALTER SCHEMA取代之,所以就要寫成:

ALTER AUTHORIZATION ON Assembly::MyAsembly TO dbo;

將Owner改掉後,就可以Drop User囉!

TIPS-建立Hyper-V VM連線捷徑

Hyper-V不像Virtual Server 2005提供了明確獨立程式-VMRC(Virtual Machine Remote Control),讓使用者可以直接啟動VMRC,再決定連線哪一台Virtual Server 2005的哪個VM。Windows 2008給人的直覺,連線方式都得透過Server Manager/Roles/Hyper-V找到VM後再連線,有點麻煩。

其實,Hyper-V也有類似VMRC的東西,它安裝在Program Files\Hyper-V下,有個vmconnect.exe(用法可以看這裡)。所以我們就可以做個捷徑,一點就連上VM囉!! (如果有開UAC,記得要設定Run as Administrator。)

不過,我發現用VMConnect連線VM,雖然有可以直接控制VM開/關/儲存/暫停、切換CD Image的便利。就日常操作上卻不如使用Terminal Service Client來得方便,主要有以下差別:

  1. 無法做雙向的剪貼簿傳送(VMConnect有Type Clipboard Text,只能送進VM內,不能從VM中把Clipboard內容取回來,而且似乎只支援英文)
  2. 我滑鼠的Scroll Wheel在VMConnect裡很難使用,用力轉輪子,視窗捲軸都愛動不動
  3. Terminal Service Client可以把本機Disk掛成VM的網碟,VMConnect則沒有類似對應。

評估了一下,最後還是選擇Terminal Service方式作為日常使用。

我的奮鬥-System.Data.OracleClient on Windows 2008 x64

自從家中與公司的機器分別改用Vista x64及Windows 2008 x64後,在享受爽快揮霍記憶體之餘,也一併開始體驗在新大陸拓荒的感覺。大部分的高階應用程式,多半可以x86虛擬環境執行,問題不大。但有不少涉及低階處理的程式選擇頓時劇減,例如: RamDisk因為Driver未具備簽章無法使用、能支援x64的免費防毒軟體屈指可數、之前提到重新Survey了DVD燒錄程式...

這些軟體上的短缺咬一咬牙就撐過了,最讓我痛苦的是原本部門大家慣用的ODP.NET 9207完全不支援x64版本,安裝程式根本沒法跑!

Google之後,知道Oracle 10.2g開始Support Vista/Windows 2008 x64,但無奈ORACLE網站不知怎麼,連續好幾天,下載連結都出現HTTP 404。最後終於在兩週後讓我下載到近500M的10204_vista_w2k8_x64_production_client.zip,安裝完成馬上用現有的專案試了一下,結果出現:

System.Data.OracleClient requires Oracle client software version 8.1.7 or greater.

檢視權限等OK,試了一下重新開機,訊息變了:

System.InvalidOperationException: Attempt to load Oracle client libraries threw BadImageFormatException. This problem will occur when running in 64 bit mode with the 32 bit Oracle client components installed. ---> System.BadImageFormatException: An attempt was made to load a program with an incorrect format. (Exception from HRESULT: 0x8007000B).

似乎是DLL版本不對,但我明明裝的是64bit Oracle Client,.NET程式限定Build for x86 CPU,為什麼跟我說: 在64 Mode下去讀了32bit Oracle Client? 感覺上恰恰相反才對。

反覆測試,忽然發現如果設定Build for AnyCPU或x64就可以順利連上Oracle,唯獨設成x86時會出現上述疑似相反的錯誤。設成x86時去讀取64bit Oracle Client DLL會發生問題,十分合理,只是一開始的錯誤訊息讓我有些迷惑。如果這是問題,那麼是否同時再裝32bit Oracle Client就好? 但,System.Data.OracleClient要如何知道該用32bit或64bit Oracle Client? 我每次都要改Registry或設PATH環境變數來切換使用的Client嗎? 一切的疑問,都要試了才知道!!

找到10203_vista_w2k8_x86_production_client.zip,裝在另一個目錄下(c:\oracle\ora10g vs c:\oracle\ora10g_x86),重新登入讓PATH環境變數中的c:\oracle\oar10g_x86\bin生效,x86就可以跑了。抱著期待的心情切成x64再跑,Yes! 也成功了!! 但這是x64 .NET用32bit Oracle Client的結果嗎?

使用Process Monitor解開了疑惑: System.Data.OracleClient會沿著PATH環境變數的路徑去找oci.dll,在x64下會略過c:\oracle\ora10g_x86\bin\oci.dll,找到c:\oracle\ora10g\bin\oci.dll後連線;x86時則是找到c:\oracle\ora10g_x86\bin\oci.dll就結束尋找,AnyCPU則與x64的行為相同。
(記得之前的PB6連Oracle9嗎? 一樣是用PATH法)

換句話說,OracleClient會依x64/x86決定適當的Oracle Client版本,不必勞煩我去手工切換。因此,如果想在Windows 2008/Vista x64上使用System.Data.Oraclient,最好32bit/64bit兩種10.2+版本的Oracle Client都要裝,並確定PATH變數中二者的bin目錄都有在其中,一切就搞定了。

說來簡單,卻花了我好幾個星期才解決,希望這點心得對其他也在x64叢林中挺進的同胞們有些幫助。

【茶包射手專欄】PB6.5連線Oracle 9.2i

PowerBuilder 6.5是化石級的軟體了,會從這篇TIPS受益的人全球可能不超過10個,不過前後處理過發生過兩次問題,花了我數小時,還是寫篇心得紀念一下。

同事最近升級了電腦,因為工作需要安裝了PB 6.5,但PB 6.5一直無法連上Oracle 9.2i的資料庫,PB跟Oracle Client重覆安裝反安裝十來次也無解,於是找我幫忙。

PB6.5真是個老軟體,內建的Native Oracle介面是7.3,額外可以多裝8.0.5,上網找到的一些說明,似乎當時最新的OS是Win95,媽呀,超過十年囉! 可是很怪,除了這台新電腦外,還有多台PC也都是用PB6.5連Oracle 9.2i,行之多年。可見,它還是具備連線Oracle 9的能力,只是不知為什麼在這台機器上不行,一直會彈出Error while trying to retrieve text for error ORA-*****。(東改西改的過程中,ORA錯誤碼變過好幾次,總之,用這些訊息Google後,唯一較能確認的就是這個錯誤跟PB沒法順利找到Oracle Driver有關。)

由sqlnet.log中看到如下的訊息:

Fatal OSN connect error 12203, connecting to:
(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=BEQ)(PROGRAM=oracle80)(ARGV0=oracle80ORCL)(ARGS='(DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))')))(CONNECT_DATA=(SID=ORCL)(CID=(PROGRAM=pb60.exe)(HOST=MyClientBox)(USER=TroubleShooter))))

  VERSION INFORMATION:
    TNS for 32-bit Windows: Version 2.3.4.0.0 - Production
  Time: 07-OCT-08 17:32:45
  Tracing not turned on.
  Tns error struct:
    nr err code: 12203
    TNS-12203: Message 12203 not found; No message file for product=NETWORK, facility=TNS
    ns main err code: 12538
    TNS-12538: Message 12538 not found; No message file for product=NETWORK, facility=TNS
    ns secondary err code: 12560
    nt main err code: 508
    TNS-00508: Message 508 not found; No message file for product=NETWORK, facility=TNS
    nt secondary err code: 0
    nt OS err code: 0

對PB6.5連線ORACLE的原理毫無所悉,當然也無法推斷哪個環節出了問題。在這種困境中,可以向誰尋求救贖了? 沒錯,還是Process Monitor!

開啟Process Monitor後,設定Process Name = pb60.exe作為Filter,再開啟PB6.5連線Oracle故意引發錯誤,再由File/Registry存取Log分析,另外還比對了一台正常機器連線成功的Log當做對照組。經過一番分析推敲,我猜測PB6.5找到Oracle Client版本的過程如下:

  1. 先找到pbo7306.dll
  2. 接著嘗試在所有PATH環境變數的路徑中找尋ociw32.dll, 都找不到再開始找ora805.dll, 再找不到找ora804.dll, ora803.dll, ora73.dll。也就是說它會從較高版本開始找,萬一找不到才用較低版本。

在成功的對照組中,oracle\ora92\bin\ociw32.dll會被發現,因此PB6定調採用Oracle9i Client。而有問題的機器,則不知誰在windows\system32下放了一個ociw32.dll,而system32目錄會在PATH各路徑之前被搜索,在system32下發現ociw32.dll就無法協助PB6釐清Oracle要用哪一版。

我將system32下的ociw32.dll給移除,PB6即可連上Oracle9i。

最後我的心得是,要讓PB6透過Oracle 9i Client連接資料庫,請把握以下原則: 清除program files\sybase\*, windows\system32 下的ociw32.dll,安排PATH環境變數內的路徑順序,oracle\ora92\bin排在第一個會最好。

【茶包射手專欄】SSRS報表網頁列印失敗

【2008-10-24更新】關於此問題,已有新的研究心得

同事回報,忽然發現SSRS(SQL Server Reporting Service)透過網頁直接列印的功能失效了,一直彈出"無法載入用戶端列印控制項"的錯誤訊息。

試了設為信任網站、刪除Windows\Downloaded Program Files\RSClientPrint Class、重開機等招式都無效。開啟Process Monitor,有看到一大串Registry查詢,但看不出所以然,又懶得抓個正常版來做個馬拉松式比對,今天決定換個戰略進攻...

利用Fiddler抓到SSRS傳回以下HTML內容,就是前述錯誤訊息的由來。

<OBJECT ID="RSClientPrint" 
CLASSID="CLSID:FA91DF8D-53AB-455D-AB20-F2F023E498D3" 
CODEBASE="/Reports/Reserved.ReportViewerWebControl.axd?
ReportSession=yiyl2jjxy5g0j55ra0vsvba&amp; 
ControlID=dec65406970b41ea8b6cde2445d3e4ab&amp; 
Culture=1028&amp;UICulture=31748&amp; ReportStack=1&amp; 
OpType=PrintCab# Version=2005,090,3042,00" 
VIEWASTEXT></OBJECT>
        <script language="javascript">
        function Print()
        {
            if (typeof RSClientPrint.Print == "undefined")
            {
                alert("無法載入用戶端列印控制項。");
                return;
            }

在正常的情況下,當<OBJECT>指定的物件不存在時,應會透過CODEBASE位址自動下載安裝才對,但這台機器在手動刪除Windows\Downloaded Program Files\RSClientPrint Class後仍然未能重新安裝,而導致下方的Javascript檢查失敗。

我的推論是,可能這台機器殘存了某些設定,讓IE誤以為RSClientPrint元件是存在的,故未觸發自動安裝;但實際上元件又未完整註冊(檔案還被我們給砍了哩),搞得不上不下。

既然是ActiveX Control,就從Registry下手吧!

利用RegEdit尋找FA91DF8D-53AB-455D-AB20-F2F023E498D3,在HKCU\Software及HKLM\Software各找到一筆,先匯出備份後刪除:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\ActiveX Compatibility\{FA91DF8D-53AB-455D-AB20-F2F023E498D3}]
"Compatibility Flags"=dword:00000400

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Ext\Stats\
{FA91DF8D-53AB-455D-AB20-F2F023E498D3}]

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Ext\Stats\
{FA91DF8D-53AB-455D-AB20-F2F023E498D3}\iexplore]
"Type"=dword:00000001
"Flags"=dword:00000000
"Count"=dword:00000025
"Time"=hex:d8,07,0a,00,01,00,0d,00,05,00,28,00,1c,00,02,00

將Registry刪除後,仍然無效。重開IE再試,就出現安裝控件的提示了。

問題排除,收工回家。

【2008-10-24更新】
發現刪除Registry只能暫時解決問題,依網友amoswu提供情資,此與KB956391 Hotfix有關,再Google了一下,網路有人找到解決方式了: http://www.dotnetgurus.net/post/2008/10/Reportviewer-Print-Problem-After-KB956391-(ActiveX-Kill-Bits-Update).aspx

Process Monitor 2.0

Process Monitor已推出V2.0了,我注意其中最大的改變是多了兩種擷取記錄: Network Activity及Profiling Events。

Network Activity檢視可以錄下程式透過網路對外溝通的狀況,等於把TCP View的功能也整合進來。如此可以一併看程式讀寫什麼檔案,接著透過網路對哪些主機收發資料,對軟體行為的剖析能力又更上一層樓。

以上回說過的C#下載網站檔案為例,Process Monitor 2.0可以抓到程式連線網站下載檔案的活動記錄。

 

Profiling Events則是要求Process Monitor定期掃瞄系統所有Active Thread的Kernel Mode/User Mode CPU耗用狀況,Context Switch次數等等。

由於Process Monitor抓取資料的範圍愈來愈豐富,很短時間內就會累積十分可觀的Event數目,對系統是項負擔(我發現2.0起啟動及停止擷取時會有幾秒的Delay)。如果你並不需要事後調整Filter及切換File/Registry/Network檢視,可以利用選單Filter/Drop Filtered Events將不符合設定條件的事件丟棄,整體操作會輕巧許多,但如此會喪失事後重調Filter抓出線索的機會,如何取捨視需要而定。

另外,Tools Menu裡有不少有用的工具,Process Tree可以展開類似Process Explorer的Process從屬結構樹狀圖,Unique Values可列出某欄位所有出現過的值,而Count Occurences則可進一步統計某欄位各值出現的次數。

Process Monitor又更威了!

CODE-使用C#程式從網站下載檔案

寫程式的人大家應該都有類似的經驗: 我想要Blah Blah,應該怎麼做?

這問題要是發生在十年前,肯定是項艱鉅的挑戰,只能到圖書館/書店翻書找看看,是否找得到跟所耗費的時間,就要看平日是否有積德行善,常扶老太太過街。運氣好一點的人,身旁剛好有高人前輩,開口問一下就有答案,多半上輩子是樂善好施員外投胎轉世才有此等造化。

Google的出現徹底改變了知識交流運用的模式,我認為它的革命性並不亞於工業革命。現在,只要接上Internet,動幾下手指,原本也許要花上十天半個月才找得到的答案,一瞬間就送到眼前。雖然有報導誇張地指出: 搜尋成癮,大腦變抽水馬桶,但也不能否認,這個時代還不懂得善用搜尋引擎及Internet,跟原始人也沒什麼兩樣。

面對沒做實作過的需求,我的第一個動作當然也是先Google。但總有種困擾,網路上提供的解答五花八門,良莠不齊,若原本對該領域就不熟悉(就是不熟才要找範例),要由其中篩選出"正確"、"有效率"的做法談何容易? 我是否走對門路挑對方法? 若一開始遇人不淑,學錯方法,說不定一輩子都用可樂瓶敲釘子,渾然不知世上有種東西叫鐵錘... orz 這一直是我在Google找尋範例程式時最大的疑慮。

有個不錯的例子: List, Dictionary or SortedList?

要克服最佳化疑惑有幾種做法: 1) 花點心思讀讀系統化介紹相關議題的書籍、Tutorial或WhitePaper 2) 挑選較具權威性的來源,例如: MSDN論壇眾多MVP的回答、專業人士的Blog、CodeProject...等 3) 透過無遠弗界的Internet跟高手互相砌磋。

CODE系列的文章應該算是我企圖實踐第三種做法的嘗試,針對某個課題,我會提出搜尋研究過的最佳解法。但純粹是我的個人意見,若來往小站的高手達人們,有人發現其中有待改進之處,還請不吝指點,我會再修正改良。

第一砲: 如何寫一小段C# Code去下載網站上的某個檔案並存檔?

【2008-10-14 Update】原做法果然是可樂瓶,鐵鎚來了,謝謝Chicken提供:

using System;
using System.Net;
 
public class CSharpLab
{
    public static void Test()
    {
        WebClient wc = new WebClient();
        wc.DownloadFile("http://blog.darkthread.net/images/darkthreadbanner.gif", 
        "b:\\darkthread.gif");
    }
}

原文做法如下,建議若為單純取檔,用以上寫法取代。若有顯示下載進度等需求,再考慮自己用Stream讀入。

using System;
using System.IO;
using System.Net;
 
public class CSharpLab
{
    public static void Test()
    {
        string url = "http://blog.darkthread.net/images/darkthreadbanner.gif";
        HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
        HttpWebResponse httpResponse = (HttpWebResponse)httpRequest.GetResponse();
 
        System.IO.Stream dataStream = httpResponse.GetResponseStream();
        byte[] buffer = new byte[8192];
        
        FileStream fs = new FileStream("X:\\TEMP\\Darkthread.gif", 
            FileMode.Create, FileAccess.Write);
        int size = 0;
        do 
        {
            size = dataStream.Read(buffer, 0, buffer.Length);
            if (size > 0)
                fs.Write(buffer, 0, size);
        } while (size > 0);
        fs.Close();                
 
        httpResponse.Close();
        
        Console.WriteLine("Done at " + DateTime.Now.ToString("HH:mm:ss.fff"));
    }
}
IE C++ Runtime Library Error

我的Windows 2008歷經一次不正常斷電,重啟後開啟IE會Popup以下錯誤對話框,IE隨即關閉:

Microsoft Visual C++ Runtime Library

Runtime Error!
Program: C:\Program Files(x86)\Internet Explorer\iexplore.exe

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

心想不妙,該不會不正常關機弄壞了什麼檔案。一般來說,不正常關機的確有系統檔案受損的風險,但機率極低,處理卻很麻煩,有時要重灌才能解決,OS才裝好沒多久,應該不會這麼賽吧?

一般來說,IE出狀況,很大的比例問題出在Add-On上。抱著姑且一試的心態,我開啟了程式集裡的Accessories/System Tools/Internet Explorer(No Add-ons),發現在沒有Add-On的情況下,IE可以正常開啟。

鬆了一口氣,IE沒壞,出問題的是Add-On,正想一一開關Add-On找出拖累IE的元凶,不慎誤啟動了一個正常的IE視窗,發現IE已經正常了。

這次的IE損壞小插曲,就在啟動一次無Add-Ons模式後,自動復原了。

TIPS-用ADO.NET讀取ORACLE VARCHAR中的BIG5罕用字

BIG5字數不足是正體中文環境存在已久的問題,在Unicode尚未普及前,出現過許多各顯神通的解決方案,自行造字、廠商推出的擴充字集(例如: Microsoft CP950、Big5_eten)、BIG5+、BIG5E、CNS11643。(註: 想進一步了解的話有兩篇不錯的文章12)

近年來Unicode已漸漸成為各家系統及平台都支援的主流標準,雖然無法滿足全部的中文字(例如: 姓名裡的自創字以及古文史籍中的罕見字),Unicode幾乎是全球解決跨國多語系的大一統終極方案,也獲得大部分開發人員的認同。

很不幸地,許多歷史悠久規模龐大牽扯複雜盤根錯結的系統,要更動文字編碼形同動搖國本,提案更換Unicode的人勢必要抱著捨身取義雖千萬人吾往矣的決心不可,在願意去掛鈴鐺的勇敢老鼠出現之前,我們還是要學著與這些舊時代的中文編碼解決方案共處。

在證券業界,主要是由台灣集保公司統一制定罕用字集作為資料交換的標準。我們今天的練習題便是如何用ADO.NET顯示ORACLE資料庫VARCHAR裡的集保罕用字。

進行以下測試時,請先至集保結算所網站下載安裝罕用字集: (【其他下載資料/華康中文罕用字集】)

裝好罕用字集後,我們可以用以下的手法建一個TDCCCHAR資料表 ,並填入"犇"這個字進VARCHAR2欄位。(注意: 要輸入罕用字集版本而非Unicode裡的,如果你不知怎麼輸入,可以用Encoding.GetEncoding("big5").GetString(new byte[] { 0x9f, 0x6e })產生再剪貼)

好了,來寫一段ADO.NET程式把犇牽出來吧!

<%@ Page Language="C#" %>
<%@ Import Namespace = "System.Data.OracleClient" %>
<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        using (OracleConnection cn = new OracleConnection(
            ConfigurationManager.ConnectionStrings["myOracle"].ConnectionString))
        {
            string sql = @"SELECT VC FROM TDCCCHAR";
            OracleCommand cmd = new OracleCommand(sql, cn);
            cn.Open();
            OracleDataReader dr = cmd.ExecuteReader();
            while (dr.Read()) 
            {
                string s = dr["VC"].ToString();
                Response.Write("<li>byte[]: " +
                    BitConverter.ToString(
                        Encoding.GetEncoding("big5").GetBytes(s)
                    ));
                Response.Write("<li>string:" + s);
            }
            cn.Close();
            Response.End();
        }
    }
</script>

事與願違,我們得到的結果會是: byte[]: 3F string:�

依我的推敲,集保罕字因為在Unicode中沒有適當的對應,而.NET的核心早已是Unicode,這些由ORACLE讀取的結果一律要強轉成Unicode,罕用字便會被置換成3F(?),在DataReader取出前就打上馬賽克(而且還不是薄碼哩),之後在.NET裡就算使出渾身解數也不可能還原。

試了許久,一直找不到相關設定可以讓System.Data.OracleClient或ODP.NET保留VARCHAR欄位的原始編碼,也無法改用byte[]/Stream方式讀取字串原始內容,看來此路不通。(知道有巧門的朋友請出個聲吧!)

山不轉路轉,我倒是找到一個解決方法,可以閃過罕用字被置換成問號的問題。先利用ORACLE的RAWTOHEX函數將字串轉成16進位碼,再從.NET端解碼。程式碼修改如下:

            string sql = @"SELECT RAWTOHEX(VC) AS HEX FROM TDCCCHAR";
            OracleCommand cmd = new OracleCommand(sql, cn);
            cn.Open();
            OracleDataReader dr = cmd.ExecuteReader();
            while (dr.Read()) 
            {
                string hex = dr["HEX"].ToString();
                byte[] buff = new byte[hex.Length / 2];
                for (int i = 0; i < hex.Length / 2; i++)
                {
                    buff[i] = byte.Parse(hex.Substring(i * 2, 2), 
                        System.Globalization.NumberStyles.AllowHexSpecifier);
                }
                string s = Encoding.GetEncoding("big5").GetString(buff);                
                Response.Write("<li>byte[]: " +
                    BitConverter.ToString(
                        Encoding.GetEncoding("big5").GetBytes(s)
                    ));
                Response.Write("<li>string:" + s);
            }

很神奇地,在已安裝罕用字集的IE上,就可順利看到byte[]: 9F-6E string: 犇;但在一般的Windows上,犇卻還是出不來。(因為Unicode裡並未對應Big5 %9f%6e的綠故)

真正的解決之道,應該還是得將罕用字集的罕字一一對應成Unicode裡有的文字,讓所有的文字回歸統一的標準。這個好問題,留待下回分解。

更多文章 下一頁 »

搜尋

Go

<October 2008>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
 
RSS
【工商服務】
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


BlogLook Score and Rank

Syndication