上色

Code Block

2023年11月29日 星期三

[RouterOS] Ikev2/IPsec VPN設定

 記個備忘錄,免得之後忘了


###設定認證

/certificate

add common-name=ca name=ca

sign ca ca-crl-host=2.2.2.2


/certificate

add common-name=2.2.2.2 subject-alt-name=IP:2.2.2.2 key-usage=tls-server name=server1

sign server1 ca=ca


/certificate

add common-name=rw-client1 name=rw-client1 key-usage=tls-client

sign rw-client1 ca=ca


###Export client certificate (pkcs12 format)

export-certificate rw-client1 export-passphrase=1234567890 type=pkcs12

export-certificate ca type=pem


###Set profile

/ip ipsec profile

add name=ike2


###Set proposal

/ip ipsec proposal

add name=ike2 pfs-group=none


###Set IP pool

/ip pool

add name=ike2-pool ranges=192.168.77.2-192.168.77.254


###Config

/ip ipsec mode-config

add address-pool=ike2-pool address-prefix-length=32 name=ike2-conf


##Policy

/ip ipsec policy group

add name=ike2-policies

/ip ipsec policy

add dst-address=192.168.77.0/24 group=ike2-policies proposal=ike2 src-address=0.0.0.0/0 template=yes


###Peer

/ip ipsec peer

add exchange-mode=ike2 name=ike2 passive=yes profile=ike2


###Identity

/ip ipsec identity

add auth-method=digital-signature certificate=server1 generate-policy=port-strict mode-config=ike2-conf peer=ike2 policy-template-group=ike2-policies


###Firewall

###If this router is VPN server (clients connect to it), then you want (before the last drop in input chain):

/ip firewall filter

add chain=input dst-port=500,4500 protocol=udp action=accept

add chain=input protocol=ipsec-esp action=accept


###VPN clients should access router itself (via encrypted tunnel),

/ip firewall filter

add chain=input ipsec-policy=in,ipsec action=accept


2023年3月12日 星期日

[JQuery] ready is deprecated

以下幾種敘述都可以代表當網頁DOM建置完畢之後再執行
$( handler )
$( document ).ready( handler )
$( "document" ).ready( handler )
$( "img" ).ready( handler )
$().ready( handler )
但根據jQuery 3.0文件說明,只建議第一種方法,因此其餘方法會被標示為deprecated

2021年7月4日 星期日

