Feeds:
文章
留言

Archive for 2007 年 06 月

Sony Ericsson 隱藏指令

[1] 寫到 Sony 手機的隱藏功能,很好奇的在手機上輸入『→ * ← ← * ← * 』就能開啟隱藏服務設定,輸入過程中不要在意畫面,最終會出現
"服務資訊"
"服務設定"
"服務測試"
"文字標籤"
上述在我的 Z300i 測試OK。想不到還有這種隱藏功能 ^^

參考資料
[1] 教你開啟Sony Ericsson 系統隱藏工具

Read Full Post »

資料來源:http://www.microsoft.com/taiwan/msdn/events/Silverlight/event070726_1.htm

內容包含三個部份
1. 從 WinForm 到 WPF
說明如何在既有的 Windows Form 應用程式基礎上,保留原主要程式邏輯,只將介面替換為WPF。透過 WPF 的技術

2. 新世代 Silverlight 向量 RIA 與既有 Web 開發技術的整合
SilverLight 在 Web 開發上的應用

3. SilverLight 與 ASP.NET 的完美結合
介紹 ASP.NET, SilverLight, XAML, AJAX 技術的結合

高雄難得有研討會,排時間去聽聽看^^

Read Full Post »

Read Full Post »

上次提到"好厲害的日本人,居然用 HTML 畫 CG",原來有工具可以輔助將圖片轉 HTML。實際作法可以參考"破解!使用記事本寫HTML來畫圖?(教你將圖片轉化為HTML)"

其實現在仔細想想,只要能夠在 HTML 上顯示絕對位置的 1×1 像素,就能夠將圖片上的每一個像素位置和顏色~所以,不一定要透過 <table> <tr> <td> 來實現,也可以透過 <div> 和 CSS 來達到。

