星期二, 9月 01, 2009

指定Java Application使用的語言

開發Java程式時, 可以利用resource的方式, 將程式的訊息以及ui等設計成支援多國語言, 然後執行時加上-Duser.language=locale_name或-Duser.country=country_name就可以切換不同的語言介面.

完整的指令為

顯示英文介面:
java -Duser.language=en -jar test.jar或
java -Duser.country=us -jar test.jar

顯示中文介面:
java -Duser.language=zh -jar test.jar或
java -Duser.country=TW -jar test.jar

星期日, 8月 23, 2009

在Linux中檢視目前cpu的運作頻率

在Windows系統中我們可以很輕易地檢視目前cpu的運作頻率, 在Linux上也是可以輕易做到的. 只要執行下面的指令:

$ cat /proc/cpuinfo

不僅可以在cpu MHz看到目前的運作頻率, 還可以看到許多其他cpu的資訊.
如果不想看到那麼多雜七雜八的資訊, 可以搭配grep將所要檢查的資訊過慮出來, 例如:

$ grep -E 'GHz|MHz' /proc/cpuinfo

這樣就只會顯示cpu的最高工作頻率以及目前的工作頻率而已.

星期四, 8月 20, 2009

解決Tab mix plus與google toolbar最常造訪的網站功能相衝

Google toolbar有一個功能, 就是在開啟新分頁時會顯示最常造訪的網站清單. 可是這個功能在安裝了Tab mix plus附加元件之後, 就會被廢了武功, 完全跑不出來. 後來在網路上找到了解決辦法, 方法很簡單, 只要將chrome://google-toolbar/content/new-tab.html如下圖所示, 加到Tab mix plus選項->分頁->開啟分頁->新增分頁時自動載入設定中即可.


星期三, 8月 19, 2009

系統自動掛載ntfs partition

ntfs-3g所賜, 我們已經可以在Linux上掛載ntfs的檔案系統, 進行資料的讀寫動作. 雖然Linux系統UI已經做得很好, 只要用滑鼠點一下ntfs partition就會自動幫我們掛載到系統上, 可是總覺得如果可以如其他的ext3 partition一樣, 在開機後就自動掛載那不是更完美嗎? 既然有這個想法, 就要想辦法解決一下. 其實很簡單, 還是一樣利用/etc/fstab就可以輕鬆達到.

首先, 找出ntfs partition的UUID, 方法可以參考取得partition的UUID
再來就是編輯/etc/fstab這個檔案, 在檔案中加上
UUID=ntfs_partition_uuid mount_point ntfs-3g defaults 0 2
這樣就可以了.

例如: 我們的ntfs partition的uuid如果是4C54A5A254A58EF0, 並且要將該partition掛載到/media/VAIO目錄中
$ sudo mkdir /media/VAIO
$ sudo echo "UUID=4C54A5A254A58EF0 /media/VAIO ntfs-3g defaults 0 2" >> /etc/fstab
$ sudo mount -a

取得partition的uuid

現在Linux掛載partition常常使用UUID的方式來指定所要掛載的partition, 要取得partition的UUID有以下幾個方法:

1. 最簡單的方式是直接執行blkid, 會列出目前連接到系統上所有磁碟的partition UUID, 檔案系統以及label, 例如:
$ blkid
/dev/loop0: TYPE="squashfs"
/dev/sda1: UUID="4C54A5A254A58EF0" LABEL="VAIO" TYPE="ntfs"
/dev/sda5: UUID="4CFCC181FCC165B4" TYPE="ntfs"
/dev/sda6: UUID="a5050769-1c80-4e7c-8b7c-da810624d910" TYPE="ext3"
/dev/sda7: UUID="8c28803c-3390-46d7-bb90-fd9a83c0bff3" TYPE="swap"
/dev/sda8: UUID="95206ddf-7ffd-4ddb-8ced-631b5df70b26" TYPE="ext3"