[.Net][C#] 禁止TextBox輸入數字

太常用到這功能了,寫下來記錄一下
以下code都寫在TextBox的KeyPress()裡面就可以了
//only allow integer (no decimal point)
if (!char.IsDigit(e.KeyChar) && (e.KeyChar != '.') && (e.KeyChar != '-') && (e.KeyChar != 8))
	e.Handled = true;
// only allow one decimal point
if ((e.KeyChar == '.') && ((sender as TextBox).Text.IndexOf('.') > -1))
	e.Handled = true;
// only allow sign symbol at first char
if ((e.KeyChar == '-') && ((sender as TextBox).Text.IndexOf('-') > -1))
	e.Handled = true;
if ((e.KeyChar == '-') && !((sender as TextBox).Text.IndexOf('-') > -1) && ((sender as TextBox).SelectionStart != 0))
	e.Handled = true;
首先利用IsDigit判斷輸入的是否為數字,若不是的話只能允許"."、"-"、BackSpace(←,KeyChar為8) 

接下來分別判斷小數點及負號
  1. 小數點只能出現一次,用IndexOf('.')判斷是否已經有小數點,若有的話則不允許輸入 
  2. -號只能出現一次而且只能出現在字串最前面,因此先以IndexOf('-')判斷是否出現過,若已出現過且位置不為0(最前面)則不允許輸入

2018年7月9日 星期一

[.Net] 以string,Join串接array of array(jagged array)

大家都知道使用string.Join可以串接陣列或清單中字串,像是下面這樣的形式
string[] source = new string[]{"one", "two", "three"};
string result = string.Join(",", source);

但是當陣列中還有陣列(jagged array, e.g. array of array)的時候怎麼辦呢?
這時候LINQ的Select語法就派上用場了,一層一層的串上去就對了
string[][][] source = new string[][][]{
 new string[][]{
  new string[]{"1", "2", "3"},
  new string[]{"one", "two", "three"}
 },
 new string[][]{
  new string[]{"4", "5", "6"},
  new string[]{"five", "six", "seven"}
 }
};

string result = string.Join(",", source.Select(m => string.Join(",", m.Select(n => string.Join(",", n)))));

如此一來就可以成功串接每一層陣列中的字串了。

Reference:
https://stackoverflow.com/questions/35102320/c-sharp-copying-jagged-arrays-to-strings


2018年7月4日 星期三

[.Net] Office interop runtime無法執行

紀錄一下最近遇到的問題,使用C#於MS Office 2013的環境上開發了一個程式,於MS Office 2010的機器上運作正常,但拿到MS Office 2007的機器上運作就會報錯。

首先,PIAs(Primary Interop Assemblies)必須與想要支援的最低版本相容,亦即想要支援Office 2007的話就必須使用v12的PIAs。
所以必須在Project的reference中加入v12版本的office PIAs(可以使用NuGet直接找到v12版本的PIA,亦即Microsoft.Office.Interop(v12)加入);但是光這樣是不夠的,因為當你的機器上有安裝更新版Office(如Office 2013)的時候,編譯時候會自動redirect到新版的PIAs,也就是在本機的GAC(global assembly cache)中所安裝的版本。

要修正這個問題,必須停止所謂的assembly binding redirection,可依照下列步驟進行:

1. 打開C:\Windows\Assembly\GAC或C:\Windows\Assembly\GAC_MSIL資料夾(根據你的windows版本而定)。
2. 找到對應的PIA,如我要使用Word的PIA,就找到Policy.12.0.Microsoft.Office.Interop.Word這樣的資料夾。
3. 在資料夾中找到設定的xml檔案,將下面這段註解掉
<bindingredirect newversion="14.0.0.0" oldversion="12.0.0.0"></bindingredirect>
變成
<!--<bindingredirect newversion="14.0.0.0" oldversion="12.0.0.0"></bindingredirect>-->
如此一來就會不會在redirect到新版本的PIAs,可以達到在不同機器上的相容性。

Reference:
https://stackoverflow.com/questions/6984733/office-2007-pia

2018年6月28日 星期四

[.Net] Embedded dll into exe

1. Add dll to resources, set the build action to "embedded resources" 2. Add following code to "Program.cs"
static void Main()
{
 //load resource dll
 AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

 Application.EnableVisualStyles();
 Application.SetCompatibleTextRenderingDefault(false);
 Application.Run(new frmTDSCreator());
}

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
 string resourceName = "TDSCreator.Resources." + new AssemblyName(args.Name).Name + ".dll";
 using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
 {
  byte[] assemblyData = new byte[stream.Length];
  stream.Read(assemblyData, 0, assemblyData.Length);
  return Assembly.Load(assemblyData);
 }
}

2016年6月3日 星期五

[.Net] 讀取CSV出現亂碼

當CSV檔中含有中文時,使用StremReader讀取通常都會出現亂碼,這是因為.Net預設使用Unicode編碼,但通常Excel還是使用Big5編碼,此時只要以下列方式指定系統編碼即可解決;
System.IO.FileStream fs = new System.IO.FileStream(fileName, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.ReadWrite);
System.IO.StreamReader sr = new System.IO.StreamReader(fs, System.Text.Encoding.Default);
使用System.Text.Encoding.Default指定系統預設編碼為StremReader的編碼格式。

[.Net] 字串轉為DateTime

通常需要將字串轉為DateTime的時候,一大問題就是來源格式常常不是固定的格式,而是有各種五花八門的格式,這時候就可以利用以下方式進行轉換:

首先定義可能出現的各種日期格式,可參照https://msdn.microsoft.com/en-us/library/8kb3ddd4(VS.71).aspx

string[] dateTimeList = { 
 "yyyy/M/d tt hh:mm:ss", 
 "yyyy/MM/dd tt hh:mm:ss", 
 "yyyy/MM/dd HH:mm:ss", 
 "yyyy/M/d HH:mm:ss", 
 "yyyy/M/d", 
 "yyyy/MM/dd",
 "M/d HH:mm:ss"
}; 

接著就可以開始處理字串,使用CultureInfo.InvariantCulture解析不同國家語言格式的字串(或是套用目前的國家地區設定解析,CultureInfo.CurrentCulture),使用DateTimeStyles.AllowWhiteSpaces避免字串中可能出現的無意義空白;此外為了避免依然轉換錯誤,可以將整段敘述加入到Try...Catch區塊中,再於Catch區塊中對轉換錯誤加以處理。

