2009/08/12

Class, Abstract Class, and Interface

最近在研究用 C# 寫 Plugin, 為了 plugin 寫了二篇文章,分別是Class Interface, 及 Plugins

這邊就有個問題,一般我們在寫程式,大概都只會用到類別 "Class", 書上講的抽象類別(Abstract Class)似乎很少用到,更別提界面(interface)。不過在用 Plugin 時,一直都是用界面(Interface), 為什麼呢?若看得懂英文的,請參考Abstract Class vs Interface一文。

.抽象類別: 抽象類別其實非常像一般的類別,可以擁有 Private 的成員及方法,當然不同的地方就在於它也可以擁有 Abstract 的成員及方法。

.界面: 可以簡單說,界面是更抽象的類別,只能擁有 Public 及 Abstract 的成員及方法。要換一種說法就是,界面不必宣告,一切都當成 public && abstract,而且而且,因為是抽象的,所以都不能實作。

用下面的範例來看會比較容易明白:


//Abstarct Class
public abstract class Vehicles
{
private int noOfWheel;
private string color;
public abstract string Engine
{
get;
set;
}
public abstract void Accelerator();
}

//Interface
public interface Vehicles
{
string Engine
{
get;
set;
}
void Accelerator();
}


文中提到一種使用時機,若繼承關係中,基礎類別有可能被用來產生實例的話,當然就只能用一般類別,但是若不會發生這種可能,用抽象類別會是比較好的。至於界面,則是這個基礎類別若有「預設行為」的話只能用抽象類別,否則它就只是個界面。

寫到這兒,也大概了解為何 Plugin 只能用界面了,因為,不可能為 Plugin 給什麼預設行為不是?

讓我們從抽象類別與界面的字面意義來思考,抽象類別畢竟是類別,只是它是抽象的,所以在繼承中需要被「overlay」,就像看到「筆」這個抽象概念時,你會知道可以寫,而且不管哪種筆的寫都「差不多」一樣。可是界面之所以是界面,就是因為你可以預期它要提供的功能是什麼,可是界面只停留在界面,怎麼實作是沒個準頭的,要實際繼承的人去實作。而之所以被稱為界面,也限定了繼承的人一定要「全部」實作。

2009/08/10

主開機磁區(MBR)被 grub/lilo 蓋掉了怎麼回覆?

這問題一直都存在,早期的文獻(好像我也貼過)都說用 fdisk /mbr 來修復,不過自從檔案系統用 NTFS 之後,此一工具也不見了。我的狀況是電腦裝的是 windows 7, 後來用 Linux 要將它安裝到我的隨身碟,不知道什麼時候做了蠢事,要用原來的方式從硬碟開機時發現停在不正常的 grub 上,因為此時隨身碟不見了。

好吧,至少原來用 grub 開機的方式我辦得到,也就是說,我讓系統停在 grub shell 上,然後用傳統的方式來試著開機進 Windows,因為我是用隨身碟,此時硬碟變成 (hd1,0):

root (hd1,0)
rootnoverify (hd1,0)
chainloader +1
makeactive
boot

注意哦,就算進 windows 7(我相信 vista 也一樣)也找不到 fdisk, 所以就先停在 Windows 開機選單畫面,注意看的話可以看到 "F8" 修復的字樣,就給它用力按 F8 吧。

上面的方式也許有人不知道怎樣進去那個 F8 畫面,也可以拿原版光碟來,裡面會有相同的畫面。

好了,假設你在系統的 F8 畫面,請選擇 Command Window 吧,然後用下面的命令:

bootsect /nt60 c: /mbr /force

上面的 C: 是因為我用 F8 以硬碟開機的方式進去系統,相信若是用安裝光碟的話不是 c:,請自行修改。

最後,要理解二件事:
一、前面的 grub 設定方式可以讓你從硬碟開機進之前安裝的 Windows 7/Vista, 但是卻尚未修改開機設定,因此下次若用純硬碟開機還是會停在不完全的 Grub 選單中。
二、為何要按 F8? 因為 bootsect 不在正常的 Windows 7 中,只存在 Windows 7 的修復控制台中,上面說的就是透過按 F8 或是用安裝光碟來進到修復控制台。

2009/08/04

Data Binding
本文主要來自Piping Value Converters一文,有興趣的人也可以直接看MSDN Data Binding Overview

先來看看該文範例執行時的畫面如下:


而用來 Data Binding 的資料如下:

<?xml version="1.0" encoding="utf-8" ?>
<Tasks>
  <Task Name="Paint the living room" Status="0" />
  <Task Name="Wash the floor" Status="-1" />
  <Task Name="Study WPF" Status="1" />
  <Task Name="Rebuild kernel" Status="-1" />
  <Task Name="Kernel Install" Status="0" />
  <Task Name="Rebuild Driver" Status="1" />
</Tasks>

問題:

根據所提供的資料,如何把它依某一個屬性(範例中是用 Status)轉換成不同的 UI 元素?以此例除了 Pending, Complete, Active 外,還有顏色。

說明:

資料在繫結時(Data Binding),若有設定 Converter 屬性的話會先經過 IValueConverter型別的 Converter 方法轉換,對單一的轉換就是這麼簡單,可是若要多重轉換時怎麼處理才好呢?該文撰寫了一個繼承自 IValueConverter 的 ValueConverterGroup 來達成此目標。在實作上, ValueConverterGroup 可以擁有多個 Converters, 在呼叫 Converter 時,會依序呼叫。比較特別需要注意的是,前一個 converter 的輸出會當成下一個 converter 的輸入,直到最後一個 converter 的輸出被當成 target 為止。

由 ValueConverterGroup 實例來看問題:

範例中定義下面三個 ValueConverterGroup 實例(Instance), 關於 ValueConverterGroup 的定義則見後面的說明。重溫前面的問題,我們只有一個 Status 屬性,可是要做三件事,一是將數值型態的 Status 轉換成易讀的 Pending, Complete, Active,一是不同的 Status 以不同的顏色來顯示,三是對不同的 Status 顯示不同的 Tooltip.

    <!-- 第一部份: 將 Status 屬性值轉換成像 Pending, Complete, Active 這樣的字串以便顯示在 GUI 上 -->
    <local:ValueConverterGroup x:Key="statusDisplayNameGroup">
      <local:IntegerStringToProcessingStateConverter  />
      <local:EnumToDisplayNameConverter />
    </local:ValueConverterGroup>

    <!-- 第二部份: 根據 Status 屬性值轉換成不同的顏色,以便反應不同的狀態 -->
    <local:ValueConverterGroup x:Key="statusForegroundGroup">
      <local:IntegerStringToProcessingStateConverter  />
      <local:ProcessingStateToColorConverter />
      <local:ColorToSolidColorBrushConverter />
    </local:ValueConverterGroup>

    <!-- 第三部份: 根據 Status 屬性值轉換成不同的 Tooltip -->
    <local:ValueConverterGroup x:Key="statusDescriptionGroup">
      <local:XmlAttributeToStringStateConverter />
      <local:IntegerStringToProcessingStateConverter  />
      <local:EnumToDescriptionConverter />
    </local:ValueConverterGroup>   

從上面的實例可以看到三個 Key 值分別是 statusDisplayNameGroup, statusForegroundGroup, statusDescriptionGroup,使用上可以由 <DataTemplate> 來引用,範例如下,剛好可以看到三個 Converter 分別引用了這三個 Key 值:

    <DataTemplate x:Key="taskItemTemplate">
      <StackPanel
        Margin="2"
        Orientation="Horizontal"
        ToolTip="{Binding XPath=@Status, Converter={StaticResource statusDescriptionGroup}}"
        >
        <TextBlock Text="{Binding XPath=@Name}" />
        <TextBlock Text="   (" xml:space="preserve" />
        <TextBlock
          Text="{Binding XPath=@Status, Converter={StaticResource statusDisplayNameGroup}}"
          Foreground="{Binding XPath=@Status, Converter={StaticResource statusForegroundGroup}}" />
        <TextBlock Text=")" />
      </StackPanel>
    </DataTemplate>


有興趣的人也可以分析上面的 來看最後 GUI 所呈現的每一筆資訊都由 @Name, (, @Status, ) 四個部份組成,運用上將 Status 用三個 Converter 來轉換成需要的元素即達成目的。

ValueConverterGroup 的定義:

這邊我只列出 Convert 的定義:

object IValueConverter.Convert( object value, Type targetType, object parameter, CultureInfo culture )
{
object output = value;

for( int i = 0; i < this.Converters.Count; ++i )
{
IValueConverter converter = this.Converters[i];
Type currentTargetType = this.GetTargetType( i, targetType, true );
output = converter.Convert( output, currentTargetType, parameter, culture );

// If the converter returns 'DoNothing' then the binding operation should terminate.
if( output == Binding.DoNothing )
break;
}

return output;
}

上面定義被標示成紅色斜線的部份,也就是上面在說明一節提到的「前一個 converter 的輸出會當成下一個 converter 的輸入」的實作方式。順帶一提的是,在 Data Binding 時對錯誤資料的處理可以透過 Binding.DoNothing 來終止繫結。

2009/08/03

Picasaweb Viewer

請參考Picasaweb Viewer

這個範例讓視窗的寬、高隨著內容調整大小,其作法是在 xaml 的<window> 裡面設定 SizeToContent="WidthAndHeight"
整個視窗分成左右兩部份,左邊是取自 http://picasaweb.google.com/data/feed/api/user/USER_NAME?kind=album 的RSS feed 列表,右邊則是照片列表。因為都是清單,因此需要將新專案的 <Grid> 拿掉,放兩個 <ListBox> 上去。

Resource
在繼續修改之前,可以先將 Resource 設定好,其內容如下:

    <DockPanel.Resources >
     
      <XmlNamespaceMappingCollection x:Key="mapping">
        <XmlNamespaceMapping Uri="http://www.w3.org/2005/Atom" Prefix="default"/>
        <XmlNamespaceMapping Uri="http://schemas.google.com/photos/2007" Prefix="gphoto"/>
      </XmlNamespaceMappingCollection>
     
      <XmlDataProvider x:Key="Picasa" XmlNamespaceManager="{StaticResource mapping}" Source="http://picasaweb.google.com/data/feed/api/user/USER_NAME?kind=album">
      </XmlDataProvider>
    </DockPanel.Resources>

左半部「相簿清單」

可以注意到這邊會採用 XmlDataProvider 來做 Data Binding, 而這正是左半部的 ListBox 的資料來源,因此我們來看看左半部怎麼寫的:

      <ListBox DockPanel.Dock="Left" SelectionChanged="OnSelctionChanged" DataContext="{StaticResource Picasa}"  ItemsSource="{Binding XPath=default:feed/default:entry/default:title}">
      </ListBox>

在 DataContext 設定其值為 {StaticResource Picasa},這樣就可以把資料 Binding 好。同時請注意到 SelectionChanged 被設定成 OnSelctionChanged,如此一來就會在左邊的「項目」被點選而改變時,會呼叫 OnSelctionChanged() 來改變右邊的圖片清單。

右半部「照片清單」

至於右邊的清單寫法清微複雜,可以分成三部份來讀:

