2020年9月28日 星期一

簡單來做一個 chrome extension

 

最近剛學到怎麼做 chrome 插件,趁著還沒忘記之前趕快紀錄下來,chrome 的插件(extension)其實很好寫,我會在下面帶一些必要的知識,如果看完了以後覺得有所不足可以在找找看 API 文件來看。

跟外面教學有所不同的是,外面大家都是講到怎麼跳一個popup.html出來,但是比較少有人講到怎麼去控制 browser,所以會講一下怎麼從 extension 傳遞 message 到瀏覽器裏面。下面這個圖就是今天我們要做的成果。

Image for post
Image for post
今天的目標就是要做出這個翻轉插件

第一步:建立 manifest.json 檔案

這個檔案主要是給瀏覽器知道你是一個插件,你的一些配置等等。

{
"manifest_version": 2,
"name": "把 google 首頁旋轉",
"description": "旋轉180度讓你看的不清不楚",
"version": "1.0.0",
"icons": {
"16": "icon.png",
"48": "icon.png",
"128": "icon.png"
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
],
"content_scripts": [
{
"matches": [
"https://www.google.com.tw/*"
],
"js": [
"execute.js"
]
}
]
}

name description 主要在描述你的 extension 名稱跟詳細功能。icons 代表你在 google 商店或是 extension 介面的縮小圖。browser_action 代表在瀏覽器上面點選插件的時候要彈出的 html 檔案。permissions 表示插件能動用到哪些權限,目前我只有控制瀏覽器的 tab。matches 的意思就是插件只能在 https://www.google.com.tw/* 的網域產生作用,而底下的 js 代表在該網域下會把哪一支 js 檔案植入。

第二步:建立要植入的檔案以及監聽 Message

我剛剛有在 manifest.json 內描述了要在在 https://www.google.com.tw/* 的網域下植入 execute.js 進去所以我們就建立這支檔案

const rotateEvent = () => {
document.body.style.transform = 'rotate(180deg)';
};
const reset = () => {
document.body.style.transform = '';
}

const onMessage = (message) => {
switch (message.action) {
case 'ROTATE':
rotateEvent();
break;
case 'RESET':
reset();
break;
default:
break;
}
}

chrome.runtime.onMessage.addListener(onMessage);

因為 chrome 的插件不能夠直接控制瀏覽器,所以我們會透過 chrome.runtime.onMessage 來取得插件傳入瀏覽器裏面的事件,再依照事件的類型執行不同的程式。當收到旋轉的事件,我們就把瀏覽器的 body 旋轉 180 度。

第三步:建立 popup

這邊要建立的就是插件點下去要跳出來的那個小視窗,其實那是一個html

popup.html

<!doctype html>
<html xmlns="http://www.w3.org/1999/xhtml" style="width:100px;">
<head>
<title>翻轉 google </title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body id="body">
<button id="rotate">翻轉</button>
<button id="reset">還原</button>
<script src="./popup.js"></script>
</body>
</html>

popup.js

var getSelectedTab = (tab) => {
var tabId = tab.id;
var sendMessage = (messageObj) => chrome.tabs.sendMessage(tabId, messageObj);
document.getElementById('rotate').addEventListener('click', () => sendMessage({ action: 'ROTATE' }));
document.getElementById('reset').addEventListener('click', () => sendMessage({ action: 'RESET' }))
}
chrome.tabs.getSelected(null, getSelectedTab);

上面我用 var 而不用 const 是因為每次點了插件的時候,他會重新載入 js 檔案,所以如果用 const 就會跳錯誤,用 var 可以避免掉。

當我們點選插件的按鈕時就會透過chrome.tabs.sendMessage 把事件傳送到瀏覽器,然後瀏覽器的 execute.js 接收到之後就會翻轉網頁了。

第四步:放到 chrome 裡面玩玩看

在瀏覽器輸入網址 chrome://extensions 就會打開插件管理器,這時候我們把上面的開發人員選項打勾,然後點選載入未封裝擴充功能的按鈕,選擇到我們的專案資料夾打開。

Image for post
Image for post

這樣就可以使用了,馬上到 google 台灣首頁,就可以測試插件功能了。

第五步:封裝給其他人用