Image to HTMLConverter(http://neil.fraser.name/software/img2html/)他使用的是<table><tr><td><img>方式來實現。找個時間我也來實現一個 Windows 版本。

Read Full Post »

ClickClickClick 相關連結

Read Full Post »

Microsoft 新發佈的 MS07-033 包含 KB929874 ,這個修正主要是發生在 IE6 瀏覽包含循環參考和使用 Function Closure 的網頁,即使切換到其他網頁時,記憶體仍然不會釋出。如果需要 WinXP SP2 繁體中文版上的修正檔請點選下載。如果需要其他作業系統的修正,請參考這個連結
 
希望 Firefox 的記憶體問題也趕快解決….

Read Full Post »

Google Doc 新介面真是讓人驚艷,標籤變成樹狀資料夾, 也多新增項目分類和分享列表。整個介面看起來更加專業,如果資料夾可以包含子資料夾就更好了。
 

Read Full Post »

MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu
2007.06.26

測試環境
1. Windows XP Pro + SP2
2. Borland C++ Builder 6.0

BCB 的訊息機制
所有 C++ Builder 的類別都具有內建的訊息處理機制,簡單的說,當類別收到訊息時會依據收到的訊息類型再分配給特定的成員函數處理,如果訊息沒有對應的處理函數,則會使用預設的處理函數。下面是 BCB 的訊息分派流程:

Windows 訊息 -> MainWndProc -> WndProc  -> Dispatch -> 訊息處理函數

Windows 訊息含有許多有用的資料紀錄,其中最重要的是用來識別訊息的整數。這些識別碼定義在 messages.hpp。當應用程式建立視窗(CreateWindow)時,會將 Windows Procedure 註冊到 Windows 核心(RegisterClass),Windows Procedure 是視窗訊息處理副程式,傳統作法是用一個很大的 switch 來處理每一個訊息。 BCB 在許多方面簡化了訊息分派方式:
1. 每個元件都繼承完整的訊息分派系統
2. 訊息分派系統都具有預設的處理函數。所以,我們只要定義必要的訊息即可。
3. 可以編修訊息處理函數的一小部份,其餘的交給繼承下來的訊息處理函數

BCB 註冊一個 MainWndProc 函數作為應用程式中各類別的 Window Procedure。MainWndProc 包含一個例外處理區塊,把 Windows 訊息資料結構傳給 WndProc 虛擬方法,並呼叫 TApplication 的 HandleException 方法來處理例外狀況。MainWndProc 不是一個虛擬函數,他對任何訊息沒有特定的處理方法。在 WndProc 中才會加以自訂,因為每個元件都可以覆蓋 WndProc 來達到自訂需求。

WndProc 可以檢查特定訊息並加以處理,使得能夠抓住任何訊息。WndProc 最後會呼叫 TObject 繼承來的非虛擬函數 Dispatch 將訊息非派到綴中處理函數。Dispatch 使用訊息資料結構中的 Msg 成員來決定訊息分派,如果元件定義這個訊息的處理函數,Dispatch 會將控制權移交給這個函數,否則會透過 DefaultHandler 處理。

攔截特定訊息
BCB 要修改元件處理某一訊息的方式,只要覆蓋該訊息的訊息處理函數即可,但如果元件預設不提供該訊息處理,此時可以宣告一個自訂訊息處理函數來處理。首先在元件中宣
告新的訊息處理函數,如:

void __fastcall OnMyMove(TMessage& Message);

接著利用下面的巨集將上述處理函數與實際的 Windows 訊息進行關聯

BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(視窗訊息, 訊息資料結構, 訊息處理函數名稱)
END_MESSAGE_MAP(TForm)

上述巨集中可以包含多個 MESSAGE_HANDLER。下面以一個簡單的範例驗證上述的說明。

Unit1.h (視窗類別宣告)

//—————————————————————————

#ifndef Unit1H
#define Unit1H
//—————————————————————————
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//—————————————————————————
// 自訂 Windows 訊息
const DWORD MY_MSG = WM_USER + 1;

class TForm1 : public TForm
{
__published:    // IDE-managed Components
    TStaticText *StaticText1;
    TButton *Button1;
    void __fastcall Button1Click(TObject *Sender);
private:    // User declarations
// 宣告訊息的處理函數
    void __fastcall OnMyMove(TMessage& Message);
    void __fastcall OnMyMsg(TMessage& Message);

// Windows 訊息攔截定義
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(WM_MOVE, TMessage, OnMyMove)
MESSAGE_HANDLER(MY_MSG, TMessage, OnMyMsg)
END_MESSAGE_MAP(TForm)

public:        // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//—————————————————————————
extern PACKAGE TForm1 *Form1;
//—————————————————————————
#endif

Unit1.cpp (視窗類別實作)

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//—————————————————————————
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//—————————————————————————
/**
 * WM_MOVE 訊息處理
 *
 * @see TMessage
 * @see WM_MOVE
 * @param   Message     訊息資訊
 */
void __fastcall TForm1::OnMyMove(TMessage& Message){
    AnsiString s;
    s += (int)(short) LOWORD(Message.LParam); // X 座標
    s += ", ";
    s += (int)(short) HIWORD(Message.LParam); // Y 座標
    StaticText1->Caption = s;

    TForm::Dispatch(&Message);
}

/**
 * 自訂訊息處理
 *
 * @see TMessage
 * @see WM_MOVE
 * @param   Message     訊息資訊
 */
void __fastcall TForm1::OnMyMsg(TMessage& Message){
    StaticText1->Caption = "WM_MYMSG";

    TForm::Dispatch(&Message);
}

/**
 * 送出自訂訊息
 *
 */
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    SendMessage(this->Handle, MY_MSG, 0, 0);
}

執行結果
1. WM_MOVE

2. WM_MYMSG

阻斷特定訊息
在某些情況可能想要讓元件忽略特定訊息,也就是讓訊息不分派到訊息處理函數。可以透過改寫(Override) 虛擬函數 WndProc 來達成。下面是阻斷滑鼠的 WM_LBUTTONUP 和 WM_LBUTTONDOWN 訊息範例,當過濾進行中,FormClick 訊息處理函數就不會進行。

Unit1.h (視窗類別宣告)

//—————————————————————————
#ifndef Unit1H
#define Unit1H
//—————————————————————————
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//—————————————————————————
class TForm1 : public TForm
{
__published:    // IDE-managed Components
    void __fastcall FormClick(TObject *Sender);
private:    // User declarations
protected:
     // Override WndProc
     virtual void __fastcall WndProc(Messages::TMessage &Message);
public:        // User declarations
    __fastcall TForm1(TComponent* Owner);
};
//—————————————————————————
extern PACKAGE TForm1 *Form1;
//—————————————————————————
#endif

Unit1.cpp (視窗類別實作)

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//—————————————————————————
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//—————————————————————————
void __fastcall TForm1::WndProc(Messages::TMessage &Message){
   // 阻斷訊息
    if(Message.Msg == WM_LBUTTONUP || Message.Msg == WM_LBUTTONDOWN){
        return;
    }

    // 繼續原始訊息的分派
    TForm::WndProc(Message);
}

void __fastcall TForm1::FormClick(TObject *Sender)
{
    Caption = "Hello";
}

參考資料
[1] C++ Builder 研究, 攔截Windows消息
[2] C++ Builder 研究, 在CB中響應消息及自定義消息
[3] C++ Builder 研究,如何捕獲VCL沒有處理的Windows消息
[4] C++ Builder 研究, CB中消息處理過程及應用
[5] Borland C++ Builder 5 進階程式設計手冊

Read Full Post »

MSN SpaceGoogle DocGoogle Blog
Chui-Wen Chiu(Arick)
2007.05.29 建立


測試環境
1. Windows XP Pro
2. Visual Studio 2005
3. Winamp 5.3.5

簡介
本文主要利用 Win32API 的 FindWindow 和 SendMessage 達成控制 Winamp。

內文
[1] 給出幾個 Winamp 介面上常用的命令控制如下:
private const Int32 WINAMP_START            = 40045;
private const Int32 WINAMP_PLAY_OR_PAUSE    = 40046;
private const Int32 WINAMP_NEXT_TRACK       = 40048;
private const Int32 WINAMP_PREVIOUS_TRACK   = 40044;
private const Int32 WINAMP_CLOSE            = 40001;
private const Int32 WINAMP_STOP             = 40047;
private const Int32 WINAMP_RAISE_VOLUME     = 40058;
private const Int32 WINAMP_LOWER_VOLUME     = 40059;
private const Int32 WINAMP_TOGGLE_REPEAT    = 40022;
private const Int32 WINAMP_TOGGLE_SHUFFLE   = 40023;       
private const Int32 WINAMP_FAST_FORWARD     = 40148;
private const Int32 WINAMP_FAST_REWIND      = 40144;

從名稱上可以很清楚知道他的用途就不贅述。再運用上述的命令之前,必須先取得 Winamp 的 Window Handle,這就是使用 FindWindow 目的,透過 SPY++ 可以取得下圖得知 Winamp 的 Class Name 為 "Winamp v1.x"。

將 Class Name 給 FindWindow 換得 Winamp 的 Window Handle,有了 Window Handle 就可以透過 SendMessage 傳送 Window Message 給 Winamp,進而控制他。[1] 所列舉的控制訊息除了 WINAMP_CUSTOM_VOLUME 之外,都是透過 WM_COMMAND 來傳送,不明的控制都放在 wParam 參數中,所以,可以依據這個規則寫出下面的控制命令

private void WCommand(int command) {
    SendMessage(m_handle, WM_COMMAND, command, 0);
}

因此,要播放音樂只需要 WCommand(WINAMP_START),關閉程式只需要 WCommand(WINAMP_CLOSE)。有了上述禀概念之後,我們就可以將程式進行封裝如下:

using System;
using System.Runtime.InteropServices;

namespace Console1 {
   
    public class Winamp {
        public class NotFoundWinamp: Exception {}

        #region native code
        // WM_COMMAND – Commands to send to Winamp Client.
        private const Int32 WINAMP_START            = 40045;
        private const Int32 WINAMP_PLAY_OR_PAUSE    = 40046;
        private const Int32 WINAMP_NEXT_TRACK       = 40048;
        private const Int32 WINAMP_PREVIOUS_TRACK   = 40044;
        private const Int32 WINAMP_CLOSE            = 40001;
        private const Int32 WINAMP_STOP             = 40047;
        private const Int32 WINAMP_RAISE_VOLUME     = 40058;
        private const Int32 WINAMP_LOWER_VOLUME     = 40059;
        private const Int32 WINAMP_TOGGLE_REPEAT    = 40022;
        private const Int32 WINAMP_TOGGLE_SHUFFLE   = 40023;       
        private const Int32 WINAMP_FAST_FORWARD     = 40148;
        private const Int32 WINAMP_FAST_REWIND      = 40144;

        public const string WINAMP_WINDOW_HANDLE = "Winamp v1.x";

        private const Int32 WM_COMMAND              = 0x0111;
        private const Int32 WM_USER                 = 0x0400;

        // WM_USER
        public const int WINAMP_CUSTOM_VOLUME = 122;

        [DllImport("user32.dll")]
        private static extern int FindWindow(
            string lpClassName, // class name
            string lpWindowName // window name
        );

        [DllImport("user32.dll")]
        private static extern int SendMessage(
            int hWnd, // handle to destination window
            uint Msg, // message
            int wParam, // first message parameter
            int lParam // second message parameter
        );
        #endregion

        public Winamp() {
            m_handle = FindWindow(WINAMP_WINDOW_HANDLE, null);

            if (m_handle == 0) {
                throw new NotFoundWinamp();
            }
        }

        #region public method
        /// <summary>
        /// 暫停或繼續播放
        /// </summary>
        public void PlayOrPause() { WCommand(WINAMP_PLAY_OR_PAUSE); }

        /// <summary>
        /// 下一首
        /// </summary>
        public void Next() { WCommand(WINAMP_NEXT_TRACK); }

        /// <summary>
        /// 前一首
        /// </summary>
        public void Previous() { WCommand(WINAMP_PREVIOUS_TRACK); }

        /// <summary>
        /// 關閉 Winamp
        /// </summary>
        public void Close() { WCommand(WINAMP_CLOSE); }

        /// <summary>
        /// 停止播放
        /// </summary>
        public void Stop() { WCommand(WINAMP_STOP); }

        /// <summary>
        /// 開始播放
        /// </summary>
        public void Start() { WCommand(WINAMP_START); }

        /// <summary>
        /// 啟動重複播放
        /// </summary>
        public void ToggleRepeat() { WCommand(WINAMP_TOGGLE_REPEAT); }

        /// <summary>
        /// 啟動隨機播放
        /// </summary>
        public void ToggleShuffle() { WCommand(WINAMP_TOGGLE_SHUFFLE); }

        /// <summary>
        /// 增加音量
        /// </summary>
        public void RaiseVolume() { WCommand(WINAMP_RAISE_VOLUME); }

        /// <summary>
        /// 減少音量
        /// </summary>
        public void LowerVolume() { WCommand(WINAMP_LOWER_VOLUME); }
       
        /// <summary>
        /// 自訂音量
        /// </summary>
        /// <param name="Volume"></param>
        public void CustomVolume(int Volume) { UCommand(Volume, WINAMP_CUSTOM_VOLUME); }

        /// <summary>
        /// 快速向前轉
        /// </summary>
        public void FastForward() { WCommand(WINAMP_FAST_FORWARD); }

        /// <summary>
        /// 快速向後轉
        /// </summary>
        public void FastRewind() { WCommand(WINAMP_FAST_REWIND); }

        #endregion

        #region private mehtod
        private void WCommand(int command) {
            SendMessage(m_handle, WM_COMMAND, command, 0);
        }

        private void UCommand(int input, int command) {
            SendMessage(m_handle, WM_USER, input, command);
        }
        #endregion

        #region private data
        private int m_handle;
        #endregion
    }

    class Program {
        static void Main(string[] args) {
            try {
                Winamp wp = new Winamp();
                wp.Start();
                wp.PlayOrPause();
                wp.Close();
            } catch (Winamp.NotFoundWinamp) {
                Console.WriteLine("Winamp Not Found"); 
            }
        }
    }
}

結語
由本文應該可以瞭解到如何透過 Windows Message 控制 Winamp,如果要擴充控制,也可以自行透過 SPY++ 攔截 Winamp 的 Windows Message。有了本文的介紹,你也可以將這個概念應用在其他程式中,如: 記事本、小算盤…等。

參考資料
[1] Winamp Control Utility via hotkeys
[2] FindWindow Function ()
[3] SendMessage Function ()

Read Full Post »


測試環境
1. Windows XP Pro SP2
2. Visual Studio 2005
3. .NET Framework 2.0

下載
1. ExportDll.exe
2. ExportDllAttribute.dll

[1] 利用 Attribute 標示會出的函數資訊,接著透過 ildasm.exe 將 DLL  反編譯成 IL 碼,然後修改標示為匯出函數的 IL 碼後再重新以 ilasm.exe 編譯成 DLL。下面簡單的示範這個工具的用法,首先建立一個 C# Class Library Project,在 Project 中加入 ExportDllAttribute.dll 參考,並撰寫如下程式產生 ClassLibrary1.dll

using System;
using ExportDllAttribute;

namespace ClassLibrary1 {
    public class Class1 {
        [ExportDllAttribute.ExportDll("ExportNameOfFunction",  System.Runtime.InteropServices.CallingConvention.Cdecl)]
        public static void SomeFunctionName() {
            System.Console.WriteLine("I really do nothing");
        }
    }
}

執行下面命令匯出函數(DLL 需加入路徑)
ExportDll.exe ./ClassLibrary1.dll /Debug

使用 depends.exe 檢測如下:

撰寫 C++ 程式測試,測試程式使用[2] 的 LibraryMrg 類別

#include "LibraryMgr.h"
#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    LibraryMgr_t lm( _T("ClassLibrary1.dll") );
    typedef void (*Test_t)(void);
    Test_t TestFn;
    TestFn = (Test_t) lm.GetProcAddress( "ExportNameOfFunction" );
    if(TestFn){
        TestFn();
    }

    return 0;
}