DataContext

      <ListBox.DataContext>
        <XmlDataProvider XmlNamespaceManager="{StaticResource mapping}">
        </XmlDataProvider>
      </ListBox.DataContext>

可以注意到右半部的 DataContext 與左半部有相同的 XmlNamespaceManager, 也許你一下子沒看出來,不過可以從左半部的 DataContext 使用 Picasa 這個資源就可以追出來。而最重要的 ItemSource 屬性則必須靠使用者點選左半部來改變,因此在 OnSelectionChanged() 中我們可以看到其定義,先讓我們瞧瞧整個事件的定義:

        public void OnSelctionChanged(Object source, RoutedEventArgs args)
        {
            ListBox lb = args.Source as ListBox;
            string simplestr;
            XmlDataProvider provider = MyListBox.DataContext as XmlDataProvider;
            if (provider != null)
            {
                simplestr = lb.SelectedValue.ToString().Substring(0,lb.SelectedValue.ToString().IndexOf('-'));
                simplestr = simplestr.Replace(" ", "");
                simplestr = simplestr.Replace(",", "");
                provider.Source = new Uri(@"http://picasaweb.google.com/data/feed/api/user/wade.fs/album/" + simplestr  + "?kind=photo");
            }
        }

  透過 provider.Source 的設值來動態改變 ListBox 的 ItemSource 屬性。

ItemTemplate

那麼我們如何設定每個清單是什麼樣的元素?可以透過 ListBox 的 ItemTemplate 屬性來設定,範例如下:

      <ListBox.ItemTemplate>
        <DataTemplate>
          <Image Source="{Binding XPath=@src}" Width="300"></Image>
        </DataTemplate>
      </ListBox.ItemTemplate>

而照片的網址則來自 RSS <content> 項目裡面的 "@src" 來指定,可以從 XPath 知道此項關聯。這邊又可以見到一項 Data Binding 的用法。

ItemsPanel

最後,照片清單的顯示若太多,常常會發生把視窗擠的變形到無法接受,若我們要採用比較像瀏覽器的方式的話,可以透過 ItemsPanel 來設定其 Layout, 範例如下:

      <ListBox.ItemsPanel>
        <ItemsPanelTemplate>
          <WrapPanel/>
        </ItemsPanelTemplate>
      </ListBox.ItemsPanel>

Proxy Server

若您的環境是透過 Proxy Server 的話,又該如何在 WPF 中使用呢?可以在執行檔所在地產生一個 EXECUTE_FILE.exe.config,其內容很簡單,我們讓它使用與 IE 相同的 Proxy Server 設定值,範例如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.net>
    <defaultProxy useDefaultCredentials="true" enabled="true"></defaultProxy>
  </system.net>
</configuration>

2009/07/31

P2P by using WCF

P2P by using WCF
這是一個用C# 寫的應用,主要是練習 P2P,使用的是 .NET 3.0 推出的 WCF,可以參考 A simple peer to peer chat application using WCF netPeerTcpBinding。若對開啟 C# 應用有興趣的人,可以參考 Button Controls。P2P 可以想成一個應用程式本身具有 Client 及 Server。

Server

在 Server 端需要額外引入三個 namespace:

* System.ServiceModel;
* System.ServiceModel.Channels;
* System.ServiceModel.PeerResolvers;

完整程式碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.PeerResolvers;

namespace ChatServer
{
public partial class ChatServer : Form
{
private CustomPeerResolverService cprs;
private ServiceHost host;

public ChatServer()
{
InitializeComponent();
btnStop.Enabled = false;
}

private void btnStart_Click(object sender, EventArgs e)
{
try
{
cprs = new CustomPeerResolverService();
cprs.RefreshInterval = TimeSpan.FromSeconds(5);
host = new ServiceHost(cprs);
cprs.ControlShape = true;
cprs.Open();
host.Open(TimeSpan.FromDays(1000000));
lblMessage.Text = "Server started successfully.";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
btnStart.Enabled = false;
btnStop.Enabled = true;
}
}

private void btnStop_Click(object sender, EventArgs e)
{
try
{
cprs.Close();
host.Close();
lblMessage.Text = "Server stopped successfully.";
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
btnStart.Enabled = true;
btnStop.Enabled = false;
}
}
}
}

很簡單,若真的要說程式片斷就在前面的六行:

cprs = new CustomPeerResolverService();
cprs.RefreshInterval = TimeSpan.FromSeconds(5);
host = new ServiceHost(cprs);
cprs.ControlShape = true;
cprs.Open();
host.Open(TimeSpan.FromDays(1000000));

cprs 是 CustomPeerResolverService 物件,用來當 ServiceHost 物件的輸入參數,主要會讀取一個設定檔(App.config), 並且設定連線資訊更新時間是 5ms。
host 是 ServiceHost 物件,並設定 timeout 時間為永不停止(1000000 天實在是非常久的時間)。然後....就這樣.....

CONFIG

至於設定檔,內容如下:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="System.ServiceModel.PeerResolvers.CustomPeerResolverService">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost/ChatServer"/>
          </baseAddresses>
        </host>
        <endpoint address="net.tcp://localhost/ChatServer" binding="netTcpBinding"
                  bindingConfiguration="TcpConfig"
                  contract="System.ServiceModel.PeerResolvers.IPeerResolverContract">         
        </endpoint>         
      </service>
    </services>

    <bindings>
      <netTcpBinding>
        <binding name="TcpConfig">
          <security mode="None"></security>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>


Client