2. 另外也可以執行sudo vol_id 也可以得到指定partition的相關資訊, 例如:
$ sudo vol_id /dev/sda1
ID_FS_USAGE=filesystem
ID_FS_TYPE=ntfs
ID_FS_VERSION=3.1
ID_FS_UUID=4CFCC181FCC165B4
ID_FS_UUID_ENC=4CFCC181FCC165B4
ID_FS_LABEL=
ID_FS_LABEL_ENC=

星期日, 8月 09, 2009

struct成員的記憶體位址alignmentt

一般來說, compiler編譯c程式時, 遇到struct時會對成員資料的位址進行alignment的動作, 以增加記憶體讀取的效率. 所以struct往往會佔有比所有資料成員大小和還要多的記憶體空間. 例如下列的struct data的記憶體大小為12.
struct data {
char num;
int size;
void *ptr;
};

如果因為特殊需要, 需要節省記憶體, 則可以在程式碼中需要進行pack memory的struct前後, 採用#pragma pack來告訴compiler是否要進行pack memory的動作, 例如下列的struct packed_data的記憶體大小則為9, 不是12.
#pragma pack(1)
struct packed_data {
char num;
int size;
void *ptr;
};
#pragma pack()

讓evince顯示中文

Ubuntu預設安裝的evince雖然可以讀取pdf檔案, 可是如果檔案內容包含中文的話, 中文字就會無法正常地顯示出來. 雖然Adobe PDF reader也很好用, 而且又是免費軟體, 但是總是有些缺點, 例如: 開啟速度較慢, 不是open source...

要解決evince無法顯示中文的問題, 只要安裝poppler-data套件後就解決囉.

星期六, 8月 08, 2009

修正VMware on Ubuntu 9.04無法使用方向鍵的問題


如果在Ubuntu 9.04中安裝VMware, 並且在VMWare發生方向鍵無法使用的情形, 只要在/etc/vmware/config檔案中加入
xkeymap.nokeycodeMap = true

VMware的虛擬機器啟動時會出現如下圖所示的提示訊息, 不用理會沒關係.



資料來源:
  • http://communities.vmware.com/message/1087494#1087494

星期六, 7月 25, 2009

切換ubuntu的預設輸入法

im-switch -s gin
用gcin當作預設輸入法

im-switch -s scim
用scim作為預設輸入法

星期三, 7月 22, 2009

用gstreamer下載mms檔案

gst-launch mmssrc location=mms://air.hihit.net/studio/090722baa.wma ! filesink location=test.wma

gstreamer真是個好東西阿

如果執行時出現ERROR: pipeline could not be constructed: no element "mmssrc", 那是因為預設mmssrc並沒有安裝mms相關的plugin, mmssrc在Gstreamer Bad Plugins中, 所以只要將它安裝起來就可以了. 安裝方式如下:

sudo apt-get install gstreamer0.10-plugins-bad

星期一, 5月 18, 2009

建立Android XML file

如果使用File->New->Android XML File會出現下列錯誤的話,

則可以改按工具列上的按鈕來建立,如下圖滑鼠指標停放處所示。

星期二, 3月 17, 2009

Windows Service學習心得

Windows service與一般Windows的應用程式一樣, 也是一個執行檔. 不同的是, Windows service程式是以console application的形式在系統背景中執行. 既然Windows service是以console application的形式存在, 所以在撰寫Windows service時, source code中第一個最重要的地方就是main(), 也是整個service program的進入點.

在main中, 我們必須完成的事情很簡單, 就是將每個service的名稱以及其對應的serivce procedure註冊就好了. 方法很簡單, 就是先將SERVICE_TABLE_ENTRY的內容填好, 然後呼叫StartServiceCtrlDispatcher()就可以了.

#include <windows.h>
#include <stdio.h>

#define SLEEP_TIME 5000
#define LOGFILE "c:\\Temp\\memstatus.txt"

int WriteToLog(char* str)
{
    FILE* log;
    log = fopen(LOGFILE, "a+");
    if(log == NULL)
        return -1;
    fprintf(log, "%s\n", str);
    fclose(log);
    return 0;
}

SERVICE_STATUS ServiceStatus;
SERVICE_STATUS_HANDLE hStatus;

void ServiceMain(int argc, char** argv);
void ControlHandler(DWORD request);
int InitService();

