Quantcast
Channel: Darkthread
Viewing all 428 articles
Browse latest View live

Windows 2012 R2執行WCF出現HTTP 404.17錯誤

$
0
0

同事報案,.NET 4 WCF在一台新裝Windows 2012 R2主機執行,嘗試連上.svc時出現404.17錯誤:

HTTP Error 404.17 - Not Found
The requested content appears to be script and will not be served by the static file handler.
要求的內容似乎是指令碼,因此靜態檔案處理常式便不會對它進行處理。

依訊息推敲,應是IIS未安裝必要模組所致。

IIS自IIS7起改為模組化安裝,安裝選項分得很細,且盡量以預設不安裝為原則,好處是管理人員可依需要只安裝最精簡組合,用多少裝多少,避免一堆無用模組拖累效能或增加被攻擊風險。但缺點則是安裝時需要較多知識,得看懂選項並找出要安裝的項目。

檢查之後,發現同事裝了.NET Framework 4.5 Features / WCF Services,但忘了勾選 HTTP Activation,即.svc出現HTTP 404.17的主因,問題也在安裝該項目後排除。

面對多如牛毛的安裝選項如果你還是不知如何下手,以下是一些參考文件:


【茶包射手日記】VS2015無法啟動IIS Express

$
0
0

Visual Studio 2015 疑難雜症一枚。

ASP.NET 專案不知何故無法在 Visual Studio 裡執行及偵錯,一直跳出以下錯誤:

Unable to launch the IIS Express Web server.

試過刪除 .sln 檔所在目錄的.vs資料夾,無效。
註:.vs 是隱藏資料夾,VS2015 儲存開發環境設定的地方,刪除後會重新產生(但會遺失檔案開啟狀態、中斷點等設定),處理 VS2015 異常挺好用

爬文找到相關討論,有人建議刪除 %userprofile%/Documents/IISExpress/config 資料夾,重啟 Visual Studio,config 會重新產生,問題排除。

補充一點,IISExpress 雖多用於開發測試,但跟 IIS 一樣會寫 Log,IIS Log 位置就在上述 IISExpress 路徑的 Logs 資料夾,另外 TraceLogFiles則用來保留 Request 錯誤細節,需要深入偵察時很管用。

【茶包射手日記】System.Data.OracleClient更新中文變問號

$
0
0

同事報案,某支在Windows 2003主機運作良好的轉檔程式,移至開發機執行更新Oracle資料庫時,中文變成問號。

比較原本執行正常的環境為Windows 2003 x86英文版 + Oracle Client 9207(真實世界永遠不乏這種與時代嚴重脫節卻維繫日常營運的中流砥柱啊),開發機則為Windows 7 x64英文版 + Oracle Client 12.1,二者存在不少差異。

寫了一段測試程式嘗試驗證問題,在我的機器(Windows 8.1 x64中文版 + Oracle Client 12.1)更新中文正常,移到同事的機器執行更新中文… 也正常。登楞!

只好回到轉檔程式用Visual Studio展開偵錯,在同事機器上確實能重現中文變問號現象。仔細比對轉檔程式與測試程式,這才發現測試程式用的是ODP.NET,而寫於民國初年的轉檔程式用的則是將被淘汰的System.Data.OracleClient。將測試程式也換成System.Data.OracleClient,有趣的事發生了,在我的電腦更新中文OK,在同事電腦則可重現中文變問號。

測試程式如下:

//using Oracle.DataAccess.Client;
using System.Data.OracleClient;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
 
namespace OracleUpdateTest
{
class Program
    {
staticvoid Main(string[] args)
        {
            TestOracle();
        }
 
conststring cnStr = "Data Source=OracleServer;User Id=user;Password=ooxx;";
 
publicstaticvoid TestOracle()
        {
            System.Environment.SetEnvironmentVariable("ORA_NCHAR_LITERAL_REPLACE", "TRUE");
using (OracleConnection cn = new OracleConnection(cnStr))
            {
                cn.Open();
                OracleCommand cmd = cn.CreateCommand();
                cmd.CommandText = "UPDATE JEFFTEST SET NAME=N'中文測試' WHERE SEQNO = 1";
int r = cmd.ExecuteNonQuery();
                cn.Close();
            }
        }
 
    }
}

爬文求得一解,在連線字串加上"unicode=true"後問題排除。

依據文件說明,設定Unicode=true時,會指定 Oracle .NET Framework 資料提供者使用 UTF16 模式的 API 呼叫。很明顯這是一個 System.Data.OracleClient 專屬且只發生在特定Windows環境(Win7英文版?),由於 System.Data.OracleClient 已來日無多,不值得繼續深究,只記結論「System.Data.OracleClient 中文變問號,Unicode=true 連線參數不可少」,Case Closed。

使用Visual Studio Code開發Angular 2專案

$
0
0

Angular 2於9/13推出RC7,許多人還在懷疑該不會一直到RC18吧?說時遲那時快,Angular 2 Team忽然在兩天後中秋節這天,閃電宣佈Angular 2.0進入正式版!

身為開發老兵,近年已鮮少加入Beta、Tech Preview版先鋒部隊拓荒,習慣等正式版再認真以待,一則避免發展早期規格一變再變白走冤枉路,二則愈晚開始,規格、工具愈成熟,參考資源也愈多。雖依黑暗技術守則第15條:「早學搶先機,晚學撿便宜」,但NG2已成為工作專案的正式選項,只能乖乖面對新挑戰,看開發教學配月餅文旦,正式步上NG2學習之路。

Angular 2官網有個Angular 2 Quick Start 教學,其中有個My First Angular 2 App範例,算是新手村第一站,決定以它為對象,練習從無到有建立一個NG2專案。

初步評估,現階段Visual Studio Code對NG2開發的支援較VS2015成熟完整,是IDE的首選。加上體驗過用VSCode 偵錯node.js,也算有一丁點基礎(其實只有0.5公分高吧 Orz),就決定是它了!

在網路上找到一段VSCode Angular2 First App Example 影片教學,一步步跟著做,踩了一些雷,總算有驚無險在VS Code完成我第一個NG2專案。影片一鏡到底,從無到有建立網站進行測試,講得很完整,但NG2及VSCode在影片錄製之後有些小改變,另外開發環境差異可能導致錯誤,故我還是做了重點整理,提供想用VSCode開發NG2的捧油參考。

  1. 開始前請先安裝node.jsVisual Studio Code
  2. 建一個FirstApp空白資料夾
  3. 使用VSCode開啟資料夾
  4. 在VSCode加入package.json等檔案

    NG2教學網站的原始碼旁邊有個Copy Code按鈕,可省下選取及按Ctrl-C的步驟,請多加利用。
  5. 依序加入package.json、tsconfig.json、typings.json、systemjs.config.js(最後一個是js不是json哦)。
  6. 開啟DOS視窗,執行npm install,npm會依package.json列舉清單,下載所需的套件:

    npm套件安裝完成後,FirstApp目錄下會多出node_modules及typings資料夾:
  7. 在FirstApp建立app資料夾,並app資料夾中新増app.module.ts、app.component.ts、main.ts並貼入內容。(app.module.ts在範例有出現兩次,第二次多加入AppComponent才是最終版本)
  8. 在FirstApp建立index.html、styles.css並貼入內容。
  9. 設定啟動作業,依下圖操作並選取Node.js:

    VSCode會建立.vs/launch.json,其中program要改成"${workspaceRoot}/node_modules/lite-server/bin/lite-server",利用內建的簡易Web Server進行測試:
  10. 接著我們要指定tsc進行編譯,開啟「檢視/命令選擇區」:

    在命令清單中找到「工作:設定工作執行器」。由於項目蠻多的,可在最上方輸入關鍵字快速過濾:

    選擇「TypeScript-tscofig.json 編譯TypeScript專案」
  11. 如果一切順利,按下Ctrl-Shift-B或用命令選擇器找到「Task: Run Build Task」,app目錄應該會出現app.module.js與app.module.js.map…等編譯結果
  12. 按下F5執行,見證奇蹟的時刻,第一支NG2程式測試成功~

重點來了,以上宛如行雲流水,背後我卻撞牆流血,花了好大功夫才搞懂並解決tsc版本問題。我遇到的狀況是ts沒被編譯成js,VSCode看不到任何訊息,後來我找到tasks.json有個"showOutput"選項,預設為"slient",導致tsc即使編譯TypeScript出錯也不會顯示錯誤。