執行結果

結語
以往要將 .NET 程式給 Unmanaged 程式使用,可能是選擇封裝成 COM 或者透過 C++/CLI 封裝成 DLL,可是透過這個工具,可以將 C# 程式很輕易的公開給 C++ 程式使用,省去層層的封裝,不過這個工具是否有什麼限制或是錯誤還需要一點時間觀察。

補充
1. ExportDll.exe 有使用 ildasm.exe 和 ilasm.exe,這兩個工具的路徑定義在 app.config 中,如果你的這兩個工具不是安裝在預設的

C:Program FilesMicrosoft Visual Studio 8SDKv2.0Bin 和 C:WINDOWSMicrosoft.NETFrameworkv2.0.50727 需要自行修改 app.config。

2. 這個工具目前似乎不能用於 VB.NET 所產生的 DLL

3. 如果要將這個工具與 VS2005 整合,打開 Project Properties |Build Events 的 Post-build event command line 加入下面的命令(請依你的實際路徑修改)
"$(SolutionDir)ExportDllbinDebugExportDll.exe" "$(TargetPath)" /$(ConfigurationName)

當編譯完成後自動執行 ExportDll.exe。

4. 可透過 rundll32.exe 執行 ExportDll.exe 產生的 DLL,如下:
rundll32.exe ClassLibrary1.dll,ExportNameOfFunction

ps. 對於 Console 輸出的資料不會顯示出來,可用 MessageBox 取代 Console 輸出

參考資料
[1] How to automate exporting .NET function to unmanaged
[2] DLL 管理類別

Read Full Post »

Older Posts »