Quantcast
Channel: 黑暗執行緒
Viewing all 2311 articles
Browse latest View live

讀書筆記-行為的藝術

$
0
0

前情提要第二本「行為的藝術」讀書筆記來了。相較於「思考的藝術」聚焦於邏輯,這本書較偏心理學層次,原本不太引起我的興趣,沒想到讀來驚喜連連,常有「對耶,好像真的是這様」「慘,完全被說中」的感嘆。人類有許多古怪的行為習慣(有些說是劣根性也不為過),不少來自遠古以來的物種演化,由於不具某些「劣根性」的人活不久,留存的基因仍繼續主宰現代人類的行為,非常有趣的邏輯。

  • 理由的正當性

    我只有5頁,可以讓我先印嗎?因為我趕時間
    我只有5頁,可以讓我先印嗎?因為我想影印
    令人驚訝地,以上兩種說法都能成功插隊。說出理由就得到諒解與協助,理由是否有意義經常不是最重要的。說出「因為BlahBlah」即使理由很爛,也會有潤滑劑效果。
  • 決策疲勞

    要跟老闆報告一件鳥事,8AM,11:30,18:00三個時段哪個最好?
    密集做決策會消耗意志力,一連串比較或評估將導致精疲力竭 —> 決策疲勞。IKEA在賣場中間設餐廳,讓客戶休息吃飽再戰
    連續審理多起案件,法官的大膽判決比率從65%降到0(血糖不夠用),休庭後再審則又回到65%
  • 傳染性偏見

    人無法忽略某些已無實際連結的人事物,例如:裝過尿的杯子徹底洗淨,就科學衡量與全新杯子無異,仍令人覺得噁心。
  • 無意義的平均值

    非常態分配下的平均值,國民平均財富,夫妻平均一顆睪丸
    M型分佈,長尾分佈,冪次法則
  • 誘因排擠

    在當地設置核廢料儲存場民調,在承諾政府提供補償金後贊成率下降–>人們非出於金錢動機的行為因報酬瓦解(感覺被羞辱) 幼稚園收取延時托兒費用後,父母遲接比例反而上升—>人際關係轉為金錢關係,降低虧欠感。
  • 廢話潮流

    請參考相聲段子「語言的藝術」。例:名嘴、明星、運動員賽後訪問、經濟學者分析預測、營運不善的執行長… 說八道可以掩飾無知。馬克吐溫:如果無話可說,就閉上嘴巴
  • 威爾 羅傑斯現象

    分期遷移:管理A,B公司,A公司出色,B公司很慘,將A公司表現低於平均的產品移到B公司,A公司平均表現上升,B公司業績上揚,但實際沒有多賺錢。
    腫瘤診斷技術進步,更早發現初期患者(過去無法被發現,視為健康未歸類為患者),即使無藥可醫,病患平均存活年限拉長。
  • 資訊偏移

    更多訊息自然而然會導致更好的決定是一種迷思。網購爬文,可選擇的項目愈多,愈難做決定。
    試著與最少的資料共存,不需要知道的資訊即使知道了也沒有任何意義。
  • 群聚錯覺

    烤土司出現聖母臉孔、烤玉米餅出現耶穌形象、火星岩石浮現人臉、機車濺起泥漿出現骷髏頭。
    大腦會尋求模式及規則,遇到分散的信號(無規則的雜訊)找不出模式規則,大腦就會自己發明。
    從過去一段時間的股票指數與黃金價格整理出鐵律,但很快就會破功,不然人人都是千萬富翁。坊間很多對樂透數字關聯的公式推敲亦是一例。
    純屬巧合的情境比你想像得多,不要過度揣測。
  • 勞力辯證

    特種部隊結訓授階插入胸肌的胸針被裱框珍藏 、缺錢出售辛苦修整的哈雷,遇到市價兩倍的出價仍不肯賣… 勞力辯證的實例:當人對一件事投入大量精力後會誇大其價值,屬於一種認知失調。
    兄弟會、幫派噁心暴力的入會儀式、極嚴苛的入學考試也是團體利用勞力辯證箝制成員的手法。
    溫和形式的勞力辯證-IKEA效應,DIY家俱比名牌家俱更有價值,自己織的圍巾破舊過時也捨不得丟。
    需要依程序操作打一個雞蛋的半成品蛋糕粉比完全調配好的蛋糕粉銷量更好-適度的勞動及挑戰性。
    陷阱:當你對事物長期投入心力後,將無法客觀地評估它。
  • 小數法則

    統計失竊率(失竊金額佔總銷售額的比例)發現鄉村分店數字高,所以要針對鄉村全面加強防盗設備?事實上鄉村分店遭竊案件少,但因分店銷售額低顯得突出,而報表失竊率最低的也是鄉村分店。
    新創公司擁有較聰明的員工?員工少,平均智商變化幅度遠大於大型企業。發現統計上有突出表現的案例時,請先排除受小數法則影響的可能性。
  • 期待

    Google發佈2005Q4財報,營業額成長97%,淨利成長82%,股價卻在數秒內重挫16%觸發熔斷,恢復交易後再跌15%,只因其低於華爾街的預測。事前有過度期待,殘酷懲罰隨之而至。
    期待導致不合理的刺激,但也能激發正向動力。1965心理實驗,隨機挑選20%學生,謊稱其大器晚成具有高度潛力,結果被挑選學生的智商增加幅度大於平均值-羅森塔爾效應/比馬龍效能。(老師對這些學生有額外的期待而加強關注)
    醫學實驗接受安慰劑的患者有1/3產生療效—>改變大腦生物化學,進而擴大到全身。(對阿茲海默患者無效,因其腦部負責期待的區域受損)
    人類不可能沒有期待,請善用之,對於自己和所愛的人更多期待,增加他們的動力;對於無法掌控的事物(如股市)請降低對它的期待。
  • 簡單的邏輯

    1) 球拍與桌球合計1010元,球拍比球貴1000元,各為多少錢? 2) 5台機器5分鐘可完成5件毛衣,100台機器完成100件毛衣要幾分鐘? 3) 池塘裡繁殖快速的睡蓮每天覆蓋多一倍面積,完全覆滿池塘要48天,覆蓋半個池塘要幾天?
    答案1005 vs 5元、5分鐘、47天。以上稱為認知反射測試(Cognitive Reflection Test, CRT),測試成績:MIT學生>普林斯頓>密西根大學。CRT值低的人傾向依直覺行事,一鳥在手勝過二鳥在林,CRT高的人較擅於以意志力駕馭直覺,採理性斟酌。CRT較高的美國人多無神論者,CRT較低則傾向信仰上帝(愈容易依直覺行事者,愈無法以理智挑戰宗教觀念)
    如何提高CRT值:對簡單的邏輯問題保持懷疑態度。
  • 福勒效應

    心理學家福勒借用星座運勢解說常用手法,將對一般人的性格描述(說穿了是廢話,誰都嘛是這樣)偽稱是為受試者量身訂製,有86%受試者相信它是在描述自己且認為準確。
    原因:其中描述適用大部分的人、帶有恭維奉承性質讓人不想否認、正面特點效應(不涉及任何人格缺陷)、確認偏誤(接受符合自己的自我描述,不自覺排除不符部分,留下和諧形象)
    如何讓星座專家的呼嚨破功:在小卡片寫下對20名受測者的性格預測,請受測者從中挑出給自己的卡片。
  • 志工的愚蠢

    名攝影師該花六小時參加鳥保協會做鳥屋,還是花一小時拍照賺錢請木匠做鳥屋並將多出的錢捐給鳥類保護協會?(創造木匠工作機會又投入更多保護資金)
    做志工並不是無私奉獻,人往往能從中獲得一絲滿足,是一種期待回饋的付出。除非自己是真正的名人,參與活動能增加曝光並帶來關注,否則捐錢比投身志工能產生更大貢獻。
  • 情意的捷徑

    該不該反對基改?理性分析:列出所有優缺點,以其利益乘上發生機率得到正負期望值,依加總結果正負決定該贊成或反對。
    實際上不可行:我們沒有足夠想像力列出所有得失,難以提供精準的計算數值,更重要的,想太久會被野獸吃掉,人類演化成快速決策者,使用很精簡的思考方式-捷思法(Heuristics)。
    情意的捷徑,飛機噪音產生負面情緒,豪華帶來正面感覺,促使人類用情緒考量風險與利益,而非獨立理性評估。
    重機車擁有者看到重機危險高的報導,心中用「它可以帶來更多自由感」去中和。
    看似不起眼的東西會影響情緒:以0.01秒時間投影笑臉、憤怒臉及中性圖像,詢問受測者是否喜歡中國字,看到笑臉的受測者絕大多數表示喜歡。早晨太陽普照,股市上漲機率高。
    面對複雜情境:以「我對此有何看法」取代「我覺得…」
  • 內省錯覺

    維他命廠老闆對吃維他命有益健康深信不移,他人懷疑其主張源於自身利益考量。換個立場,每個人卻很少懷疑自己某項深信的觀念,因為評估自已信念是否可信是內在觀察,但內在觀察真的坦率無偏見嗎?
    實驗:快速出示兩張人像照片請受測者擇一,接著用很相似的照片調包請受測者看著照片說明選擇理由,大部分受測者仍可解釋得頭頭是道。內省不可靠,我們常會虛構某些事情以求合理。
    內省錯覺又稱為「選擇性的盲目」,當別人不認同我們的信念時,有三種反應:1.無知的看法:別人是因缺乏某些必要資訊,否則一定會支持我,政治人物常認定只要宣傳得宜,就能說服別人 2.白痴假設:不同意的人已知必要資訊,但因人笨所以無法得到正確結論 3.惡意假設:對方已可理解但選擇惡意對抗。例:不信我教者必為魔鬼
    愈是相信某樣東西,愈應該以批判的角度看待它。聰明人不需要信條,應該自己當自己的異教徒。相信自己太久太深,覺醒時的真相將會很殘酷。
  • 無法關上機會之門

    同時交往三個女人,總無法決定該娶誰,因為一旦決定就要放棄其他兩個。在大學雙修或三修,為未來創造更多就業選擇,是否真的有利?
    大部分人會盡力保留選擇的權利,即使理性分析保留選項無利可圖且需付出成本。
    值得敬佩的特例:項羽破斧沈舟,西班牙科爾特斯在登陸後把船弄沈。
    每個選擇都要付出成本(精力、思維、生活空間、金錢),即使成本很不明顯,也會減損成功的機會,保留太多選項有可能導致一場空。上策:忽略某些可能性,以做出明智決定。
  • 嗜新狂

    塑膠發明之初,預測50年後人類會生活在塑膠房屋裡;登月探索期,預測50年會出現火星校外教學。我們系統性地高估新發明的作用,瘋狂熱衷於新事物-嗜新狂。最新熱潮退色的事物,比我們想像得還要快。那些存在幾百到幾千年的老東西(皮鞋、椅子、叉子、書架),其中必有原因,不會被輕易取代。
    早期採用者:沒有最新一代iPhone就活不下去的人,某種程度上是患了某種病的非理性人類,新發明的實質好處一點都不重要,重點是要新,要潮。
  • 睡眠效應

    二戰期間愛國宣傳影片在播放時並未立即激起士兵對戰爭的熱情(知是官方宣傳,對其不齒),但九個月後,看過影片的士兵對戰爭的同情明顯較高。科學家百思不得其解,以為論據說服力應隨時間遞減。
    宣傳剛好相反,對於論點來源遺忘的速度比論點本身快,忘了是官方宣傳(來源),只記得戰爭是必要的(論點),因此「從不可信來源得來的訊息,時間一久會變得可信」,稱之睡眠效應。
    如何對抗?不要接受不請自來的訊息、遠離不良來源、盡量記清楚訊息來源。
  • 選擇盲從性

    MBA廣告給人付出昂貴學費獲得MBA會有巨大收益的分析說服它是值得的投資。盲點:1.念MBA者收入較高非因MBA課程,而是游泳選手身材錯覺 2.除了學費,還有因唸書減損的收入 3.分析引用未來30年收入折現,變異性極高 4.有其他成本更低的投資,效益更高<-選擇盲從性
    理財專員:買債券比定存利息高,所以值得買債券->忽略其他更佳的投資選項
    政客:建體育場 vs 草皮 –> 還存在學校、醫院等其他選項
    腫瘤手術切除50%致死率,不動它五年內有50%死亡率 -> 也許有另一種風險更低的手術選項,未來幾年出現新療法…
    面臨A與現況,直覺就在現況與A做決擇是種錯誤,應考量比較A、B、C、D、E… 避免落入陷阱。
  • 社會比較差異

    不想幫助別人,以免對方成為自己的競爭對手,即便其可能性極低。例:科學家特別嚴審年輕學者的突破性文章、諾貝爾得主阻止年輕有為同科學者進入同一大學任教。
    新創公司:A咖延攬A+咖同事加入公司造就成功。B咖找來C咖、C咖聘請D咖… 最後公司只剩Z咖。聘用能力更強的人,否則公司很快就滿是輸家->井蛙現象,能力差的人無法認知自己的無能。
    支持比你優秀的人,短期或使自己陷入危險,長期而言終將獲益,那些人遲早會超越你,在此之前請與之和睦相處並從中學習。
  • 首因效能和時近效應

    張三很聰明、勤奮、衝動、頑固、好嫉妒;李四好嫉妒、頑固、衝動、勤奮、聰明。你較願意跟誰一起被困在電梯裡?(註:抗議!此為「選擇盲從性」陷阱,我可以選郭雪芙嗎?)
    大腦評價第一個形容詞會比後續詞彙來得強 –> 首因效應 或 第一印象效應
    富麗堂皇的Lobby、律師穿著光高的名牌牛津鞋。對完美回答第一題的考卷特別有好感影響其他題目評分—>先改全部考卷的第一題,再改全部考卷的第二題。在討論議題時,第一個發表的意見容易左右結論,有意見要搶著講,若求公平主席應隨機詢問意見。
    「時近效應」則相反,大腦記不住太多資訊,後面得到的較容易記住。若項目多又要一段時間才做決定,時近效應明顯,例:演講常只記得結論。
    結論:處於中間位置,無法引人注意。面試避免偏見:每隔五分鐘評分一次再取平均。
  • 放血效應

    十九世紀前西方以四大體液說解釋疾病醫學,並以荒謬的放血療法治百病,而這種主張稱霸兩千年。我們不會因為一個理論是錯的而揚棄它,只會在有更好的選擇後才放棄它。
    人無法忍受「自己不知道某事」的感覺,慣於創造理論並選擇相信之,即便明知其存在不合理的部分。
  • 非我發明症群(Not Invented Here)

    這在軟體工程上也很常見,看別人的Code萬般不爽,永遠存在把舊系統翻掉重寫的衝動。
    人們喜歡並陶醉在自己的想法裡,有時需從遠處回頭審視自己的想法才能清醒。想想,過去十年裡,你有哪些想法是真正優秀的?
  • 黑天鵝效應

    幾個世紀以來歐洲人為天鵝都是白色的,直到1697年在澳洲第一次看到黑天鵝,推翻了公認的事實,造成震憾。
    黑天鵝效應指對生活造成重大影響的出人意料事件。美前國防部長倫斯斐的哲學主張:已知的事實、已知的未知、未知的未知,其中未知的未知一旦揭露,影響巨大。
    石器時代人類很少遇到改變人生的大事,網路時代一個轉彎收入就可能增加一萬倍(例:Google創辦人、哈利波特作者)。
    因應思維:假設看似乎不可能發生的事,其機率仍略高於零。
    留在正面黑天鵝效應區,能夠成為藝術家、發明家或擁有可複製產品的企業家。以出售時間為業(職員、牙醫、記者),不會出現黑天鵝效應。
    防止負面黑天鵝效應,保守儲蓄,成功時不要過於安逸,做最壞的打算。
  • 領域依賴性

    用醫學例子解釋「基本比率」比用商業活動舉例更能讓醫生秒懂其義。對投資者說明,用金融案例說明會比生物學例子更容易被理解。理解力很難從一個領域跨到另一個領域—>領域依賴性。以投資組合選擇理論得到諾貝爾獎的馬可維茲卻未在自己理財時活用該理論,學術知識未能跨到私人領域。行銷達人轉任CEO、3C超級業務轉任軟體銷售後失色無光。成功CEO家庭失敗、醫生抽菸者比例高於其他職業、全職執法人員家暴比例高、文學批評家寫的小說超乏味、婚姻治療師的婚姻狀況比其客戶還糟。在某領域有卓越表現的人,很難將其成功複製到其他領域。
  • 假共識效應

    我們常高估別人跟我們的一致性。實驗:徵求學生穿上庸俗廣告牌在校園招攬生意,願意的學生認為有62%的學生願意這麼做,不願意的學生認為有67%的學生會拒絕。政黨高估自己關注問題的緊迫性(覺得自己才是主流民意)、選民高估所屬政黨的支持率。產品開發部門對自己的產品充滿信心,覺得消費者肯定買單。
    假共識效應讓我們為與自己意見相左的人貼標籤,前例中穿廣告者認為不穿者缺乏幽默感,不穿廣告認為另一方是「白痴」「愛出風頭」。
    假共識效應不同於社會認同,後者來自於無意識的同儕壓力,前者源於演化,假共識讓人類更勇敢且具說服力,獲得更多資源,提高存活率。
    當你的觀點與眾人不同,不要把觀點不同的人當成傻瓜,不要懷疑別人,先質疑自己。
  • 歷史修正

    實驗:請三千人寫下對爭議性議題的看法,十年之後,請其回想十年前的答案並寫下現在的看法,二者幾乎一致,但與實際十年前填的答案相去甚遠。人類會不自覺調整過去的意見以配合現在的想法,藉此逃避自己錯誤的尷尬,認錯很困難,當我們意識到「自己錯了」,該為自己鼔鼔掌。
    有些記憶會深入人心,細節精準猶如照片-閃光燈記憶。但它也會犯錯,是修正過的結果。三年前後兩次要求寫下對挑戰者號太空梭爆炸的印象,只有7%前後一致,50%有2/3不同,25%完全錯誤。
    第一次見到初戀情人的那一幕多半能歷歷在目,但有可能超過一半的細節都是錯的,我們的記憶有缺陷,包含閃光燈記憶。有些錯誤可能致命,即使目擊者發誓能確認罪犯,仍需詳加查證。
  • 圈內圈外偏見

    為什麼會瘋狂地支持國家代表隊?團體認同。演化由來:被團體排擠後難以自力更生,死亡率高。
    隨機分配成小組,謊稱某小組因共同喜愛某種陌生畫風而編組,莫名提高該組成員對彼此的好感。(基於微不足道的基礎也能形成團體)
    高估圈外團體的同質性,科幻電影人類有不同文化,外星人卻如一個模子刻出來的。
    團體基於共同價值基礎,成員個別意見獲得不成比例的高度支持,形成失真-日常工作盲點。
    為隨機組合的團隊犠牲生命的思考錯誤-上戰場。哄騙手法:效忠祖國、同袍兄弟
    對陌生人存在偏見與反感是生物學事實,認同團體會讓人對事實產生扭曲,為他人而戰並非勇敢,而是愚蠢。
  • 無法忍受模糊

    兩個箱子,A箱放入紅黑球各50顆,B箱放入不知比例的紅黑球,摸到紅球得一千塊,大部人選A。
    艾斯伯格矛盾,人類傾向選擇已知,而非未知的機率,換句話說:人類無法忍受模糊。
    風險可以用機率評估,模糊不確定則無從預估。如果不希望過於倉促或判斷錯誤,必須容忍模糊性。
  • 預設值效應

    大多數人選擇預設值(招牌餐、手機預設設定)。政府如何在不限制自由不違憲的狀況下引導國民?在多個選擇方案外,增加一個標準選項。
    萬一沒有預設值,就用過往經驗。人們喜歡熟悉的事物,比起選擇、嘗試新事物,更愛維持現狀—>安於現狀偏誤。
    損失規避:損失造成的不悅是等值獲利帶來快樂的兩倍,重新談判各退一步也讓人覺得吃虧。
    我們強烈安於現狀,即便它讓我們處於不利位置。
  • 對懊悔的恐懼

    張三原想將賣A股票改買B股票,但後來沒做,B股上漲,等同損失10萬元。李四賣掉B股票改買A股票,B股上漲,等同損失10萬元。誰比後悔。92%的人覺得李四比較後悔。張三較符合一般人的行為,把錢放著不動,李四行為較異於常人,故感到更懊悔。
    最後一分鐘決定改搭失事班機的罹難旅客,格外令人同情。
    對懊悔的恐懼讓人不理性,阻止你丟掉不再需要的東西。(害怕有一天要用到時後悔你丟了它)
    最後機會式廣告「現場只剩最後一件,賣完了就沒有了」(你其實沒有很喜歡這件商品)
    最後機會讓我們陷入恐慌,對懊悔的恐懼導致不理性的決策。
  • 顯著性效果

    記者依據車禍現場發現大麻,未經查證就訂出新聞標題「大麻肇禍,駕駛喪命」-顯著性效果。顯著特性受到過度觀注,引誘人做出沒有根據的推論。例:對新任女性CEO的評論聚焦在其性別而非其能力、犯罪者的外藉移民身分被放大(未來在談及新移民時聯想到犯罪)。
    投資者對重大消息(CEO被解雇)的敏感度大於較不顯著卻重要的訊息(公司長期獲利表現)。顯著訊息過度影響我們的思維與判斷,讓我們輕忽隱藏性、緩慢發展的微小因素,我們應避免被奇特性蒙蔽。
  • 知識的另一面

    讀過上千本書沒開過刀的醫生 vs 只讀過一本書但做過千次手術的醫生。萊特兄弟沒讀過任何科學報告就做出飛機,飛機建造理論一直到30年後才出現。自動織布機、蒸汽機、汽車、燈泡全來自發明家而非理論家,我們高估知識分子、學者、理論家、作家、作者,低估實踐者與創造者。理念、產品及技術大多透過試誤成形,很少經由閱讀和思考。
    知識有兩種:一種可以用語言表達,另一種無法言傳。我們常高估語言可以表達的知識範圍。
    依賴語言知識的危機:學術理論與實務的差距、忽略沒寫下來的知識、不擅於自我表達者的意見被輕視
  • 私房錢效應

    撿到的錢被拿去買奢侈品。努力工作多賺十萬塊會存起來或投資,買彩券得到十萬則常拿來旅遊。賭場梭哈一萬元輸光卻覺得沒損失,因為那一萬元是之前嬴來的。
    相對賺來的錢,我們會輕率地使用嬴來、撿到、繼承的錢—>私房錢效應,人們較勇於將投機贏來的獲利花在高風險的地方。同理,樂透得主常常在幾年後變得潦倒。
    應用於行銷:賭博網站開戶送100美元、申請信用卡送100美元紅利、加入航空公司飛行俱樂部送數千哩里程…
    結論:當你嬴錢或獲得企業贈送某些東西時,請小心因不知節制,反而損失更多。
  • 拖延症

    拖延症指人類會拖延不愉快但重要的事(重要但不緊急的事):轉個彎去健身房、換個更划算的保險、寫張感謝卡。
    拖延是不理性的,因為計劃不會自動完成,麻煩永遠都在,大家心知肚明。但因付出與回報有時間上的差距,需要耗費大量心力才能跨越差距。
    心理實驗:學生解數學題時被餅乾香氣圍繞,一組被規定解完前不准吃餅乾,另一組則無限制。禁吃餅乾組多花了一倍時間才解完題目,因需花費精力自我控制,削弱了解題的意志力。
    防止自我放逐:專心做事時關掉網路,消除雜念。別人訂的時限比較有約束力,自己擬訂時程表最好分成小步驟各自訂出期限較易成功(注:蕃茄鐘工作法),把自訂的期限公告天下也是個好方法。
  • 嫉妒

    A.朋友平均收入都增加,只有你不變 B.朋友平均收入不變,只有你減少 C.朋友跟你的平均收入都減少。A最易讓人惱怒。
    俄羅斯故事:農民向神燈精靈許願,鄰居有牛我沒有,請讓鄰居的牛死掉
    財產、地位、健康、青春、技能、受歡迎、美麗都能引發嫉妒。我們特別嫉妒年齡、職業或生活方式與我們相仿的人。
    嫉妒感一旦出現很難消除,如何避開?停止與人比較、尋找競爭優勢圈(比上不足比下有餘,山中無老虎,猴子當大王)
    嫉妒的演化來源:其他人分到較大塊獵物,嫉妒引發行動,增加存活機率。但現今嫉妒多與生存無關,鄰居買了保時捷你不會少塊肉
  • 擬人化

    過去美國禁止媒骿刊登陣亡士兵照片,以防引發反戰思想。傷亡數字一直是公開的,但照片造成更大的震憾。
    過去十萬年來,人類基於團體生活的需要,發展出一種對他人想法與感受的敏銳感受力,稱之「心智理論」
    面對統計數據時我們較為冷漠,面對人則不然。哀求眼神的兒童照片,比受饑荒威脅兒童的嚴重比例數字募得更多捐款。
    故事比數字有效,每篇報導都要有人,股價消息要配CEO,國家事務由總統出面,地震要有受害者。
    應用:進攻時善用故事打動人心,防守時用事實與統計檢驗聽到的故事。
  • 「那殺不死我的,將使我更強壯」謬論

    「那殺不死我的,將使我更強壯」是尼采的名句,呼應我們的「愈挫愈勇」「打斷手骨顛倒勇」。
    在危機中存活者,往往只是幸運,危機就是危機,不是強大自我的過程。若危機讓人改變,那麼即使沒有危機人也可以做出相同的改變,相信可怕之事是有益的是一種錯覺,大病之後的人體一定不會比之前更健康。
    注:這是少數我不完全認同的論點,危機能刺激人體的防衛機制,激發團體憂患意識,荒唐度日者歷經喪親才清醒,生活習慣不佳的人在大病後開始注重養生。
  • 注意力錯覺

    只看導航沒注意警示牌跟路面的駕駛開車開進淹水路段、大猩猩影片實驗(要求計算白衣球員傳球次數,受測者幾乎都無視影片裡曾出現大猩猩)
    開車講電話降低的反應速度與酒駕相近,肇因於注意力錯覺。
    我們注意某些特定事物,對部分事物視而不見,而最危險的地方在於:我們錯以為自己已注意所有重要事物,請時時提醒自己是否忽略某些細節。
  • 策略性不實陳述

    面試時承諾自己並無把握的目標以爭取工作、承諾不合理的時程以爭取案子(屆時再用優秀的「藉口管理」逼迫對方妥協)。
    在一次性冒險上最常見(應徵面試、手術成效)。大型計劃總是延遲完成且超出預算,拿到案子的往往是最會吹牛的人(保守者提出的時程及成本通常不討喜)。人們有系統地忽略謊話,容忍策略性不實陳述。當事關重大,除了對方的承諾,請參酌其過往成績以為借鏡,並加上違約懲罰並要求保證金。
  • 想太多

    高爾夫名將在奪冠前一刻無法決定如何出桿,最後一洞打得荒腔走板。請學生評比54種果醬,原本結果與專家評比一致,在要求詳細陳述評分理由後排名大亂,公認冠軍反而拿下低分。
    想太多會遏阻情感智商。涉及熟練事物如運動技能或已回答過數千次的問題(巴菲特稱之競爭優勢圈),最好不要多想,想太多會妨礙以直覺尋求解決辦法,石器時代以來就存在的類似情境,用捷思法較有利;演化裡從未出現的複雜問題(例如投資策略)宜冷靜思考,此時邏輯勝過直覺。
  • 規劃謬論

    每天排訂當日作計劃卻很少如期完成,我們會高估自己的辦事能力,即使自知以往預測都過於樂觀,每次預測還是真心相信這次的計劃很實際肯定成功—>規劃謬論。
    要求學生為研究報告訂出「保守務實」及「諸事不順」兩種版本的時程,只有30%在務實時程內交付,平均時間比務實時程晚了一倍,甚至比諸事不順版多了七天。
    計劃執行速度及收益常被高估,成本與風險則相反。雪梨歌劇院1957年完成規劃,原本應在1963年完工,耗費七百萬美金,最後1973年才完成,花掉一億兩萬百美金,預算多了14倍。
    無法做好規劃的原因:1.妄想自己很優秀,能夠完成所有擬定工作 2.忽略計劃以外的因素(意外狀況)
    解決方法:參考過往經驗及外部案例,事前推演最糟的狀況。
  • 專業曲解

    馬克吐溫:「一鎚在手,萬物皆釘」 是「專業曲解」的最佳註解。
    同一個故事的不同觀點:某人貸款創業,不久後破產罹患憂鬱症,最終自殺身亡。企業管理家:管理不善?策略不佳?市場不利?行銷專家:目標族群設定錯誤。財務專家:財務槓桿操作不當。報社記者:自殺才有報導價值,讚。作家:如何改寫成動人的悲劇故事。銀行家:信貸部門有錯。社會主義者:哼!資本主義,不意外。虔誠教徒:這是上天的懲罰。心理醫生:悲劇源於血清素濃度過低。
    當你詢問一個問題,多數人會以自己的專案為思考基礎,不要成為自己狹隘思維模式的囚犯,試著多增加幾種工具,不同情境使用適合的觀點,思維才會靈活。
  • 蔡加尼克效應

    服務生不用紙筆正確無誤記下所有顧客點餐要求,但事後回頭請他代尋遺落的圍巾,服務生對當時客人所坐位置都毫無印象,蔡加尼克以此發表論文,稱之「蔡加尼克現象」-我們會緊記未完成的任務,並時時感受到壓力,直到完成則立即從記憶消除。
    而另一項研究指出,未完成的任務會不斷折磨我們,直到我們清楚知道如何與之相處,而為任務訂定計劃有很好的緩和效果。
    面對繁瑣雜事,擬訂完整書面計劃後我們才會心安。
  • 能力錯覺

    人們常會低估運氣對於成功的重要性,覺得辛勤努力是最大關鍵。研究指出,隨機比較兩家公司找出表現較佳的公司,只有60%的狀況,勝出公司的CEO能力也較強,有40%的狀況是勝出公司的CEO能力反而較差,只比隨機多10%。巴菲特:「執行長如果是划船手,其表現主要取決於他坐的船,而非他的划船技術」
    在某些領域能力幾乎無用武之地,統計公司多位投資顧問十年間的年度績效排名,幾乎呈現隨機分佈,同一位投資顧問有時名列前茅有時墊底,換言之,其表現來自運氣而非能力。有人確實以能力謀生,如飛行員、水電工、律師,請對其寄予尊敬,至於創業家、經理人、投資顧問就…(注:作者很輕視金融服務業呀)
  • 正面特點效應

    存在事物對我們的意義遠超過不存在的事物,健康時很少意識到可能生病,和平時不會想到戰爭,飛機降落時不會因為沒墜機感到訝異。偶爾想想不存在的事物,我們將會更快樂。
    膽固醇過高的沙拉標註其內含的20種維生素,不提膽固醇。科學期刊上很少出現成功否定某個假設的論文。我們傾向接受正面建議「去做X」,不是負面的提議「不要做Y」,但忽略了建議是否有意義。
  • 撿櫻桃

    吃泡麵時不會期望看到與包裝照片相同的內容物,看房子時不會期待跟DM或網站照片一樣的佈置,因為我們已就熟知賣方只會突顯美化好的一面而自動降低期待,這種行為稱為「撿櫻桃」(Cherry Picking)
    領域愈專門,愈容易掉入撿櫻桃陷阱。學術研究擅長告訴我們它們所做的貢獻,對於沒做的事三緘其口。自我美化是闖蕩職場的基本功,破解之道:追查失敗計劃與未實現目標,可以發現人們常隨著時間修改目標,射箭畫靶。
  • 單因謬誤

    為什麼會發生九一一攻擊事件?金融大海嘯誰該負責?入侵伊拉克的真正原因是什麼?
    許多問題絕非單一原因造成,但人們總不放棄追求單一的簡單答案。解決之道,畫魚骨圖(要因分析法)。
    單因謬誤歷史悠久,將成功或天災人禍歸因於一人,將其標為負責人/代罪羔羊,是權力運作的重要手法。
  • 治療意向錯誤

    銀行統計公司客戶,債台高築公司的營利高於無貸款公司:1.不賺錢的公司借不到錢,自動歸為未負債 2.還不起利息的公司被銀行接管抛售,不再是客戶
    時速150開完指定里程的駕駛為「賽車手組」,反之為「理性駕駛組」,賽車手肇事率低,開車比較安全?:一旦肇事就無法在時限內開完,一律被歸為理性駕駛組。
    未定期服用新藥的患者死亡率較高:因副作用或病況嚴重無法定時服用,都被歸為未定期服用
    克服之道:應將所有曾參與治療的樣本都納入分析,以防止結果偏差。
  • 新聞錯覺

    建議遠離新聞,純粹浪費時間而無益處。理由:1.大腦易被腥羶色吸引,為求點擊/收視率,智慧性、複雜和抽象的消息與背景知識被刻意淡化。 2.回想過去一年內看過的一萬則消息,是否有任一則對生活、事業有幫助?3.全球人類因觀注某則消息可能損失數十億小時的生產力。
    請拒絕吸收即時新聞,選擇深入探討背景的文章與書籍。