將參數調整為"always"後,得知無法編譯TypeScript是因為 error TS5023: Unknown compiler option 'p'.  錯誤,爬文得知這是tsc版本不對造成,再費了好大力氣,搞懂幾件事:

  1. tsconfig.json是新版tsc提供的功能,如果你的tsc版本太舊,就會出現error TS5023: Unknown compiler option 'p'. 
    VSCode理應使用npm下載的tsc,會出錯多半是因為PATH殘留1.5以前版本TypeScript路徑所致,因新版已不需在PATH設定路徑,故請將其刪除(請刪除,不要改指向新版),詳情可參考保哥的文章
  2. 記用npm install -g typescript在node.js環境安裝TypeScript編譯元件
  3. tsc有兩種!上述PATH路徑放在C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.x\的是tsc.exe,C:\Users\User\AppData\Roaming\npm下的則是tsc.cmd,以JavaScript寫成,依附於node.js執行,二個是不同的東西。tsc.exe目前已經到了1.8.34,而tsc.cmd還在1.8.10,二者行為也有別。例如我就發現tsc.cmd編譯時會自動省略node_modules,故tsconfig.json不需要透過exclude排除,加了反而會有錯誤。而實測發現,NG2官網範例專案使用tsc.exe –p .方式執行,ts不會被編譯也不會有錯誤訊息,故建議將PATH中tsc.exe的路徑移除,並確認node.js的tsc元件安裝妥當。
  4. 如何檢查方法tsc版?在CMD視窗下執行where tsc及tsc -v,確認其路徑為AppData\Roaming\npm,且版本為1.8.10

就這樣,我完成了VSCode NG2首航!祝大家NG2 Coding不NG~

潛盾機-網頁版COPY指令產生器

$
0
0

工作環境有不少Web Farm主機,部署程式時需要將一或多個檔案同步複製到多台主機上,依上線流程需產生指令檔交給OP人員執行。這種需求用COPY、XCOPY或ROBOCOPY指令寫成批次檔是最直覺有效的做法,但以「將四個檔案複製到八台主機」為例,需寫成32條指令,沒營養又躲不掉的枯躁苦工,交給機器人才是王道。

輸入IP清單轉成一串指令的小工具,印象裡我寫過WinForm版也寫過WebForm版,最近有類似需求,卻不幸遇到記憶斷層,怎麼也想不起程式放哪… Orz 心一橫,索性拿它當成練功題材,花了點時間寫成純前端網頁版,順手分享出來。

操作介面如上圖,設好來源項目與目標IP按個鈕即展開成批次指令,亦提供複製到剪貼簿的功能。Script Template是產生指令的樣版字串,{0}代表來源清單的項目,{1}則是對象機器IP,另外有個{0:path}語法可以只取來源項目資料夾路徑,方便組裝COPY指令。主機清單通常是固定的,右上方我放了快捷鈕,可直接帶入預先定義的主機IP清單,IP資料由一個JavaScript物件提供,使用時請自行調整成實際在用的群組名稱與IP。

var groups = {
"GRP1": "191.168.1.1\n192.168.1.2",
"GRP2": "10.10.100.1\n10.10.100.2\n10.10.100.3\n10.10.100.4"
}   

程式靠Angular MVVM配合JavsScript輕鬆搞定,沒什麼技術含量,寫成網頁的好處是只需單一檔案,丟上網路人人可用,懂JavaScript就能修改,差不多是路邊奉茶的概念(笑)。

程式碼如下,同時我也放上JSBin了,有需要的朋友請自取。

<!DOCTYPEhtml>
<htmlng-app="app">
<head>
<metacharset="utf-8">
<metaname="viewport"content="width=device-width">
<title>Copy Script Generator</title>
<style>
    .deploy {
        width: 1024px;
    }
 
    legend {
        font-size: 9pt;
    }
 
    .list {
        width: 100%;
        margin: 6px 0;
    }
 
        .list td {
            padding-right: 6px;
        }
 
    textarea {
        max-width: 100%;
        width: 100%;
        height: 100px;
    }
 
    input {
        max-width: 100%;
    }
 
    .op {
        margin: 12px 0;
        background-color: #ccc;
        padding: 3px;
    }
 
    .result {
        height: 200px;
    }
 
    .show-hide {
        transition: all linear 0.5s;
    }
 
        .show-hide.ng-hide {
            opacity: 0;
        }
 
    .txt-button {
        color: blue;
        text-decoration: underline;
        cursor: pointer;
        margin-right: 3px;
    }    
</style>
</head>
<body>
<divng-controller="ctrl"class="deploy"ng-cloakafa-loading-indicator="vm.IsBusy">
<h3>
        Copy Script Generator by darkthread
</h3>
<div>
<tableclass="list">
<tr>
<td>Source Item</td>
<td>
                    Source IPs
<spanstyle="float: right">
                        IP Groups:
<spanng-repeat="(k,v) in vm.Groups"
class="txt-button"ng-click="vm.SetDest(k)">{{k}}</span>
</span>
</td>
</tr>
<tr>
<td>
<textareang-model="vm.SrcList"></textarea>
</td>
<td>
<textareang-model="vm.DstList"></textarea>
</td>
</tr>
</table>
</div>
<divclass="op">
<div>Script Template</div>
<textareang-model="vm.ScriptTmpl"style="height: 50px"></textarea>
<buttonng-click="vm.GenScript()">Generate Script</button>
<buttonng-click="vm.CopyScript()">Copy to Clipboard</button>
<spanclass="show-hide"ng-bind="vm.Msg"ng-show="vm.ShowMsg"></span>
</div>
 
<div>
<fieldset>
<legend>Scripts</legend>
<textareang-model="vm.Script"class="result"></textarea>
</fieldset>
</div>
</div>
<scriptsrc="https://code.jquery.com/jquery-3.1.0.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js">
</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular-animate.min.js">
</script>
<script>
//REF: http://stackoverflow.com/a/30810322/4335757
function copyTextToClipboard(text) {
var textArea = document.createElement("textarea");
  textArea.style.position = 'fixed';
  textArea.style.top = 0;
  textArea.style.left = 0;
  textArea.style.width = '2em';
  textArea.style.height = '2em';
  textArea.style.padding = 0;
  textArea.style.border = 'none';
  textArea.style.outline = 'none';
  textArea.style.boxShadow = 'none';
  textArea.style.background = 'transparent';
  textArea.value = text;
  document.body.appendChild(textArea);
  textArea.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
  }
catch (err) {
    console.log('Oops, unable to copy');
  }
  document.body.removeChild(textArea);
}
 
var groups = {
"GRP1": "191.168.1.1\n192.168.1.2",
"GRP2": "10.10.100.1\n10.10.100.2\n10.10.100.3\n10.10.100.4"
}    
angular
    .module("app", ["ngAnimate"])
    .controller("ctrl", function($scope) {
function myViewModel() {
var self = this;
            self.IsBusy = false;
            self.SrcList = "scripts\\common.js\nscripts\\common.min.js\ncss\\style.css";
            self.DstList = "192.168.1.1\n192.168.1.2";
            self.ScriptTmpl = "copy d:\\WWW\\blah\\{0} \\\\{1}\\d$\\WWW\\blah\\{0:path} /Y";
            self.ShowMsg = false;
            self.GenScript = function() {
var self = this;
var srcAry = self.SrcList.split('\n');
var dstAry = self.DstList.split('\n');
var cmds = [];
                $.each(srcAry, function(i, src) {
var p = src.split('\\');
                  p.pop();
                  var srcPath = p.length ? p.join('\\') : src;
                  $.each(dstAry, function(j, dst) {
                        cmds.push(self.ScriptTmpl
                                  .replace(/[{]0[}]/g, src)
                                  .replace(/[{]0:path[}]/g, srcPath)
                                  .replace(/[{]1[}]/g, dst));
                    });
                });
                self.Script = cmds.join('\n') + "\n";
            };
            self.GenScript();
            self.CopyScript = function() {
var self = this;
                copyTextToClipboard(self.Script);
                self.Msg = "Copied!";
                self.ShowMsg = true;
                setTimeout(function() {
                    self.Msg = "";
                    self.ShowMsg = false;
                    $scope.$digest();
                }, 2000);
            };
            self.Groups = groups;
            self.SetDest = function(grpName) {
var self = this;
                self.DstList = groups[grpName];
            };
        }
        $scope.vm = new myViewModel();
    });    
</script>
</body>
</html>

後記:小工具寫完沒幾天,NG2正式版就閃電問市惹… 所以,敬請期待「COPY指令產生器 in NG2」,Coming Soon~

TypeScript 1.4 - 2.0 改版整理

$
0
0

TypeScript 2.0 已於 9/22 正式推出,想起從1.4版起已好久沒有深入了解改版差異,順勢做個重點整理。

TypeScript 改版歷程

TypeScript 1.0 推出時,由於具有支援強型別、介面、繼承等物件導向語言特性,提供編譯期錯誤檢查,再加上完整 IDE 支援,很適合開發大型且複雜的 JavaScript 程式,獲得許多前端開發者青睞,TypeScript 也自此成為我開發網站的首選。

TypeScript 在 1.1 時選擇重寫編譯器,提供四倍速度,同時也移上 Github,鼔勵開源社群參與協作。