string DateTime date;
try
{
 date = DateTime.ParseExact(dateString, dateTimeList, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces);
 transferFlag = true;
}
catch (Exception e)
{
 throw new Exception("日期格式錯誤: " + e.Message);
}

Ref: https://dotblogs.com.tw/chhuang/2008/03/18/1921

2016年2月1日 星期一

[Android] Android 6.0 (marshmallow) run-time permission 執行期間權限取得

自Android 6.0開始,除了必須在AndroidManifest.xml中宣告權限,在安裝時告知使用者外,執行期間需要用到特殊權限的話也必須告知使用者並取得。

權限可區分為以下兩大類

  1. 一般權限(PROTECTION_NORMAL),亦即在AndroidManifest.xml中宣告即可,在程式安裝時即授予程式該權限,用戶也無法手動取消。在Android 6.0(API 23)中,一般權限列表如下︰
    • ACCESS_LOCATION_EXTRA_COMMANDS
    • ACCESS_NETWORK_STATE
    • ACCESS_NOTIFICATION_POLICY
    • ACCESS_WIFI_STATE
    • BLUETOOTH
    • BLUETOOTH_ADMIN
    • BROADCAST_STICKY
    • CHANGE_NETWORK_STATE
    • CHANGE_WIFI_MULTICAST_STATE
    • CHANGE_WIFI_STATE
    • DISABLE_KEYGUARD
    • EXPAND_STATUS_BAR
    • GET_PACKAGE_SIZE
    • INTERNET
    • KILL_BACKGROUND_PROCESSES
    • MODIFY_AUDIO_SETTINGS
    • NFC
    • READ_SYNC_SETTINGS
    • READ_SYNC_STATS
    • RECEIVE_BOOT_COMPLETED
    • REORDER_TASKS
    • REQUEST_INSTALL_PACKAGES
    • SET_TIME_ZONE
    • SET_WALLPAPER
    • SET_WALLPAPER_HINTS
    • TRANSMIT_IR
    • USE_FINGERPRINT
    • VIBRATE
    • WAKE_LOCK
    • WRITE_SYNC_SETTINGS
    • SET_ALARM
    • INSTALL_SHORTCUT
    • UNINSTALL_SHORTCUT
  2. 需要執行期間授權的權限,用戶也可以在設定中隨時關閉他們,Android將之分為數個類別,當取得類別中的其中一個權限時,也就等於取得該類別所有權限,分類如下︰

若要取得權限,需在程式碼中加入
ActivityCompat.requestPermissions(Activity activity, String[] permissions, int requestCode)

若要確認是否具有權限,可使用
ContextCompat.checkSelfPermission (Context context, String permission)

而當使用者拒絕授予此權限時,開發者可能會需要說明該權限的用途並再次詢問,這時可使用
ActivityCompat.shouldShowRequestPermissionRationale(Activity activity, String permission)
當使用者拒絕授予權限時,下次調用此函式就會返回true,開發者可在此時加入權限說明。

當使用者選擇授予或拒絕此權限時,會呼叫onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)方法,此時override此方法即可處理需要權限的操作。


具體程式碼如下(以讀寫SD卡為例)︰
    

private final int REQUEST_CODE_ASK_PERMISSIONS = 10;

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
 switch (requestCode) {
  case REQUEST_CODE_ASK_PERMISSIONS:
   if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
    // Permission Granted
    //執行需要讀寫SD卡的操作
   } else {
    // Permission Denied
    Toast.makeText(MainActivity.this, getString(R.string.noPermission), Toast.LENGTH_LONG).show();
   }
   break;
  default:
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);
 }
}

private void checkPermission(){
 //讀取關於讀寫外部儲存裝置的權限
 int hasReadExternalStoragePermission = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
 //若沒有權限則詢問取得
 if (hasReadExternalStoragePermission!= PackageManager.PERMISSION_GRANTED)
 {
  if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE)){
   //當被使用者拒絕後再次詢問時,說明該權限用途,用戶可選擇重試(重新詢問是否授予)或取消(維持不授予權限)
   new AlertDialog.Builder(MainActivity.this)
     .setMessage("該權限用以讀寫SD卡")
     .setPositiveButton("重試", new DialogInterface.OnClickListener() {
      @Override
      public void onClick(DialogInterface dialog, int which) {
       ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE_ASK_PERMISSIONS);
      }
     })
     .setNegativeButton("取消", null)
     .create()
     .show();
   return;
  }
  ActivityCompat.requestPermissions(MainActivity.this,new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE},REQUEST_CODE_ASK_PERMISSIONS);
 }
 else
  //已有權限,直接執行需要讀寫SD卡的操作
}