在PowerPoint投影片剪貼Visual Studio程式碼

$
0
0

最近在準備開發教學投影片,有大量從Visual Studio剪貼程式碼範例到PowerPoint的需求,遇到一個大問題:從Visual Studio複製程式碼再貼到PowerPoint時,語法顏色有被保留,但換行消失,排版全亂,讓人極度困擾。

想起VS2010推出時也有類似的剪貼簿亂碼問題,當時寫了個小工具自力救濟。這次面對的情況類似,理應可如法炮製,便花了點時間研究,發現Visual Studio複製到剪貼薄的內容包含HTML格式,使用Regex將原始碼中的\n換成<br />,在PowerPoint就能忠實反應原有的換行與排版。以修正版HTML換掉原本的剪貼薄內容,惱人的換行問題便可迎刃而解。

修正剪貼簿內容的核心邏輯只有短短幾行,如下所示:

privatevoid btnAddLineBreaks_Click(object sender, EventArgs e)
        {
            IDataObject dataObject = Clipboard.GetDataObject();
if (!dataObject.GetDataPresent(DataFormats.Html)) return;
string html = dataObject.GetData(DataFormats.Html) asstring;
string endFragmentTag = "EndFragment";
if (!html.Contains(endFragmentTag)) return;
 
 
string pattern = endFragmentTag + ":(?<n>[0-9]{8})";
            var findLen = Regex.Match(html, pattern);
if (!findLen.Success) return;
            var len = int.Parse(findLen.Groups["n"].Value);
 
            var htmlStart = html.IndexOf("<HTML");
            html = Regex.Replace(html, "\n", (m) =>
            {
if (m.Index < htmlStart) return m.Value;
                len += 6;
return m.Value + "<br />";
            });
//change EndFragment and EndSelection
string origLenText = findLen.Value;
            html = html.Replace(
                        origLenText, 
string.Format("{0}:{1:00000000}", endFragmentTag, len))
                   .Replace(
                        origLenText.Replace("Fragment", "Selection"), 
string.Format("EndSelection:{0:00000000}", len));
 
            DataObject newDataObject = new DataObject();
            newDataObject.SetData(DataFormats.Html, html);
            newDataObject.SetData(DataFormats.Text, 
                dataObject.GetData(DataFormats.Text) asstring);
            Clipboard.SetDataObject(newDataObject, true);
        }