點選封裝擴充功能,就可以把你的插件變成 crx 給別人使用了,那要注意產生的 pem 檔案要保留下來,這樣如果之後你要出新的版本就要把 pem 檔案放進去重新封裝。

Image for post
Image for post

總結

這次我們使用了sendMessageonMessage 的功能實現了由插件操控瀏覽器的功能讓 google 的首頁翻轉,雖然簡單的範例,但是我們可以由這個範例知道差件不能夠直接控制瀏覽器要透過植入的腳本接收訊息。這次範例的程式碼放在這邊,可以把他 clone 下來玩玩看。

資料來源:https://medium.com/hybrid-maker/%E7%B0%A1%E5%96%AE%E4%BE%86%E5%81%9A%E4%B8%80%E5%80%8B-chrome-extension-2359e43f282a

 

2020年9月24日 星期四

軟體界常見協議一覽

軟體界有很多常用的協議,開源和閉源協議皆有。而每種協議都有它的特色和限制,在開發產品時需要多加注意。這邊分析了一下常見開源協議,以及它們的特性和限制。

首先先來看一張比較簡單的分類圖,這張圖主要是針對會變更原始碼的部分--我們在後面會說明,有一個協議會因為不變更原始碼而可以轉成閉源。

常見協議一覽

  1. GPL
  2. LGPL
  3. Mozilla
  4. BSD
  5. MIT
  6. Apache

GPL

流氓。有四大自由標準必須遵守

  1. 基於任何目的,按你的意願執行軟體的自由。
  2. 學習軟體如何工作的自由,按你的意願修改軟體以符合你的計算的自由。可存取原始碼是此項自由的先決條件。
  3. 分發軟體副本的自由。
  4. 將你修改過的軟體版本再分發給其他人的自由。這樣可以讓整個社區有機會共享你對軟體的改動。可存取原始碼是此項自由的先決條件。

強迫完全開源,只要沾到GPL就全滅

LGPL

GNU較寬鬆公共授權條款(英語:GNU Lesser General Public License,簡稱:LGPL)是由自由軟體基金會公布的自由軟體授權條款。

它允許企業與軟體開發者使用,或將LGPL授權的軟體整合至他們自己的軟體內(即使該軟體是私有軟體也被允許)

屬於"可商用但不可變更原始碼的協議" 比GPL(完全強迫開源)緩和一點,不強迫開源其他授權的檔案。

Mozilla Public License

MPL允許在其授權下的原始碼與其他授權的檔案進行混合,包括私有授權條款。但在MPL授權下的代碼檔案必須保持MPL授權,並且保持開源。

比GPL(完全強迫開源)緩和一點,不強迫開源其他授權的檔案。

BSD

遵守BSD License的軟體,允許用作商業用途,甚至可按照專屬授權條款進行再釋出。

所有從以BSD授權條款授權的軟體衍生著作,都必須要包含一段文字(滿長的,這邊容許我不貼上)以交代原始碼的來源。 符合以上要求的時候就可以商業運用。

MIT

被授權人有權利使用、複製、修改、合併、出版發行、散布、再授權和/或販售軟體及軟體的副本,及授予被供應人同等權利,惟服從以下義務。

在軟體和軟體的所有副本中都必須包含以上著作權聲明和MIT的許可聲明。

此授權條款並非屬copyleft的自由軟體授權條款,允許在自由及開放原始碼軟體或非自由軟體(proprietary software)所使用。

必填的版權宣告比BSD少,其餘差不多。

Apache

Apache不會強制衍生和修改產物使用相同的授權條款進行釋出(與一些著作權授權條款不同)。

但它仍然要求對所有未修改的部分應用相同的授權條款,並且在每個授權檔案中,必須保留再分發代碼中的任何原始著作權,專利,商標和歸屬通知(不需要包括任何部分的衍生作品);並且在每個更改的授權檔案中,都必須添加一條通知,說明對該檔案進行了更改。

一種比較特殊的,可更動原始碼但必須說明的可商業用條款授權。

若以後各位有機會開發自己的產品時,都要小心注意自己是不是有使用到不可商用的協議,否則可能產生著作權上面的問題喔!

資料來源 https://playrobotwiki.github.io/?fbclid=IwAR3p_d1sR7xWJ08MBWzqew84ftpmXjaaxHLc3NaKgA5enayyLnnYHi746hY#

2020年9月23日 星期三