Client 是稍微複雜了點,先貼一下原始程式碼如下,只需要用到兩個 namespace(System.ServiceModel, 及System.ServiceModel.Channels):

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace ChatClient
{
[ServiceContract(CallbackContract = typeof(IChatService))]
public interface IChatService
{
[OperationContract(IsOneWay = true)]
void Join(string memberName);
[OperationContract(IsOneWay = true)]
void Leave(string memberName);
[OperationContract(IsOneWay = true)]
void SendMessage(string memberName, string message);
}

public interface IChatChannel : IChatService, IClientChannel
{
}

public partial class ChatClient : Form, IChatService
{
private delegate void UserJoined(string name);
private delegate void UserSendMessage(string name, string message);
private delegate void UserLeft(string name);

private static event UserJoined NewJoin;
private static event UserSendMessage MessageSent;
private static event UserLeft RemoveUser;

private string userName;
private IChatChannel channel;
private DuplexChannelFactory factory;

public ChatClient()
{
InitializeComponent();
this.AcceptButton = btnLogin;
}

public ChatClient(string userName)
{
this.userName = userName;
}

private void btnLogin_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(txtUserName.Text.Trim()))
{
try
{
NewJoin += new UserJoined(ChatClient_NewJoin);
MessageSent += new UserSendMessage(ChatClient_MessageSent);
RemoveUser += new UserLeft(ChatClient_RemoveUser);

channel = null;
this.userName = txtUserName.Text.Trim();
InstanceContext context = new InstanceContext(
new ChatClient(txtUserName.Text.Trim()));
factory =
new DuplexChannelFactory(context, "ChatEndPoint");
channel = factory.CreateChannel();
IOnlineStatus status = channel.GetProperty();
status.Offline += new EventHandler(Offline);
status.Online += new EventHandler(Online);
channel.Open();
channel.Join(this.userName);
grpMessageWindow.Enabled = true;
grpUserList.Enabled = true;
grpUserCredentials.Enabled = false;
this.AcceptButton = btnSend;
rtbMessages.AppendText("*****************************WEL-COME to Chat Application*****************************\r\n");
txtSendMessage.Select();
txtSendMessage.Focus();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}

void ChatClient_RemoveUser(string name)
{
try
{
rtbMessages.AppendText("\r\n");
rtbMessages.AppendText(name + " left at " + DateTime.Now.ToString());
lstUsers.Items.Remove(name);
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}

void ChatClient_MessageSent(string name, string message)
{
if (!lstUsers.Items.Contains(name))
{
lstUsers.Items.Add(name);
}
rtbMessages.AppendText("\r\n");
rtbMessages.AppendText(name + " says: " + message);
}

void ChatClient_NewJoin(string name)
{
rtbMessages.AppendText("\r\n");
rtbMessages.AppendText(name + " joined at: [" + DateTime.Now.ToString() + "]");
lstUsers.Items.Add(name);
}

void Online(object sender, EventArgs e)
{
rtbMessages.AppendText("\r\nOnline: " + this.userName);
}

void Offline(object sender, EventArgs e)
{
rtbMessages.AppendText("\r\nOffline: " + this.userName);
}

#region IChatService Members

public void Join(string memberName)
{
if (NewJoin != null)
{
NewJoin(memberName);
}
}

public new void Leave(string memberName)
{
if (RemoveUser != null)
{
RemoveUser(memberName);
}
}

public void SendMessage(string memberName, string message)
{
if (MessageSent != null)
{
MessageSent(memberName, message);
}
}

#endregion

private void btnSend_Click(object sender, EventArgs e)
{
channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
txtSendMessage.Clear();
txtSendMessage.Select();
txtSendMessage.Focus();
}

private void ChatClient_FormClosing(object sender, FormClosingEventArgs e)
{
try
{
if (channel != null)
{
channel.Leave(this.userName);
channel.Close();
}
if (factory != null)
{
factory.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
}

雖然小小長了點,可以看到 interface 中只有定義了三個動作:

Join : 用來登入用
Leave: 用來登出用
SendMessage: 用來傳送訊息

在繼續看這份 souce code 之前,先來讀一下設定檔:

Config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint name="ChatEndPoint" address="net.p2p://chatMesh/ChatServer"
                binding="netPeerTcpBinding" bindingConfiguration="PeerTcpConfig"
                contract="ChatClient.IChatService"></endpoint>
   </client>

    <bindings>
      <netPeerTcpBinding>
        <binding name="PeerTcpConfig" port="0">
          <security mode="None"></security>
          <resolver mode="Custom">
            <custom address="net.tcp://localhost/ChatServer" binding="netTcpBinding"
                    bindingConfiguration="TcpConfig"></custom>
          </resolver>
        </binding>
      </netPeerTcpBinding>
      <netTcpBinding>
        <binding name="TcpConfig">
          <security mode="None"></security>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>


對應到前面講的 Join, Leave, SendMessage 三個介面,需要三個事件來配合:

private static event UserJoined NewJoin;
private static event UserSendMessage MessageSent;
private static event UserLeft RemoveUser;

Event 的使用在程式中如下:

NewJoin += new UserJoined(ChatClient_NewJoin);
MessageSent += new UserSendMessage(ChatClient_MessageSent);
RemoveUser += new UserLeft(ChatClient_RemoveUser);

聊天室功能裡面,最重要的就是聊天,對,有點廢話,不過難就難在網路連線的維護,幸好都被 WCF 做掉了,在程式裡面是透過 DuplexChannelFactory() 物件來達成:

factory = new DuplexChannelFactory(context, "ChatEndPoint");
channel = factory.CreateChannel();

若我們看看幾個動作即可明白中間的關聯:

channel.Join(this.userName);
channel.SendMessage(this.userName, txtSendMessage.Text.Trim());
channel.Leave(this.userName);
channel.Close();

好了,就這樣。什麼?沒講完?對,聰明的你一定有發現,好像沒指出收訊息的機制?呵,其實是沒特別講清楚而已。

訊息收送

收: MessageSent += new UserSendMessage(ChatClient_MessageSent);
送: channel.SendMessage(this.userName, txtSendMessage.Text.Trim());

2009/07/24

在 blog 中優化顯示程式碼片段

底下的內容是 CSS, 放在 blog 設定畫面裡版面配置==>修改 HTML==>找到類似 body{ 附近(意指在它的前面或是那一段的後面, 不要插到裡面去了)插入下面兩種可以選擇之一的 CSS 碼, 一個是用 <Code> 括住,使用時的範例如下:

<Code>
code {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee url(http://sites.google.com/a/hc888.com.tw/file/img/Code_BG0.gif) left top repeat-y;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}
</CODE>


效果如下:

code {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee url(http://sites.google.com/a/hc888.com.tw/file/img/Code_BG0.gif) left top repeat-y;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}


或者用有縮編的 PRE tag, 在寫 blog 時的範例如下:

<PRE>
pre {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}
</PRE>

效果如下:

pre {
display: block;
font-family: Courier New;
font-size: 9pt;
overflow:auto;
background: #fff5ee;
border: 1px solid #ccc;
padding: 5px 5px 5px 20px;
max-height:200px;
line-height: 1.2em;
margin: 5px 0 0 15px;
}

2009/07/18

The value of Multi touch 的價值

要說 multi touch 的價值實在是愧不敢當,但是最近的工作剛好就是在開發多點觸控的應用程式,跟著一群年輕小夥子工作,感覺非常溫馨之餘,也讓我有機會在跟他們探討中去深思多點觸控到底價值何處?

一開始我對多點觸控的期待,就是改變對電腦的操作,當然我更想要像駭客任務那樣,不止可以觸控,最希望的是立體感那種操作,最好要更先進的思想操作。但是在接觸多點觸控一年來,發現裡面的問題不少,但是這篇不是要講問題點,而是要講價值。

大概最快想到的就是「手勢操作」。可是問題是誰來定義手勢?M$ 確實定義了些手勢操作,可是若實際去開發軟體的話就會發現其中存在不少問題,這邊指的是我們開發軟體的過程,而不是「應用」。問題在我們的功力還不夠,幸好也是因為大家都聚精會神的研究並解決問題,因而讓我更有機會去探索多點觸控的價值,而這樣的思考至少目前必須先面對 M$ 所建立起來的環境是那麼的複雜與充滿矛盾上。

第一個價值,當然希望能有更直覺式的操作,剛剛提到手勢就是這樣的產物。目前開發出來的就是 Zoom In/ Zoom Out, Scroll, PAN(Move) 這三個。事實上滑鼠也可以做到。我一直在思考,click, double click, move, drag....與 what is gesture! 困惑著我。答案或許是,請拋棄滑鼠吧。(註: 鍵盤留著)

第二個價值,是希望能讓整個螢幕上出現的框框有立體操作感。立體感早就有了,我說的是立體的操作感,否則只是多了另一隻滑鼠而已。要有立體操作感很難,要在平面表達立體已經發展多年,早期都是用滑鼠,當然相安無事,現在要在投射在平面所產生的立體物件上操作,難度非常大。幸好,我也定義了三種操作,有機會再來分享。我們定義了一些立體操作與手勢,希望能改變人類操作平面電腦的習慣,這邊講平面的意思當然是希望將來的顯示裝置操作起來能隨想隨動。

第三個價值,是......趁著這波可攜熱與觸控熱,讓觸控拉近你我彼此間的距離變得更加容易。我個人一直是在玩 Linux, 嵌入式是我的擅項,突然來搞 M$ 真是有夠突兀,不過這不妨礙,問題是我最想要的是建立一個龐大而自然的虛擬世界,能因此而實現嗎?

且拭目以待吧。

2009/06/28

win7 比 vista 便宜 ... 20% off!

微軟在其網站上,正式公告售價:

昇級版售價:
* Windows 7 Home Premium (Upgrade): $119.99
* Windows 7 Professional (Upgrade): $199.99
* Windows 7 Ultimate (Upgrade): $219.99

完整版售價:

* Windows 7 Home Premium (Full): $199.99
* Windows 7 Professional (Full): $299.99
* Windows 7 Ultimate (Full): $319.99

這意味著,因應經濟不景氣,微軟大發慈悲心,大降價,以 Win7 Home Premium 為例大約降價 20%!

2009/06/23

xp, vista, win7 共舞 multi-boot -2

先前寫過一篇xp, vista, win7 共舞,後來又因為不知名的原因把某一個開機選項弄不見了。真的,不是故意為之。那問題來了,怎樣把之前的開機選項搞出來?

其實這問題或許最好的答案是養成備份習慣。不過因為是實驗機,所以我也就懶得備份,再說,工作機也不需要搞這東東。

BCDE FAQ....這文章還真怪,BCDEF...Ok Ok, 不離題,在這篇文章中提到要怎樣自己弄一個新的開機選項,請見 How to create a new Windows Vista operating system entry 這一條的說明。步驟大約如下:

1. bcdedit /copy {GuidToCopy} /d 「NewEntryDescription」
==> 我是這麼下命令的:
bcdedit /copy {current} /d "win7 enterprise"
==> 這樣一來,會把目前的開機選項複製並附在選單的最後。若是注意的話,會發現 Identifier 是新的,其餘的都是與 {current} 相同。

2. 用下列兩個命令來設定到你要的磁區,也就是「磁碟機」:
bcdedit /set {NewGuid} device partition=x:
bcdedit /set {NewGuid} osdevice partition=x:
==> 這邊的 {NewGuid} 就是上一條產生的 Identifier, 而 x: 就是你要的磁碟機

3. 用下列命令調整開機順序,不執行此命令也行
bcdedit /displayorder {NewGuid} /addlast

2009/06/12

screen capture

我們有一個應用程式,需要抓取全螢幕的畫面,有人會截取 PrtScn Keyboard 事件來達到目的,我當然認定這個方法非常不好,就像前面講的 mouse hook 一樣,別人一樣也可以做 keyboard hook。

後來用了某種 screen capture 的方法,結果把螢幕上所有東西都抓下來,或許這結果是某些人想要的,但是我們卻不要像滑鼠或是一些 Gesture 提示的畫面。目前找到一篇Screen Captures, Window Captures and Window Icon Captures with Spy++ style Window Finder!有不錯的範例,請自行前往觀看。

Touch 與 mouse 共舞

最近在搞 Windows 7 的 Multi Touch 應用程式,其中有幾項議題困擾很久,其中一項是 sleep(1) 已於前面說明。另一項是 Touch 發生時我們不想要有 Mouse Event, 我說的是不想要,不是不處理即可,這問題讓我煩到快抓狂。

不抱怨了,直接說答案,就是用 Global Mouse Hook, 然後把 Mouse Message 濾掉。是有不少例子在說明 mouse hook, 例如我看的最懂的是Processing Global Mouse and Keyboard Hooks in C#,卻沒有人講明白怎樣濾掉 mouse message.

事實上在該文中有答案,只是它沒明說而已。我直接寫一個範例片段來說明,當然說明直接放程式片段中:

// 註冊 global mouse hook
void TFormPaint::RegMouseHook()
{
MouseHookDelegate = gcnew HookProcDelegate(this, &TFormPaint::MouseHookProc);
MouseHookHandle = ::SetWindowsHookEx(WH_MOUSE_LL, MouseHookDelegate,
Marshal::GetHINSTANCE(System::Reflection::Assembly::GetExecutingAssembly()->GetModules()[0]),0);
}

// 我們處理的 mouse hook 的函式
int TFormPaint::MouseHookProc(int nCode, int wParam, IntPtr lParam)
{
WCHAR Buffer[30];

// 把 Mouse Hook 的訊息從 lParam 指標中取出來
MOUSEHOOKSTRUCT* mouseHookStruct=(MOUSEHOOKSTRUCT*)lParam.ToPointer();

// 只濾掉發生在本視窗的滑鼠事件,
// 所以要比對滑鼠所發生時的視窗
HWND CurHwnd = WindowFromPoint(mouseHookStruct->pt);
GetWindowText(CurHwnd, Buffer, 30);
String^ WindowTitle = gcnew String(Buffer);

// ☆重點來了,
// 當發現滑鼠事件是作用在我們的視窗上,就以
// return -1 來濾掉
if (WindowTitle->IndexOf(this->Text) != -1)
{
return -1;
}

// 下面這行非常重要,否則整個系統的滑鼠會因此壞掉
return CallNextHookEx(MouseHookHandle, nCode, wParam, lParam);
}

sleep 1 ms ?

還記得四月初時我寫的這一篇timer 與 while(1)嗎?最近在用 visual studio 開發應用程式時有用到 sleep() 函式來暫時停止程式運行,用我們的話說,就是我們要讓程式的執行頻率是 125 次/秒,也就是我們想要用 sleep(8) 來達到目的。sleep(8) 應該要能暫停 8ms, 結果實驗數據是 16ms, 甚至當我們調整成 sleep(1), 答案也一樣。

慘了,效率只有一半!這問題困擾很久,試著用 timer,答案一樣的令人沮喪,也試過透過 Windows Message 的方式,沒用。

好了,最後結果是參考timeBeginPeriod搭配 timeEndPeriod。因為這會改到系統的時間設定,因此記得要成對。

簡單寫個範例如下:
timeBeginPeriod(8);
sleep(8);
timeEndPeriod(8);


想對 Timer 有更進一步了解的人,可以自行參考Timer 介紹一文。

2009/06/08

xp, vista, win7 共舞 multi-boot

最近真的是離不開 M$ OS, 甚至一台電腦要裝九個作業系統,當然啦,是資源不足才需要這樣。問題來了,開機選單在 winxp 還可以透過界面修改,可是 vista, win7 全都不開放!現在研究後的心得如下,請在 vista or win7 下執行:

一、首先在 Command Prompt, 也就是 DOS command, 上用右鍵選「Run as Administrator」,這樣才有足夠的權限,若找不到的話請參考:
Start Menu->All Programs->Accessories->Command prompt(在上面點右鍵選擇"Run as administrator")

二、可以用 bcdedit /enum 列出所有開機選單,例如下面的是我的電腦的範例, # 號後面的是我加的註解:


Windows Boot Manager
--------------------
identifier {bootmgr}
device partition=D:
description Windows Boot Manager
locale en-US
inherit {globalsettings}
default {current}
resumeobject {42231781-52f5-11de-92a1-f650bd7a3f2b}
displayorder {ntldr}
{current}
{42231782-52f5-11de-92a1-f650bd7a3f2b}
{4223177e-52f5-11de-92a1-f650bd7a3f2b}
{b1252275-52ef-11de-ac3a-e7ceed221dbd}
toolsdisplayorder {memdiag}
timeout 30

Windows Legacy OS Loader
------------------------
identifier {ntldr}
device partition=D:
path \ntldr
description 舊版 Windows

# 可以用下列命令改掉
# bcdedit /set {ntldr} Description "XP 32/64"

Windows Boot Loader
-------------------
identifier {current}
device partition=C:
path \Windows\system32\winload.exe
description Microsoft Windows Vista
locale en-US
inherit {bootloadersettings}
osdevice partition=C:
systemroot \Windows
resumeobject {adc6e5dc-5450-11de-b61c-8253c0de8538}
nx OptIn
# 可以用下列命令將原來的"Microsoft Windows Vista" 改掉
# bcdedit /set {current} Description "Vista 32 en Enterprise"

Windows Boot Loader
-------------------
identifier {42231782-52f5-11de-92a1-f650bd7a3f2b}
device partition=I:
path \Windows\system32\winload.exe
description Windows 7
locale zh-TW
inherit {bootloadersettings}
recoverysequence {42231783-52f5-11de-92a1-f650bd7a3f2b}
recoveryenabled Yes
osdevice partition=I:
systemroot \Windows
resumeobject {42231781-52f5-11de-92a1-f650bd7a3f2b}
nx OptIn
# 可以用下列命令將原來的 "Windows 7" 改掉
# bcdedit /set {42231782-52f5-11de-92a1-f650bd7a3f2b} Description "Win7 64 Ultimate"

Windows Boot Loader
-------------------
identifier {4223177e-52f5-11de-92a1-f650bd7a3f2b}
device partition=H:
path \Windows\system32\winload.exe
description Windows 7
locale en-US
inherit {bootloadersettings}
recoverysequence {4223177f-52f5-11de-92a1-f650bd7a3f2b}
recoveryenabled Yes
osdevice partition=H:
systemroot \Windows
resumeobject {4223177d-52f5-11de-92a1-f650bd7a3f2b}
nx OptIn
# 可以用下列命令將原來的 "Windows 7" 改掉
# bcdedit /set {4223177e-52f5-11de-92a1-f650bd7a3f2b} Description "Win7 32 Ultimate"

Windows Boot Loader
-------------------
identifier {b1252275-52ef-11de-ac3a-e7ceed221dbd}
device partition=E:
path \Windows\system32\winload.exe
description Microsoft Windows Vista
locale en-US
inherit {bootloadersettings}
osdevice partition=E:
systemroot \Windows
resumeobject {b1252276-52ef-11de-ac3a-e7ceed221dbd}
nx OptIn
# 可以用下列命令將原來的 "Microsoft Windows Vista" 改掉
# bcdedit /set {b1252275-52ef-11de-ac3a-e7ceed221dbd} Description "Vista 64 Enterprise"


三、在上文提到的 {current} 還有 {default} 等選擇文字,其實就在 /enum 列出的 "identifier" 欄位裡面

四、 用 bcdedit /? 取得進一步資訊

2009/05/20

Programming with Windows 7 Multitouch & Gesture 續

軟體與硬體需求

當然要有硬體與作業系統及 SDK:
A multi-touch monitor
A PC or NB installed Windows 7
Installed Windows 7 SDK
Visual Studio(我用的是 Visual Studio 2008)

環境設定

設定Win7 SDK的路徑, 包含include與library都要設定:
Header :Declared in Winuser.h; include Windows.h.
Library:Include User32.lib

接下來請參考前一篇的資料結構,因為微軟的開發模式都差不多,在寫 Multitouch 與寫 Mouse 的方式很像,就是收 Message, 定義自己得 event handler, 底下就列出相關的 Message:


WM_GESTURE Message

Passes information about a gesture.

Parameters

wParam


Provides information identifying the gesture command and gesture-specific argument values. This information is retrieved by calling GetGestureInfo.

lParam

Provides information identifying the gesture command and gesture-specific argument values. This information is retrieved by calling GetGestureInfo.

Return Value

If an application processes this message, it should return 0.

If the application does not process the message, it must call DefWindowProc. Not doing so will cause the application to leak memory because the touch input handle will not be closed and associated process memory will not be freed.

WM_GESTURENOTIFY Message

Indicates that a gesture message is about to be received.

Parameters

wParam


Unused.

lParam

A pointer to a GESTURENOTIFYSTRUCT.

Return Value

If an application processes this message, it should return 0.

Remarks

When the WM_GESTURENOTIFY message is received, the application can use SetGestureConfig to specify the gestures to receive.

Examples

switch (message)
{
case WM_GESTURENOTIFY:
{
GESTURECONFIG gc = {0,GC_ALLGESTURES,0};
BOOL bResult = SetGestureConfig(hWnd,0,1,&gc,sizeof(GESTURECONFIG));

if(!bResult)
{
// an error
}
}

break

GESTUREINFO Structure

Stores information about a gesture.
Syntax

typedef struct _GESTUREINFO {
UINT cbSize;
DWORD dwFlags;
DWORD dwID;
HWND hwndTarget;
POINTS ptsLocation;
DWORD dwInstanceID;
DWORD dwSequenceID;
ULONGLONG ullArguments;
UINT cbExtraArgs;

} GESTUREINFO, *PGESTUREINFO;

Members

cbSize

The size of the structure, in bytes. The caller must set this to sizeof(GESTUREINFO).

dwFlags

The state of the gesture. For additional information, see Remarks.

dwID

The identifier of the gesture command.

hwndTarget

A handle to the window that is targeted by this gesture.

ptsLocation

A POINTS structure containing the coordinates associated with the gesture. These coordinates are always relative to the origin of the screen.

dwInstanceID

An internally used identifier for the structure.

dwSequenceID

An internally used identifier for the sequence.

ullArguments

An unsigned long long that contains the arguments for gestures that fit into 8 bytes.

cbExtraArgs

The size, in bytes, of extra arguments that accompany this gesture.

Remarks

The GESTUREINFO structure is retrieved by passing the handle to the gesture information structure to the GetGestureInfo function.

The following flags indicate the various states of the gestures and are stored in dwFlags.
Name Value Description
GF_BEGIN 0x00000001 Indicates a gesture is starting
GF_INERTIA 0x00000002 Indicates a gesture has triggered inertia
GF_END 0x00000004 Indicates a gesture has finished

Note Most applications should ignore the GID_BEGIN and GID_END messages and pass them to DefWindowProc. These messages are used by the default gesture handler and application behaviour is undefined when the GID_BEGIN and GID_END messages are consumed by a third party application.

The following table indicates the various identifiers for gestures.


Name Value Description
GID_BEGIN 1 Indicates a gesture is starting.
GID_END 2 Indicates a gesture is ending.
GID_ZOOM 3 Indicates the zoom gesture.
GID_PAN 4 Indicates the pan gesture.
GID_ROTATE 5 Indicates the rotation gesture.
GID_TWOFINGERTAP 6 Indicates the two-finger tap gesture.
GID_ROLLOVER 7 Indicates the rollover gesture.
Examples

GESTUREINFO gestureInfo = {0};

gestureInfo.cbSize = sizeof(gestureInfo);

BOOL bResult = GetGestureInfo((HGESTUREINFO)lParam, &gestureInfo);


dwCommand dwArguments ptsLocation
Pan Distance between contacts Current center of gesture
Zoom Distance between contacts Current center of gesture
Rotate Absolute angle on rotate start, delta on updates Current center of gesture
Two-finger tap NA Current center of gesture

GESTURENOTIFYSTRUCT Structure

When transmitted with WM_GESTURENOTIFY messages, passes information about a gesture.

Syntax

typedef struct tagGESTURENOTIFYSTRUCT {
UINT cbSize;
DWORD dwFlags;
HWND hwndTarget;
POINTS ptsLocation;
DWORD dwInstanceID;

} GESTURENOTIFYSTRUCT, *PGESTURENOTIFYSTRUCT;

Members

cbSize

The size of the structure.

dwFlags

Reserved for future use.

hwndTarget

The target window for the gesture notification.

ptsLocation

The location of the gesture.

dwInstanceID

A specific gesture instance with gesture messages starting with GID_START and ending with GID_END.

GESTURECONFIG Structure

Gets and sets the configuration for enabling gesture messages and the type of this configuration.

Syntax

typedef struct _GESTURECONFIG {
DWORD dwID;
DWORD dwWant;
DWORD dwBlock;

} GESTURECONFIG, *PGESTURECONFIG;

Members

dwID

The identifier for the type of configuration that will have messages enabled or disabled. For more information, see Remarks.

dwWant

The messages to enable.

dwBlock

The messages to disable.

Remarks

When you pass this structure, the dwID member contains information for a set of gestures. This determines what the other flags will mean. If you set flags for pan messages, they will be different from those flags that are set for rotation messages.

The following table indicates the various identifiers for gestures that are supported by the dwID member of the GESTURECONFIG structure. Note that setting dwID to 0 indicates that global gesture configuration flags are set.

GetGestureInfo Function

Retrieves a gesture information structure given a handle to the gesture information.

Syntax

BOOL WINAPI GetGestureInfo(

__in HGESTUREINFO hGestureInfo,

__out PGESTUREINFO pGestureInfo

);

Parameters

hGestureInfo [in]

The gesture information handle.

pGestureInfo [out]

A pointer to the gesture information structure.

Return Value

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, use the GetLastError function.

Examples

GESTUREINFO gestureInfo = {0};

gestureInfo.cbSize = sizeof(gestureInfo);

BOOL bResult = GetGestureInfo((HGESTUREINFO)lParam, &gestureInfo);

SetGestureConfig Function

Configures the messages that are sent from a window for multitouch gestures.

Syntax

BOOL WINAPI SetGestureConfig(
__in HWND hwnd,
__in DWORD dwReserved,
__in UINT cIDs,
__in PGESTURECONFIG pGestureConfig,
__in UINT cbSize
);

Parameters

hwnd [in]

A handle to the window to set the gesture configuration on.

dwReserved [in]

This value is reserved and must be set to 0.

cIDs [in]

A count of the gesture configuration structures that are being passed.

pGestureConfig [in]

An array of gesture configuration structures that specify the gesture configuration.

cbSize [in]

The size of the array of gesture configuration structures.

Return Value

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, use the GetLastError function.

Programming with Windows 7 Multitouch & Gesture

請參考Microsoft MSDN Windows Touch for Developer一文,以及微軟「阿倫」的部落格的說明。

我這邊先直接貼一些資料結構的定義:


#if(WINVER >= 0x0601)

/*
* Gesture defines and functions
*/

/*
* Gesture information handle
*/
DECLARE_HANDLE(HGESTUREINFO);


/*
* Gesture flags - GESTUREINFO.dwFlags
*/
#define GF_BEGIN 0x00000001
#define GF_INERTIA 0x00000002
#define GF_END 0x00000004

/*
* Gesture IDs
*/
#define GID_BEGIN 1
#define GID_END 2
#define GID_ZOOM 3
#define GID_PAN 4
#define GID_ROTATE 5
#define GID_TWOFINGERTAP 6
#define GID_PRESSANDTAP 7
#define GID_ROLLOVER GID_PRESSANDTAP

/*
* Gesture information structure
* - Pass the HGESTUREINFO received in the WM_GESTURE message lParam into the
* GetGestureInfo function to retrieve this information.
* - If cbExtraArgs is non-zero, pass the HGESTUREINFO received in the WM_GESTURE
* message lParam into the GetGestureExtraArgs function to retrieve extended
* argument information.
*/
typedef struct tagGESTUREINFO {
UINT cbSize; // size, in bytes, of this structure (including variable length Args field)
DWORD dwFlags; // see GF_* flags
DWORD dwID; // gesture ID, see GID_* defines
HWND hwndTarget; // handle to window targeted by this gesture
POINTS ptsLocation; // current location of this gesture
DWORD dwInstanceID; // internally used
DWORD dwSequenceID; // internally used
ULONGLONG ullArguments; // arguments for gestures whose arguments fit in 8 BYTES
UINT cbExtraArgs; // size, in bytes, of extra arguments, if any, that accompany this gesture
} GESTUREINFO, *PGESTUREINFO;
typedef GESTUREINFO const * PCGESTUREINFO;


/*
* Gesture notification structure
* - The WM_GESTURENOTIFY message lParam contains a pointer to this structure.
* - The WM_GESTURENOTIFY message notifies a window that gesture recognition is
* in progress and a gesture will be generated if one is recognized under the
* current gesture settings.
*/
typedef struct tagGESTURENOTIFYSTRUCT {
UINT cbSize; // size, in bytes, of this structure
DWORD dwFlags; // unused
HWND hwndTarget; // handle to window targeted by the gesture
POINTS ptsLocation; // starting location
DWORD dwInstanceID; // internally used
} GESTURENOTIFYSTRUCT, *PGESTURENOTIFYSTRUCT;

/*
* Gesture argument helpers
* - Angle should be a double in the range of -2pi to +2pi
* - Argument should be an unsigned 16-bit value
*/
#define GID_ROTATE_ANGLE_TO_ARGUMENT(_arg_) ((USHORT)((((_arg_) + 2.0 * 3.14159265) / (4.0 * 3.14159265)) * 65535.0))
#define GID_ROTATE_ANGLE_FROM_ARGUMENT(_arg_) ((((double)(_arg_) / 65535.0) * 4.0 * 3.14159265) - 2.0 * 3.14159265)

/*
* Gesture information retrieval
* - HGESTUREINFO is received by a window in the lParam of a WM_GESTURE message.
*/
WINUSERAPI
BOOL
WINAPI
GetGestureInfo(
__in HGESTUREINFO hGestureInfo,
__out PGESTUREINFO pGestureInfo);

/*
* Gesture extra arguments retrieval
* - HGESTUREINFO is received by a window in the lParam of a WM_GESTURE message.
* - Size, in bytes, of the extra argument data is available in the cbExtraArgs
* field of the GESTUREINFO structure retrieved using the GetGestureInfo function.
*/
WINUSERAPI
BOOL
WINAPI
GetGestureExtraArgs(
__in HGESTUREINFO hGestureInfo,
__in UINT cbExtraArgs,
__out_bcount(cbExtraArgs) PBYTE pExtraArgs);

/*
* Gesture information handle management
* - If an application processes the WM_GESTURE message, then once it is done
* with the associated HGESTUREINFO, the application is responsible for
* closing the handle using this function. Failure to do so may result in
* process memory leaks.
* - If the message is instead passed to DefWindowProc, or is forwarded using
* one of the PostMessage or SendMessage class of API functions, the handle
* is transfered with the message and need not be closed by the application.
*/
WINUSERAPI
BOOL
WINAPI
CloseGestureInfoHandle(
__in HGESTUREINFO hGestureInfo);


/*
* Gesture configuration structure
* - Used in SetGestureConfig and GetGestureConfig
* - Note that any setting not included in either GESTURECONFIG.dwWant or
* GESTURECONFIG.dwBlock will use the parent window's preferences or
* system defaults.
*/
typedef struct tagGESTURECONFIG {
DWORD dwID; // gesture ID
DWORD dwWant; // settings related to gesture ID that are to be turned on
DWORD dwBlock; // settings related to gesture ID that are to be turned off
} GESTURECONFIG, *PGESTURECONFIG;

/*
* Gesture configuration flags - GESTURECONFIG.dwWant or GESTURECONFIG.dwBlock
*/

/*
* Common gesture configuration flags - set GESTURECONFIG.dwID to zero
*/
#define GC_ALLGESTURES 0x00000001

/*
* Zoom gesture configuration flags - set GESTURECONFIG.dwID to GID_ZOOM
*/
#define GC_ZOOM 0x00000001

/*
* Pan gesture configuration flags - set GESTURECONFIG.dwID to GID_PAN
*/
#define GC_PAN 0x00000001
#define GC_PAN_WITH_SINGLE_FINGER_VERTICALLY 0x00000002
#define GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY 0x00000004
#define GC_PAN_WITH_GUTTER 0x00000008
#define GC_PAN_WITH_INERTIA 0x00000010

/*
* Rotate gesture configuration flags - set GESTURECONFIG.dwID to GID_ROTATE
*/
#define GC_ROTATE 0x00000001

/*
* Two finger tap gesture configuration flags - set GESTURECONFIG.dwID to GID_TWOFINGERTAP
*/
#define GC_TWOFINGERTAP 0x00000001

/*
* PressAndTap gesture configuration flags - set GESTURECONFIG.dwID to GID_PRESSANDTAP
*/
#define GC_PRESSANDTAP 0x00000001
#define GC_ROLLOVER GC_PRESSANDTAP

#define GESTURECONFIGMAXCOUNT 256 // Maximum number of gestures that can be included
// in a single call to SetGestureConfig / GetGestureConfig

WINUSERAPI
BOOL
WINAPI
SetGestureConfig(
__in HWND hwnd, // window for which configuration is specified
__in DWORD dwReserved, // reserved, must be 0
__in UINT cIDs, // count of GESTURECONFIG structures
__in_ecount(cIDs) PGESTURECONFIG pGestureConfig, // array of GESTURECONFIG structures, dwIDs will be processed in the
// order specified and repeated occurances will overwrite previous ones
__in UINT cbSize); // sizeof(GESTURECONFIG)


#define GCF_INCLUDE_ANCESTORS 0x00000001 // If specified, GetGestureConfig returns consolidated configuration
// for the specified window and it's parent window chain

WINUSERAPI
BOOL
WINAPI
GetGestureConfig(
__in HWND hwnd, // window for which configuration is required
__in DWORD dwReserved, // reserved, must be 0
__in DWORD dwFlags, // see GCF_* flags
__in PUINT pcIDs, // *pcIDs contains the size, in number of GESTURECONFIG structures,
// of the buffer pointed to by pGestureConfig
__inout_ecount(*pcIDs) PGESTURECONFIG pGestureConfig,
// pointer to buffer to receive the returned array of GESTURECONFIG structures
__in UINT cbSize); // sizeof(GESTURECONFIG)

2009/04/27

關於視窗的一點心得


/// how to capture window @ mouse?
// capture the window under the cursor's position
IntPtr hWnd = Win32.WindowFromPoint(Cursor.Position);
// handle
_textBoxHandle.Text = string.Format("{0}", hWnd.ToInt32().ToString());

// class
_textBoxClass.Text = this.GetClassName(hWnd);

// caption
_textBoxText.Text = this.GetWindowText(hWnd);

Win32.Rect rc = new Win32.Rect();
Win32.GetWindowRect(hWnd, ref rc);

// rect
_textBoxRect.Text = string.Format("[{0} x {1}], ({2},{3})-({4},{5})", rc.right - rc.left, rc.bottom - rc.top, rc.left, rc.top, rc.right, rc.bottom);

/// 自己的 Root Form Handler
if (hWnd == IntPtr.Zero)
{
}

/// 如何抓 window image?

// capture that window
Image image = ScreenCapturing.GetWindowCaptureAsBitmap(handle);

// fire our image read event, which the main window will display for us
this.OnImageReadyForDisplay(image);

/// 如何等候 Image 已經畫好再做事?

public event DisplayImageEventHandler ImageReadyForDisplay;
ImageReadyForDisplay += new DisplayImageEventHandler(this.DisplayImage);
if (ImageReadyForDisplay != null)
ImageReadyForDisplay(image, false, PictureBoxSizeMode.CenterImage);

/// 如何抓例外
try
{
if (this.ImageReadyForDisplay != null)
this.ImageReadyForDisplay(image, false, PictureBoxSizeMode.CenterImage);
}
catch(Exception ex)
{
Debug.WriteLine(ex);
}

public class WindowHighlighter
{
public static void Highlight(IntPtr hWnd)
{
const float penWidth = 3;
Win32.Rect rc = new Win32.Rect();
Win32.GetWindowRect(hWnd, ref rc);

IntPtr hDC = Win32.GetWindowDC(hWnd);
if (hDC != IntPtr.Zero)
{
using (Pen pen = new Pen(Color.Black, penWidth))
{
using (Graphics g = Graphics.FromHdc(hDC))
{
g.DrawRectangle(pen, 0, 0, rc.right - rc.left - (int)penWidth, rc.bottom - rc.top - (int)penWidth);
}
}
}
Win32.ReleaseDC(hWnd, hDC);
}

public static void Refresh(IntPtr hWnd)
{
Win32.InvalidateRect(hWnd, IntPtr.Zero, 1);
Win32.UpdateWindow(hWnd);
Win32.RedrawWindow(hWnd, IntPtr.Zero, IntPtr.Zero, Win32.RDW_FRAME | Win32.RDW_INVALIDATE | Win32.RDW_UPDATENOW | Win32.RDW_ALLCHILDREN);
}
}

2009/04/23

timer 與 while(1)

連續貼三篇文章,有的寫的很簡單卻搞到很頭痛,還有一個最頭痛的沒貼上來,等整理心得出來後再來與大家分享。這邊講一個被 M$ 氣的半死的一項。

想當然爾,timer() 的單位的 ms, 所以若設定 1ms 與 10ms, 被呼叫的頻率差應該是 10 倍吧?答案是....No!!! 究竟是多少?網路上有人在問要怎樣設定 timer 到微秒的等級,嗚呼哀哉。目前我們測試的 timer 最快也就 60HZ 而已。

因為 timer() 無法達到預期目標,那麼就用 while(1), 又怕吃掉系統資源,以前知道要用 usleep(), 不要說這會讓系統負擔變重,事實上是我們找不到!,後來大膽測試 while(1), 結果卻發現頻率夠高,而且而且,系統負擔竟然出奇的輕!偉哉,M$!

這邊要提醒的是,我說 while(1) 不會造成系統負擔過重的原因不是 OS 排程做的好,而是 while() 裡面做的是 device IO。至於如何調整 sleep() 的時間單位?答案有機會再揭曉

Visual Studio 中的記憶體管理

這篇也是同事的心得,因為程式一開始沒有作記憶體釋放動作,因而造成整個系統資源被吃光,希望不要再有人犯同樣錯誤。畢竟保持環境清潔人人有責。

C++中有new 和 gcnew(garbage collection new)(C#裡面的new 其實就是gcnew)

原則上managed的物件都只能用gcnew去創造

而unmanaged則是用new

這兩個東西的差別是需不需要主動去做garbage collection(delete的動作)

在managed的程式中

隔一段時間,GC會去判斷目前的記憶體空間是否沒有reference

沒有的話就會釋放出來

也可以採用GC::Collect()去做這件事(不需要特別再對每個ref做delete,delete沒有實際效果)

new的話就需要自己在確定沒有使用的時候去把他delete掉

另外還有一類,使用global的函式所給出的handle(比如canvas裡用的CreatePen)

不知道為什麼,他不會計算到程式的記憶體使用量裡面(一直create不會發現程式記憶體用輛增加)

但是因為沒有清掉,所以還是會在使用過多的時候發生問題(程式crash)

所以得要去做DeleteObject的動作

怎樣讓程式不會重複被執行

最近我只在 M$ 平台上混,真的被氣死了。話不多說,來說一個怎樣讓程式不會重複被叫起來的方法,這是同仁的心得,我代貼而已。

主要使用到CreateMutex()函數和GetLastError()以及一個常量
ERROR_ALREADY_EXISTS.

下面為sample code:

HANDLE hMutex = CreateMutex(NULL, false, "Process");
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(hMutex);
MessageBox(Application->Handle, "程序已經執行中,不能重覆啟動!", "提示",
MB_OK);

return 0;
}

參考網頁 : http://hi.baidu.com/machh03/blog/item/52c0cc3864fd5b2697ddd815.html

2009/03/30

resource:///

繼上次談Fennec之後, 發現 Firefox 竟然支援 resource:/// 這種 url. 當時在玩 Fennec 的那十分鐘裡,找不到設定畫面,我就是透過 about:config 來做設定 proxy 的,而 resource:/// 可以快速開啟 Local File,尤其是安裝 Firefox 的目錄下。

不過....據說目前還有漏洞....

2009/03/27

嵌入式瀏覽器又多一椿 Fennec/Firefox

可以說,Fennec 是 Firefox 的兒子!
懶得貼太多東西,請見下列網址,感覺非常棒,雖然要稱之為嵌入式恐怕還太早,但是非常棒!有機會可以跟 Ubuntu 的 Netbook(Remix)比比看。

先讀讀 http://shiuan-pc.blogspot.com/2008/10/fennecmozilla.html
裡頭會講到 windows 上也可以跑,網址: http://ftp.mozilla.org/pub/mozilla.org/mobile/fennec-1.0a1.en-US.win32.zip
https://wiki.mozilla.org/Mobile/FennecVision
http://www.mozilla.org/projects/fennec/1.0a1/releasenotes/

2009/02/22

把二進位內容放進 shell script

貼過一篇 把文字檔放進 C 執行檔中。記得曾貼過類似下面的文章將二進位內容放進 shell 中,有興趣的人請看舊作建立自解壓檔一文,現在再來貼一次作法類似的文章。

分兩個檔,一個叫 addpayload.sh, 一個叫 install.sh.in, 前者如下:


#!/bin/bash

# Check for payload format option (default is uuencode).
uuencode=1
if [[ "$1" == '--binary' ]]; then
binary=1
uuencode=0
shift
fi
if [[ "$1" == '--uuencode' ]]; then
binary=0
uuencode=1
shift
fi

if [[ ! "$1" ]]; then
echo "Usage: $0 [--binary | --uuencode] PAYLOAD_FILE"
exit 1
fi


if [[ $binary -ne 0 ]]; then
# Append binary data.
sed \
-e 's/uuencode=./uuencode=0/' \
-e 's/binary=./binary=1/' \
install.sh.in >install.sh
echo "PAYLOAD:" >> install.sh

cat $1 >>install.sh
fi
if [[ $uuencode -ne 0 ]]; then
# Append uuencoded data.
sed \
-e 's/uuencode=./uuencode=1/' \
-e 's/binary=./binary=0/' \
install.sh.in >install.sh
echo "PAYLOAD:" >> install.sh

cat $1 | uuencode - >> install.sh
fi


install.sh.in 是個樣本檔,template file, 最後會產生 install.sh 才是加了二進位內容的 shell script....原文有執行例,請自行前往觀看怎麼執行。


#!/bin/bash

uuencode=1
binary=0

function untar_payload()
{
match=$(grep --text --line-number '^PAYLOAD:$' $0 | cut -d ':' -f 1)
payload_start=$((match + 1))
if [[ $binary -ne 0 ]]; then
tail -n +$payload_start $0 | tar -tzvf -
fi
if [[ $uuencode -ne 0 ]]; then
tail -n +$payload_start $0 | uudecode | tar -tzvf -
fi
}

read -p "Install files? " ans
if [[ "${ans:0:1}" || "${ans:0:1}" ]]; then
untar_payload
# Do remainder of install steps.
fi

exit 0

2009/02/09

QEMU 與 VirtualBox 的影像檔互換

QEMU img to VirtualBox vdi
代碼:
VBoxManage convertdd qemu.img qemu.vdi

或是
代碼:
./vditool DD qemu.vdi qemu.img


VirtualBox vdi to QEMU img
代碼:
./vditool COPYDD virtualbox.vdi virtualbox.img

initrd 機制

這篇文章是因為他寫的好,有在看我的文章的會比較容易找到。

cpio-initrd 的處理流程

1. boot loader 把內核以及 initrd 文件加載到內存的特定位置。

2. 內核判斷initrd的文件格式,如果是cpio格式。

3. 將initrd的內容釋放到rootfs中。

4. 執行initrd中的/init文件,執行到這一點,內核的工作全部結束,完全交給/init文件處理。

image-initrd的處理流程

1. boot loader把內核以及initrd文件加載到內存的特定位置。

2. 內核判斷initrd的文件格式,如果不是cpio格式,將其作為image-initrd處理。

3. 內核將initrd的內容保存在rootfs下的/initrd.image文件中。

4. 內核將/initrd.image的內容讀入/dev/ram0設備中,也就是讀入了一個內存盤中。

5. 接著內核以可讀寫的方式把/dev/ram0設備掛載為原始的根文件系統。

6. .如果/dev/ram0被指定為真正的根文件系統,那麼內核跳至最後一步正常啟動。

7. 執行initrd上的/linuxrc文件,linuxrc通常是一個腳本文件,負責加載內核訪問根文件系統必須的驅動, 以及加載根文件系統。

8. /linuxrc執行完畢,常規根文件系統被掛載

9. 如果常規根文件系統存在/initrd目錄,那麼/dev/ram0將從/移動到/initrd。否則如果/initrd目錄不存在, /dev/ram0將被卸載。

10. 在常規根文件系統上進行正常啟動過程 ,執行/sbin/init。

cpio-initrd的製作方法更加簡單

cpio-initrd的製作非常簡單,通過兩個命令就可以完成整個製作過程

#假設當前目錄位於準備好的initrd文件系統的根目錄下
bash# find . | cpio -c -o > ../initrd.img
bash# gzip ../initrd.img


傳統initrd的製作過程比較繁瑣,需要如下六個步驟

#假設當前目錄位於準備好的initrd文件系統的根目錄下
bash# dd if=/dev/zero of=../initrd.img bs=512k count=5
bash# mkfs.ext2 -F -m0 ../initrd.img
bash# mount -t ext2 -o loop ../initrd.img /mnt
bash# cp -r * /mnt
bash# umount /mnt
bash# gzip -9 ../initrd.img

2009/01/14

Multi-Touch & Gesture support on 最新的 VS 2008/.NET framework 3.5

==== Gesture ====

InkEdit.Gesture Event (Microsoft.Ink)
Basic C# Visual C++ J# JScript .NET Framework Class Library InkEdit..::.Gesture Event InkEdit Class Example See Also Send Feedback Updated: November 2007 Occurs when an application gesture is recognized. Namespace: Microsoft.Ink Assembly: Microsoft.Ink (in Microsoft.Ink.dll ...
Source: microsoft.ink

InkOverlay.Gesture Event (Microsoft.Ink)
Basic C# Visual C++ J# JScript .NET Framework Class Library InkOverlay..::.Gesture Event InkOverlay Class Example See Also Send Feedback Updated: November 2007 Occurs when an application gesture is recognized. Namespace: Microsoft.Ink Assembly: Microsoft.Ink (in Microsoft.Ink.dll ...
Source: microsoft.ink

InkCollector.Gesture Event (Microsoft.Ink)
Basic C# Visual C++ J# JScript .NET Framework Class Library InkCollector..::.Gesture Event InkCollector Class Example See Also Send Feedback Updated: November 2007 Occurs when an application gesture is recognized. Namespace: Microsoft.Ink Assembly: Microsoft.Ink (in Microsoft.Ink.dll ...
Source: microsoft.ink

InkPicture.Gesture Event (Microsoft.Ink)
Basic C# Visual C++ J# JScript .NET Framework Class Library InkPicture..::.Gesture Event InkPicture Class Example See Also Send Feedback Updated: November 2007 Occurs when an application gesture is recognized. Namespace: Microsoft.Ink Assembly: Microsoft.Ink (in Microsoft.Ink.dll ...
Source: microsoft.ink

Gesture Event Event (Windows)
InkEdit.Gesture Event Occurs when an application gesture is recognized. Syntax HRESULT Gesture( [in] IInkCursor *Cursor, [in] IInkStrokes *Strokes, [in] VARIANT Gestures, [in, out] VARIANT ...
Source: TabletandTouch

InputBinding.Gesture Property (System.Windows.Input)
Visual C++ J# JScript .NET Framework Class Library InputBinding..::.Gesture Property InputBinding Class Example See Also Send Feedback Updated: November ... com/winfx/xaml/presentationSyntaxVisual Basic (Declaration) Public Overridable Property Gesture As InputGestureVisual Basic (Usage) Dim instance As InputBinding Dim value ...
Source: System.Windows.Input

Gesture Class (Microsoft.Ink)
Basic C# Visual C++ J# JScript .NET Framework Class Library Gesture Class Members See Also Send Feedback Updated: November 2007 Represents the ability to query particular properties of a gesture returned from a gesture recognition. Namespace: Microsoft.Ink Assembly: Microsoft ...
Source: microsoft.ink

MouseBinding.Gesture Property (System.Windows.Input)
Basic C# Visual C++ J# JScript .NET Framework Class Library MouseBinding..::.Gesture Property MouseBinding Class Example See Also Send Feedback Updated: November 2007 Gets or sets the gesture associated with this MouseBinding. Namespace: System.Windows.Input Assembly: PresentationCore (in PresentationCore ...
Source: System.Windows.Input

Gesture Event Event (Windows)
InkCollector.Gesture Event Occurs when an application-specific gesture is recognized. Syntax void Gesture( [in] IInkCursor *Cursor, [in] IInkStrokes *Strokes, [in] VARIANT Gestures, [in, out ...
Source: TabletandTouch

Gesture Event Event (Windows)
InkOverlay.Gesture Event Occurs when an application-specific gesture is recognized. Syntax void Gesture( [in] IInkCursor *Cursor, [in] IInkStrokes *Strokes, [in] VARIANT Gestures, [in, out ...
Source: TabletandTouch

Gesture Event Event (Windows)
InkPicture.Gesture Event Occurs when an application-specific gesture is recognized. Syntax void Gesture( [in] IInkCursor *Cursor, [in] IInkStrokes *Strokes, [in] VARIANT Gestures, [in, out ...
Source: TabletandTouch

KeyBinding.Gesture Property (System.Windows.Input)
Basic C# Visual C++ J# JScript .NET Framework Class Library KeyBinding..::.Gesture Property KeyBinding Class Example See Also Send Feedback Updated: November 2007 Gets or sets the gesture associated with this KeyBinding. Namespace: System.Windows.Input Assembly: PresentationCore (in PresentationCore ...
Source: System.Windows.Input

InkCanvas.Gesture Event (System.Windows.Controls)
C# Visual C++ J# JScript .NET Framework Class Library InkCanvas..::.Gesture Event InkCanvas Class Example See Also Send Feedback Updated: November 2007 Occurs when the InkCanvas detects a gesture. Namespace: System.Windows.Controls Assembly: PresentationFramework (in PresentationFramework.dll) XMLNS for ...
Source: System.Windows.Controls

Gesture Members (Microsoft.Ink)
Members Include .NET Framework Members Include .NET Compact Framework Members Include XNA Framework Members .NET Framework Class Library Gesture Members Gesture Class Methods Properties See Also Send Feedback Updated: November 2007 Represents the ability to query particular properties of a ...
Source: microsoft.ink

Using the Microsoft Gesture Recognizer Only (Windows)
Using the Microsoft Gesture Recognizer Only You can use an ink collector (InkCollector, InkOverlay, or InkPicture) to access the default Microsoft gesture recognizer directly. To use an ink collector to access the gesture ...
Source: TabletandTouch

Gesture Properties (Microsoft.Ink)
Members Include .NET Framework Members Include .NET Compact Framework Members Include XNA Framework Members .NET Framework Class Library Gesture Properties Gesture Class See Also Send Feedback Updated: November 2007 The Gesture type exposes the following members.Properties NameDescriptionConfidenceGets the level ...
Source: microsoft.ink

Using a Custom Gesture Recognizer (Windows)
Using a Custom Gesture Recognizer You can use a custom gesture recognizer independently, or in addition to the GestureRecognizer. Create your custom gesture recognizer as an IStylusSyncPlugin ...
Source: TabletandTouch

Gesture Methods (Microsoft.Ink)
Members Include .NET Framework Members Include .NET Compact Framework Members Include XNA Framework Members .NET Framework Class Library Gesture Methods Gesture Class See Also Send Feedback Updated: November 2007 The Gesture type exposes the following members.Methods NameDescriptionEquals Determines whether ...
Source: microsoft.ink

Application Gestures and Semantic Behavior (Windows)
Windows Vista SDK. The Microsoft gesture recognizer is built to recognize these gestures. By default, no gestures are enabled. Applications must choose the gestures to enable. In addition to recognizing gestures, the Microsoft gesture recognizer also provides alternates along with ...
Source: TabletandTouch

InkApplicationGesture Enumeration (Windows)
set the interest in a set of application-specific gesture. Application gestures are gestures that you can choose to have ... enumeration type). This means you can incorporate an application gesture that has a component that may be construed as a ...
Source: TabletandTouch


=========================================================================
Multi Touch
=========================================================================

Intuitive User Experience (Windows)
time, Windows 7 allows developers and their end-users to control their computers by touching the screen. Touch and multi-touch features provide a natural, intuitive way for users to interact with PCs. The developer platform includes high-level gesture ...
Source: Windows7DeveloperGuide

Supporting Pen and Touch Input (Windows)
Supporting Pen and Touch Input Tablet PC users rely on pen and touch input as primary methods of controlling applications and entering information. This section discusses the use ...
Source: MobilPC

Pen and Touch Input in Windows Vista (Windows)
Pen and Touch Input in Windows Vista Windows Vista introduces touch input capability for Tablet PCs that support it, a new cursor scheme to use with the ...
Source: MobilPC

Designing for Touch Input (Windows)
Designing for Touch Input If your users will use touch as a primary way of interacting with your application, consider implementing a user interface that's ...
Source: MobilPC

Touch Input Support in Windows Vista (Windows)
Touch Input Support in Windows Vista Starting with Windows Vista, Tablet and Touch Technology has support for touch input on Tablet PC's with touch capable digitizers ...
Source: TabletandTouch

Touch Members (Microsoft.Build.Tasks)
Members Include .NET Framework Members Include .NET Compact Framework Members Include XNA Framework Members .NET Framework Class Library Touch Members Touch Class Constructors Methods Properties See Also Send Feedback Updated: November 2007 This API supports the .NET Framework infrastructure and ...
Source: microsoft.build.tasks

Touch Class (Microsoft.Build.Tasks)
NET Framework Class Library Touch Class Members See Also Send Feedback Updated: November 2007 This API supports the .NET Framework infrastructure and is not intended to be used directly from your code. Implements the Touch task. Use the Touch element ...
Source: microsoft.build.tasks

Touch Constructor (Microsoft.Build.Tasks)
Code: Visual C++ Code: J# Code: JScript Visual Basic C# Visual C++ J# JScript .NET Framework Class Library Touch Constructor Touch Class See Also Send Feedback Updated: November 2007 This API supports the .NET Framework infrastructure and is not intended ...
Source: microsoft.build.tasks

No Touch Deployment (Windows)
No Touch Deployment Implementation of no-touch deployment of ink-enabled applications from a Web or file server. The Microsoft?.NET Framework gives you ...
Source: TabletandTouch

Optimizing Touch Command and Control (Windows)
Optimizing Touch Command and Control A well-designed Ultra-Mobile PC user interface provides users simple, direct and reliable touch interaction. Design and organize touch targets, such as buttons and links ...
Source: MobilPC

No-Touch Deployment Web Sample (Windows)
No-Touch Deployment Web Sample Description of the No-Touch Deployment sample for the Tablet PC. This sample shows how to deploy a managed Tablet PC ...
Source: TabletandTouch

Touch Task
J# JScript .NET Framework General Reference Touch Task Example See Also Send Feedback Updated: November 2007 Sets the access and modification times of files. Parameters The following table describes the parameters of the Touch task. Parameter Description AlwaysCreate Optional Boolean ...
Source: .NET Framework: General Reference

Touch Properties (Microsoft.Build.Tasks)
Members Include .NET Framework Members Include .NET Compact Framework Members Include XNA Framework Members .NET Framework Class Library Touch Properties Touch Class See Also Send Feedback Updated: November 2007 This API supports the .NET Framework infrastructure and is not intended ...
Source: microsoft.build.tasks

Touch Methods (Microsoft.Build.Tasks)
Members Include .NET Framework Members Include .NET Compact Framework Members Include XNA Framework Members .NET Framework Class Library Touch Methods Touch Class See Also Send Feedback Updated: November 2007 This API supports the .NET Framework infrastructure and is not intended ...
Source: microsoft.build.tasks

2009/01/07

base64 encode 如何把外部的檔放進同一個 html 中?

底下要談的主要是針對 SVG, 但是一般的 HTML 其實也行,只是範例是使用 SVG 來講解而已,請參考SVG 範例, HTML 範例

這邊有講解,並且有示範用 perl 如何完成。之所以能將外部的檔案放在 html / svg 裡,其原理是要先對想包的內容利用 base64 的演算法編碼過,這樣才不會在內容中有特殊字元,例如 ", ', <, >,或一般的控制字元(ASCII < 0x20),而這樣的事是 W3C 制定的標準,因此瀏覽器必須支援.....(不支援的大概就是沒聽過的瀏覽器)

若要轉換,可以參考底下的線上轉換工具,或是安裝 cygwin, 或是在 Linux 下,都有預設的工具,其命令名稱就叫 base64(在 coreutils 套件下):

http://webnet77.com/cgi-bin/helpers/base-64.pl
http://rumkin.com/tools/compression/base64.php
這邊有一個用 javascript 寫的工具,你可以自己寫頁面來轉換, 上面有提到utf-8 的問題,值得大家參考。還有一點參考資料可助理解 base64. 這邊有一個號稱效率更高的演算法,用的是查表法.

SVG 範例裡面,可以看到用法上如下,其中 &si; 其實是個變數,定義在前面

<!ENTITY si 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA'>

<symbol id="Invader1b">
<image width="8" height="8" xlink:href="&si;EAAA...." />
</symbol>
====== 上面相當於 =====>
<symbol id="Invader1b">
<image width="8" height="8" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAA...." />
</symbol>


上面 href="data:image/png;base64, 是型別宣告,若你的內容是 gif, jpg 請改成適當的字串值,後面接著的就是經過 base64 編碼後的內容,通常你用純文字檔檢視即可,複製後再貼上即可。要用 base64 進行轉換相當簡單,譬如把 a.png 編碼成 a.png.base64:
% base64 a.png > a.png.base64
反編碼(解碼)的話如下:
% base64 -d a.png.base64 > a.png

同樣的事若是放在 C/C++ 中呢?可以參考這一篇說的方式去做,這邊的意思是,原來說的是文字檔,其實任何檔都可以看成是文字檔不是嗎?差別只在於你怎麼用而已,而不是在轉換過程。這麼說不是沒根據的,譬如網頁,怎麼傳圖片?譬如 email 怎麼夾帶圖片與影片?我說個最簡單的,你想想看,同樣的內容用 printf() 裡面的 %d, %f, %x, %s 來印,結果是不是不相同?why?

2008/12/16

Windows 7 Touch Digitizer 心得

本文要說明的主要來自微軟的文件

For Windows 7, touch digitizers appear through HID as a touch digitizer (page 0x0D, usage 0x04).
The following usages are required:
• X (page 0x01, usage 0x30) and Y (page 0x01, usage 0x31).
• Contact ID (page 0x0D, usage 0x51).
• Tip switch (page 0x0D, usage 0x42) - 指示出手指是否碰觸到螢幕(Digitizer Surface)
• In-range (page 0x0D, usage 0x32).

關於 z 軸我一直覺得很有用,微軟有一段說明如下:
If the device supports z-axis detection, it reports in-range correctly. If the device does not support z-axis detection, the driver reports packets with in-range and tip switch set when a finger comes in contact with the digitizer.

我試著解釋一下(不保證我的理解正確):
如果有 z 軸值,就放在 In-Range 中,此時 Tip Switch 不必設定。
如果沒有 z 軸值,那麼碰觸時請設定 Tip Switch 及傳回適當的 In-Range 值。
(註: 這一版本與之前的 OS 規範不一樣,如與 XP Tablet PC,請見Windows XP Tablet PC OEM Preinstallation Kit (OPK))

底下的資訊是選擇性的,意思是硬體有支援再提供即可:
• Confidence (page 0x0D, usage 0x47) - 這一點很奇怪,意思是前面的觸控點資訊有可能是意外產生時,可以由裝置設定這個值。
• Width and height (page 0x0D, usages 0x48 and 0x49).

來看看一個 USB HID device report 的 sample:

Here is a sample report descriptor for a touch digitizer device:
0x05, 0x0d, // USAGE_PAGE (Digitizers) 0
0x09, 0x04, // USAGE (Touch Screen) 2
0xa1, 0x01, // COLLECTION (Application) 4
0x85, REPORTID_TOUCH, // REPORT_ID (Touch) 6
0x09, 0x20, // USAGE (Stylus) 8
0xa1, 0x00, // COLLECTION (Physical) 10
0x09, 0x42, // USAGE (Tip Switch) 12
0x15, 0x00, // LOGICAL_MINIMUM (0) 14
0x25, 0x01, // LOGICAL_MAXIMUM (1) 16
0x75, 0x01, // REPORT_SIZE (1) 18
0x95, 0x01, // REPORT_COUNT (1) 20
0x81, 0x02, // INPUT (Data,Var,Abs) 22
0x95, 0x03, // REPORT_COUNT (3) 24
0x81, 0x03, // INPUT (Cnst,Ary,Abs) 26
0x09, 0x32, // USAGE (In Range) 28
0x09, 0x37, // USAGE (Data Valid-Finger) 30
0x95, 0x02, // REPORT_COUNT (2) 32
0x81, 0x02, // INPUT (Data,Var,Abs) 34
0x95, 0x0a, // REPORT_COUNT (10) 36
0x81, 0x03, // INPUT (Cnst,Ary,Abs) 38
0x05, 0x01, // USAGE_PAGE (Generic Desktop) 40
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767) 42
0x75, 0x10, // REPORT_SIZE (16) 45
0x95, 0x01, // REPORT_COUNT (1) 47
0xa4, // PUSH 49
0x55, 0x0d, // UNIT_EXPONENT (-3) 50
0x65, 0x00, // UNIT (None) 52
0x09, 0x30, // USAGE (X) 54
0x35, 0x00, // PHYSICAL_MINIMUM (0) 56
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0) 58
0x81, 0x02, // INPUT (Data,Var,Abs) 61
0x09, 0x31, // USAGE (Y) 63
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0) 65
0x81, 0x02, // INPUT (Data,Var,Abs) 68
0xb4, // POP 70
0x05, 0x0d, // USAGE PAGE (Digitizers) 71
0x09, 0x60, // USAGE (Width) 73
0x09, 0x61, // USAGE (Height) 75
0x95, 0x02, // REPORT_COUNT (2) 77
0x81, 0x02, // INPUT (Data,Var,Abs) 79
0x95, 0x01, // REPORT_COUNT (1) 81
0x81, 0x03, // INPUT (Cnst,Ary,Abs) 83/85
#ifdef _ CUSTOMVENDORUSAGE _
0x06, 0x00, 0xff, // USAGE_PAGE (Vendor Defined) 0
0x09, 0x80, // USAGE (Vendor Usage 0x80) 3
0x09, 0x81, // USAGE (Vendor Usage 0x81) 5
0x15, 0x00, // LOGICAL_MINIMUM (0) 7
0x27, 0xff, 0xff, 0xff, 0xff, // LOGICAL_MAXIMUM (0xffffffff) 9
0x75, 0x20, // REPORT_SIZE (32) 14
0x95, 0x02, // REPORT_COUNT (2) 16
0x81, 0x02, // INPUT (Data,Var,Abs) 18/20
#endif
0xc0, // END_COLLECTION 0/1
0xc0, // END_COLLECTION 0/1


底下的資訊由微軟提出,隨時有可能修正:









Proposed Additions to the HID Specification to Support Multitouch
Description Page Type Name ID
Contact identifier Digitizer DV (Dynamic Value) Contact identifier 0x51
Configuration Digitizer CA (Collection Application) Configuration 0x0E
Input mode Digitizer DV (Dynamic Value) Input mode 0x52
Device index Digitizer DV (Dynamic Value) Device index 0x53
Actual contact count Digitizer DV (Dynamic Value) Contact count 0x54
Maximum number of contacts supported Digitizer DV (Dynamic Value) Maximum count 0x55


Reporting 模式有三種, 建議採用 Parallel Mode:
• Serial mode: 一次只有一個單一接觸點. 這種模式有可能會改變裝置報告的速度。
• Parallel mode: 每個封包夠寬,足以容納所有接觸
• Hybrid mode: 混合模式可以將前面兩個模式視為它的特例,它擁有固定寬度的封包,卻同時可以擁有多個封包內容。

Parallel Mode 會使用到 Actual contact count 及 Null Value, 底下有個範例,根據 maximum count 及 actual count 的不同可以視為 Parallel Mode 或 Hybrid Mode:

0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x04, // USAGE (Touch Screen)
0xa1, 0x01, // COLLECTION (Application)
0x85, REPORTID_PEN, // REPORT_ID (Touch)
0x09, 0x22, // USAGE (Finger)
0xa1, 0x02, // COLLECTION (Logical)
0x09, 0x42, // USAGE (Tip Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x32, // USAGE (In Range)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x47, // USAGE (Touch Valid)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x75, 0x08, // REPORT_SIZE (8)
0x09, 0x51, // USAGE (Contact Identifier)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desk..
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x75, 0x10, // REPORT_SIZE (16)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x09, 0x30, // USAGE (X)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x31, // USAGE (Y)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0xa1, 0x02, // COLLECTION (Logical)
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x42, // USAGE (Tip Switch)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x32, // USAGE (In Range)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x47, // USAGE (Touch Valid)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x05, // REPORT_COUNT (5)
0x81, 0x03, // INPUT (Cnst,Ary,Abs)
0x75, 0x08, // REPORT_SIZE (8)
0x09, 0x51, // USAGE ( Temp Identifier)
0x95, 0x01, // REPORT_COUNT (1)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x05, 0x01, // USAGE_PAGE (Generic Desk..
0x26, 0xff, 0x7f, // LOGICAL_MAXIMUM (32767)
0x75, 0x10, // REPORT_SIZE (16)
0x55, 0x00, // UNIT_EXPONENT (0)
0x65, 0x00, // UNIT (None)
0x09, 0x30, // USAGE (X)
0x35, 0x00, // PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x31, // USAGE (Y)
0x46, 0x00, 0x00, // PHYSICAL_MAXIMUM (0)
0x81, 0x02, // INPUT (Data,Var,Abs)
0xc0, // END_COLLECTION
0x05, 0x0d, // USAGE_PAGE (Digitizers)
0x09, 0x54, // USAGE (Actual count)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x27, 0x08, // LOGICAL_MAXIMUM (255)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x09, 0x56, // USAGE(Maximum Count)
0xb1, 0x02, // FEATURE (Data,Var,Abs)
0xc0, // END_COLLECTION


Top-Level Collection 每個裝置一定要有此描述內容,至少要指出來裝置是單點輸入、多點輸入或是滑鼠。

Collection Applications (CAs) 值對應的裝置若為底下的值,均可以用來做多點輸入:
Digitizer - 1
Pen - 2
Touch Screen - 4
Touch Pad - 5


多點觸控裝置應該在 top-level collections中採用 Finger (0x22) CL (Collection Logical) 來裝 data 及控制訊息。

多觸控裝置的尋找與組態能力:
尋找是透過 report descriptor 裡的 input mode 完成,也應該允許作業系統重新規劃成 multi-input, single-input, or mouse top-level collection。為了系統安全考量,此項 report descriptor 只允許作業系統存取,不開放第三方應用程式存取。這一點跟舊的 Vista 或 XP, 2000 都不一樣,舊的作業系統允許第三方應用程式透過 feature report 重設裝置為 single touch or the mouse。

Feature Report Requirements
所有裝置都需要一個 feature report 在其 report descriptor 中,以便報告裝置的組態。底下來看個實例:

0x06, 0x0d // USAGE_PAGE (Digitizer)
Ox09, 0x0E // USAGE (Configuration)
0x85, REPORTID_FEATURE, // REPORT_ID (Feature)
0xa1, 0x01, // COLLECTION (Application)
0x09, 0x52, // USAGE (Input Mode)
0x09, 0x53, // USAGE (Device Index)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x27, 0x08, // LOGICAL_MAXIMUM (255)
0x75, 0x08, // REPORT_SIZE (8)
0x95, 0x02, // REPORT_COUNT (2)
0xb1, 0x02, // FEATURE (Data,Var,Abs)
0xc0, // END_COLLECTION


Input Mode的值有:



Mode Value
Mouse (recommended default) 0x00
Single-input (single touch or stylus) 0x00
Multi-input 0x02

所以,多觸控點裝置是可以被重新設定成單一輸入裝置的,此時要表現的如作業系統所期望才行,而且微軟建議所有的多觸控裝置都預設採用 Mouse Mode。

Device Index
The device index usage selects the top-level collection to configure. A value of 0 tells the device to change the mode for every multi-input top-level collection in its report descriptor. A value of 1 and higher indicates the position of the top-level collection in the report descriptor. Applications that use values other than 0 must correctly handle the positions of the multi-input top-level collections in the report descriptor.

參考文獻:
  1. Device Class Definitions for Human Interface Devices (HID)
  2. HID Usage Tables

2008/12/08

Introduction to InterProcess Communication IPC 簡介

探討 IPC 的文章與文件相當多,例如 http://www.linuxhq.com/guides/LPG/node7.html, http://ai.ee.ccu.edu.tw/os/projects/2004fall/ipc_11/os-3.ppthttp://www.comms.scitech.susx.ac.uk/fft/computer/ipc.pdf是兩篇我覺得寫的很棒的文件,當然不是我寫的。

這邊畫蛇添足地說明一下,正常情況下,OS 為了保護每個 Process 的獨立性,不止從 User 來切割整個作業環境,而且也從 Process 來切割,意思是,每個 User 間是獨立的,互相不會影響,不會因為甲用戶惡搞當機而造成乙用戶也跟著當機,當然行程也是如此。這就導致行程間要互相溝通必須做到一件事:

透過 Kernel!

是可以自行利用 file lock 來溝通,但是這也會有問題,例如別人是否也想這麼做?同步問題怎麼解決?附件有提到幾種正規的作法我就不多談,只是要強調出來,正規作法,其實都是透過 kernel 來達成的。因此有必要對 Kernel 提供的機制進行瞭解,包括 Pipe, Name Pipe, Socket, 信號、共享記憶體等等作法都是如此。

事實上 Kernel 提供出來的機制除了 system call 外,還有其他作法,例如 /proc filesystem, 不過這一點有機會再來說明,這邊簡單的說一下,很多情況下,不止是 Process 間要溝通,連裝置間或是裝置與 Process 間要溝通,透過 /proc filesystem 是非常方便的,甚至比 ioctl 來得簡易。但是簡易不代表有效率。當然這作法有一種限制,你必須去改 kernel, 但是若你剛剛好是作嵌入式系統,必須動到 Kernel , 那麼順便修改並提供利用 /proc filesystem 來溝通的機制也是相當方便的。

/proc, /sys 等,都透露出 kernel 愈來愈 Open, 學習是值得的。


Best Regards
--
自由的精靈, 狂想的空間
Free Spirit, Fantasy Space
Marie von Ebner-Eschenbach  - "Even a stopped clock is right twice a day."

2008/12/02

Open Source 法律問題

想知道你的程式中,用了多少 Open Source 的片斷嗎?有一種暴力搜尋法軟體可以用,但是在說明之前先來回答一個問題,我猜會有人問「既然是 Open Source, 怎麼還會有法律問題」?答案其實很明顯,Open Source 所 Open 的是 Source, 並非智慧,也就是軟體並非免費,而是無價(ps:中文就是棒)。

台灣的自由軟體鑄造場裡頭還有工具可以幫你決定要採用哪一種授權,這邊直接回到主題:

http://www.blackducksoftware.com/ 上面問題的答案。
http://www.palamida.com/ 還可以幫你買保險,讓產品減少授權爭議的損失
http://fossology.org/ HP 丟出來的開源軟體