以下是動畫展示。原本從VS2015複製程式碼,貼到PowerPoint時換行符號會不見,叫出自製小工具,複製程式碼後先按「Add Line Breaks」再貼到PowerPoint,換行及排版便能完整保留。註:以上程式碼丟進WinForm按鈕事件就可運作,如果嫌麻煩,我在FB社團放了一個現成版本

另外,將程式碼貼到內容區塊的做法雖然能保留換行,但原有底色及文字顏色會被PowerPoint置換,若想讓Visual Studio程式碼樣式在投影片原汁原味重現,我找到一個好方法:不要將程式碼貼入中央的內容區塊,改將滑鼠移到投影片的空白處透過右鍵選單貼上(或將焦點自內容區塊移開再按Ctrl-V),程式碼會被貼到投影片最上方,程式碼樣式被完整保留,但高度未依內容自動調整。此時只要隨意更動區塊寬度,高度就會依內容自動縮放,接著Visual Studio程式碼就在簡報上完美重現囉!

在IIS設定SVG與字型檔MIME Type

$
0
0

開始試用docfx產生API文件(docfx是微軟程式庫文件產生器SandCastle的接班人),編譯專案時可一併產生API文件網頁,非常方便,大推!(docx的安裝使用可參考網友霧隱虎的文章,VS2015用NuGet在專案安裝docfx.msbuild,以後每次編譯時可在_site目錄找到API文件網頁。)