void main()
{
    SERVICE_TABLE_ENTRY ServiceTable[2];

    ServiceTable[0].lpServiceName = "MemoryStatus";
    ServiceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)ServiceMain;
    ServiceTable[1].lpServiceName = NULL;    // 最後一個service entry table欄位必須為NULL
    ServiceTable[1].lpServiceProc = NULL;

    // Start the control dispatcher thread for our service
    StartServiceCtrlDispatcher(ServiceTable);
}

有幾點必須要說明一下, 首先, 一個service program不一定只能包含一種service, 如果有多個service, 則上列的ServiceTable陣列的element則會有多個. 只要記住最後一個element的service name和service procedure都必須為NULL表示陣列結束就可以了. 如此一來, service program就是負責作為SCM (Service Configuration Manager)與service之間溝通的橋樑, 也就是所謂的control dispatcher, 負責將SCM傳送過來的request傳遞給相對應的service, 並且監控service的執行.

另外, SCM啟動service program後, 必須在30秒內呼叫StartServiceCtrlDispatcher(), 否則會回報為啟動錯誤. 所以如果每個service的啟動有一些啟動程序必須執行, 應該寫在service procedure中, 也就是上例中的ServiceMain.

ServiceMain是service的entry point, 也就是我們在service program中為每個service所註冊的service procedure. 每個service的service procedure會由dispatcher產生一個新的thread來執行. 在service procedure中我們首先必須盡快為service註冊control handler, 也就是呼叫RegisterServiceCtrlHandler()來註冊該service名稱所對應的service control handler. 不管service的狀態是否有改變, 在control handler中都要回報service status, 透過呼叫SetServiceStatus()回報service status給SCM.

void ServiceMain(int argc, char** argv)
{
    int error; 

    
    hStatus = RegisterServiceCtrlHandler("MemoryStatus", (LPHANDLER_FUNCTION)ControlHandler);
    if(hStatus == (SERVICE_STATUS_HANDLE)0)
    {
        // Registering Control Handler failed
        return;
    }
   
    ServiceStatus.dwServiceType = SERVICE_WIN32;
    ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
    ServiceStatus.dwWin32ExitCode = 0;
    ServiceStatus.dwServiceSpecificExitCode = 0;
    ServiceStatus.dwCheckPoint = 0;
    ServiceStatus.dwWaitHint = 0;
    SetServiceStatus(hStatus, &ServiceStatus);

    // Initialize Service
    error = InitService();
    if(error)
    {
        // Initialization failed
        ServiceStatus.dwCurrentState = SERVICE_STOPPED;
        ServiceStatus.dwWin32ExitCode = -1;
        SetServiceStatus(hStatus, &ServiceStatus);
        return;
    }

    // We report the running status to SCM.
    ServiceStatus.dwCurrentState = SERVICE_RUNNING;
    SetServiceStatus(hStatus, &ServiceStatus);

    MEMORYSTATUS memory;
    // The worker loop of a service
    while(ServiceStatus.dwCurrentState == SERVICE_RUNNING)
    {
        char buffer[16];
        GlobalMemoryStatus(&memory);
        sprintf(buffer, "%d", memory.dwAvailPhys);

        int result = WriteToLog(buffer);
        if(result)
        {
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus.dwWin32ExitCode = -1;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        }

        Sleep(SLEEP_TIME);
    }
    return;
}

// Service initialization
int InitService()
{
    int result;
    result = WriteToLog("Monitoring started.");
    return result;
}

在service control handler中, 會針對SCM所送出的request呼叫相對應的處理程式. 可以接受的request種類則是在ServiceMain註冊service control handler時所設定. 基本上SERVICE_CONTROL_STOP和SERVICE_CONTROL_SHUTDOWN的處理方式一樣, 用來關閉service, 差別在於SERVICE_CONTROL_SHUTDOWN是在系統關機時, 由SCM發送給每個service.