在C#中使用SQLite資料庫

自己開發程式的時候,想用資料庫又不希望花一大筆錢

常常會使用SQLite,一個輕量級的嵌入式資料庫系統(僅單一檔案)
由於.net原生並不支援SQLite
可以去System.Data.SQLite找到在公開的函式庫
注意如果是x64的程式,則需要使用x64版本的函式庫,無法混用

在每次使用資料庫的時候,都需要建立與相關資料庫的連結

private SQLiteConnection ConnectDatabase()
{
string path = "Test.db";
string password = "Password";
SQLiteConnection connection;

if (!File.Exists(_databasePath))
{
/// for non-exist database, create it
SQLiteConnection.CreateFile(path);
connection = new SQLiteConnection(
"Data Source = " + path);
connection.Open();
connection.ChangePassword(password);
}
else
{
/// for exist database, connect it
connection = new SQLiteConnection(
"Data Source = " + path);
connection.SetPassword(password);
connection.Open();
}

return connection;
}

這邊要注意的是,由於SQLite本身並沒有支援資料庫加密
有關Password的功能(紅字部分)是System.Data.SQLite實做的
所以製作出來的加密資料庫並不能被其他的SQLite應用讀取
(例如Firefox的附加元件SQLite Manager)
所以如果不打算加密,建議將紅字部分拿掉

在連結之後,就可以透過該連結,執行SQL的命令

private void RunCommand(
SQLiteConnection connection, string commandText)
{
SQLiteCommand command =
new SQLiteCommand(connection);
command.CommandText = commandText;
command.ExecuteNonQuery();
}

除了不需要回傳值的命令(例如CREATE TABLE)以外
SQL最重要的當然是Query的指令

private List RunQueryCommand(
SQLiteConnection connection, string commandText)
{
List<string> result = new List<string>();

SQLiteCommand command =
new SQLiteCommand(connection);
command.CommandText = commandText;

using (SQLiteDataReader queryResult =
command.ExecuteReader())
{
/// read one row one time
while (queryResult.Read())
{
/// change index to get different column
result.Add(queryResult.GetValue(0)
.ToString());
}
}

return result;
}

當然,在使用完畢後一定要記得將連結中止,否則資料庫檔案會被鎖住

private void DisconnectDatabase(
SQLiteConnection connection)
{
connection.Close();
}

另外,讀取資料庫的時候,如果不能肯定資料表是否存在
則需要先做一個檢查,否則會跳出例外狀況
(當然也是可以直接用try catch將Command包住,端看習慣)

private bool IsTableExist(
SQLiteConnection connection, string name)
{
return connection.GetSchema("Tables")
.Select("Table_Name = '" + name + "'")
.Length > 0;
}

--
參考資料
SQLite 簡介
System.Data.SQLite
C sharp or .Net 使用sqlite 設定
SQLite Manager
SQL語法教學
Check if table exists

資料來源:http://codingjames.blogspot.com/2010/12/csqlite.html

2020年9月22日 星期二

在C#的Picturebox上畫線畫圓

 在C#的Picturebox上畫線畫圓, 以下是相關介紹:


可先參考 Graphics 成員
實作(注意*那行Graphics objGraphic = e.Graphics;):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
            int picBoxWidth = pictureBox1.Size.Width;
            int picBoxHeight = pictureBox1.Size.Height;
            int halfWidth = pictureBox1.Size.Width / 2;
            int halfHeight = pictureBox1.Size.Height / 2;
            Graphics objGraphic = e.Graphics; //**請注意這一行**
            Pen pen = new Pen(Color.Black);
            int b = 2; int m = 2; for (int x = 0; x < 426; x++)
            {
                int y = m * x + b; x = (y - b) / m;
                objGraphic.DrawLine(pen, x, y, -x, -y);
                System.Drawing.Drawing2D.GraphicsState graph = objGraphic.Save();
                objGraphic.Restore(graph);
            }
            objGraphic.DrawLine(pen, 0, halfHeight, picBoxWidth, halfHeight);
            objGraphic.DrawLine(pen, halfWidth, 0, halfWidth, picBoxHeight);
            objGraphic.DrawLine(pen, 20, 20, 300, 300);
}


資料來源 http://trufflepenne.blogspot.com/2010/11/cpicturebox.html