直接從檔案總管瀏覽文件網頁大致正常,只有左側目錄總表因本機網頁JavaScript受跨網域存取限制無法顯示,將文件丟到IIS,目錄總表正常了,但出現另一個小問題,docfx的Logo「頭文字D」變成圖檔破損圖示。使用F12開發者工具偵察,問題出在瀏覽無法下載.svg檔案。

檢查logo.svg檔案確實存在,我想起類似問題在使用FontAwesome時也遇過,woff等字型檔對IIS是未知檔案類型,基於資安原則IIS一律回應HTTP 404,需加入MIME設定才能解決。一直沒為此寫過筆記,在此補上。

IIS7以上的版本可在web.config加入設定:參考來源

<system.webServer>
<staticContent>
<mimeMapfileExtension=".eot"mimeType="application/vnd.ms-fontobject"/>
<mimeMapfileExtension=".ttf"mimeType="application/octet-stream"/>
<mimeMapfileExtension=".svg"mimeType="image/svg+xml"/>
<mimeMapfileExtension=".woff"mimeType="application/font-woff"/>
<mimeMapfileExtension=".woff2"mimeType="application/font-woff2"/>
</staticContent>
</system.webServer>

而我這次遇上的是一台古老IIS6,MIME對應要用IIS管理員設定:

設定妥當,頭文字D就順利出現囉~

MVP十年有感

$
0
0

廉頗老矣,尚能飯否?

收到微軟寄來MVP 10年環,驚喜之餘帶點感觸,遙想十年前首獲MVP時還是個對技術充滿狂熱的熱血男兒,轉眼十年過去,人生走進另一塊領域,中年大叔心所嚮往的已是不同風景,常自嘲自己已是一尾準鹹魚(鹹到什麼地步呢?連跟同事朋友聊天都怕自己會散發無為不爭的灰色思想,害熱血青年跌進消極深淵,罪過罪過),差別只在於此鹹魚看見茶包還會激動,遇到邏輯謬誤會憤慨,對累積多年的開發技能微微自豪,不捨放棄而繼續經營,一撐十年還沒死在沙灘上,連自己都意外。說來,得謝謝各位讀者朋友支持捧場,對資深技術老人不離不棄,人間有溫暖,四季皆如春。

年過40好幾,不是顧問講師卻硬要留在技術職的道路是冷僻而孤獨的,常有黑暗力量誘惑或將你推向管理職務,需要幾近耍賴的堅持跟很好的運氣才能如願,非常幸運一把年紀之際卻有份只靠技術不必管人的工作並樂在其中。(換個角度,深知自己的長處短處,這才是發揮最大價值的角色,但很多類似條件的技術咖卻不一定能如願。參考:50歲的IT人都到哪裡去了呢?)說起這幾年的角色,有點像部隊裡的老士官長,三行四進狙擊刺槍奪刀(破解、想解法、射茶包)難不倒我,體力雖然下滑,憑著豐富實戰經驗與技巧在戰場還可以撐很久死不了,但只要談到帶兵攻防戰略(專案管理、軟體工程)就皺眉(XD)。部隊總是需要將軍領兵打仗,企業誰不希望找到將領帶更多的兵攻城略地,迅速壯大?不往管理職發展很容易被上司同事親朋好友誤解為不知上進不成材(笑),這大概可以解釋為什麼很多技術好手最終被推上不擅長或沒興趣的管理職,變成彼得原理活生生的悲情鐵證,在我看來是暴殄天物,在台灣職場卻是常態。(彼得原理:在組織或企業制度下,人會因其某種特質或技能被擢升到不能勝任的職位,變成組織的障礙物(冗員)及負資產)

雖然才智與成就不及大神的1/100,對Anders Hejlsberg在去年一場座談的談話心有戚戚焉:

我非常幸運我的愛好和工作是結合在一起的,但不是人人都像我這麼幸運,最重要的是你要找到你的熱情所在,而這個又能帶給你快樂,這就是帶來更好的工作成就,如果你面臨一個選擇,一方面是工作能多給你錢,另一方面是你很熱愛的,這樣的話才能工作得非常帶勁,你也能夠成長,同時也能學到非常多的東西。

我是自主選擇做技術方面的工作,實際上我不管人,也沒有人向我彙報,但在技術方面我負的責任是非常大的。因為對我的工作描述是我必須要創作出在全球開發者社區都非常有影響的產品,所以我是不管人的,我更願意從事我非常擅長的技術方面的工作而不是管理人,這是我的專長所在,也是我的熱情所在。

文章開頭是10 年來的MVP環合體照。我應該是全世界唯一湊出12枚拍照的MVP(XD),理由是微軟2009年10月才推出可堆疊MVP環的獎座,所以沒有2007/2008環這種東西,我的第一枚則從2010年開始。我特別托人翻模搞來三枚替代環,再修圖補上年份(謎之聲:這麼搞剛又假掰,不愧是黑大呀!),讓合體照的加法運算不觸發Assert.AreEqual()。:P

MVP十年,期許自己在這條路上還能再戰十年,懇請舊雨新知繼續支持愛護。

我的.NET舊版相容情結

$
0
0

前陣子有篇文章提到微軟中止.NET 4.0/4.5/4.5.1技術支援,結論是:1) Windows預設會自動升級到.NET 4.5.2,2) 用.NET 4.0/4.5/4.5.1寫的程式不需修改就可直接執行(僅有極少例外)。

很妙的是,每回建立新專案,我下意識總是挑選較低版本,例如:開.NET 4專案若沒用到async、await等.NET 4.5專屬特色,我會優先選.NET 4.0,心裡總覺得:萬一使用者的電腦的.NET版本比較舊,程式也能跑。

其實,依目前.NET的版本更新政策,使用者機器都該升級到.NET 4.5.2,唯一的例外是Windows XP/Windows 2003,而我程式部署對象全部都是Windows 7以上,換言之,選.NET 4.0根本不會有任何好處!但我總是沒來由的偏好舊版,已然變成一種「舊版相容情結」。

但,也不致於有壞處吧?我以前也這麼想,直到我膝蓋中了一箭…

上週分享的Visual Studio程式碼剪貼修正小工具,依照自己不成文的慣例,我又選了.NET 4.0。而在處理剪貼簿讀取時,發現取出的HTML內容,中文變成亂碼…