Reference: Android developer documents

2015年12月14日 星期一

[Android] 偵測方向使用不同layout

若要偵測使用者螢幕方向,並使用對應不同xml的layout,可使用下列方式︰

  1. 設計兩種不同layout的xml檔案。
  2. 在AndroidManifest.xml中要改變的activity中新增屬性︰
  3. android:configChanges="orientation|keyboardHidden|screenSize"
  4. 如此一來當方向或螢幕大小改變的時候,就會觸發︰
    onConfigurationChanged(Configuration newConfig)
  5. 依照類似下面的方式判斷長寬,以決定適用的layout file。
    public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            Display display = getWindowManager().getDefaultDisplay();
            int width;
            int height;
            Point size = new Point();
            display.getSize(size);
            width=size.x;
            height=size.y;
            if (width>height) { //Landscape
                setContentView(R.layout.activity_main_land);
                initView();
            }
            else{
                setContentView(R.layout.activity_main_port);
                initView();
            }
        }
    

2015年12月11日 星期五

[Android] Shell Tool for root commands


import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Scanner;

import android.content.res.AssetManager;
import android.os.Build;
import android.util.Log;

public class ShellUtils
{
 private static String busyboxPath="/system/xbin/busybox";

 /**
  * Set custom busybox path
  * @param path Path
  */
 public static void setCustomBusybox(String path){
  busyboxPath = path;
 }
 
 public static boolean checkBusyboxPath(){
  try{
   if (!new File("/system/xbin/busybox").exists()){
    if(!new File("/system/bin/busybox").exists())
     return false;
    else busyboxPath="/system/bin/busybox";
   }
            else busyboxPath="/system/xbin/busybox";
  }catch (SecurityException e){
   return false;
  }
  return true;
 }

 /**
  * Try to get root permission
  * @return true means success; false means fail
  */
 public static boolean rootCheck(){
  //List all the files under "/data/data", doing this operation needs root permission
  //CommandResult commandResult = execCommand(false,true, "ls /data/data/");
  //Empty result means there's no root permission
        //return !commandResult.successMsg.isEmpty();
  return execCommand(false,true,"").result==0;
 }