void ControlHandler(DWORD request)
{
    switch(request)
    {
        case SERVICE_CONTROL_STOP:
            WriteToLog("Monitoring stopped.");
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        case SERVICE_CONTROL_SHUTDOWN:
            WriteToLog("Monitoring stopped.");
            ServiceStatus.dwWin32ExitCode = 0;
            ServiceStatus.dwCurrentState = SERVICE_STOPPED;
            SetServiceStatus(hStatus, &ServiceStatus);
            return;
        default:
            break;
    }

    // Report current status
    SetServiceStatus(hStatus, &ServiceStatus);
}

要安裝service可以利用系統提供的command line utility, 如下所示. 要注意的是, binpath=後面必須有一個空格.

sc create MemoryStatus binpath= c:\Temp\MemoryStatus.exe

要刪除service也同樣可以使用系統提供工具, 如下所示.

sc delete MemoryStatus

 

參考資料:

在Windows XP上安裝Windows CE模擬器出現錯誤

安裝Microsoft eMbedded Visual C++在Windows XP上時出現了"虛擬 PC / Windows CE 模擬器' 將造成 Windows 變得不穩定。 Windows 已禁止載入這些驅動程式。"這個訊息, 則無法開啟Windows CE模擬器來測試所撰寫的Windows CE程式. 要解決這個問題很簡單, 只要稍微修改一下C:\Boot.ini這個檔案的內容就可以了. 修改步驟如下:

  1. 在桌面上"我的電腦"的圖示上按滑鼠右鍵, 選擇內容開啟"系統內容"
  2. 選擇"進階"頁面
  3. 在視窗下方的"啟動及修復"方塊中按一下"設定"
  4. 在出現的"啟動及修復"視窗中按一下"編輯"
  5. 然後將"/noexecute=optin"刪除, 並加上"/execute"
  6. 存檔後重新開機就大功告成了

參考資料:

星期五, 3月 13, 2009

Install VMware workstation v6.0.0.45731 on Ubuntu 8.0.4

After installing VMware workstation on Ubuntu 8.0.4, a compiler error which says "conflicting types for ‘poll_initwait’" will raise. The same error will also generate when you run vmware-config.pl to configure the VMware. We can fix the problem in the following steps:

  1. Download the patch from Petr Vandrovec at http://knihovny.cvut.cz/ftp/pub/vmware/vmware-any-any-update115.tar.gz.
  2. tar xfz vmware-any-any-update115.tar.gz
  3. Change the working directory into vmware-any-any-update115
  4. sudo ./runme.pl

Then, you will get another compiler error which says "only <linux/bitops.h> can be included directly". Fix the probelm in the following steps:

  1. cd /usr/lib/vmware/modules/source
  2. cp vmmon.tar vmmon.tar.orig
  3. sudo tar xvf vmmon.tar
  4. cd vmmon-only/include/
  5. sudo vi vcpuset.h
  6. change line 74 from: #include "asm/bitops.h" to #include "linux/bitops.h"
  7. rm vmmon.tar
  8. sudo tar cvf vmmon.tar vmmon-only/
  9. sudo rm -rf vmmon-only/
  10. sudo vmware-config.pl

星期五, 3月 06, 2009

Static initialization block and initializer block

Static initialization block在類別載入時會執行該區塊中的程式碼, 而且整個程式的執行過程中只會執行一次.

Initializer block則是在每個物件被建立時執行該區塊中的程式碼, 接著才執行contructor中的程式碼.

class ClassA {
    // static initialization block
    static {
        System.out.println("This is static initialization blocks");
    }

    // initializer block
    {
        System.out.println("This is initializer block");
    }

    public ClassA()
    {
        System.out.println("Constructor A");
    }

    public void print()
    {
        System.out.println("I am A");
    }

    public static void staticPrint()
    {
        System.out.println("Static print");
    }
}

public class Test01 {
    public static void main(String[] args) {
        System.out.println("Start testing...");
        ClassA.staticPrint();
        ClassA a = new ClassA();
        a.print();
    }
}

星期日, 3月 01, 2009

Java的String串接

String物件的長度是固定的, 不能改變它的內容. 在程式中, 使用+來串接字串實際上會產生新的字串物件. 如果程式中此動作非常頻繁, 程式會變得比較沒有效率.