起初以為是單純中文編碼問題,試著用Big5解成byte[]再以UTF8轉回字串,由於某些字元被吃掉,研判是底層解析階段已發生不可逆的資料遺失,不可能靠轉碼還原。我可是打不死的小強老士官長耶,豈有被這種小問題難倒的道理?開始動腦筋想如何直接回取回原始資料,不走.NET元件,莫非要呼叫Windows API?

爬文找到一篇文章,針對同樣問題做了剖析,確認這是.NET 4.0的已知問題,已經在.NET 4.5+修正。

將專案版本切到.NET 4.5.2,什麼都沒改,問題消失!

由這次經驗,我決定調整自己的.NET版本選擇原則:除非有很明確且無法抵抗的版本限制(例如:幫田僑仔阿土伯寫「地契管理系統  for XP」),否則一律選用最新版本並要求使用者升級。新版本排除掉一些Bug、修正安全漏洞,基本上要比舊版好,像本次案例,用新版的話當場少踩一個坑。

別跟自己過不去,新專案請用.NET 4.5.2/4.6.1!

註:該選擇.NET 4.5.2還是.NET 4.6.1?我沒找到權威解答,猜想差別在於部署廣度,目前只有Widows 10內附.NET 4.6,其他版本Windows需要另外下載安裝。(如有錯請指正)

Google Cardboard體驗與Viewer Profile 調校心得

$
0
0

Google Cardboard是 Google 發明的窮人版觀落陰3D立體眼鏡,用瓦楞紙板、兩塊凸透鏡、魔鬼氈折成將手機固定在眼前的小紙箱,執行特殊App對左右眼分別顯示有視差的影像產生立體感,再感測手機的移動隨頭部轉動改變視角,營造逼真的虛擬實境感。另外,Cardboard 在側面還加了一對隔紙板相吸的磁鐵,手機如有 NFC 功能,移動磁鐵改變磁場還可控制 App,十分巧妙的點子。

收到好心人贈送 Cardboard 一付,三兩下組好正想體驗,「休士頓,我們有個麻煩…」

Cardboard 設計的對象是 6 吋以下的手機,我的手機 Lumia 920  4.5 吋,大小沒問題,但它是 Window Phone 呀,啊啊啊啊啊~ 幸好家裡還有一支退役的 iPhone 4 可供把玩,但 Cardboard App 要 iOS 8 才能跑,啊啊啊啊啊~ 無機可測的困境直到跟女王借測 Xperia Z3+ 才算搞定,試玩效果挺不賴。App 還有個巧妙設計,可以掃 Cardboard 上印的 QR Code 針對不同 Cardboard 尺寸規格最佳化,我拿到的 Cardboard 沒附 QR Code,但用 App 預設值就跑得很好。

借來的手機怎能玩得盡興,想起我的 Nexus 7 來了,30 公分 7 吋尺寸大了點,蓋子合不上用手抓可以湊和,但畫面太大超出可視範圍是個問題。不想動手改造硬體,想起 App 可透過 QR Code 針對規格最佳化這招。Google 很貼心地提供輸入尺寸產生 QR Code的網站,輸入鏡片到手機距離、兩眼孔距、高度等資訊,可產生 QR Code,讓 App 產生最佳化的影像。

摸索一陣子抓到訣竅,以下是我的心得:

    1. Viewer Profile 製作網頁有教你怎麼量尺寸,基本上照量照填即可。
    2. 若出現重疊影像,可增減 Inter-lens distance 直到疊影消失。
    3. 由於我想在 6 吋規格硬套 7 吋平板,有個作弊方法是短報 Screen to lens distance,數字愈小,App 產生的影像也愈小,縮小只顯示在螢幕中下半截(如照片),終於看到全景了。 不過,App只有部分螢幕能用,解析度較差。
    4. Distortion coefficients 用來修正凸透鏡形成的邊緣變形,只能靠試誤法,調整讓物體移到視野邊緣時不要嚴重變形,我試了k1/k2設0.3看起來OK就沒再細調。
    5. 不知是內附磁鐵磁力不足還是離NFC感應器太遠,磁鐵得拿到平板背後滑動才能被感應到,所幸 Nexus 7 螢幕有一大截露在紙箱外,可用手指點螢幕也能操作代替。XD(註:將手機/平板豎直可退回上一步)
    6. 人臉 T 字部位出油量超乎你的想像,不想用幾分鐘紙箱就變得油油髒髒,除了花大錢買化妝品改善膚質,便宜解法是在接觸 T 字部位的表面貼一層包水果的泡綿,當然出油還是照樣出油,但看不出來不影響心情就無所謂了。

Cardboard 的虛擬實境需要轉頭控制視角或方向,手機的重量讓人不敢鬆手,在旁觀者眼裡,只見某個人抓著便當盒貼在眼前搖頭晃腦嘴角上揚,傻氣指數爆表,呵~ (玩久頭還會暈,Nexus 7 意外有防暈效果,因為在頭暈之前手會先酸,哈)

前陣子一張集體 VR 設備體驗大會跟觀落陰對照圖引人會心一笑,不知道幾年之後,一家人自顧自滑手機的冷漠場面會不會變本加厲,人人頭戴著VR頭盔各自沈醉在自己的副本,小孩有沒有回家?爸爸人在客廳還是在房間?不重要 XD

NuGet packages目錄舊檔大掃除

$
0
0

NuGet已是開Visual Studio寫專案的必備工具,用關鍵字找到項目就能安裝,有新版本點一下就更新,非常方便。

我們遇到一項困擾:NuGet在升級程式套件前會移除專案的舊版DLL,至於packages目錄下的舊版檔案若已無其他專案參照,也會一併刪除,理論上不會累積。依NuGet 文件說明,packages目錄之檔案移除原則如下:

  • Files in the solution folder. The folder for the package you removed is deleted from the packages folder. If it is the only package you had installed, the packages folder is also deleted.)

不過因為我們配合Build Service採取 packages簽入TFS策略,雖然NuGet升級時會刪除本機pacakges裡舊版檔案,但習慣上大家不會主動將package刪除操作簽入版控,而TFS版控裡的舊檔仍在,下回Get Latest就又通通跑回來,加上怕Build Service建置失敗,寧可錯加也不敢錯殺的心態,日積月累之下,版控packages裡的項目多如牛毛,一堆無用項目也拖累 Build Service 建置效率。(若Build Service設定成每次建置新開資料夾不覆寫,浪費的頻寬及儲存空間會更可觀)

最後,我們專案的 packages 宛如一座歷史博物館(以下圖),從毛公鼎、唐三彩、青花瓷到漢玉寶塔都有。NLog從2.1.0到4.2.3、Json.NET從5.0.7到7.0.1,各有七個版本。哪些有用哪些沒用,孰可殺誰該留則是個謎!讓開發小組興起「大掃除」的念頭。

爬文求解,網路上有人提到 Resolve Unused Reference套件,試用發現它主要用於修改 csproj 移除沒用到參照,移除 packages 屬於附加功能,而強行移除專案未用參照的做法有點粗暴,有違我只想清理 packages 舊版的原意,再加上一些被它誤殺導致無法編譯的Bug回報(提醒:要使用它前務必先 Check In 或備份,以免造成遺憾。),此一選項迅速被排除。

最後,想到一解,先手動將本機 packages 目錄清空,再借用 NuGet 還原功能重新下載(如果有多個 sln 共用 packages 目錄,務必逐一還原或重新編譯以防漏載),做完一輪,理論上 packages 就只會剩下各專案所需的必要版本的聯集。TFS 似乎沒有功能自動將本機缺少的檔案從版控刪除(實務上很少有此需求),我想到的愚公移山法是在 TFS Source Explorer 逐項巡視,若Local Path 未出現藍底連結表示資料夾不存在,可以安心刪除。(補充:文末有更有效率的做法)

最後共計刪掉55個無用舊版資料夾,packages 目錄總算清爽多了。

【2016-03-03更新】

抛磚引玉成功,Franma 大人開示使用 TFS 比對功能快速找出被刪檔案的妙計,下回就不用再愚公移山囉!特此感謝。

步驟為在 Solution Explorer packages 目錄叫出右鍵選單執行 Compare:

選擇只列出在來源路徑出現的檔案(TFS有,但實體路徑沒有):

比對結果就是所有被刪除的檔案,酷!

【茶包射手日記】只在CSHTML發生的編譯錯誤

$
0
0

在ASP.NET MVC專案新一個View,編輯CSHTML時Visual Studio爽快地賞了我一個錯誤:某個關鍵型別同時出現在兩顆組件(DLL)中!

看了兩顆組件名稱,Afa.WebApi是MVC網站的編譯結果,Afa.WebApi.Client則是先前嘗試不同做法時曾短暫加入,現已不再參照,二者不該並存。詭異點在於這個關鍵型別被Controller及Model廣泛使用,若重複出現在不同組件,MVC專案也該出錯,為何在加入CSHTML前編譯、執行完全正常?

檢查MVC的bin目錄,果然發現Afa.WebApi.Client.dll的蹤影,判斷為先前短暫加入參照時所殘留,而Afa.WebApi.dll與Afa.WebApi.Client.dll意外並存,就是CSHTML抱怨型別重複出現的根源。至於為什麼組件並存只影響CSHTML,原本MVC專案還是可以順利編譯?仔細想想不難理解-關鍵在於二者編譯方式不同!

ASP.NET MVC屬於Web Appplication專案,編譯時要參照哪些組件由csproj列舉項目決定;而CSHTML/Razor採取執行期間動態編譯,像Web Site Project時代的.aspx /.aspx.cs一樣,享有「改完存檔,重新整理網頁就生效」的即時性,而要實現這一點,編譯的參照來源也跟Web Site Project一樣,由放進bin目錄的組件決定。由此編譯行為差異,就不難解釋為何只有CSHTML出錯,Controller/Model未受影響。Case Closed.


新裝備入手-Nokia N1

$
0
0

陪伴兩年多的Nexus 7最近很不乖,常發生觸控沒反應或點擊位置誤差,尤以右上角最嚴重。最討厭的狀況是想關Chrome分頁關不掉,變成新増分頁,分頁愈關愈多令人光火;有時則是手指沒摸到螢幕,卻像有無形的手指亂點,畫面不斷放大縮小抖動不停,好氣又好笑。爬文在Mobile01上找到不少類似症狀的討論,觸控失靈貌似Nexus 7的常見問題,找來網友推薦的YAMTT測試工具,妖怪立即現形。如以下影片,手指一移到右上角,進入影分身模式,一點變五點,點擊頻率賽過加X鷹呀~ (YouTube上有加壓排線接頭解決觸控問題的教學,試過無效,而且我的失靈問題明顯集中在右上角,硬體損壞機率較高。)