 @SuppressWarnings("deprecation")
 public static boolean initiateBusybox(){
  //Clear old files
  File busybox = new File(GlobalVariable.getContext().getFilesDir()+"/busybox");
  if (busybox.exists())
   busybox.delete();
  //Copy corresponding busybox to internal folder
  String cpuVersion;
  if (android.os.Build.VERSION.SDK_INT <21)
   cpuVersion= Build.CPU_ABI;
  else
   cpuVersion=Build.SUPPORTED_ABIS[0];
  //Determine busybox version
  String filename;
  cpuVersion=cpuVersion.toLowerCase();
  if (cpuVersion.contains("arm"))
   filename="busybox-armv5l";
  else if (cpuVersion.contains("x86"))
   filename="busybox-i686";
  else
   return false;
  AssetManager assetManager = GlobalVariable.getContext().getAssets();
  InputStream in = null;
  OutputStream out = null;
  try {
   in = assetManager.open(filename);
   out = new FileOutputStream(busybox);
   byte[] buffer = new byte[1024];
   int read;
   while ((read = in.read(buffer)) != -1) {
    out.write(buffer, 0, read);
   }
  }
  catch (IOException e) {
   e.printStackTrace();
   return false;
  }
  finally {
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
   if (out != null) {
    try {
     out.close();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
  }
  //Make busybox executables
  ShellUtils.execCommand(false, true, "chmod 0755 " + GlobalVariable.getContext().getFilesDir() + "/busybox");
  return true;
 }

 /**
  * Execute commands and get the results
  * @param isBusybox Run cmds via busybox or not
     * @param isRoot Run cmds as root?
  * @param cmds Command(s)
  * @return Results of commands
  */
 public static CommandResult execCommand(boolean isBusybox, boolean isRoot, String...cmds){
        int result=-1;
        StringBuilder successMsg = null;
        StringBuilder errorMsg = null;
        if (cmds==null || cmds.length==0)
            return new CommandResult(result,null,null);
  try{
            Process proc=Runtime.getRuntime().exec(isRoot?"su":"sh");
   DataOutputStream opt = new DataOutputStream(proc.getOutputStream());
   boolean exitCmdFound=false;
   //Write cmds
   for(String cmd:cmds){
                //Using busybox and check its path
    if (isBusybox)
     opt.writeBytes(busyboxPath + " ");
    //Unless make sure cmd is in English, using UTF-8 coding, avoid wrong coding.
    opt.write(cmd.getBytes(Charset.forName("utf-8")));
    if(!exitCmdFound && cmd.equals("exit"))
                    exitCmdFound=true;
    opt.writeBytes("\n");
   }
   if(!exitCmdFound)
                opt.writeBytes("exit\n");
   opt.flush(); 
            //Get results
            result=proc.waitFor();
            successMsg = new StringBuilder();
            errorMsg = new StringBuilder();
            Scanner scanner;
            scanner=new Scanner(new InputStreamReader(proc.getInputStream()));
            while (scanner.hasNextLine())
                successMsg.append(scanner.nextLine());
            scanner=new Scanner(new InputStreamReader(proc.getErrorStream()));
            while (scanner.hasNextLine())
                errorMsg.append(scanner.nextLine());
  } catch (Exception e){
            e.printStackTrace();
        }
        return new CommandResult(result,successMsg==null?null:successMsg.toString(), errorMsg==null?null:errorMsg.toString());
 }

    public static class CommandResult {

        /**
         * Result of command
         * 0: normal
         * else: error
         */
        public int    result;
        public String successMsg;
        public String errorMsg;

        public CommandResult(int result, String successMsg, String errorMsg) {
            this.result = result;
            this.successMsg = successMsg;
            this.errorMsg = errorMsg;
        }
    }
}

2015年7月31日 星期五

[C#] Load custom cursor from resources

Sometime you need a custom cursor for your application, the following step let you use your custom cursor in the resources.


  1. Add cursor files (*.cur) to resources
    • In [Properties] -> [Resources] tab, click [Add Resource] -> [Add Existing File...]
    • Choose your custom cursors and click OK.
  2. Load cursor with following code:
this.Cursor = new Cursor(new System.IO.MemoryStream(Properties.Resources.MyCursor));
, where Mycursor refers to your cursor name.

2015年7月25日 星期六

[C#] 取得picture box在zoom模式時的正確圖片座標

private Point unScale(Point scaledP)
{
 if (picturebox1.SizeMode != PictureBoxSizeMode.Zoom) //only zoom mode need to scale
  return scaledP;
 Point unscaled_p = new Point();
 // image and container dimensions
 int w_i = picturebox1.Image.Width;
 int h_i = picturebox1.Image.Height;
 int w_c = picturebox1.Width;
 int h_c = picturebox1.Height;
 float imageRatio = w_i / (float)h_i; // image W:H ratio
 float containerRatio = w_c / (float)h_c; // container W:H ratio

 if (imageRatio >= containerRatio)
 {
  // horizontal image
  float scaleFactor = w_c / (float)w_i;
  float scaledHeight = h_i * scaleFactor;
  // calculate gap between top of container and top of image
  float filler = Math.Abs(h_c - scaledHeight) / 2;
  unscaled_p.X = (int)(scaledP.X / scaleFactor);
  unscaled_p.Y = (int)((scaledP.Y - filler) / scaleFactor);
 }
 else
 {
  // vertical image
  float scaleFactor = h_c / (float)h_i;
  float scaledWidth = w_i * scaleFactor;
  float filler = Math.Abs(w_c - scaledWidth) / 2;
  unscaled_p.X = (int)((scaledP.X - filler) / scaleFactor);
  unscaled_p.Y = (int)(scaledP.Y / scaleFactor);
 }
 return unscaled_p;
}

2015年7月22日 星期三

[C#] Rotate bitmap with a special angle

public unsafe void Rotate(Bitmap bmp, float angle)
{
  int width=bmp.Width;
  int height=bmp.Height;
 /*
  * right, down = positive
  * p1------------p2
  * |            | 
  * p4------------p3
  * 
  * p1(0,0), p2(width-1,0), p3(width-1,height-1), p4(0,height-1)
  * 
  * In this coordinate system(left-handed coordinate system), clockwise rotation matrix (theta): (cos -sin 
  *                                                                                               sin  cos)
  * 
  */
 //Calculate new vertex
 Point p1 = RotatePoint(new Point(0, 0), angle);
 Point p2 = RotatePoint(new Point(width - 1, 0), angle);
 Point p3 = RotatePoint(new Point(width - 1, height - 1), angle);
 Point p4 = RotatePoint(new Point(0, height - 1), angle);
 //Calculate new size
 int dstWidth = Math.Max(Math.Abs(p3.X - p1.X) + 1, Math.Abs(p4.X - p2.X) + 1);
 int dstHeight = Math.Max(Math.Abs(p3.Y - p1.X) + 1, Math.Abs(p4.Y - p2.Y) + 1);
 /*
  * Calculate offset between old and new coordinate system
  * left-top point in new coordiante system -> (0,0)
  * 
  */
 int offsetX = -new int[4] { p1.X, p2.X, p3.X, p4.X }.Min();
 int offsetY = -new int[4] { p1.Y, p2.Y, p3.Y, p4.Y }.Min();
 //create bmp
 Bitmap dstBitmap = new Bitmap(dstWidth, dstHeight, PixelFormat.Format32bppArgb);
 Rectangle srcRect = new Rectangle(0, 0, width, height);
 Rectangle dstRect = new Rectangle(0, 0, dstWidth, dstHeight);
 BitmapData srcBmpData = bmp.LockBits(srcRect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
 BitmapData dstBmpData = dstBitmap.LockBits(dstRect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
 //define sin and cos
 double sin = Math.Sin(angle * Math.PI / 180);
 double cos = Math.Cos(angle * Math.PI / 180);
 int srcStride = srcBmpData.Stride;
 int dstStride = dstBmpData.Stride;
 //define pointer
 byte* srcP = (byte*)srcBmpData.Scan0.ToPointer();
 byte* dstP = (byte*)dstBmpData.Scan0.ToPointer();
 Parallel.For(0,dstHeight,i=>
 {
  Parallel.For(0, dstWidth, j =>
  {
   int k = 4 * j + i * dstStride;
   //Calculate corresponding point in old coordinate system
   Point oldPoint = RotatePoint(new Point(j - offsetX, i - offsetY), -angle);
   if (oldPoint.X >= 0 && oldPoint.X < width && oldPoint.Y >= 0 && oldPoint.Y < height)
   {
    dstP[k] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y];
    dstP[k + 1] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y + 1];
    dstP[k + 2] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y + 2];
    dstP[k + 3] = srcP[4 * oldPoint.X + srcStride * oldPoint.Y + 3];
   }
   else
   {
    dstP[k] = dstP[k + 1] = dstP[k + 2] = 0xff;
    dstP[k + 3] = 0x0;
   }
  });
 });
 bmp.UnlockBits(srcBmpData);
 dstBitmap.UnlockBits(dstBmpData);
 bmp = (Bitmap)dstBitmap.Clone();
 dstBitmap.Dispose();
 width = bmp.Width;
 height = bmp.Height;
}

2015年6月17日 星期三

[Android] commit()與apply()

SharedPreferences.Editor中的commit()與apply()都是向SharedPreferences寫入數據,差別如下︰

commit()︰立刻同步寫入數據到磁碟中的SharedPreferences,並且具有回傳值(boolean)表示成功或失敗

apply()︰API 9之後加入的method,會將欲寫入的數據暫存於記憶體中,進行一個非同步寫入,因此速度較commit()快,但是不具有傳回值

因此假如不需要傳回值的時候,建議使用apply()取代commit()

Android Developer的解釋如下︰
As SharedPreferences instances are singletons within a process, it's safe to replace any instance of commit() with apply() if you were already ignoring the return value.
You don't need to worry about Android component lifecycles and their interaction with apply() writing to disk. The framework makes sure in-flight disk writes from apply() complete before switching states.
 Reference: http://developer.android.com/reference/android/content/SharedPreferences.Editor.html

2015年6月13日 星期六

[Windows] Windows 7 / Windows 8破解使用者密碼

若是連Administrator都被密碼鎖住無法登入,可依照以下方法破解︰

1. 使用任何可開機裝置進入系統(Windows安裝光碟->修復主控台、Windows PE、MS-DOS with NTFS support、Linux),只要可以操作檔案系統即可

2. 輸入以下命令將放大鏡工具改為命令提示字元(Command Prompt)︰

    c: (或是你安裝windows的硬碟代號,不一定是c)
    cd windows\system32
    ren Magnify.exe Magnify1.exe (將原本的放大鏡程式改名)
    ren cmd.exe Magnify.exe (將命令提示字元改名為放大鏡)

3. 重新開機,進入Windows 7登入畫面

4. 點選左下角的「輕鬆存取」,勾選「讓螢幕上的項目放大一些(放大鏡)」,按下「確定」,即可開啟命令提示字元(Command Prompt)

5. 輸入以下命令,創建一個具有系統管理員身份的帳戶︰

    net user test123 /add
    net localgroup administrators test123 /add

    以上兩行命令代表創建一個test123的帳戶,並賦予系統管理員權限

6. 以剛剛創立的test123帳戶即可成功登入系統

7. 輸入以下命令,還原放大鏡工具︰
 
    ren Magnify.exe cmd.exe
    ren Magnify1.exe Magnify.exe

8. 進入「控制台」->「使用者帳戶」-> [原本的使用者] -> 「移除密碼」

9. 登入後即可以原本的使用者登入(不需密碼)

2015年6月10日 星期三

[Android] Custom ListView with SimpleAdapter

若要以SimpleAdapter建立包含RadioButton或是CheckBox的Custom ListView,可依照以下方法︰ 1. 將ListView中將各元件設定為以下屬性
android:focusable="false"
android:focusableInTouchMode="false"
android:clickable="false"

2. 建立一個變數以管理RadioButton或是CheckBox的狀態 

3. 建立ListView的OnItemClickListener方法以管理RadioButton或是CheckBox的狀態

Example:
//Create data
private ArrayList<<HashMap<String, Object>> itemList;

依照以下格式設定資料來源
Title: 主標題
Content: 副標題(內容)
Checked: 核取狀態(true / false)

final SimpleAdapter simpleAdapter = new SimpleAdapter(this,itemList,R.layout.customListView,new String[]{"Title","Content","Checked"},new int[]{R.id.TextView1,R.id.TextView2,R.id.RadioButton1});
simpleAdapter.setViewBinder(new SimpleAdapter.ViewBinder() {
 @Override
 public boolean setViewValue(View view, Object data, String textRepresentation) {
  //Hide empty textView
  if (data == null) {
   view.setVisibility(View.GONE);
   return true;
  }
  view.setVisibility(View.VISIBLE);
  return false;
 }
});
ListView listview = (ListView) findViewById(R.id.ListView1);
listview .setAdapter(simpleAdapter);
listview .setOnItemClickListener(new AdapterView.OnItemClickListener() {
 @Override
 public void onItemClick(AdapterView parent, View view, int position, long id) {
  RadioButton radiobutton1 = (RadioButton) findViewById(R.id.RadioButton1);
  //Save position
  for (HashMap<String, Object> data : itemList)
   data.put("Checked", false); //Clear state
  itemList.get(position).put("Checked", true); //Set current radio button to checked
  simpleAdapter.notifyDataSetChanged();
 }
});

最後需要取得資料時,直接判斷ArrayList中HashMap的Checked欄位即可得知被選取的資料

[Android] 避免ListView高度過高,遮蓋下方元件

在LinearLayout中設定
android:layout_weight="0.9"
即可避免遮蓋下方元件

[Android] 將App從「最近App列表」中隱藏

1. 在AndroidManifest.xml中加入以下屬性
<activity>
  ...
  android:excludeFromRecents="true"
  ...
</activity>
或是以intent啟動activity時加入以下屬性
intent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
2. 在AndroidManifest.xml中加入以下屬性
<activity>
  ...
  android:label=""
  ...
</activity>

2013年10月30日 星期三

[EndNote] 避免EndNote誤判文件中的Refence

  當文件中含有左右括號的文字時(通常是化學式或者方程式),EndNote預設會將之判別為參考文獻,但是事實上他根本不是參考文獻,因此就會出現如下的錯誤︰

此時只能按下"Ignore all"來告訴EndNote不要把該筆資料視為參考文獻,但是每次都要這麼做總是十分的魯洨...

那要如何避免這種情形呢? 只要在設定中把「暫時參考文獻格式」改掉就好,如下圖︰

要改成什麼都可以,只要不要與文件中會出現的符號衝突就可以,比如此處我是改為「<<......}」,如此一來只有被這樣的符號包圍的文字,EndNote才會將之視為參考文獻,就再也不會誤判了。