在J2SE 5.0之前, 可以使用StringBuffer來做這些字串的處理, 從J2SE 5.0開始, 也可以使用StringBuilder來做相同的事情. 這兩個類別的差別在於多執行緒的同步管理. 如果程式是多執行緒程式, 則使用StringBuffer, 因為StringBuilder沒有處理同步的問題.

public class AppendStringTest {
    public static void main(String[] args) {
        StringBuilder builder = new StringBuilder("");
        long beginTime = System.currentTimeMillis();
        for(int i=0; i<10000; i++)
            builder.append(String.valueOf(i));
        long endTime = System.currentTimeMillis();
        System.out.println("執行時間: "+(endTime-beginTime));
        String text = "";
        beginTime = System.currentTimeMillis();
        for(int i=0; i<10000; i++)
            text = text + i;
        endTime = System.currentTimeMillis();
        System.out.println("執行時間: "+(endTime-beginTime));
    }
}

Java的String pool

Java執行時會維護一個String pool, 對於一些可以共享的字串物件, 會先在String pool中尋找, 有相同的字串內容就直接傳回, 減少記憶體的耗用.

public class test02 {
    public static void main(String args[])
    {
        String a = new String("abc");
        String b = new String("abc");
        String c = "abc";
        String d = "abc";
        String e = "def";
        String f = "abcdef";
        String g = c+e;
        String h = (c+e).intern(); 
        String i = new String("abc").intern();

        System.out.println(a == b);    // false
        System.out.println(c == d);    // true
        System.out.println(f == g);    // false
        System.out.println(f == h);    // true
    }
}

星期四, 2月 05, 2009

Java中的foreach

J2SE 5.0之後新增了foreach的語法, 可用於存取陣列元素. 以下為一些簡單的範例:

一維陣列

public class test02 {
    public static void main(String args[])
    {
        int[] a = new int[] {1, 2, 3, 4, 5};

        for(int v : a)
            System.out.println(v);
    }
}

二維陣列

public class test02 {
    public static void main(String args[])
    {
        int[][] a = new int[][] {
            {1, 2, 3, 4, 5},
            {10, 11, 12}
        };

        for(int[] row : a) {
            for(int element : row)
                System.out.print(element + " ");
            System.out.println();
        }
    }
}

Autoboxing in Java

自動boxing/unboxing是J2SE 5.0之後提供的新功能, 可以由compiler視程式需要自動在primitive data type以及object之間進行轉換. Boxing就是將primitive data type轉成object, 而unboxing則是把object轉成對應的primitive data type. 如下範例所示:

public class test02 {
    public static void main(String args[])
    {
        Integer i1;
        int a = 100;

        i1 = new Integer(a); // boxing
        i1 = a/2; // auto boxing

        a = i1.intValue()/5; // unboxing
        a = i1/2; // auto unboxing
    }
}

雖然這功能非常方便, 但是有幾點是必須要注意的. 首先, 因為這動作是compiler time的動作, 所以有可能會發生程式可以成功編譯但是執行時卻發生執行錯誤的現象. 例如:

public class test02 {
    public static void main(String args[])
    {
        Integer i = null;
        int a;

        a = i / 2; // null pointer exception
    }
}

另外, 數值大小不同也會使boxing的行為有些不同, 例如:

public class test02 {
    public static void main(String args[])
    {
        Integer i = 100;
        Integer j = 100;

        if(i == j)
            System.out.println("equal");
        else
            System.out.println("not equal");
    }
}

此程式會得到equal. 但是下一個範例則會得到not equal.

public class test02 {
    public static void main(String args[])
    {
        Integer i = 200;
        Integer j = 200;

        if(i == j)
            System.out.println("equal");
        else
            System.out.println("not equal");
    }
}

這兩個範例唯一的差別在於程式中所指定的數值, 一個為100, 另一個為200, 但是結果卻也不同. 這是因為auto boxing 在數值介於-128到127之間時, 記憶體會重複使用, 所以第一個範例的i與j會是相同的物件. 反之, 第二個範例則會是兩個不同的物件, 所以兩支程式的執行結果也就不一樣.