1.4 TypeScript 加入大量 ES2015/ES6 支援以及新特性,包含:

  • Union Types ★★
    例如: x: number | number[],x 的型別可以是數字或是數字陣列
  • Type Alias
    為型別取別名,例如:type NgScope = ng.IScope, type Callback = () => void
  • Const Enums
    編譯時直接置換為數字,在 JavaScript 端完全隱形
  • 支援 ES6 的 Let/Const
    let 的用法與 var 相同,差別在於 let 嚴格限定變數存活範圍,杜絕干擾區塊外變數的可能性。const 則可用於宣告後定義好就不容更改的變數。
  • Template String ★★★★
    直接看範例:
    var rectangle = { height: 20, width: 10 };
    var areaMessage = `First line,
    Rectangle area is ${rectangle.height * rectangle.width}`;
    樣版字串使用「`」符號取代單引號或雙引號,在字串可嵌入變數,還像C# @"…"可支援換行,串接HTML標籤接到落落長時格外好用,大推!
    延伸閱讀:TypeScript Template Strings

TypeScript 1.5陸續加入更多 ES6 支援,包含:

  • ES6 Module概念
    在 ES6 習慣以模組概念拆解成多個程式碼檔案,使用 export 決定對外公開範圍,要用時以 import 匯入引用。前幾天初試 Angular 2已體驗過這種新做法。
  • Destructing
    使用陣列形式快速指定變數值
    const iterable = ['a', 'b'];
    var [x, y] = iterable; // x = 'a'; y = 'b'
  • Spread運算子 ★★
    用法 function drawText(x: number, y: number, ...strings: string[]) { … }
    概念相當於 C# 的 params,例如:void DrawText(decimal x, decimal y, params string[] strings)
    故寫成 drawText(10,20,"hello") drawText(10,20,"hello","world")都通,但Spread運算子還可加於呼叫參數前方,例如:
    var pos=[10,20], strings=["one","two"];
    drawText(…pos, "hello",…strings,"world");
    等同drawText(10,20,"hello","one","two","world");
  • for … of 語法
    類似 C# foreach (var … in …):
    for (var v of expr) { }
    等同於
    for (var _i = 0, _a = expr; _i < _a.length; _i++) {
        var v = _a[_i];
    }
  • 支援ES6內建Symbol 參考
  • ES6 Computed Property
  • Module 輸出選擇
    除了 AMD、CommonJS 外,再新增 SystemJS、UMD
  • 支援使用 tsconfig.json 設定專案以及編譯選項
  • Decorator
    與 Angular、Ember、Aurelia 開發團隊合作,TypeScript 1.5 融入 ES7 的 Decorator 特性,它也是 Angular 2 開發的重要關鍵:(如以下程式的 @Component 及 @View )
    import {Component, View, NgFor, bootstrap} from “angular2/angular2”;
    import {loadFile} from “audioFile”;
    import {displayAudioFile} from “displayAudio”;
     
    @Component({selector: ‘file-list’})
    @View({template: `
    <select id=”fileSelect” size=”5″>
    <option *ng-for=”#item of items; #i = index”
          [selected]=”selected === item”(click)=”updateSelection()”>{{ item }}</option>
    </select>`,
      directives: [NgFor]
    })
     
    class MyDisplay {
      items: string[];
      constructor() {
    this.items = [“item1”, “item2”];
      }
     
      updateSelection() { … }
    }

TypeScript 1.6-1.8 陸續再加入改良。

TypeScript 1.6

  • 支援 React Typing 及 JSX
  • Class Expression
    一列寫完類別宣告,例如:
    class StateHandler extends class { reset() { return true; } } {
  • 自訂型別檢核
    例如以下程式,若 a 是 Dog,在編譯時期就會出錯
    function isCat(a: Animal): a is Cat {
      return a.name === ‘kitty’;
    }
  • Intersection Type
    在 JavaScript 裡有時會使用 Mixin 概念或 jQuery.extended()方法融合兩個不同型別物件同時具備兩種型別的介面,過去在這種情況下要實現強型別,需宣告一個新介面或類別以兼容兩種型別的介面,1.6 起可用 T & U 代表融合 T 與 U 屬性方法的混合型別,例如:
    function extend<T, U>(first: T, second: U): T & U
  • 支援抽象類別(Abstract Class)
  • 支援泛型別別名

TypeScript 1.7

  • 支援 async / await
  • Polymorphic this Typing
    多形概念的 this,主要用於 Fluent 串接式 API,看範例比較好懂:
    interface Model {
        setupBase(): this;
    }
    interface AdvancedModel extends Model {
        setupAdvanced(): this;
    }
    declare function createModel(): AdvancedModel;
    newModel = newModel.setupBase().setupAdvanced(); // fluent style works
  • ES6 Module Emitting
    新增 module 參數,面對 Node.js v4 不支援 ES6 模組但支援 ES6 特性的情境,可以 target 參數設 es6,但模組用 commonjs。
  • ES7 Exponentiation
    ES7 規格,let cubed = 2 ** 3 –> cubed = 2 * 2 * 2,取代 Math.pow()

Type Script 1.8

  • Module Augmentation
    允許 import Module 後再擴充其介面加入新屬性、方法
  • String Literal Types ★★
    限定字串變數只能使用列舉的字串值,例如: easing: "ease-in" | "ease-out" | "ease-in-out"; 若 easing = "out" 會出現Error: Type '"out"' is not assignable to type '"ease-in" | "ease-out" | "ease-in-out"'
  • 流程分析更加智慧化
    例如:return 後馬上換行陷阱偵測,未 return 等同傳回 undefined 警告。

至於 TypeScript 2.0,台灣 MSDN 部落格有篇文章有 Beta 版的詳細介紹,這裡簡單條列:

  • --strictNullChecks 限定 string、number 等型別變數不允許被設為 null 或 undefined,除非使用 string | null 或 string[] | undefined,如必要可加上「!」排除變數為 null 或 undefined 情況以避開編譯錯誤。例如:
    let lowerCased = strs!.map(s => s.toLowerCase());
  • 編譯器能更精準掌握變數在某段程式碼位置時是什麼型別
  • 模組宣告簡化
    在 TS1.x 為描述外部程式庫,我們可能需要寫成以下宣告
    declare module "foo" {
        var x: any;
        export = x;
    }
    在 TS2.0 只要寫成 declare module "foo" 即可

2.0 RC加入的改良如下:

  • Tagged Unions ★★
    Tagged Unions(又稱為 Discriminated Unions, Disjoint Unions, 或 Algebraic Data Types)已是 F#, Swift, Rust, JavaScript 常用的設計模式。用個例子來說明,Circle 與 Square 都有 kind 屬性,但在 Circle 寫死為 "circle",在 Square 則為 "square":
    interface Circle {
        kind: "circle";
        radius: number;
    }
     
    interface Square {
        kind: "square";
        sideLength: number;
    }
     
    type Shape = Circle | Square;

    在 TS1.8 裡,取得 Shape 聯集型別(Union Type)後必須轉型成 Circle 才能取得半徑,轉成 Square 才能讀取邊長:
    function getArea(shape: Shape) {
    switch (shape.kind) {
    case"circle":
    // Convert from 'Shape' to 'Circle'
                let c = shape as Circle;
    return Math.PI * c.radius ** 2;
     
    case"square":
    // Convert from 'Shape' to 'Square'
                let sq = shape as Square;
    return sq.sideLength ** 2;
        }
    }

    TS2.0 變聰明了,知道何時 Shape 是 Circle,何時是 Square:
    function getArea(shape: Shape) {
    switch (shape.kind) {
    case"circle":
    // 'shape' is a 'Circle' here.
    return Math.PI * shape.radius ** 2;
     
    case"square":
    // 'shape' is a 'Square' here.
    return shape.sideLength ** 2;
        }
    }
  • 1.8 推出的 String Literal Type 廣受好評,2.0 再擴大到 boolean, number ★★
    type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
    let nums = Digit[];
    nums.push(16); <= 出錯,因為16不在列舉範圍內
  • tsconfig.json 支援萬用字元
    例如:
    {
    "include": [
    "./src/**/*.ts"
        ],
    "exclude": [
    "./src/tests/**"
        ]
    }

    其中:*代表0或多個非分隔符號字元、? 代表一個非分隔符號字元、**/ 代表任意層的子目錄

再補上幾條 TypeScript 2.0 革新:

  • 簡化宣告檔(d.ts)的引用程序
    在 VS Code 等應用中,使用 npm install –s @types/lodash 安裝 Scoped Package後,在專案中遇到需要 lodash 定義時將自動找到適合的版本下載。
  • 增加 readonly 修飾字
    用法與 C# readonly 相同,只允許由建構式指定值,之後不得再變動。

註:標上 ★★的是我覺得特別好用的功能,黑大嚴選,大力推薦!

要使用 TypeScript 2.0,VS2015 必須更新到 Update 3,並安裝 TypeScript 2.0 for Visual Studio 2015;Visual Studio Code 則可透過 npm install –g typescript@2.0 安裝新版。

.NET Standard 2.0 是什麼?可以吃嗎?

$
0
0

這幾天(9/26-30) Microsoft Ignite 2016(去年起由 Ignite 取代 TechEd)正在美國如火如荼召開,乍見一個新名詞-.NET Standard 2.0。我得了一種聽到新名詞就會焦慮的病,趕緊查資料壓壓驚…

.NET Team Blog 同步貼了一篇介紹文,可說是目前最詳細最權威的資料來源,抱著懂個大概就好的心態,整理重點如下。

先用兩張圖說明為什麼沒事要搞出一套 .NET Standard 新標準?(註:圖片取自 MSDN Blog 文章

.NET Standard 的核心使命在於解決 .NET 跨平台時基礎程式庫不一致的問題。.NET 發展至今,已初步實現跨平台, 用 C# 就可以寫 Windows、macOS、Linux、iOS、Android 程式。但如下圖所示,.NET Framework BCL、.NET Core Library 與 Xamarin 的 Mono Class Library 各自發展,缺乏統一的介面標準,像是System.Collections, System.IO, System.Xml… 這類性質的基礎類別,各家支援程度不一,某些 API 可能在某個平台不存在,又或者 API 介面存在差異。

如果你只專注一種平台,當然可以無視各平台基礎程式庫的差異,專心學好一種就好。但如果系統被要求跨平台,差異那怕再小,都會跑出來咬你屁股。首先,你必須搞懂不同平台的差異,第二,差異提高「跨平台共同程式庫/元件」開發的難度。(腦海中出現一堆噁心的 #if NETCORE … #elif XAMARIN … #endif) 。

過去針對跨平台共用程式庫的主推做法是PCL(Portable Class Library),取多個平台的交集,篩選保留各平台都支援的 API,但開發者仍需知道不同平台的差異。.NET Standard 則試圖規範一套標準基礎程式庫 API 介面,各平台依此介面實作出一致的程式庫,如此程式碼不需修改即可針對不同平台編譯、執行。

即使有 .NET Standard,還是無法逃避各平台支援度不一的現況。.NET Standard 版本號碼與 API 完整度成正比,與支援平台範廣度成反比。.NET Standard 2.0 提供的 API 數目一定比 1.0 多,但如果想涵蓋 Windows Phone 8.1,就只能選擇 .NET Standard 1.0 – 1.2。

隨著 .NET Standard 2.0 制定,.NET Core 與 Xamarin 將在新版加入支援,而 .NET Framework 4.6.1 則已符合 2.0 標準。有沒有注意上表的玄機?.NET Standard 1.4 對應到 .NET Framework 4.6.1、1.5 對 4.6.2,結果 2.0 又倒車回到 4.6.1,原來是基於部署普及率考量,.NET Standard 2.0 拿掉 1.5/1.6 增加但應用不廣的 API,好讓 .NET Framework 4.6.1 符合 .NET Standard 2.0。各位同學,今天我們要介紹的成語是-「削足適履」~(笑 )

以下是 .NET Standard 2.0 的主要涵蓋範圍,細節則參考 github 上的文件。 .NET Standard 2.0 仍在發展中,未來可能還會有變動。

至於一些與平台高度相關的 API,例如:只有 Windows 才有的 Registry、Reflection Emit 功能不適用 .NET Native、UWP、Xamarin iOS。.NET Standard 採取「需額外安裝 NuGet Package 才能使用,在不適用平台執行時拋出例外」的原則,不同的 API 處理方式不一。

如果你需要開發跨平台程式庫,官方建議改走 .NET Standard,以降低平台相依性,並允許混合參照 PCL 及 .NET Framework,但 PCL 仍適用特定場合,例如:某些平台不在 .NET Standard 支援之列,PCL 是唯一解。實作上有個 API Port可偵測程式適用的最低 .NET Standard 標準,再配合檢查目標平台是否在該標準支援範圍,以便決定標準版本。

完整的 .NET Standard 工具支援將內建於下一版本 Visual Studio "Dev 15",以 NuGet Package 方式加入參照,而未來 Visual Studio、VSCode、Xamarin Studio 均會提供一線支援。

報告完畢。

網路抓來的圖能不能用,可不可以修改?淺談創用CC授權

$
0
0

之前只模糊知道 CC 是一種授權方式,常在網站看到下方這種標誌或 CC-BY-NC 之類的註記:

但對於 BY NC SA ND 這堆縮寫與符號一知半解,總搞不清楚我在網路上找到的圖或影片,到底能不能用,可不可以修改,要不要標示作者…

Lag 了很久,最近終於搞懂「創用 CC 授權條款」是怎麼回事,寫篇筆記兼分享。

時至今日,相信大家都已有著作權觀念,知道網路上看到的創作(照片、文章、繪畫、音樂、影片…)都有著作權,觀賞自用沒啥爭議,若要在自己的作品裡引用,必須徵求作者同意。不過,寫部落格或做投影片為了一張照片或一段音樂,得連絡原作者協商取得授權(如果作者有留連絡方式的話…)即曠日費時又有些本末倒置。尤其這年頭很流行以照片為主體的投影片風格,試想如果投影片用到數十張網路上的攝影作品,光逐一連絡作者取得授權大概就飽了…

從另一個角度,不少創作者很歡迎別人引用及推廣他的作品,但其他人卻害怕觸犯著作權法不敢使用,阻礙了知識與藝術交流以及人類文明演進(謎之聲:有這麼嚴重嗎?)。於是,在 2001 年由美國法律學者推動的 Creative Commons 公眾授權條款,鼓勵大家在作品上主動標示授權範圍,明確作品開放給公眾利用,促進內容資源的散佈與流通,讓創作者與使用者同時受惠。

關於 Creative Commons 的細節,中研院科技創新研究中心推動的「台灣創用 CC 計劃」網站有非常詳細的說明,很值得一讀(其中還有 FAQ 解答常見的疑惑,例如:用 CC 授權素材做成文宣有沒有觸法?參加比賽得獎算不算商業行為?)。以下則是我簡單整理的重點:

CC 授權有四大要素,就是一開始圖檔上出現的小圖示,每個要素另有兩個字母縮寫:


圖片來源:台灣CC創用計劃

四項要素分別是 BY 姓名標示、NC 非商業性、ND 禁止改作、SA 相同方式分享,除了圖示,也可使用「CC BY-SA」這種格式表示。四個要素共有六種組合:CC BY、CC BY-NC、CC BY-NC-SA、CC BY-ND、CC BY-NC-ND、CC BY-SA。

CC 網站有圖示可供下載,而應用時多半還會加上連結指向授權說明文件,有個簡便做法是使用授權選擇網頁經由簡單問答產生適用的授權標誌嵌入程式碼,圖示與連結一次搞定,放進作品網頁就算完成 CC 授權宣告。

搞懂這些,未來在網路尋找照片、影片,如果看到 CC 授權,就代表作者同意授權使用,但要留意其限制,有 BY 代表必須標記作者,若有 ND 代表不能進行任何修改,出現 NC 代表不允許用於商業行為謀利,若有 SA 代表引用後你的作品也要採相同授權分享出去。

在所有可利用資源中,有一種佛心到極致(差不多是萬佛朝宗的等級)的授權模式叫 Public Domain (公眾領域),標示為 CC0 (公眾領域貢獻宣告)與 PDM (Public Domain Mark,公眾領域標章),CC0 象徵著作權人自願拋棄著作權,不保留任何權利,將作品貢獻給公眾,任何人可以任何方法自由運用,不論公益或營利;而 PDM 主要應用於圖書館、博物館將文物資產與公眾共享。

換言之,CC0 授權允許自由修改且不需標註出處,使用起來全無限制,是創作素材中的極品,網路上可以找到很多 CC0 圖檔甚至有 CC0 免費圖庫搜尋引擎,大家可以善加利用,別再甘冒侵犯著作權風險亂抓亂用圖片。

最後,補充一點小技巧,Google 圖片搜尋功能的「搜尋工具」有個「使用權限」篩選工具,提供「不限使用權」、「標示為允許再利用且可修改」(相當於 CC BY)、「標示為允許再利用」(相當於 CC BY-ND)、「標示為允許非商業用途再利用且可修改」(相當於 CC BY-NC)、「標示為允許非商業用途再利用」(相當於 CC BY-NC-ND)五種篩選等級,能依應用方式快速找到符合授權範圍的圖片。但要留意,除了 CC0 之外,只要有 BY 就需依要求標註創作者姓名或來源。

除了 Google 查詢,許多專業的搜尋服務都有授權過濾選項,有了創用 CC 授權的知識,下回再看到篩選器裡的成串選項,應該就不會再一頭霧水囉~


2016烏來馬拉松

$
0
0

步入秋季,九月跑了兩場10K路跑暖身,下半年跑馬由烏來馬揭開序幕。週六比賽,週二、週三因梅姬連放兩天颱風假,媒體傳來的烏來實況照片挺嚇人,原本擔心山區受創會取消或延期。所幸一切無恙比賽照常,但主辦人邵老師肯定已洗完一輪心情三溫暖。(據説這次辦比賽瘦了17公斤 XD)

凌晨四點趕到捷運新店站,接駁遊覽車花了30分鐘到鳥來,在觀光大橋附近換小巴再開10分鐘到會場-勇士廣場。時間不到5點天色仍暗,但現場已人聲鼎沸。

天色漸亮,慢慢看清會場全貌,舞台就在瀑布正前方。

馬場聲樂家許文龍老師登場,獻唱一曲「You Raise Me Up」,感染力十足~

近六點整集結出發,大會介紹幾位特別的參賽者,分別有跑友要在今天挑戰百馬、兩百馬、三百馬、五百馬、六百馬… 媽呀,六百馬是什麼狀況?每個週末跑兩場全年無休,還要夠多比賽可報才能達標。台灣這幾年路跑風行,每年路跑賽事高達三百場,才造就了另類「台灣奇蹟」。

出發後先繞行老街,接著一路爬升,7K左右到達海拔四百多公尺的賽道最高點(勿高興,後面會再爬回四百米外加一路上上下下)。下點最高點水站照片裡有位穿整套原住民服飾(還是印地安風?)的女跑友,手拿小石斧頭加誇張羽毛頭飾十分吸晴,速度頗快,我還想一路當成明顯目標緊跟,肯定成績不會太差。不過,才過高點沒多久,我就被海放 海放 海放~~~

過最高點的下坡又陡又濕滑,沒人敢跑,大家通通緩步前進以防仆街。

10K左右繞完山頭再回到會場附近,看到瀑布已在下方。

美景

多雲到晴的好天氣,狀闊的峽谷,讓孤陋寡聞的阿宅重新認識美麗的烏來~

 

 

 

瀑布

幾天前下完大雨,山區處處都是肥壯而有怒氣的大小瀑布,一口氣把一年份的瀑布看完,過癮。XD

   

   

   

美食

現在本紹本場戶外餐會的菜單,餐前水果有「海鹽田園檸檬」、「厚切當季鮮蕉」。

另外「洋石榴玉女蕃茄佐梅粉」的絕妙搭配令人驚豔。開胃菜則有兩道,分別是「黃金凝脂糖心蛋」:

以及「福爾摩莎火腿佐香蒜」:

前菜來了,「辣味洋葱拌炒野味山豬肉」放在芭蕉葉上格外好吃。邵老師就在山豬肉水站外熱情招呼跑友,遇到熟識跑友,還飆出「王八蛋,為什麼不吃山豬肉?」,當場笑翻。

主菜登場,巴巴巴巴馬乾酪嫩雞潛艇堡佐蜂蜜芥末,別懷疑,就是Subway的潛艇堡三明治沒錯,誰叫邵老師開Subway餐廳呢?連手工餅乾都登場了。

飲料也很豐富,喝到山粉園、仙草蜜、舒跑、維大力、可樂… (嗝~)

雜記

身處峽谷間,GPS訊號不佳,12K左右配速數字亂跳,還天外冒出三分速,而全程里程也誤差近2公里。事後看GPS軌跡,有段通過隧道的軌跡猶如鬼畫符。

折返點在福山部落大羅蘭溪,又一個如果不是跑馬我不會來的地方。

5K一個里程板,我蒐集到四塊。

   

   

大會開放自訂號碼布姓名,所以,薑薑薑薑~~~

自訂姓名帶來額外趣味,最後幾K我盯上一位「奔跑的茼萵」小姐,由於不想輸給蔬菜,跑得格外賣力,成績比預期好。另外,路上也被讀者跑友認出,小聊了兩句。

最後5h40m18s完賽,本來以為會破六,比想像好很多,但這回穿練跑鞋登場,透氣性及重量有差,上下坡路段不少,最後10K腳趾發痛,估計會領幾塊黑指甲當紀念獎牌。

就這樣,開心結束一馬,順利搭上接駁車,還及時趕上原本以為會大遲到的同學會。

賽道風景優美(雖屬山路,但與櫻花馬、石碇馬相比難度並不高),補給豐盛,接駁周到,完賽獎牌別緻,參賽背心及完賽紀念T-Shirt都很好看,小而美賽事口袋名單再添一筆!

 

Oracle故障後續處理經驗一則

$
0
0

不經一事不長一智,以下經驗價值1.5小時。

接獲回報,部分 ASP.NET 網頁出現資料庫錯誤,錯誤指向某 Oracle 資料庫,使用 Telnet oracel_server_ip 1521 測試無反應,通報系統人員,查出為資料庫主機網路異常,並在隨後修復。

真正的茶包在 Oracle 資料庫主機恢復後才現身,部分使用者通報他們還是無法使用網頁,但我測試是成功的,而有問題的使用者「多試幾次」也會成功。網站為 Web Farm 架構,參雜使用者連上主機可能不同的因素,歷經一番追查彙整,才理頭緒:

  • 網頁連線 Oracle 資料的動作時好時壞,失敗時出現連線錯誤或 Timeout
  • 問題集中在 Web Farm 其中兩台主機
  • 事件檢視器有 EntityFramework 連線錯誤 The underlying provider failed on Open

由於該網頁使用者不多,推測 Oracle 出錯時兩台問題主機剛好有使用者在線上,當時用的 OracleConnection 無法連線出錯,而出錯連線殘留在 Connection Pool。之後 Oracle 資料庫恢復正常,但若是網頁從 Connection Pool 中取出的是曾出錯的連線,便會產生 The underlying provider failed on Open 錯誤。由於 Connection Pool 部分連線是好的,部分是壞的,造成時好時壞,多試幾次就會成功的現象。而重啟 Application Pool 後,問題宣告排除。

事後才想起七年前 ODP.NET 9207 時代我也遇過 Oracle Connection Pool 殘留錯誤連線問題,當時還研究過 ClearPool() ClearAllPool() 指令,可惜未及時想起白走了冤枉路,扼腕!

教重訂 SOP 如下:

遇 Oracle Server 出錯恢復後,應重啟 AppPool 或程序,以清除 Connection Pool 中的有毒連線。 

【茶包射手日記】OLE DB讀取CSV時文字變空白

$
0
0

同事回報一個鬼問題,某段古老 ASP.NET 程式使用 OLEDB 讀取 Excel/CSV 檔案(寫法範例,屬民國初年流行做法,現多會改用 EPPlusClosedXML),原本運作良好,自從 Windows 2003 EOS 退役移至 Windows 2012R2 x64 後出現狀況。情境為使用者上傳CSV給網頁解析,其格式如下:
SN,Data
1,1234
2,AB12
3,9527
4,5978

Data欄位有可能是純數字或英數字,當Data為英數字有時會讀不到,而且是全部英數字都被忽過,數字部分正常,但有時在下方多加一筆英數字就又可全部讀到,頗為詭異。修改程式碼將讀取結果逐筆印出後有了較明顯線索:發現在「某種條件」下,從Data欄位讀入的英數字會全部變成空白,但數字部分正常。英數字資料的筆數、位置似乎會影響結果,但多次實驗仍未理出頭緒。

爬文查到保哥的一篇老文章提到 TypeGuessRows 機碼及 Jet Engine 由前 8 列資料猜測欄位型別的行為。換言之,是「欄位格式自動偵測」惹的禍,由於  Data 欄位混雜純數字與英數字,Jet 引擎自動偵測決定將其視為純數字或是英數字,若被判定成數字,其中的英數字資料就會被當成NULL,轉為字串後變成空字串。在德瑞克的另一篇文章,則有更詳細的情境範例描述:

如果您在同一欄中混合使用數值和文字值,會發生嚴重的問題。
Jet 和 ODBC 提供者都會傳回主要類型的資料,但是次要資料類型則會傳回 NULL (空) 值。
如果同一欄中兩種類型混合使用的比例相同,提供者會選擇數值而非文字值。
例如:
(1) 在 8 個已掃描的列中,如果欄位中包含 5 個數值和 3 文字值,提供者會傳回 5 個數值和 3 個 Null 值。
(2) 在 8 個已掃描的列中,如果欄位中包含 3 個數值和 5 文字值,提供者會傳回 3 個 Null 值和 5 個文字值。
(3) 在 8 個已掃描的列中,如果欄位中包含 4 個數值和 4 文字值,提供者會傳回 4 個數值和 4 個 Null 值。

但有個大疑點:我確認 Registry 中 TypeGuessRows 為 8,ImportMixedTypes 為 Text,實測就算前 8 筆都是英數字,欄位仍可能被判定為數字導致英數字空白,而這種混雜型別狀況依 ImportMixedTypes='Text' 明明要為文字才對。

最後,我才驚覺 Jet 解析 CSV 時使用的 Registry 與 Excel 有別,ImportMixedTypes 預設 Majority Type,而 MaxScanRows為 25,「為什麼前 8 筆全是英文仍會導致英數字變 NULL」有了完美解釋。


補充:Text Jet Engine Registry設定說明

又長了知識。

【2016-10-08更新】感謝網友張峰銘補充,若不想讓Jet Engine瞎猜一通,可使用Scehma.ini強制指定欄位型別(參考)。

【延伸閱讀】

中文亂碼「嚙踝蕭嚙踝蕭」是怎麼來的?

$
0
0

在 FB  看到 91 貼了一張照片,提到某廠商的電子報一直存在亂碼問題,寄信人與信件主旨出現一堆嚙調客、嚙踝蕭… 之類的怪字亂碼。

有種畫面上到處是老鼠的感覺… (嚙是的異體字)

連 Chrome 也想吶喊「有老鼠!Encoding 碰上麻煩了。」 XD

之前寫過一篇中文亂碼"蕞蕞蕞蕞"是怎麼來的?,猜想同樣是錯用編碼解析的結果,雖然最近案子忙到火燒屁股,但就看到球滾進了我的守備範圍,還是忍不住研究起老鼠從哪來… XD

用一小段程式驗證問題來自「錯用 UTF8、BIG5 解碼」:

首先將「嚙踝蕭嚙踝蕭」做一次 BIG5 逆轉回 UTF8,得到 ����,� (EF-BF-BD)是 UTF8 無法解碼時使用的無效字元符號。由此推測問題來自非 UTF8 字串卻誤用 UTF8 解碼,產生大量�及少量英數字交雜的亂碼字串,接著該 UTF8 字串再被誤當成 BIG5 再解碼一次,「嚙踝蕭嚙踝蕭」就這麼誕生了!結案。

「以排程方式呼叫Word/Excel注意事項」補充包

$
0
0

程式搬家出錯的老梗又來了~ 古老的 VB.NET 傳真傳送程式,內部用 CreateObject("Word.Application") 呼叫 Office Word 2003,借重其傳真發送功能。不意外地,原本在 Window 2003 執行得好好的程式,移到 Windows 2008 R2 x64 後水土不服。新主機裝的是 Word 2010,程式出錯,為避免踩到 Word 2003 / Word 2010 差異的地雷,決定在 Windows 2008 R2 上裝 Word 2003。雖然官方文件已言明不支援,實際安裝及測試倒沒啥問題,決定這様頂著先,要改的東西太多,所以就改天吧!

換裝 Word 2003 後程式可手動執行成功,但移入排程後出現錯誤:

System.Exception: 無法建立 ActiveX 元件。 於 Microsoft.VisualBasic.Interaction.CreateObject(String ProgId, String ServerName)   
System.Exception: Cannot create ActiveX component. at Microsoft.VisualBasic.Interaction.CreateObject(String ProgId, String ServerName)".

Office 相關程式桌面互動執行OK,移入 Windows Service 或排程出錯,過去已有不少經驗,關鍵是要手動建立 c:\windows\syswow64\config\systemprofile\desktop 資料夾。快手快腳建完目錄,錯誤依舊… 這下糗了,黔驢技窮。

思索了一陣子,由 CreateObject("Word.Application") 得到靈感,建立 ActiveX 物件依賴 Registry,而 Registry 體系 32 位元 64 位元有別,莫非問題與 32/64 有關?使用 corflags 工具檢查程式的目標平台為 AnyCPU,懷疑問題出在排程中程式是在 64 位元模式執行,用 corflags 將程式改為 x86 再試一次,問題排除!特此筆記。

【延伸閱讀】

檔案部署指令實戰技巧整理

$
0
0

比起滑滑鼠,我更喜歡開DOS視窗敲鍵盤下指令解決問題,效率高,操作程序可以保存並加以優化,成果可反覆使用並散佈供他人使用。當手指飛快在鍵盤上躍動,還有種自己正忙著拯救世界,或是藝術家沈醉在表演中的錯覺 XD

前陣子分享過產生批次指令部署檔案的小工具,在最近上線過程耍得虎虎生風,習得實戰技巧幾則,特筆記備忘。

COPY 三寶

COPY、XCOPY、ROBOCOPY 三者各有無法被取代的特色,視場合使用,俗稱 COPY 三寶(喂!不要亂取名字啦)。完整參數介紹:COPYXCOPYROBOCOPY

複製檔案時如何覆寫被設為唯讀的檔案?

XCOPY 有個 /R 參數,例如:xcopy d:\src\blah\a.js d:\dst\blah\a.js /y /r。(記得還要加上/y,省略確認直接覆寫。

如何找出某個時點之後更新的檔案?

上線程式時若想挑出有更動的檔案,ROBOCOPY 有個好用參數 /xo /maxage,可以搜尋整個目錄挑出指定時間後才更新的檔案,並依原資料夾結構擺放,用它直接對目的資料夾進行覆寫,一個動作完成更新。

例如以下指令可挑出2016/10/01之後有異動的檔案:robocopy d:\WWW\Oasis d:\Set9527\Oasis /MAXAGE:20161001 /XO /S

接下來執行 xcopy d:\Set9527\Oasis \\192.168.1.1\d$\WWW\Oasis /s /y就可用整個資料夾覆寫目的資料夾,但只更新有異動的檔案。

如何將 DIR 結果轉為檔案清單?

部署指令產生器使用檔案清單跟主機 IP 清單展開成 COPY 指令,要將 DIR 結果轉成文字清單,可使用 PowerShell Get-ChildItem取得檔案物件陣列,從物件FullName屬性取得完整路徑再去除父資料夾部分轉成相對路徑。例如:Get-ChildItem -Path E:\Set9527\Oasis -Recurse –File | % {$_.FullName.Replace("E:\Set9527", "") }

實際應用時,有時 Get-ChildItem 的結果還需加上篩選條件或一些花式變化,透過 Pipeline 串接可以輕易達成。參考

如果想將執行結果複製到剪貼簿,不要再傻傻滑滑鼠選範圍或輸出到檔案再取回,後方用 Pipeline 串接 clip.exe 工具,一次搞定。

[2016-10-16更新] 感謝安德魯補充,dir /b /s 也可快速產生檔名清單,另外 for 也能產生相同結果且功能更強大。

複製檔案時如何自動建立資料夾?

遇到複製整個資料夾(或以萬用字元指定多檔)而目的路徑不存在時,XCOPY 會詢問目標路徑為檔案還是資料夾:

要避免 XCOPY 詢問,可加上 /i 參數,可指示 XCOPY 自動建立資料夾並複製檔案。 若複製來源為單一檔案便不適用 /i 參數,一定跳出詢問:

最簡單的解法是在目的路徑後方加上一個"\"搞定。

NancyFx-打造小型 WebAPI 與 Microservice 的輕巧利器

$
0
0

在做非網站系統整合時,我很愛用一招:寫個 Process 提供 WebAPI 介面給其他系統呼叫,不管你用什麼烏語言鬼平台,怎麼可能找不到 HttpCllient 元件或程式庫?都民國幾年了敢跟我說你不會寫Call 網頁的程式?你瞧瞧,多理直氣壯,乘著主流趨勢我們站上制高點,高舉 Web API 大旗, 天下無敵,哇哈哈哈~這招很棒吧?

不直接在 IIS 跑 ASP.NET,多半因為程式有執行身分權限或特殊限制(例如:整合Word、Excel等重量級互動式應用程式),通常我會寫成 Console Application 或 Windows Service,因此需要有 Self-Host 模組提供 TCP 連線、路由、Request/Response 處理管線等基礎建設。過去我研究過一些解決方案,例如:

從頭自已寫,純為好玩體驗一下還OK,要搞到順手好用的境界只怕要血流成河,且有重覆造輪子的重嫌。我在實務情境遇到許多小型 WebAPI,往往只有一到兩個 API,搬出功能強大的全套 ASP.NET Web API 跑 Self-Host 總給我有殺雞用牛刀的感覺,不符合 KISS (Keep It Simple, Stupid)中心思想。最近試玩過 NancyFx,驚為天人,簡直就是迷你 WebAPI 界的 Dapper!想必日後會在很多地方派上用場,特整理範例供日後參考。

訂個超簡單的題目:一個每次產生新 GUID 的 WebAPI。看看用 NancyFx 實做需要多少步驟、幾行程式碼。

首先我們開一個 Console Application 專案,用 NuGet 安裝 Nancy.Hosting.Self 套件(Nancy 也會一併安裝好 ):

宣告一個繼承 NancyModule 的 GuidGeneratorModule,建構式中為 Get["/"] 定義 Lambda 函式傳回 Guid.NewGuid().ToString()。這様 WebAPI 接 Request 的部分就寫完了,接著處理 Self-Hosting 的部分,在 Main() 裡建立一個 NancyHost,指定繫結的 URL,呼叫 Start() 就可以開門做生意了。提醒:Windows 7+需以管理者權限執行(較不建議)或使用 netsh http add urlacl url=http://+:32767/ user=machine\username 開放權限,參考

using Nancy;
using Nancy.Hosting.Self;
using System;
 
namespace NancyFxDemo
{
class Program
    {
staticvoid Main(string[] args)
        {
using (var host = new NancyHost(
new Uri("http://localhost:9527")))
            {
                host.Start();
                Console.WriteLine("Press any key to stop...");
                Console.Read();
                host.Stop();
            }
        }
    }
 
publicclass GuidGeneratorModule : NancyModule
    {
public GuidGeneratorModule()
        {
            Get["/"] = (p) =>
            {
return Guid.NewGuid().ToString();
            };
        }
    }
 
}

薑薑薑薑~ 一不小心我們就把 WebAPI 寫完了!如此短小精悍,是我的菜沒錯 :P

最後補充一些常見應用:

1.由路由取得參數

//由URL路由取得參數
            Get["route/{blah}"] = (p) =>
            {
return"param=" + p.blah.ToString();
            };

在路由使用{blah}標示參數,p.blah即為參數內容(或寫成 p["blah"] 也通)

2.定義 Get 及 Post,由 Form 及 QueryString 取得參數

//由QueryString或Form讀取參數
            Get["concat"] = Post["concat"] = (p) =>
            {
//Query, Form是dynamic,取屬性時可寫成Query.A或Query["A"]
//Query.Blah傳回型別為DynamicDictionaryValue, 有HasValue及Value
returnstring.Format("{0} {1}",
                    Request.Query.A.Value ?? Request.Form.A.Value ?? string.Empty, 
                    Request.Query["B"].Value ?? Request.Form["B"].Value ?? string.Empty);
            };

Nancy 的 Request 也有 Query 及 Form,不過不像 ASP.NET 可以用 Rquest["blah"] 通吃 QueryString、Form 及 Cookie,上面程式我示範用 ?? 運算子(學名為 Null 聯合運算子,Null-coalescing Operator)通吃 Query 及 Form。並同場加映如何讓 GET 及 POST 共用方法,

執行結果如下:

GET

POST

摁~ Pen Pineapple Apple Pen…

3.Model Binding

如果你覺得用 Request.Form/Query 取值太 Low,用 Model 才是王道,這也難不倒 Nancy。加上 using Nancy.ModelBinding 後,呼叫 this.Bind<T> 就可將參數 Bind 到預設定義好的 Model 型別物件上。

class ConcatParams
        {
publicstring A { get; set; }
publicstring B { get; set; }
        }
 
public GuidGeneratorModule()
        {
            Get["concatByModel"] = Post["concatByModel"] = (p) =>
            {
                var param = this.Bind<ConcatParams>();
returnstring.Format("{0} {1}", param.A, param.B);
            };
        }

4.POST JSON 及回傳 JSON

以下展示怎麼接收 POST 傳入的 JSON 內容以及傳回 JSON:

            Post["concatByJson"] = (p) =>
            {
                var param = this.Bind<ConcateParams>();
return Response.AsJson(new
                {
                    Resullt = string.Format("{0} {1}", param.A, param.B)
                });
            };

神奇的 Bind() 除了能從 Query 及 Form 對應屬性,還可直接將 POST 傳入的 JSON 字串反序列為物件,而 Response.AsJson() 則支援將物件序列化成 JSON 字串,沒花什麼力氣就輕鬆搞定。

除了以上的簡單應用,Nancy 也支援許多網站標準功能(例如:身分驗證、BeforeRequest/OnError事件、客製化錯誤頁面…),甚至能用 Razor 寫 View,要做出完整度不輸 IIS+ASP.NET MVC 的網站並非難事。但依我的策略,複雜的網站功能仍會選擇用 ASP.NET MVC 打正規戰,至於在 Console Application/WPF/Windows Service 加入 WebAPI 功能這類要近身肉博的場合,就仰賴 NancyFx 這把鋒利的輕巧短兵刃,用兩顆 DLL 加幾行程式秒殺需求。


硬碟 storahci 129 事件經驗一則

$
0
0

電腦怪怪的,開機只操作了幾分鐘,某些涉及磁碟寫入的程式會卡住無回應。重開機後暫時恢復,但幾分鐘後又發生同樣問題。

事件檢視器看到大量 storahci 警告事件,訊息為「重設為裝置 \Device\RaidPort0 的指令已發出」(Reset to device, \Device\RaidPort0, was issued):

爬文相關討論不少,多半指向 SATA 控制器驅動程式問題,最常見的解法是將電源選項的 PCI Express/連結狀態電源管理 設為關閉,另外還有人建議修改 Registry 後調整「AHCI Link Power Management – HIPM/DIPM」「AHCI Link Power Management – Adaptive」。

在我的案例這些修改都無效,但上回同一顆硬碟出現過一次抓不到的狀況,最後換了個 SATA 插槽才解決,後來聽 Aska說 SATA 排線可能導致硬碟問題,推測可能又是 SATA 排線作亂。摸摸鼻子拆了機殼,換條 SATA 排線也換個插孔,重開機後一切正常。(重新爬文,我找到因為 SATA 排線導致 storahci 129 的同伴:In my case the reason was a weak sata cable)

歷經兩次經驗學到的心得:

當 SATA 硬碟問題不斷,請把排線也列為嫌犯

程式範例-使用 C# 查詢 CPU 與記憶體使用狀況

$
0
0

有個小需求想透過程式取得 CPU 與記憶體使用率,爬文發現用 C# 寫簡單到不行:建一個 PerformanceCounter 物件,指定分類、計數器名稱、執行個體,接著用 NextValue() 取值,輕鬆搞定。

using System;
using System.Diagnostics;
using System.Threading;
 
namespace JetEngine
{
class Program
    {
static PerformanceCounter cpu = new PerformanceCounter(
"Processor", "% Processor Time", "_Total");
static PerformanceCounter memory = new PerformanceCounter(
"Memory", "% Committed Bytes in Use");
 
staticvoid Main(string[] args)
        {
while (true)
            {
                Console.WriteLine("CPU: {0:n1}%", cpu.NextValue());
                Console.WriteLine("Memory: {0:n0}%", memory.NextValue());
                Thread.Sleep(1000);
            }
        }
    }
}

如果不知道分類、計數器及執行個體名稱,可以參考效能監視器的新増計數器介面:

實測驗證,程式與效能監視器抓到的數據一致。

使用 PerformanceCounter 類別需要讀取特定 Registry 的權限,一般登入帳號都可使用,但如果想搬進ASP.NET會卡在權限問題,得到以下錯誤:Access to the registry key 'Global' is denied.

解決方案有二,第一是讓 ASP.NET 用一般使用者等級的身分執行,但會衍生網站程式權限變大的風險,較不建議。第二種做法是以一般使用者或系統帳號另跑獨立 Process 或 Windows Service 讀取計數器再以 WebAPI 方式供本機查詢,實作可考慮使用前幾天介紹的輕巧兵器-NancyFx

使用 WMI 匯出 IIS 6 網站設定

$
0
0

IIS 6 網站要移轉到 Windows 2012 R2 主機,轉換前打算匯出網站完整設定檢視一次,排除過期或廢棄的網站應用程式,另外還想嘗試依據現有設定產生設定網站應用程式與虛擬目錄的自動化 Script,第一步要取得現有網站設定資料。

使用 PowerShell Get-WmiObject -class IISWebVirtualDirSetting -namespace "root/MicrosoftIISv2" -computername "IIS6主機名稱" -authentication 6(參考:Get-WmiObject 參數說明)可列出虛擬目錄及網站應用程式相關設定,要寫程式對匯出資料進行花式應用,轉成 JSON 是首選,而 PowerShell 有個 ConvertTo-Json Cmdlet 可以幫忙。

看似完美對吧?But!天殺的 But 又出現惹~當屬性為物件陣列(如下圖的 HttpCustomHeaders 及 HttpErrors)時,ConvertTo-Json 會轉成"System.Management.ManagementBaseObject"組成的字串陣列,看不出其中內容。

ConvertTo-Json 有個 –Depth 參數指定序列化深度,實測提高到 5 才能看到 HttpErrors 內容:

不過 Depth 提高後,資料結構裡供系統用的 Properties、SystemProperties、Qualifiers、ClassPath 也會被展開,資料變得雜亂仰賴後續過濾簡化。心想橫豎都要寫程式,不如擷取邏輯也自己寫吧!

使用 System.Management.ManagementObjectSearcher 可取回與 Get-WmiObjet 相同的查詢結果,配合 Json.NET LINQ 好用的 JObject、JArray、JValue 動態組裝產生 JSON:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
 
class IISSettingDumper
{
publicstaticvoid Dump()
    {
        ConnectionOptions options = new ConnectionOptions();
        options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
        options.Authentication = AuthenticationLevel.PacketPrivacy;
        ManagementScope scope = new ManagementScope(@"\\.\root\MicrosoftIISv2", options);
        scope.Connect();
        ObjectQuery query = new ObjectQuery("SELECT * FROM IISWebVirtualDirSetting");
        ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
        ManagementObjectCollection res = searcher.Get();
        JArray pool = new JArray();
foreach (var m in res)
        {
            JObject jo = new JObject();
foreach (var p in m.Properties)
            {
 
if (p.Value is ManagementBaseObject[])
                {
                    var jsonArray = new JArray();
                    var array = (ManagementBaseObject[])p.Value;
foreach (var q in array)
                    {
                        var child = new JObject();
foreach (var childProp in q.Properties)
                            child.Add(childProp.Name, new JValue(childProp.Value));
                        jsonArray.Add(child);
                    }
                    jo.Add(p.Name, jsonArray);
                }
elseif (p.Value != null&& p.Value.GetType().IsArray)
                {
                    var jsonArray = new JArray(p.Value);
                    jo.Add(p.Name, jsonArray);
                }
else
                {
                    jo.Add(p.Name, new JValue(p.Value));
                }
            }
            pool.Add(jo);
        }
        System.IO.File.WriteAllText("c:\\temp\\VirDirSetting.json", pool.ToString());
        Console.WriteLine(pool.ToString());
    }
}

輸出結果如下,是不是賞心悅目多了?

補充說明:以上做法適用 IIS 6 時代的 WMI 資料規則,如要在 IIS 7+使用,需安裝「IIS 6 Management Compatibility」

【延伸閱讀】

程式範例-IIS WMI 網站設定資料解析

$
0
0

前一篇文章成功將 IIS 6 網站設定匯出成 JSON,不過原始資料太過龐雜,每筆虛擬目錄屬性超過140條,讓人眼花瞭亂。事實上因 IIS 設定具有繼承性,父目錄與子目錄的屬性絕大部分是相同的,針對某個虛擬目錄做的額外設定才是觀注焦點。例如:掛在可匿名存取 P 目錄下的 C 目錄被設成整合式驗證,描述 C 目錄設定時時只要列出 AuthNTLM = true 就好,與 P 目錄相同的設定可以全部省略。為實現這點,我想到一個簡單有效的演算法:拿子目錄的所有屬性跟父目錄比較,只顯示有差異部分。

WMI 匯出的 IISWebVirtualDirSetting 資料為 JSON 格式,轉為強型別處理起來才順手。講到這個不得不推 Visual Studio 強大的 Paste JSON As Classes 功能:選取 IISWebVirtualDirSetting 匯出的全部 JSON 內容,點選「Edit / Paste Special / Paste JSON As Classes」:

見證奇蹟的時刻… Visual Studio 自動依 JSON 資料產生對應型別,連 HttpCustomHeader 這種物件陣列屬性,陣列元素物件也被轉成強型別!

將 public class Class1 更名並轉為部分宣告 public partial class VirDirSetting,我另外再補上額外屬性及方法方便後續處理:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
 
namespace WmiDataAnalyzer
{
publicpartialclass VirDirSetting
    {
//所屬子網站
public List<VirDirSetting> Children = new List<VirDirSetting>();
//父網站名稱
publicstring ParentName;
//父網站物件
public VirDirSetting Parent;
//所有可能的父網站名稱
publicstring[] AncestorNames
        {
            get
            {
                var p = this.Name.Split('/');
return Enumerable.Range(1, p.Length - 1)
                    .Select(i => string.Join("/", p.Take(i).ToArray())).ToArray();
            }
        }
//層級深度
publicint Level
        {
            get { returnthis.Name.Split('/').Length - 2; }
        }
//動態比對屬性用屬性集合
static Dictionary<string, PropertyInfo> Properties
        {
            get
            {
returntypeof(VirDirSetting)
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
                    .Where(o => !"AncestorNames,Level,ScriptMaps".Split(',').Contains(o.Name))
                    .ToDictionary(o => o.Name, o => o);
 
            }
        }
//與Parent比較,找出有差異的設定
public Dictionary<string, string> GetExplicitSettings()
        {
            var diff = new Dictionary<string, string>();
if (this.Parent == null)
            {
                diff.Add("Remark", "**Root**");
            }
else
            {
foreach (var p in VirDirSetting.Properties.Values)
                {
                    var pv = JsonConvert.SerializeObject(p.GetValue(this.Parent));
                    var cv = JsonConvert.SerializeObject(p.GetValue(this));
if (pv.CompareTo(cv) != 0)
                        diff.Add(p.Name, cv);
                }
            }
return diff;
        }
    }
}

寫一小段程式將 JSON 反序列化為物件集合,並找出彼此從屬關係,再印出網站結構及差異設定:

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
namespace WmiDataAnalyzer
{
class Program
    {
staticvoid Main(string[] args)
        {
            Dictionary<string, VirDirSetting> data = 
                JsonConvert.DeserializeObject<VirDirSetting[]>(
                File.ReadAllText("Sample.json")).ToDictionary(o => o.Name, o => o);
            var roots = new List<VirDirSetting>();
//建立從屬關係
foreach (var vds in data.Values)
            {
foreach (var anc in vds.AncestorNames)
                {
if (data.ContainsKey(anc))
                    {
                        vds.ParentName = anc;
                        vds.Parent = data[anc];
                        vds.Parent.Children.Add(vds);
break;
                    }
                }
if (string.IsNullOrEmpty(vds.ParentName))
                {
                    roots.Add(vds);
                }
            }
//印出從屬關係
foreach (var root in roots)
            {
                DumpStructure(root);
            }
            Console.Read();
        }
 
staticvoid DumpStructure(VirDirSetting vds)
        {
            Console.WriteLine("{0}[{1}]", 
newstring(' ', vds.Level * 4), vds.Name);
foreach (var kv in vds.GetExplicitSettings())
                Console.WriteLine("{2} *{0}:{1}", kv.Key, kv.Value,
newstring(' ', vds.Level * 4 ));
foreach (var child in vds.Children)
            {
                DumpStructure(child);
            }
        }
    }
}

我弄了一個測試網站結構如下,建立多個虛擬目錄並故意加入設定差異,例如:驗證方式、IP限制、不同ApplicationPool… 等等。

產生結果如下:

有少部分設定細節未包含在 IISWebVirtualDirSetting 中,要藉由其他 WMI 資料才能拼湊出完整設定。例如:虛擬目錄是否為網站應用程式可由  IISWebVirtualDir "AppRoot": null 與否判斷;IP 限制則要查詢 IISIpSecuritySetting,限定 IP 時會得到如下結果:

  {
"Caption": null,
"Description": null,
"DomainDeny": [],
"DomainGrant": [],
"GrantByDefault": false,
"IPDeny": [],
"IPGrant": [
"127.0.0.1, 255.255.255.255",
"192.168.1.78, 255.255.255.255"
    ],
"Name": "W3SVC/1/ROOT/ParentWebApp/UNCVirtualDir",
"SettingID": null
  },

由這些 WMI 資料,我們就能描繪現有網站的結構,進行檢視調整,並做為撰寫設定 IIS 網站自動腳本的依據。

筆記-使用 PowerShell 設定 IIS 網站

$
0
0

前篇文章由 WMI 資料解析出現有網站設定,經過篩選及調整,下一步計劃轉換成設定 IIS 網站的自動化腳本,以省去在 WebFarm 伺服器一台一台點選操作的煩人手工,也避免人為操作發生疏漏。

PowerShell 已是管理 Windows 的奧林匹克官方指定語言, 可支援 IIS 的大小管理操作,擺著不用是跟自己過不去,雖然對 PowerShell 一知半解,還是得硬著頭皮學會,以下整理我還原 IIS 網站設定用到的指令:(各方達人如知更便捷的做法,請不吝指教)

1. 建立 Application Pool

開始前請記得 Import-Module WebAdministration 載入 IIS 管理模組。New-Item 指定 Path 就可建立 Application Pool,建立後用 Get-ItemProperty 可以看到該集區的設定。

Get-ItemProperty 取回的屬性資料可一一對應到 IIS 管理介面的集區進階設定,例如:queueLength 是 Queue Length、enable32BitAppOnWin64 是 Enable 32-Bit Applications:

如果要修改設定,可使用 Set-ItemProperty Cmdlet,例如以下指令啟用 Enable 32-Bit Applications:
Set-ItemProperty -Path IIS:\AppPools\Blah -Name enable32BitAppOnWin64 -Value  true

有些設定在第二層,例如:processModel.identityType,寫成 Set-ItemProperty -Path IIS:\AppPools\Blah -Name "processModel.identityType" -value 2 (2 = Network Service)

AppPool 各設定屬性及說明可參考 MSDN 文件:applicationPool processModel

註:如要查詢現有 processModel 設定,可用 Get-ItemProperty -Path IIS:\AppPools\Blah -Name prcessModel

2. 設定網站應用程式及虛擬目錄

建立網站應用程式
New-Item 'IIS:\Sites\Default Web Site\Blah' -physicalPath C:\WWW\Blah -type Application -applicaionPool Blah

建立虛擬目錄
New-Item 'IIS:\Sites\Default Web Site\BlahDir' -physicalPath C:\WWW\BlahDir -type VirtualDirectory

3. 設定 UNC 外部虛擬目錄並指定帳號密碼

New-Item 'IIS:\Sites\Default Web Site\UNCTest' -physicalPath \\RemoteServer\ShareName -type VirtualDirectory
Set-ItemProperty -Path ''IIS:\Sites\Default Web Site\UNCTest' -Name userName -Value logonName
Set-ItemProperty -Path ''IIS:\Sites\Default Web Site\UNCTest' -Name password -Value XXXX

4. 設定匿名存取或整合式驗證

以下範例關閉匿名存取並啟用 Windows 整合式驗證

Set-WebConfigurationProperty -Filter /system.webServer/security/authentication/anonymousAuthentication -Name enabled -Value false -PSPath IIS:\ -location 'Default Web Site/Blah'
Set-WebConfigurationProperty -filter /system.webServer/security/authentication/windowsAuthentication -Name enabled -Value true -PSPath IIS:\ -Location 'Default Web Site/Blah'

屬性參考:authentication

5. 限定存取 IP

Set-WebConfigurationProperty -Filter /system.webserver/security/ipsecurity -Name allowUnlisted -Value false -PSPath IIS:\ -Location 'Default Web Site/Blah'
Add-WebConfiguration /system.webServer/security/ipSecurity -Value @{ipAddress="192.168.1.1";allowed="true"} -PSPath IIS:\ -Location 'Default Web Site/Blah'

屬性參考:ipSecurity

 

【參考資料】

Viewing all 428 articles
Browse latest View live