星期二, 5月 31, 2016

[C#] 建立視窗控制代碼時發生錯誤

撰寫 ERP 時,跳出下圖 Exception,用中斷點查到問題點且寫個簡單範例也可以重現該問題

[C#] 建立視窗控制代碼時發生錯誤-1

簡化後的簡易範例,有兩個 Form
  • MDIForm:MDI Form,為起始 Form,Form1 要在 MDI Form 內
  • Form1:在 Load Event 內會呼叫 this.Hide(),Form1 上沒有任何控件
[C#] 建立視窗控制代碼時發生錯誤-2

MDIForm Layout:有個 Button 而已,用來呼叫 Form1

[C#] 建立視窗控制代碼時發生錯誤-3

MDIForm Code
namespace ExceptionDemo
{
    public partial class MDIForm : Form
    {
        private void button1_Click(object sender, EventArgs e)
        {
            Form1 frm1 = new Form1();
            frm1.MdiParent = this;
            frm1.Show();
        }
    }
}
Form1 Code
namespace ExceptionDemo
{
    public partial class Form1 : Form
    {
        private void Form1_Load(object sender, EventArgs e)
        {
            this.Hide();
        }
    }
}
實際執行就會拋出 Exception

[C#] 建立視窗控制代碼時發生錯誤-4

用中斷點去看才發現,原來 Form1 New 出來後,並沒有執行 Load,是 frm1.Show() 才會執行,上述情況,Show() 程序中有 Hide() 會有所衝突,推測跟生命週期有關係,先筆記一下

星期一, 5月 30, 2016

[Win] 刪除修復磁碟分割

某顆硬碟修復磁碟分割竟然無法直接在 Windows 磁碟管理 GUI 內刪除,查到要下 diskpart 指令才能刪除,Orz

下圖的修復磁碟分割,滑鼠右鍵沒有刪除選項,而且又剛好把中間,沒有辦法透過磁碟管理介面,把未配置空間變成單一 Partition

[Win] 刪除修復磁碟分割-1

系統管理員開啟 dos,並利用 diskpart 指令來清除

[Win] 刪除修復磁碟分割-2

清除完後,就可以在磁碟管理內看見效果啦

[Win] 刪除修復磁碟分割-3

星期四, 5月 26, 2016

[X.Andriod] ImageView

查到這篇官網文章 Display An Image,文章就是把 drawable 內圖片顯示到 ImageView 上,稍微做點變化來練習 - 隨機顯示 drawable 內圖片

drawable 內放進三張圖片
[X.Andriod] ImageView-2

Main.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:id="@+id/myButton"
        android:layout_width="fill_parent"
        android:layout_height="50.0dp"
        android:text="@string/changeImage" />
    <ImageView
        android:src="@drawable/sample1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/DemoImageView"
        android:scaleType="fitCenter" />
</LinearLayout>
[X.Andriod] ImageView-1

MainActivity.cs
using System.Reflection;

namespace ImageViewBase
{
    [Activity(Label = "ImageView 基礎練習", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            SetContentView(Resource.Layout.Main);

            Button btn = FindViewById<Button>(Resource.Id.myButton);
            btn.Click += (sender, e) =>
            {

                var image = FindViewById<ImageView>(Resource.Id.DemoImageView);

                // 產生隨機整數
                Random r = new Random(Guid.NewGuid().GetHashCode());
                int IndexID = r.Next(0, typeof(Resource.Drawable).GetFields().Length);

                // 從 Drawable 內的圖片抓出來
                FieldInfo[] infos = typeof(Resource.Drawable).GetFields();

                //取得 Drawable 圖片名稱
                string FileName = infos[IndexID].Name.ToLower();

                // 取得 ResourceID
                int RecID = Resources.GetIdentifier(
                            FileName, 
                            "drawable", 
                            "ImageViewBase.ImageViewBase");

                // 利用 ResourceID 顯示圖片
                image.SetImageResource(RecID);
            };
        }
    }
}
[X.Andriod] ImageView-3

星期二, 5月 24, 2016

[C#] EventLog

根據這篇 逐步解說:探索事件記錄檔、事件來源和項目 改寫的簡易範例,幫助自已了解 EventLog 相關使用
  • WinForm Layout
[C#] EventLog-1
  • Code
using System.Diagnostics;

namespace WalkthroughEventLog
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        EventLog log = new EventLog();
        ErrorProvider errorSource = new ErrorProvider();
        ErrorProvider errorLog = new ErrorProvider();

        private void Form1_Load(object sender, EventArgs e)
        {
            dgvEntry.AutoGenerateColumns = false;
            dtpFormatSetting();

            cboSearchEntryTypeSource();
            cboEventLogEntryTypeSource();
            ControlDefaultValue();

            txtSourceName.Validated += Text_Validated;
            txtLogName.Validated += Text_Validated;

            ExistsCheck();
            EventLogSetting();
            SearchDefault();
        }

        private void Text_Validated(object sender, EventArgs e)
        {
            ExistsCheck();
            EventLogSetting();
        }

        #region EventLog 相關
        /// <summary>
        /// 設定 EventLog.Source 和 EventLog.Log
        /// </summary>
        private void EventLogSetting()
        {
            // Source 是指 Regedit 內的機碼
            log.Source = txtSourceName.Text;
            // 應用程式及服務紀錄檔內會產生一個 Log
            log.Log = txtLogName.Text;
        }

        /// <summary>
        /// 判斷 EventLog 的 Source 是否存在
        /// </summary>
        /// <returns>true 或 false</returns>
        private bool SourceExists()
        {
            return EventLog.SourceExists(txtSourceName.Text); ;
        }

        /// <summary>
        /// 判斷 EventLog 的 Log 是否存在
        /// </summary>
        /// <returns>true 或 false</returns>
        private bool LogExists()
        {
            return EventLog.Exists(txtLogName.Text);
        }

        /// <summary>
        /// EventLog Log 不存在,必須在 lblLogGuide 顯示說明文字
        /// </summary>
        private void LogNotExistsGuide()
        {
            lblLogGuide.Text = $"{txtLogName.Text} 不存在,無法進行讀取";
        }

        /// <summary>
        /// 判斷 EventLog Source 和 Log 是否存在,不存在必須透過 ErrorProvider 提出警告
        /// </summary>
        private void ExistsCheck()
        {
            errorSource.Clear();
            errorLog.Clear();

            if (SourceExists() == false)
                errorSource.SetError(
                    txtSourceName,
                    $"{txtSourceName.Text} 不存在");

            if (LogExists() == false)
                errorLog.SetError(
                    txtLogName,
                    $"{txtLogName.Text} 不存在");
        }
        #endregion

        #region 控件設定相關
        /// <summary>
        /// 設定 DateTimePicker 格式
        /// </summary>
        private void dtpFormatSetting()
        {
            foreach (DateTimePicker dtp in this.Controls.OfType<DateTimePicker>())
            {
                dtp.Format = DateTimePickerFormat.Custom;
                dtp.CustomFormat = "yyyy/MM/dd";
            }
        }

        #region 設定 ComboBox 資料來源
        /// <summary>
        /// 設定 cboEventLogEntryType 資料來源
        /// </summary>
        private void cboEventLogEntryTypeSource()
        {
            ComboBox cbo = FindComboBox("cboEventLogEntryType");
            GetEntryType(cbo);
        }

        /// <summary>
        /// 設定 cboSearchEntryType 資料來源
        /// </summary>
        private void cboSearchEntryTypeSource()
        {
            ComboBox cbo = FindComboBox("cboSearchEntryType");
            cboSearchEntryType.Items.Add("All");
            GetEntryType(cbo);
        }

        private ComboBox FindComboBox(string cboName)
        {
            ComboBox cbo = this.Controls.Find(cboName, false).OfType<ComboBox>().FirstOrDefault();
            if (cbo == null)
                throw new Exception($"{cboName} 控件不存在");

            return cbo;
        }

        private void GetEntryType(ComboBox cbo)
        {
            cbo.Items.Add(EventLogEntryType.Error);
            cbo.Items.Add(EventLogEntryType.FailureAudit);
            cbo.Items.Add(EventLogEntryType.Information);
            cbo.Items.Add(EventLogEntryType.SuccessAudit);
            cbo.Items.Add(EventLogEntryType.Warning);
        }
        #endregion

        /// <summary>
        /// 設定控件初始值
        /// </summary>
        private void ControlDefaultValue()
        {
            txtSourceName.Text = "Source1";
            txtLogName.Text = "NewLog1";
            txtMessage.Text = "EventLog 小範例練習";

            cboEventLogEntryType.SelectedIndex = cboEventLogEntryType.FindStringExact(EventLogEntryType.Information.ToString());

            cboSearchEntryType.SelectedIndex = cboSearchEntryType.FindStringExact("All");

            dtpStartDate.Value = DateTime.Today.AddDays(-7);
            dtpEndDate.Value = DateTime.Today;
        }
        #endregion

        #region 介面操作相關
        private void btnCreateLog_Click(object sender, EventArgs e)
        {
            if (SourceExists())
                EventLog.DeleteEventSource(txtSourceName.Text);

            EventLog.CreateEventSource(txtSourceName.Text, txtLogName.Text);

            ExistsCheck();
            SearchDefault();
        }
        private void btnRemoveSource_Click(object sender, EventArgs e)
        {
            if (SourceExists())
                EventLog.DeleteEventSource(txtSourceName.Text);

            ExistsCheck();
            SearchDefault();
        }
        private void btnDeleteLog_Click(object sender, EventArgs e)
        {
            // 刪除 Log 檔案,會一併連 Source 一起刪除喔
            if (LogExists())
                EventLog.Delete(txtLogName.Text);

            ExistsCheck();
            SearchDefault();
        }
        private void btnWriteEntry_Click(object sender, EventArgs e)
        {
            if (LogExists() == false) return;

            log.WriteEntry(
                txtMessage.Text,
                (EventLogEntryType)cboEventLogEntryType.SelectedItem
                );

            SearchDefault();
        }
        private void btnClearLog_Click(object sender, EventArgs e)
        {
            if (LogExists() == false) return;

            // 清除 Log 裡面的紀錄
            log.Clear();
            SearchDefault();
        }
        #endregion

        #region 讀取 EventLogEntry 相關
        private void ReadEventLogEntry(DateTime StartDate, DateTime EndDate)
        {
            dgvEntry.DataSource = null;

            if (LogExists() == false)
            {
                LogNotExistsGuide();
                return;
            }

            dgvEntry.DataSource = log.Entries.OfType<EventLogEntry>()
                .Where(
                        e =>
                        e.TimeWritten >= StartDate &&
                        e.TimeWritten < EndDate.AddDays(1))
                .ToList();
            EventLogEntryCount();
        }

        private void ReadEventLogEntry(DateTime StartDate, DateTime EndDate, EventLogEntryType EnterType)
        {
            dgvEntry.DataSource = null;
            if (LogExists() == false)
            {
                LogNotExistsGuide();
                return;
            }

            dgvEntry.DataSource = log.Entries.OfType<EventLogEntry>()
                .Where(
                        e =>
                        e.EntryType == EnterType &&
                        e.TimeWritten >= StartDate &&
                        e.TimeWritten < EndDate.AddDays(1))
                .ToList();
            EventLogEntryCount();
        }

        /// <summary>
        /// 計算 EntryCount 資料
        /// </summary>
        private void EventLogEntryCount()
        {
            lblLogGuide.Text = $"{txtLogName.Text} 內,有 {log.Entries.Count} 筆資料";
        }

        private void btnSearch_Click(object sender, EventArgs e)
        {
            string EntryType = cboSearchEntryType.SelectedItem.ToString();
            if (EntryType == "All")
                SearchDefault();
            else
                ReadEventLogEntry(dtpStartDate.Value, dtpEndDate.Value, (EventLogEntryType)EntryType);
        }

        /// <summary>
        /// 預設搜尋模式
        /// </summary>
        private void SearchDefault()
        {
            ReadEventLogEntry(dtpStartDate.Value, dtpEndDate.Value);
        }
        #endregion
    }
}

星期一, 5月 23, 2016

[Win] 刪除連接埠內印表機

在 Win10 上設定 LQ-680C 時,該 PC 上有多個 LPT 介面,測了一下印表機到底是連接哪一個,結果沒想到確定後把印表機刪除後,連接埠內仍然有設定殘留,即使重開機也還是在,導致再安裝時,必須取別的名稱,Orz

忘記截問題圖,這是在某張截圖的背景抓出來的,可以看出 LTP2 內還有 Epson LQ-680C
LTP1 是正確的連接介面,只好先加個 2 在名稱後面,先讓使用者可以正常列印

[Win] 刪除連接埠內印表機-1

GUI 介面內,要直接刪除該連接埠,也被防呆擋下來,最後只好進 regedit 內直接刪除該印表機機碼,機碼路徑為HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Print\Prints\Epson LQ-680C

[Win] 刪除連接埠內印表機-2

刪除後 LPT2 內的印表機,就不會有 LQ-680C 存在

[Win] 刪除連接埠內印表機-3

最後再把 LPT1 的 LQ-680C-2 改為 LQ-680C,就搞定收工

星期五, 5月 20, 2016

[SQL] 使用者自訂函數應用

論壇問題
要在預設值(default)和計算欄位(Computered Column)內,把欄位名稱當成變數傳入使用者自訂函數(udf)內來填值
以下利用 TSQL 測試該情況

使用 AdventureWorks2014 DB,在內建立 Orders 和 OrdersDetail Table
USE [AdventureWorks2014]
GO

IF OBJECT_ID('Orders') IS NOT NULL
    DROP TABLE Orders

IF OBJECT_ID('OrdersDetail') IS NOT NULL
    DROP TABLE OrdersDetail

CREATE TABLE [dbo].[Orders](
    [OrderNO] [char](11) NOT NULL,
    [DetailCount] [int] NOT NULL, -- 之後要設定 udf(ColumnName) 用
    CONSTRAINT [PK_Orders] PRIMARY KEY CLUSTERED([OrderNO] ASC)
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[OrdersDetail](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [OrderNO] [char](11) NOT NULL CONSTRAINT [DF_OrdersDetail_Orders]  DEFAULT (''),
    [Product] [nchar](30) NOT NULL CONSTRAINT [DF_OrdersDetail_Product]  DEFAULT (''),
    [UnitPrice] [money] NOT NULL CONSTRAINT [DF_OrdersDetail_UnitPrice]  DEFAULT ((0)),
    [Qty] [int] NOT NULL CONSTRAINT [DF_OrdersDetail_Qty]  DEFAULT ((0)),
    CONSTRAINT [PK_OrdersDetail] PRIMARY KEY CLUSTERED ([ID] ASC)
) ON [PRIMARY]
GO
建立使用者自訂函數
  • uspGetDetailCount:用來取的 detail 內有多少筆資料
  • uspGetTotalPrice:用來加總 detail 內金額
IF OBJECT_ID(N'dbo.uspGetDetailCount', N'FN') IS NOT NULL
    DROP FUNCTION dbo.uspGetDetailCount
GO

IF OBJECT_ID(N'dbo.uspGetTotalPrice', N'FN') IS NOT NULL
    DROP FUNCTION dbo.uspGetTotalPrice
GO

CREATE FUNCTION uspGetDetailCount(@OrderNO char(11))
RETURNS int
BEGIN
    RETURN
    (
        SELECT count(*)
        FROM OrdersDetail
        WHERE OrderNO = @OrderNO
    )
END
GO

CREATE FUNCTION uspGetTotalPrice(@OrderNO char(11))
RETURNS int
BEGIN
    RETURN
    (
        SELECT sum(UnitPrice * Qty)
        FROM OrdersDetail
        WHERE OrderNO = @OrderNO
    )
END

星期二, 5月 17, 2016

[C#] 將參數值從 runtimemethodinfo 轉換為 string 失敗

設計 Global Exception Handler 時,希望能把 Exception 放進 DB 內,撰寫 ADO.NET 語法要把相關資訊存進去
public void Exception2DB(Exception ex)
{
    using (SqlConnection Conn = new SqlConnection(ConnectionString))
    {
        try
        {
            SqlCommand cmd = new SqlCommand("dmlException2DBI", conn);
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add("@ExceptionTime", SqlDbType.DateTime).Value = DateTime.Now.ToString();
            cmd.Parameters.Add("@Source", SqlDbType.Char, 100).Value = ex.Source;
            cmd.Parameters.Add("@TargetSize", SqlDbType.Char, 100).Value = ex.TargetSite;
            cmd.Parameters.Add("@Message", SqlDbType.NChar, 100).Value = ex.Message;
            cmd.Parameters.Add("@StackTrace", SqlDbType.VarChar, 1000).Value = ex.StackTrace;

            Conn.Open()
            cmd.ExecuteNonQuery();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message , "錯誤" , MessageBoxButtons.OK , MessageBoxIcon.Error);
    }
}
該語法一直發生錯誤,錯誤如下圖

[C#] 將參數值從 runtimemethodinfo 轉換為 string 失敗

一個參數一個參數拿掉來測試,才發現 Exception.TargetSite 是回傳 MethodBase,根本就不是 string,Orz
cmd.Parameters.Add("@TargetSize", SqlDbType.Char, 100).Value = ex.TargetSite;
-- 改為
cmd.Parameters.Add("@TargetSize", SqlDbType.Char, 100).Value = ex.TargetSite.Name;

星期日, 5月 08, 2016

[C#] Global Exception Handler

要在 WinForm 內建立 Global Exception Handler 機制,避免 Exception 沒有處理到,而直接拋給使用者,這篇 MSDN Application.ThreadException 事件 內範例來看就是要處理
  • AppDomain.unhandleException event 針對 non-UI thread exceptions
  • Application.ThreadException event 針對 UI thread exceptions
下面 Code 是從 MSDN 文章內抓出來,稍微整理過,方便閱讀和了解

[C#] Global Exception Handler-0

星期六, 5月 07, 2016

HP 1920-24G Switch - 利用 CLI 設定 IPv4

新買 HP 1920-24G Switch,老大要我設定並上線取代原有的 D-Link Switch,原以為是個簡單工作,就把 Switch 預設 IP(169.254.66.208) 改成內部 IP(192.168.0.49)就可以啦,但沒想到透過 Web 連線進入用 Wizard 設定 IP,竟然都會 timeout,Wizard 都不 Wizard 了,Orz
閱讀 User Guide 發現還可以利用 CLI 方式來進行設定,下面紀錄內容都是根據 User Guide 去進行設定,紀錄方便日後閱讀,完整內容,請自行參考 User Guide

超級終端機(HyperTerminal)

超級終端機(HyperTerminal)從 Windows 7 開始就已經不在內建,從這篇文章 How to Enable Hyper Terminal In Windows 7 知道,可以從Windows XP 中把 hypertrm.exe and hypertrm.dll Copy 回來用,檔案路徑分別為
  • C:\Program Files\Windows NT\hypertrm.exe
  • C:\WINDOWS\system32\hypertrm.dll

利用 HyperTerminal 連線 HP 1920-24G Switch

輸入設定檔名稱,ICON 部分在 Win10 上沒有顯示
Connection Using 選擇 COM1,公司 PC 剛好有 1 個 COM Port 可以用,要不然還真的不知道要怎麼進行設定
設定 COM Port 細節,紅框框處是有變更過,非預設值選項
在 HyperTerminal 內還要設定 Property
Emulation 選擇 VT100

以上即完成連線設定

在 HyperTerminal 內輸入指令來進行 IPv4 變更
  1. 登錄 Switch
    • Username:admin
    • Password:空
  2. 輸入 ipsetup 192.168.0.49 24 default-gate-way 192.168.0.1 來進行 IPv4 設定
  3. reboot Switch,會詢問些事項,基本上都是按 Y 往下一步(下圖沒有,懶得再去設定截圖囉)
輸入 summary 可以看見設定結果
設定第二次才成功,第一次設定完後,沒有 reboot,後來發現設定的 IP 沒有生效,後來有下 reboot 才成功

第一次設定 HP Switch,搞了好久,>.<

星期五, 5月 06, 2016

Windows 7 和 Thunderbolt

公司 Mac mini (只跑 Windows 7) 內建網路卡好像故障,發現 ping 公司網路設備常常逾時,用無線網路則是 OK,後來老大買了一個 Thunderbolt 對 Gigabit 乙太網路轉換器 來取代內建網路卡

Windows 7 和 Thunderbolt-1

安裝後,一直無法在 Windows 7 裝置管理員內看見該裝置,後來才又在官方查到,原來 Thunderbolt 設備要在 Windows 8.1 才能隨插即用,Windows 7 則是必須重啟電腦,讓它來抓,Orz

Windows 7 和 Thunderbolt-2

星期二, 5月 03, 2016

[Win10] 共享印表機電腦的預設印表機設定

公司標籤機 Godex EZPi 1300,因為機身上網路卡故障,供應商告知該機型已經沒有辦法進行網路卡更換,被迫只能用共享方式來提供服務,被當成 Printer Server PC 使用者回報,發現 PC 預設印表機都會跑掉,導致每次要進行列印前,都必須手動設定預設印表機,Orz

內部環境
  • Printer PC:Win10(10586.218) + Godex EZPi 1300
  • Client PC:Win7 和 Win10
檢查方向
  • 根據這篇記錄 [Win10] 預設印表機,確定 Printer PC 和 Clinet PC 都已經關閉 "讓 Windows 管理我的預設印表機" 選項
  • Godex EZPi 1300 Driver 已確定是官網最新的 Driver
  • 在 Google 上找到這篇討論,是跟我的情況比較類似,但也沒有討論出根本的解決方式,還有人說這是 Win10 的 bug
解決方式

突發奇想,建立一個本機帳號來當成 Client PC 驗證帳號,不要用 Printer PC 使用者本身帳號,沒想到還真的成功,原 Printer Server 上使用者的預設印表機,就不會因為 Clinet PC進行列印,而又跳掉,YA

[Win10] 共享印表機電腦的預設印表機設定-5