眼見病發日益頻繁,醫生嘆了口氣,向滿面愁容的中年男子說道:「老爺,少奶奶的日子只怕不多了…」,只見那男人臉上表情忽然轉憂為喜,轉身對老僕說「來福,快把上回婚友社送來的候選資料拿來給我…」(喂)

已有手機筆電,平板只拿來傳LINE、供通勤或居家簡易上網用,另外也是要測試App或網頁的Android裝置代表。3C設備生命週期有限,即使配備攻頂,時間到了照樣得換,以消耗品心態找個中價位的可靠機型才是明智之舉。習慣了Nexus 7的輕巧,鎖定4:3 8吋尺寸,在網站上逛了一輪,發現Nokia去年出的Nokia N1,7.9吋4:3螢幕適合看網頁多過看影片的我,跟iPad mini 3相近的金屬外殼,2048x1536 IPS螢幕,Gorilla防刮玻璃,800萬像素後主鏡頭及500萬前鏡頭都很討喜,唯一可挑剔的就是少了NFC跟GPS。沿續對Nokia Lumia手機的好印象,加上網購價格下殺到六千有找,還送保護貼/耳機/觸控筆(到之貨才發現連保護皮套都內附了,真好),腦波一弱手一滑,東西就來了。

小插曲是網購網站上標明預購商品,下單14天後才會出貨,訂購確認通知也寫明兩週後的某月某日才出貨,心想Nexus還在,用等待換取優惠是值得的。人算不如天算,下單後Nexus 7病情突然惡化,讓我擔心等貨的這兩週會被觸控失靈搞瘋?萬萬沒想到,下單隔天一早就有人送貨上門… 嘖,真是一家沒制度不講章法胡亂出貨的網購公司,把14天預購搞成24小時到貨…不過,因為別人不講信用而嘴角上揚的經驗實在不多呀~ XD

試用心得,Nokia N1的質感一如預期(跟Limia 920一樣好),沒有硬塞一堆廠商自製軟體(這點我喜歡),唯一附上的Z Launcher還不錯用,主畫面顯示最近使用的App,據說還能觀察主人習性依不同時段提供「搭捷運常用App」、「蹲馬桶專用程式」等。畫面可直接寫字母搜尋App,可惜不支援中文,右側頁面則是以字母排序(有Windows Phone的味道,對我格外親切)的完整App清單,以我安裝App數量不多的慣例,說不定連多桌面跟資料夾分群管理都省了。

最後,附上沒誠意新裝備展示照一張。

花園傳來嬉鬧聲,男子與少女在花叢間追逐嬉戲,「老爺別這様,夫人會看見…」(喂)

系統升級導致Android App閃退

$
0
0

新到手的Nokia N1設定好後收到升級通知,Adroid系統由5.0.2升級到5.1.1。重開機後立即冒出「Goolge書報攤」當機訊息,接著陸續發現Google圖書、萌典、Garmin Connect Mobile等App都無法使用,一啟動就閃退。心中暗叫不妙,是升級過程出了問題還是碰上機王?

一度懷疑是App不支援Nokia N1所致(先前用Google Nexus沒這疑慮,App跟Google自家平板不相容,一定是App的錯呀),但爬文未見類似案例,看來是人品問題 orz。新買平板有個App不能跑頗悶,興起恢復原廠設定重來一次的衝動。

動手前系統提示有幾項更新,其中有一項「Android System WebView」引起我的注意,雖然不熟Anroid App開發,約略知道這是在App顯示網頁的常用元件,莫非Google圖書、萌典、Garmin Connect Mobile這幾個App的共通點就是用了WebView,作業系統更新後元件沒更新才無法執行?

滿懷希望等待更新安裝完成,三個App的當機問題果真一次消失!答案揭曉,本茶包為「Android作業系統更新5.1.1後,Android Systm WebView也需配合更新才能運作」,而作業系統更新到WebView更新的這段時間,使用WebView的App就發生一執行就當機的現象。

好奇系統核心等級的元件怎麼沒跟作業系統一起升級?爬文找到一些線索,Android從Lollipop版本起將WebView拆成可獨立更新的套件(參考),較易於更新修正。基於安全理由,WebView需要較頻繁的更新,這個考量可以被理解。而網路上有WebView元件更新問題導致多個App不斷當機的案例,未來遇類似狀況,倒是可以優先考量此一因素,先更新或還原WebView版本試試。

將複合字串拆成多欄位-以ORACLE及SQL為例

$
0
0

先說說我的需求。某資料表使用複合欄位當Primary Key,例如:由OrgId、DeptId、UserId三欄組成唯一鍵值。當要查詢特定資料,理論上應寫成WHERE OrgId='…' AND DeptId = '…' AND UserId = '…'。為求簡便,在.NET程式端以及某些資料表我發明了一種複合代碼字串"OrgId-DeptId-UserId",只用一個參數或一個欄位就搞定關聯,省時又省力。不過,每個做法總有黑暗面,當複合代碼字串要拿來查資料時,得先拆解成OrgId、DeptId、UserId三個值,在C#裡用個.Split('-')可以輕鬆搞定,但如果要在SQL裡處理就麻纇多了。

有一種很鳥但絕對可行的解法:

SELECT * FROM BLAH WHERE OrgId + '-' + DeptId + '-' + UserId = 'OO-XX-orz'

保證你能正確查到資料,但你知道我知道獨眼龍也知道,遇到欄位加工再比對的寫法,DB必須逐筆處理無法靠索引加速,速度慢又不環保。

最近在ORACLE再次與此問題狹路相逢,這回爬文學到一個神奇函數-REGEXP_SUBSTR,可在ORACLE用Regular Expression處理字串,做到類似Split()的效果。透過REGEXP_SUBSTR('FBI-IT01-12345', '[^-]', 1, 2),用'-'符號可將字串拆成三段,最後一個參數2可指定要取出第幾段。於是,老問題找到新解法了:

-- :pFindKey = 'FBI-IT01-12345'
--方法1
select * from BLAH where OrgId || '-' || DeptId || '-' || UserId = :pFindKey
--方法2
select * from BLAH where
       OrgtId = REGEXP_SUBSTR(:pFindKey, '[^-]+', 1, 1) and
       DeptId = REGEXP_SUBSTR(:pFindKey, '[^-]+', 1, 2) and
       UserId = REGEXP_SUBSTR(:pFIndKey, '[^-]+', 1, 3)

那那那那,在SQL Server遇到這個問題怎麼辦?SQL沒提供支援Regular Expression的內建字串函數,這類官方多半建議用威力強大的SQLCLR解決,但實務上一旦引用自訂Stored Procedure、User Defined Function會增加部署的需求,在我眼中,完全靠內建函式搞定才是王道呀!

找到一記妙招,將'FBI-IT01-12345'轉成'<n>FBI</n><n>IT01</n><n>12345</n>'後轉型成SQLXML型別,就可用.value('(/n)[2]', 'varchar(16)')取出'IT01',如此在SQL也能將複合字串拆成多欄位囉!

declare @pFindKey varchar(32) 
set @pFindKey = 'FBI-IT1-12345'
--方法1
select * from BLAH where OrgId + '-' + DeptId + '-' + UserId = @pFindKey
--方法2
select BLAH.* from BLAH
join (selectconvert(xml, '<n>' + replace(@pFindKey, '-','</n><n>') + '</n>') as x) Keys
on OrgId = Keys.x.value('(/n)[1]', 'varchar(16)') and
   DeptId = Keys.x.value('(/n)[2]', 'varchar(16)') and
   UserId = Keys.x.value('(/n)[3]', 'varchar(16)')

2016雙溪櫻花馬

$
0
0

路跑熱潮稍退,賽事報名不再場場秒殺,硬得要命的櫻花馬卻依然搶手。心知不一定搶得到,報名當天只想碰碰運氣,哪知忙起來整個忘光,等回神已錯過報名開始,報名網站早被眾人殺到暈頭轉向,怎麼喴都沒回應,混亂間看到「已額滿」訊息,心想大勢已去,罷了。幾分鐘後接獲線報,馬拉松LDS搭檔忠孝哥傳來還有名額的消息,雖然一下額滿一下可報有失誠信,但我完全不計較,再報一馬。

為配合火車時刻,櫻花馬照往例遲至8:30起跑,氣溫較高,但樂得睡飽一點也好。馬拉松跑多心態大不同,以前跑馬像出國,前一天就慎重地照Check List打包行囊,緊張到輾轉難眠;如今已如上菜市場,東西抓一抓就走,想想都覺好笑。

六點多出發,剛好趕上日出。

遇上氣溫24度以上的大熱天,跟前場渣打馬的4度相比,足足差了20度,不折不扣如洗三溫暖的跑馬人生。XD

起跑前的「公主徹夜未眠」聲樂表演盪氣迴腸,現場聽超級震憾!(謎之聲:誰叫你站在音響喇叭前面?)沒機會聽到主持人介紹演出者,爬文猜想是在藍鯨坪林超馬表演過的許文龍老師。(影片

司令台旁獎典組帳篷,閃閃發亮的金馬獎盃真漂亮,自知腿弱體衰,此生與凸台算是無緣了,跑健康就好,跑健康就好。今年意外櫻花馬有項好傳統--捐衣活動,家裡一堆沒穿過的路跑排汗衫有了好歸宿。

五千人的規模真盛大,到起跑區時間稍晚一點,所在位置已看不到起跑拱門。

天氣也太好了,氣象預報說好的多雲呢?今年氣候特殊,花開得不整齊,沒能目睹萬花齊放盛況,而三月初大部分櫻花已謝,所幸一路仍有幾株「大器晚成」的櫻花相挺,不愁無花可賞。

人多路窄,加上難以完全封路,前10K不太好跑,水站還出現高難度的人車交錯表演。

虎豹潭附近的這間公廁是我的奧林匹克指定水費繳交處,與貓空半馬的明德宮洗手間齊名,是我每回跑櫻花馬一定要來報到的打卡點,解放一下身心舒暢。

鄉間農舍旁有一棵盛開櫻花,在樹下發現野生山貓路跑團成員。

本場開LDS模式,與忠孝哥邊跑邉聊,心跳超標就立刻減速,以「跑健康」為最高指導原則。

前半馬花了三小時,差不多在中段班。回程收容車已在路旁待命,站在車旁招呼的志工很壞心:「快上車哦,天氣那麼熱,上車就可以休息吹冷氣囉!」哈。而回程只要聽到後方傳來小巴士引擎聲,就知道有一台「坐好坐滿」的冷氣回容車回去了。 XD

倒數的某個水站,只見滿地的雞精空瓶… 我們跑太慢,雞精發完惹,水站志工急中生智:「沒喝到沒關係,跑步喝雞精沒用啦」(哈)

沒喝到雞精讓人沮喪,但吾等必須坦誠面對此一挫敗,拿不到選票雞精全因自己努力不夠,無法獲得大會的認同,但我們不會懷憂喪志,必將深刻檢討自省,會在下次比賽重新嬴回信任與支持… 抓抓~~~(瓦斯氣笛聲)

回程路過蘭平千里石碑,趁人少拍照一張留念。(想起去年Kuso版由「雙膝硬化馬」題字的「勿忘在雙溪」 碑)

來到下個水站,還有雞精耶!嘿,不用檢討反省囉~

最後9K是爽爽的下坡,但跑快還是會爆心跳,慢慢來,健康就好。

勉強挺前SUB 6,事後查成績,分組排名約落在50%,若依總排名計,則還有近2900人要在40分鐘內趕回來才能擺脫落馬命運。

領完寄物遇上雙溪國小的小朋友:「叔叔,要買我們的手工皂嗎?」,明知這是行為的藝術裡所說的「擬人化」戰術,還是很沒抵抗力地拿退晶片的100元換了手工皂,回家一看,居然是螃蟹造型,蠻可愛的。

喀完便當準備回家路過終點拱門,剛過關門時間,但桌上還有成堆獎牌苦等主人歸來。大會宣告6:40之後回來的跑友有獎牌有毛巾,但不會有成績。依大會統計,約有15%,約670名跑友落馬,高溫豔陽是幫凶,沒關係,雙溪的硬斗山路永遠都在,明年再來。

去年題字的雙膝硬化馬先生今年一躍成為完賽獎牌的主角,很有意思:

 

Nokia N1麥克風無聲問題經驗一則

$
0
0

Nokia N1新入手用了幾天,感覺一切都好,心想「網購七天退貨大絕」是用不到了,貼上保護貼正式啟用。

昨天想到試試語音輸入才發現大事不妙,麥克風全無反應!試了影片錄製,有影無聲,檢查確認App有「錄製音訊」授權。更!我放棄七天退貨權才發現自己拿到機王,心中滿是狂奔羚羊~

既然無法退貨上網查查送修資訊,發現一則N1送修的鬼故事令我冷汗直冒,雖然故事喜劇收場,且現在維修服務應已改善,但要奔波送修短期無機可用總是麻煩,即使能七天退貨,搬回Nexus 7也得花功夫。遇上硬體問題,怎麼都少不了折騰,難道全因為我的人品?orz

平撫情緒,換上甲種射手服裝,來個基本故障分析。找來耳機麥克風對照測試,以確認問題出在硬體還是軟體。插上耳麥,進入Google App語音輸入模式按下麥克風鍵,發現用耳麥可以正常輸入。所以,這代表內建麥克風硬體是壞的?並不是!移除耳麥後,N1就能正確收音了,拍影片收音也正常,再測了LINE通話,判定麥克風功能正常。

結論,Nokia N1 Anroid平板內建麥克風要用耳麥開光才能解除封印。(誤)

雖不知其所以然,還是留文一篇,期望能幫到有緣人。

2016-03-14補充 接獲網友回報,同樣是新機麥克風無聲,採用以上手法也排除了,目前案例數累積到2。

T-SQL使用逗號分隔字串當作WHERE IN條件

$
0
0

寫Stored Procedure時有一個麻煩情境是由外界傳入參數當作WHERE IN條件,由於參數數量不定,難以事先寫成WHERE … IN (@val1, @val2, @val3),開發者往往會走上用傳入參數組裝SQL指令的險路,稍有不慎就搞出SQL Injection,導致難以想像的災難。(是的,SQL Injection不只會出現在ASP/ASP.NET/PHP/Java/C++,也可能藏在Stored Procedure裡)

最常見的例子是開放使用者勾選一個到多個類別作為篩選條件,例如使用者選取了「主機」、「螢幕」與「耗材」,要轉換成SELECT … FROM Products WHERE Category IN ('主機','螢幕','耗材')。如果你會Dapper,這需求絕對是小菜一碟,cn.Query<T>("SELECT … FROM Products WHERE Category IN @categories", new { categories = "主機,螢幕,耗材".Split(',') })搞定收工,而且是以SqlParameter方式傳遞字串參數,完全沒有SQL Injection疑慮。然而同樣場景搬到T-SQL,卻沒有類似的現成簡便解法可用。

前幾天學會用SQLXML拆解字串的技巧,剛好可以拿來解決難題,將CSV逗號分隔字串先轉成XML型別,再使用.nodes()拆成多筆,就能當成WHERE IN的比對陣列來源囉,如以下範例:

DECLARE @deptIds VARCHAR(128)
SET @deptIds = '1,3,5,7'
DECLARE @x XML
SET @x = CONVERT(XML, '<n>' + replace(@deptIds, ',', '</n><n>') + '</n>')
SELECT * FROM HumanResources.Department
WHERE DepartmentID IN (
SELECT T.n.value('.','varchar(5)') FROM @x.nodes('n') T(n)
)

拿SQL Server的AdventureWorks範例資料庫來練槍,真的可以用"1,3,5,7"字串查出DepartmentID是1,3,5,7的部門資料!

實務應用時,大家應該會選擇將CSV拆多筆資料的邏輯包成函式,方便重覆利用。

CREATEFUNCTION [dbo].[SplitCsv]
(
    @csvString nvarchar(2048),
    @delimiter nchar(1)
)
RETURNS @valuesTABLE (value nvarchar(2048))
AS
BEGIN
DECLARE @x XML
SET @x = CONVERT(XML, 
'<n>' + replace(@csvString, @delimiter, '</n><n>') + '</n>')
INSERTINTO @values
SELECT T.n.value('.','varchar(2048)') FROM @x.nodes('n') T(n)
RETURN
END
 
--使用範例
SELECT * FROM HumanResources.Department
WHERE DepartmentID IN (
SELECTvalueFROM dbo.SplitCsv('1,3,5,7', ',')
)

學會這招,以後就不用再因為WHERE IN挺而走險組SQL字串囉~ (補充:以上方法不考慮CSV字串夾帶XML內容的罕見情境,如不幸遇上請自行克服)

溫馨小提醒:因為你因為不可抗力因素必須走上組SQL這條路,請用生命擔保它沒有SQL Injection風險,以免造業。

有小道消息指出,閻羅王去年研發了一批新刑具,專門對付寫出SQL Injection的程式設計師,很可怕,不要問!

無法使用Windows帳號登入防火牆內的SQL Server

$
0
0

要穿過防火牆連上一台SQL(1433 Port有開,網路芳鄰NETBIOS封閉),發現用SQL帳號登入(SQL Authentication)可成功登入,若用AD帳號(Windows Authentication)則會出錯。

錯誤訊息為:

已超過連接逾時的設定。在嘗試使用登入前的信號交換確認時超過逾時等待的時間。這可能是登入前的信號交換發生失敗,或伺服器無法及時回應所造成。

Connection Timeout Expired. The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement. This could be because the pre-login handshake failed or the server was unable to respond back in time.

 

爬文找到線索,有人回報ADO.NET連線字串使用Integrated Security=SSPI時發生相同狀況,但較常原因是SQL Server的Named Pipe或TCP/IP協定未啟動所致,與我受防火牆阻隔的情境不同。心生一計,Server Name用機器名稱取代IP,就登入成功了。推測使用Windows Authentication需由IP解析機器名稱,但因防火牆阻擋無法反查,故造成操作逾時,直接輸入機器名稱避開此一無法完成的程序。以上僅限揣測,還是PO文一篇備忘,順便供給遇到這些罕見情境的有緣人參考。

【2016-03-18 補充】

文章貼出後,於FB獲網友高錦川先生提供MSKB一則,文中提到:

當用戶端的 SQL Server 驅動程式使用整合式安全性連線至 SQL Server,用戶端驅動程式的程式碼會嘗試解析執行 SQL Server 電腦的完整格式 DNS 網域名稱,方法是使用 WinSock 網路 API。為了執行這項作業,驅動程式的程式碼會呼叫 gethostbyname 及 gethostbyaddr WinSock API。即使 IP 位址或主機名稱通過執行 SQL Server 的電腦驗證,如果該電腦使用整合式安全性,SQL Server 驅動程式仍然會嘗試解析連線電腦的 DNS 完整格式。

算是證實使用Windows驗證(整合式安全性)會觸發由IP反查機器名稱的動作,由於出問題的主機身處防火牆內無法用NETBIOS反查機器,再因DNS設定問題從IP反查FQDN也失敗,造成以上的結果。依此原理,在hosts中設定SQL的IP與機器對應後,用IP也能用AD帳號登入了。

KB提供一招檢測技巧,使用「ping –a xxx.xxx.xxx.xxx」檢查解析名稱是否成功,例如以下例子,192.168.32.11解析失敗,192.168.32.12則成功解析為sqlserver01,結果為用AD帳號可登入192.168.32.12,登入192.168.32.11則會失敗。

C:\Windows\System32\drivers\etc>ping -a 192.168.32.11

Ping 192.168.32.11 (使用 32 位元組的資料):
回覆自 192.168.32.11: 位元組=32 時間=3ms TTL=116
回覆自 192.168.32.11: 位元組=32 時間=3ms TTL=116
回覆自 192.168.32.11: 位元組=32 時間=3ms TTL=116
回覆自 192.168.32.11: 位元組=32 時間=3ms TTL=116

192.168.32.11 的 Ping 統計資料:
    封包: 已傳送 = 4,已收到 = 4, 已遺失 = 0 (0% 遺失),
大約的來回時間 (毫秒):
    最小值 = 3ms,最大值 = 3ms,平均 = 3ms

C:\Windows\System32\drivers\etc>ping -a 192.168.32.12

Ping sqlserver01 [192.168.32.12] (使用 32 位元組的資料):
回覆自 192.168.32.12: 位元組=32 時間=2ms TTL=116
回覆自 192.168.32.12: 位元組=32 時間=2ms TTL=116
回覆自 192.168.32.12: 位元組=32 時間=2ms TTL=116
回覆自 192.168.32.12: 位元組=32 時間=2ms TTL=116

192.168.32.12 的 Ping 統計資料:
    封包: 已傳送 = 4,已收到 = 4, 已遺失 = 0 (0% 遺失),
大約的來回時間 (毫秒):
    最小值 = 2ms,最大值 = 2ms,平均 = 2ms

學會這招,下回再遇SQL帳號可登,AD帳號無法登入的狀況,可以先用「ping -a」技巧初步偵斷一下。


【茶包射手日記】Java內嵌IE網頁疑案

$
0
0

接獲報案:某支Java開發的程式以內嵌IE方式顯示特定網頁,在特定機器執行時網頁出現異常。

初步蒐集情報如下:

  • 於問題機器單獨使用IE或Chrome可正常顯示該網頁
  • 問題機器之IE版本為IE11
  • 問題僅出現在特定機器,同一Java程式於其他機器執行正常
  • 於異常網頁按右鍵檢視HTML原始碼完整,異常部分推測為JavaScript出錯導致
  • IE在內嵌模式下無法使用F12開發者工具,偵錯困難

沒有F12可用,改用Fiddler側錄往來封包,找出後半段Request未發送證據,推測最大可能是JavaScript程式在某階段中止執行。

反覆推敲程式邏輯,包含簡單jQuery Deferred串接數個AJAX呼叫、ViewModel屬性設定、變數比對if分支、console log偵錯,未見可疑之處。無法使用F12偵錯,只好拿出石器時代狩獵技巧,在程式碼各階段埋下alert,確認程式執行進度。

流傳千古的愚公移山法原始歸原始,效果倒不容懷疑,很快把搜索範圍縮小,我忽然眼睛一亮,console.log(blah)!大膽妖孽,還不現形?

讓我們回顧三年前的文章

IE8/IE9要先按F12開啟IE Dev Tools才能存取console物件啦! 笨蛋!

將console.log註解掉,網頁果真恢復正常,證明凶手是console.log無誤!還原案情, 推測是IE被內嵌於Java程式時因某種原因啟動了IE相容模式,而JavaScript在使用console前未確認console是否存在,於是… 轟!

所以我們該寫成window.console && console.log()解決問題嗎?當然不是,從2016/1/12起,微軟只對最新版本的IE(目前是IE11)提供技術支援及安全更新,理論上IE6/7/8/9/10該馬上從地球消失!(理論上啦~前陣子處理一起網頁異常,調查後驚覺使用者還在用他X的IE8,詢問才知受限某個不支援IE9+的重要系統,只好含淚不升級… 被老舊系統壓迫的使用者快站出來反抗吧!)

所以我心目中最好的解法是「把程式寫成不升級新版IE不能跑,把因舊系統不能升級的使用者逼上絕路督促無法升級的使用者向死抱老IE的廠商施壓」。建議做法如下:

  1. 在<head>加上<meta http-equiv="X-UA-Compatible" content="IE=edge">防止IE進入相容模式
  2. 加上一小段<script>window.console || console.log("你媽知道你還沒升級IE嗎?");</script>

全案偵結,收隊!

對付SQL Injection,換掉單引號到底夠不夠?

$
0
0

雖然現在遇到使用者輸入條件查詢DB,我一律都用參數化查詢(順推超好用的Dapper)不再偷懶組裝SQL指令,但關於SQL Injection,我心中始終藏著一個疑問:流傳千古的… WHERE Col = '" + input.Replace("'", "''") + "'"換單引號大法,人人都知它不夠安全,但網路流傳一種說法,指稱換單引號只是自欺欺人根本無效,奇怪的是卻很少看到它被打穿的實例。我完全同意置換法不夠安全,黑名單法一旦漏列就會破功,但既然要把它說得不堪一擊,拿點證據出來很難嗎?很難嗎?很難嗎?

我試了,還真的不簡單…

爬文找到幾種主張置換法無效的說法:

  • 遇到WHERE NumberCol = " + input.Replace("'", "''")時置換無效
    如果欄位是數字型別,將input字串轉成數字才嚴謹,寫成這様是自己跳坑,不列入考慮
  • 利用編碼轉換偷渡單引號
    字串置換函式、資料庫驅動程式或資料庫採用非Unicode編碼,透過巧妙安排讓傳入的Unicode字串轉換後形成單引號。若以SQL Server為例,Client與Server幾乎都已是Unicode,以C# System.String.Replace()、內建SqlClient程式庫、SQL2005-2012為例,置換單引號的做法不致留下轉碼漏洞(參考),但在其他環境則很難說。
  • 利用單引號等效字元
    爬文發現一種傳說中的神奇字元0x02bc(Modifier Letter Apostrophe),串接在SQL指令效果等同單引號。這讓我為之一震,可以替代單引號的神祕字元?好一隻黑天鵝

眾多說法中最吸引我目光的莫過0x02bc字元,尤其不少人說這招在SQL Server及MySQL都管用,甚至有範例程式:參考

string badValue = ((char)0x02BC).ToString();
badValue = badValue + ";delete from widgets--";
string sql = "SELECT * FROM WIDGETS WHERE ID=" + badValue.Replace("'","''");
TestTheSQL(sql);

迫不及待想驗證,很可惜,對SQL Server Express 2014做了類似測試,攻擊失敗!0x02bc被視為一般字元,安全過關。

如果SQL不行,那MySQL呢?特地安裝了MySQL 5.7 Community,如法泡製拔出0x02bc進攻,再次鎩羽而歸。

由以上實測,至少證明0x02bc已不具備打穿SQL Server Express 2014跟MySQL 5.7的神奇功效。或許在以前的某些SQL或MySQL版本上是可行的,但至少在我測的兩種DB版本已不存在。無心再去找活化石老DB試玩,就此打住。

意思是我們可以安心繼續組SQL字串再用單引號置換法嗎?當然不是,遇上特定資料庫、程式庫版本,只置換單引號必然存在等效特殊字元偷渡做亂的可能性,無論換掉多少可疑的字元,只要有漏網之魚就會破功,這是黑名單法無法被補強的本質缺陷。所以,乖乖使用參數化查詢,不要組裝SQL指令才是治本之道!

【結論】我百分之百贊同「置換單引號不能杜絕SQL Injection,在某些特殊情境下會被攻破」,但在沒有強力佐證的情況下要說「哼!換掉單引號根本沒屁用」則略嫌浮誇。

以上看法屬爬文及實驗後的心得,受限個人所見所學,就當拋磚引玉吧,以下開放想打臉、補刀的朋友提出實證討論。(我真的好想看到單引號置換法被打穿)

TFS Build Queue卡單排除經驗一則

$
0
0

今早使用TFS Build Service建置部署時,建置作業卡在Queue裡遲遲不開始,等了五分鐘感到不對勁,展開調查。

狀態顯示我排在第二順位,似乎在等待其他建置執行完畢,但超過5分鐘頗為異常,決定查查前面的烏龜車是哪一台。

登楞!整個Queue裡只有我一人。夾緊擴約肌在廁所門口苦等十分鐘,忍不住破門想抓出是誰著佔茅坑,結果沒人?

推測是TFS Build Service故障,啟動3R(Restart, Reboot, Reinstall)第一步,遠端登入Build Service主機開啟TFS管理主控台,原本想重啟Build Controller或Build Agent就好,但兩個服務的Restart鈕沒反應,最後按Build Service的Restart鈕重啟服務後問題排除。

開啟TFS Build Service Log

$
0
0

前天提過的TFS Build Queue卡單今天再度上演,一樣又是重啟TFS Build Service才解決,由於已非偶發罕例,決定展開調查。由事件檢視器查到一筆發生在卡單前一刻的可疑錯誤,Build 1257號,正好是被卡住1258號的前一筆,訊息為An error occurred while calling tracking participants causing the instance to be aborted. 而回頭檢查前天出錯前也有一筆一模一樣的錯誤訊息,推測極可能是導致Queue停止運作的元凶。

我沒找到方法由數字1257反查是哪個專案及編譯結果,只好回歸土方法-逐一偵訊詢問關係人,最後找出失敗建置的Log,確認建置的部署步驟發生檔案複製錯誤,但單純建置失敗為何導致Build Service故障則是個謎。想進一步調查卻苦無線索,爬文得知TFS Build Service可以開啟Log以保留更多偵錯資訊。網路上找到不少教學(),教導修改TFSBuildServiceHost.exe.config設定BuildServiceTraceLevel及TeamFoundationTextWriterTraceListener 的方法,我找到config,發現已內附被<!-- -->註解掉與教學文相同的<system.diagnostics>,立即取消註解,指定Log路徑,但試了好久都不成功,Log檔始終沒出現。再爬文,才發現被擺了一道,Microsoft Connect上有一則Issue Report

This is a feature that had been improved with TFS 2012, and the MSDN documentation for it can be found at
http://msdn.microsoft.com/en-us/library/dd723544.aspx#events
(Manage Your Build System, Use event logs to diagnose problems)

In short, all logging is now available in the Event Viewer, and this can be accessed from the Team Foundation Server Administration Console in the Build Configuration section. As described in the referenced MSDN page, increasing levels of detail can be found in the recent events, operational log, and analytic log (which must first be enabled). Please take a look and let us know if you have any further problems.

Our apologies for the confusion regarding this change. Obviously the leftover comments in the TFSBuildServiceHost.exe.config file regarding trace logging were misleading, and we’ll have those removed in a future release.

意思是從TFS2012起,<system.diagnostics>只用來控制Log詳細度,Log不再寫成檔案而是改存入Windows事件(說實在話,寫成Event是比Log檔方便很多)。TFSBuildServiceHost.exe.config 設定檔殘留被註解掉的TeamFoundationTextWriterTraceListener 是個陷阱,真是抱歉!orz

照著MSDN文件說明,在事件檢視器開啟「Show Analytic and Debug Logs」:

找到Event Viewer/Application and Service Logs/Microsoft/Team Foundation Server/Build Services/Analytic按右鍵選單「Enable Log」:

重啟TFS Build Service後就會啟用記錄功能,我試著排入一個編號1258的建置,在事件中果然找到1258號對應到的建置定義資訊。下回再遇到狀況就會有多一點線索可追,至於能不能抓出卡單之謎?就讓我們拭目以待。

Chrome的Button Click行為差異

$
0
0

同事報案網頁在IE與Chrome表現不同,依稀記得遇過,但沒寫成沒找到明確記錄,花了時間回想、研究、實驗,得到結論後才恍然憶起,從HipChat對談翻出以前的辦案記錄。明明是前科犯還重啟調查,記憶力壞掉好可怕,也懊悔浪費了時間。由此得一結論-「勿以茶包小而不記,永遠別信任中年人的記憶力」,故寫此文。

用範例程式說明如下: Live Demo

<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<metaname="viewport"content="width=device-width">
<title>Button Click</title>
<style>
    button { padding: 6px; }
    span { 
      background-color: yellow; 
    }
</style>
</head>
<body>
<button>
<span>CLICK</span>
</button>
<scriptsrc="https://code.jquery.com/jquery-2.1.4.js">
</script>
<script>
    $("button").click(function(e) { 
      console.log('Button Clicked'); 
      console.log("e.target=" + e.target.outerHTML);
    });
    $("span").click(function(e) {
      console.log("Span Clicked");
    })
</script>
</body>
</html>

在<button>裡放了一個<span>,二者都掛上Click事件。在Chrome,點擊文字部分,Span跟Button的Click事件都會被觸發,而在Button Click事件裡e.target指向Span;若點擊文字以外的區域,則只有Button Click被觸發。

IE以及Firefox行為則不同,藏在Button中的Span不接受Click事件,也不會成為e.target,無論點擊哪裡都只觸發Button Click。換句話說,對IE跟Firefox來說,整個Button是一體的,內部的元素不會獨立回應Click事件。

顯然這又是各家瀏覽器對Button Click行為的詮釋差異,但這回不合群的是Chrome同學,在跨瀏覽器之路撒了一顆小石頭…:P

Viewing all 2311 articles
Browse latest View live