1. 程式人生 > >微軟.net精簡框架最常見問題(轉載)

微軟.net精簡框架最常見問題(轉載)

  • //C#
    Cursor.Current = Cursors.WaitCursor;
    
    'VB
    Cursor.Current = Cursors.WaitCursor
    
    這段程式碼可以把游標設定為預設:
    //C#
    Cursor.Current = Cursors.Default;
    
    'VB
    Cursor.Current = Cursors.Default
    

    這項功能還不被.net精簡框架支援。使用"&&"不會在選單項的文字中顯示"&"符號。


    這篇文章將告訴你如何製作基於.net精簡框架的動畫控制元件:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/animationcontrol.asp



    學習製作.net精簡框架控制元件,提高您的技巧。(文章附帶了自定義控制元件的示例程式碼):
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/customctrlscompactfx.asp


    這篇文章討論了建立基於.net精簡框架的帶圖片的按鈕:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/ImageButton.asp


    學習如何使用.net精簡框架 MessageWindow 類建立一個提示圖示:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/messagewindow.asp


    這篇快速入門實現了,當滑鼠點選矩形自定義控制元件或點選Panel控制元件時,使用MessageWindow把訊息傳送給傳送窗體:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/messagewindow.aspx


    這篇快速入門教程解釋瞭如何在執行時向DataGrid控制元件新增或刪除行、列:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/datagridadding.aspx


    .net精簡框架中的DataGrid控制元件提供了幾乎.net框架中的DataGrid控制元件的所有功能。一個主要的區別是.net精簡框架中的DataGrid不能在執行時編輯單元。這篇快速入門教程演示瞭如何通過程式實現編輯單元格的一種方法:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/datagridediting.aspx



    與.net框架的DataGrid的另一個區別是,.net精簡框架的DataGrid不支援把DataSource設定為DataSet。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/datagrid.aspx


    與.net框架的DataGrid的另一個區別是,.net精簡框架的DataGrid不支援在執行時按照列進行排序。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/datagridsorting.aspx


    .net精簡框架不支援ListView.Sort方法,但任然可以排序。這篇快速入門教程定義了一個繼承ArrayList.Sort的IComparable介面的方法:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/listviewsort.aspx


    這篇快速入門教程演示了在PocketPC上開啟和關閉軟輸入板(SIP),以及當SIP顯示時,tab控制元件大小也跟隨變化:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/inputpanel.aspx

    多個窗體應該共享一個輸入板物件。可以通過先在主窗體中建立SIP物件,然後把它傳給子窗體或暴露SIP物件的一些方法、屬性給其他需要使用SIP的窗體。


    這篇快速入門教程描述瞭如何繼承Button類、過載方法來事現雙擊事件。這個自定義事件會在按鈕被雙擊時觸發,兩次點選的間隔時間是SystemInformation.DoubleClickTime 屬性的值,以毫秒為單位。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/btndclick.aspx

    .net精簡框架的控制元件不支援OnEnter和OnLeave方法,包括Windows.Forms.Control基類。但是,因為支援Control.OnMouseMove方法,您可以通過它和Control.Capture 屬性判斷滑鼠什麼時候進入和離開控制元件。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/enterleave.aspx


    您可以製作一個.net精簡框架的owner drawn list box。.net精簡框架的ListBox或其他控制元件不支援DrawMode、DrawItem, 或其他drawing方法,但您可以程式設計實現。這篇快速入門教程提供一個自定義控制元件類,建立一個owner-drawn list box,並實現了選擇字型的控制元件的功能。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/ownerdrawnlistbox.aspx


    這篇快速入門教程提供了在Windows.Forms.CheckBox控制元件上建立真/假多選框:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/tfcheckbox.aspx


    InputPanel元件需要窗體包含MainMenu控制元件,而且那個窗體是顯示在螢幕上的。


    這個功能不被.net精簡框架所支援。


    在程式碼中改變控制元件的值 或 按下了上、下箭頭才會觸發ValueChanged和SelectedItemChanged事件。當用戶往控制元件中輸入字元的時候時不會觸發的。


    當您按了上、下後出現的值,不是增長值的倍數,它將向著那個方向(上或下)直到下一個增長值的倍數的值。


    StatusBar控制元件只能停靠在窗體的底部,它的大小不能改變。


    這個功能不被.net精簡框架所支援。可以採取的方法是繼承OnParentChanged方法手動設定顏色:
    //C#
    protected override void OnParentChanged(EventArgs e)
    {
      base.OnParentChanged(e);
      this.BackColor = Parent.BackColor;
    }
    
    'VB
    Protected Overrides Sub OnParentChanged(ByVal e As EventArgs)
      MyBase.OnParentChanged(e)
      Me.BackColor = Parent.BackColor
    End Sub 'OnParentChanged
    

    雖然NumericUpDown控制元件接受decimal型別的值,但.net精簡框架把這個控制元件的值當作int型別來處理。如,10.23當作10。同樣此控制元件在PocketPC上不接受大於帶符號的16位整型。


    DomainUpDown控制元件不會對輸入的文字進行確認(不象完整的.net框架)。如果您先輸入了一些文字,再按上、下箭頭,它會顯示內容改變前的值的下一個值。


    OpenFileDialog的初始化目錄被限制在"My Documents"資料夾或它的子資料夾中。這個限制是由PocketPC系統強加的,為了幫助使用者在標準目錄下管理自己的文件。
     
    The SIP can be activated by P/Invoking the function "SipShowIM" as follows.
    //C#
    using System.Runtime.InteropServices;
    
    const uint SIPF_OFF = 0x0;
    const uint SIPF_ON = 0x1;
    
    [DllImport("coredll.dll")]
    private extern static void SipShowIM(uint dwFlag);
    
    'VB
    Imports System.Runtime.InteropServices
    
    Const SIPF_OFF As Integer = &H0
    Const SIPF_ON As Integer = &H1
    
    <DllImport("coredll.dll")> _
    Private Shared Function SipShowIM(ByVal dwFlag As Integer) As Integer
    End Function
    


    Adding subnodes to all nodes is accomplished by iterating through all of the nodes in the TreeView and adding a new node to each.
    //C#
    foreach (TreeNode node in treeView1.Nodes)
    {
        node.Nodes.Add(new TreeNode("SubNode"));
    }
    
    'VB
    Dim node As TreeNode
    For Each node In  treeView1.Nodes
        node.Nodes.Add(New TreeNode("SubNode"))
    Next node
    


    The number of rows and columns in a DataGrid can be determined from the data source itself. For example:
    //C#
    DataSet ds = new DataSet();
    
    int numRows = ds.Tables[0].Rows.Count;
    int numCols = ds.Tables[0].Columns.Count;
    
    'VB
    Dim ds As New DataSet()
    
    Dim numRows As Integer = ds.Tables(0).Rows.Count
    Dim numCols As Integer = ds.Tables(0).Columns.Count
    
    If the DataGrid is bound to the DataView you can also use DataView.Count.

    See the .NET Compact Framework QuickStarts, Implementing Events topic:

    http://samples.gotdotnet.com/quickstart/compactframework/doc/btndclick.aspx



    The tab order of the controls in the .NET Compact Framework correspond directly to the order of the Controls in the Form.Controls collection. Therefore, GetNextControl can be implemented by determining the index of the specified Control and determing its neighbors in the collection.
    //C#
    public Control GetNextControl(Control ctl, bool forward)
    {
        int curIndex = this.Controls.IndexOf(ctl);
    
        if (forward)
        {
            if (curIndex < this.Controls.Count)
                curIndex++;
            else
                curIndex = 0;
        }
        else
        {
            if (curIndex > 0)
                curIndex--;
            else
                curIndex = this.Controls.Count - 1;
        }
    
        return this.Controls[curIndex];
    }
    
    'VB
    Public Function GetNextControl(ByVal ctl As Control, _
      ByVal forward As Boolean) As Control
        Dim curIndex As Integer = Me.Controls.IndexOf(ctl)
    
        If forward Then
            If curIndex < Me.Controls.Count Then
                curIndex += 1
            Else
                curIndex = 0
            End If
        Else
            If curIndex > 0 Then
                curIndex -= 1
            Else
                curIndex = Me.Controls.Count - 1
            End If
        End If
        
        Return Me.Controls(curIndex)
    
    End Function 'GetNextControl
    


    TreeView does not support the Click event, however, a workaround is to use the AfterSelect event instead.



    This is not supported by the current version of the .NET Compact Framework.



    Setting the SelectedValue property only works if the control is databound.



    Handle the ContextMenu.Popup event, and then query the current mouse coordinates using 'Control.MousePosition'.



    Similar to the NumericUpDown control, the maximum achievable value is the first empty row above the thumb. More specifically, from the editor properties, this equates to:

    Maximum - (LargeChange + 1).



    Call this.Parent.Controls(this.Parent.GetChildIndex(customcontrol) - 1).Focus() in the KeyDown event handler when a Keys.Up key is detected.



    Icons support transparency, however, there is a known bug in Visual Studio .NET 2003 designer that creates incorrect code and makes icons non-transparent. A work around is to add an icon file to the ImageList outside of InitializeComponent and add the icon files to the project as content or embedded resources. The following code demonstrates this:
    //C#
    using System.Drawing;
    using System.IO;
    using System.Reflection;
    
    // Loaded as content example
    private void Form1_Load(object sender, System.EventArgs e)
    {
        this.imageList1.Images.Add(new Icon(File.Open("fullFileName.ico",
          FileMode.Open)));
    
        this.toolBar1.Buttons[0].ImageIndex = 0;
    }
    
    // Loaded as a resource example
    private void Form1_Load(object sender, System.EventArgs e)
    {
        this.imageList1.Images.Add(new
          Icon(Assembly.GetExecutingAssembly().GetManifestResourceStream(
          ".filename.ico")));
    
        this.toolBar1.Buttons[0].ImageIndex = 0;
    }
    
    'VB
    Imports System.Drawing
    Imports System.IO
    Imports System.Reflection
    
    ' Loaded as content example
    Private Sub Form1_Load1(ByVal sender As Object, ByVal e As System.EventArgs)
    
        Me.imageList1.Images.Add(New Icon(File.Open("fullFileName.ico", _
          FileMode.Open)))
    
        Me.toolBar1.Buttons(0).ImageIndex = 0
    
    End Sub 'Form1_Load1
    
    ' Loaded as a resource example
    Private Sub Form1_Load2(ByVal sender As Object, ByVal e As System.EventArgs)
    
        Me.imageList1.Images.Add(New _
          Icon([Assembly].GetExecutingAssembly().GetManifestResourceStream( _
          ".filename.ico")))
    
        Me.toolBar1.Buttons(0).ImageIndex = 0
    
    End Sub 'Form1_Load2
    
      Top of Page Top of Page

    6. 與原生代碼(Native Code)互用


     


    本地DLL程式碼可以通過系統的Invoke (P/Invoke)方法呼叫。這些文章提供瞭如何實現呼叫和更多的呼叫技巧:


    見本問答的 "6.1. 如何呼叫本地寫的DLL中的函式? " 章節。


    見本問答的 "6.1. 如何呼叫本地寫的DLL中的函式? " 章節。


    不需要使用P/Invoke呼叫GetTickCount功能,因為Environment.TickCount就提供了這個功能。
    見本問答的 " 7.2. 如何計算精確的時間間隔?" 章節。


    您可以呼叫GetSystemMemoryDivision和GlobalMemorySystem函,數獲得程式和儲存器間有多少記憶體是隔離的和已經分配的。

    引數的說明可以在API參考文件中找到。
    //C#
    using System.Runtime.InteropServices;
    
    public class MEMORYSTATUS
    {
      public uint dwLength;
      public uint dwMemoryLoad;
      public uint dwTotalPhys;
      public uint dwAvailPhys;
      public uint dwTotalPageFile;
      public uint dwAvailPageFile;
      public uint dwTotalVirtual;
      public uint dwAvailVirtual;
    }
    
    [DllImport("CoreDll.dll")]
    public static extern void GlobalMemoryStatus
    (
      MEMORYSTATUS lpBuffer
    );
    
    [DllImport("CoreDll.dll")]
    public static extern int GetSystemMemoryDivision
    (
      ref uint lpdwStorePages,
      ref uint lpdwRamPages,
      ref uint lpdwPageSize
    );
    
    public void Test()
    {
      uint storePages = 0;
      uint ramPages = 0;
      uint pageSize = 0;
      int res = GetSystemMemoryDivision(ref storePages, ref ramPages, ref pageSize);
    
      MEMORYSTATUS memStatus = new MEMORYSTATUS();
      GlobalMemoryStatus(memStatus);
    }
    
    'VB
    Imports System.Runtime.InteropServices
    
    Public Structure MEMORYSTATUS
      Public dwLength As UInt32
      Public dwMemoryLoad As UInt32
      Public dwTotalPhys As UInt32
      Public dwAvailPhys As UInt32
      Public dwTotalPageFile As UInt32
      Public dwAvailPageFile As UInt32
      Public dwTotalVirtual As UInt32
      Public dwAvailVirtual As UInt32
    End Structure 'MEMORYSTATUS
    
    <DllImport("coredll.dll")> _
    Private Shared Sub GlobalMemoryStatus(ByRef ms As MEMORYSTATUS)
    End Sub
    
    <DllImport("CoreDll.dll")> _
    Public Shared Function GetSystemMemoryDivision( _
      ByRef lpdwStorePages As UInt32, _
      ByRef lpdwRamPages As UInt32, _
      ByRef lpdwPageSize As UInt32) As Integer
    End Function
    
    Public Shared Sub Test()
      Dim storePages As UInt32
      Dim ramPages As UInt32
      Dim pageSize As UInt32
      Dim res As Integer = GetSystemMemoryDivision(storePages, ramPages, pageSize)
    
      Dim memStatus As New MEMORYSTATUS
      GlobalMemoryStatus(memStatus)
    End Sub 'Test
    
    1. 繼承窗體的OnGotFocus方法。
    2. 找到窗體的視窗控制代碼。
    3. 呼叫ShowWindow(hwnd, SW_MINIMIZE)強制窗體最小化。
    //C#
    using System.Runtime.InteropServices;
    
    [DllImport("CoreDll")]
    public static extern IntPtr FindWindow(string className,string WindowsName);
    
    [DllImport("CoreDll")]
    public static extern bool ShowWindow(IntPtr hwnd,int nCmdShow);
    
    const int SW_MINIMIZE = 6;
    
    protected override void OnGotFocus(EventArgs e)
    {
      IntPtr hwnd = FindWindow(null, this.Text);
      ShowWindow(hwnd, SW_MINIMIZE);
      base.OnGotFocus(e);
    }
    
    'VB
    Imports System.Runtime.InteropServices
    
    <DllImport("CoreDll")> _
    Public Shared Function FindWindow(ByVal className As String, ByVal WindowsName As String) As IntPtr
    End Function
    
    <DllImport("CoreDll")> _
    Public Shared Function ShowWindow(ByVal hwnd As IntPtr,ByVal nCmdShow As Integer) As Boolean
    End Function
    
    Private Const SW_MINIMIZE As Integer = 6
    
    Protected Overrides Sub OnGotFocus(ByVal e As EventArgs)
      Dim hwnd As IntPtr = FindWindow(Nothing, Me.Text)
      ShowWindow(hwnd, SW_MINIMIZE)
      MyBase.OnGotFocus(e)
    End Sub 'OnGotFocus
    

    見本問答的 "6.1. 如何呼叫本地寫的DLL中的函式? " 章節。


    其實有一些使用呼叫原生代碼的方法可以獲得控制元件的控制代碼HWND。下面列出其中兩種,一種使用GetCapture,另一個使用FindWindow。
    //C#
    [DllImport("coredll.dll"]
    public static extern IntPtr GetCapture();
    
    [DllImport("coredll.dll")]
    public static extern IntPtr FindWindow(String lpClassName, String lpWindowName);
    
    this.Text = "FindMe";
    IntPtr hwnd1 = FindWindow(null, "FindMe");
    
    this.Capture = true;
    IntPtr hwnd2 = GetCapture();
    this.Capture = false;
    
    'VB
    <DllImport("coredll.dll", SetLastError:=True)> _
    Public Shared Function GetCapture() As IntPtr
    End Function
    
    <DllImport("coredll.dll", SetLastError:=True)> _
    Public Shared Function FindWindow(ByVal lpClassName As String, ByVal lpWindowName As String) As IntPtr
    End Function
    
    Me.Text = "FindMe"
    Dim deskWin As IntPtr = FindWindow(Nothing, "FindMe")
    
    Me.Capture = True
    Dim hwnd As IntPtr = GetCapture()
    Me.Capture = False
    

    使用QueryPerformanceFrequency函式和QueryPerformanceCounter函式可以建立精確的計時程式。 這些功能是和裝置提供商相關的,如果他們不能執行,那麼只能和GetTickCount功能得到一樣的結果。如果能執行這些函式,就能保證計時器最準確的執行,比GetTickCounter或Environment.TickCount準確得多。TickCount其實是呼叫GetTickCounter的。

    如果效能計數器是GetTickCount的一個例項,QueryPerformanceFrequency將把1000作為計時頻率。如果這些函式不能執行,將得到返回值為0。以下程式碼演示瞭如何使用這些函式。
    //C#
    [DllImport("CoreDll.dll")]
    public static extern int QueryPerformanceFrequency(ref Int64 lpFrequency);
    
    [DllImport("CoreDll.dll")]
    public static extern int QueryPerformanceCounter(ref Int64 lpPerformanceCount);
    
    private void TestTimer()
    {
      System.Int64 freq = 0;
      if (QueryPerformanceFrequency(ref freq) != 0)
      {
        System.Int64 count1 = 0;
        System.Int64 count2 = 0;
    
        if (QueryPerformanceCounter(ref count1) != 0)
        {
          System.Threading.Thread.Sleep(1200);
          QueryPerformanceCounter(ref count2);
          System.Int64 time_ms = (count2 - count1) * 1000 / freq;
        }
      }
    }
    
    'VB
    <DllImport("CoreDll.dll")> _
    Public Shared Function QueryPerformanceFrequency(ByRef lpFrequency As Int64) As Integer
    End Function
    
    <DllImport("coredll.dll")> _
    Public Shared Function QueryPerformanceCounter(ByRef lpPerformanceCount As Int64) As Integer
    End Function
    
    Private Sub TestTimer()
        Dim freq As System.Int64 = 0
    
        If QueryPerformanceFrequency(freq) <> 0 Then
            Dim count1 As System.Int64 = 0
            Dim count2 As System.Int64 = 0
    
            If QueryPerformanceCounter(count1) <> 0 Then
                System.Threading.Thread.Sleep(1200)
                QueryPerformanceCounter(count2)
                Dim time_ms As System.Int64 = (count2 - count1) * 1000 / freq
            End If
        End If
    End Sub 'TestTimer
    


    • 返回值
      • 只能是長度小於等於32位的型別
      • 非浮點型not floating point
    • 引數
      • Only support marshaling blittable types
        • blittable types -> same representation in memory in both managed and native
        • non-blittable -> memory transformation required
        • Since only blittable types, all objects are pinned and never copied
          • Exception: passing String ByVal in VB.NET
        • Implies that you can't marshal nested objects since this requires a memory transformation (non-blittable)
      • 只能是長度小於等於32位的型別
        • 值通過堆疊傳遞
        • 例外:float32
      • 參考(References)
        • Pass blittable reference types
        • 把參考傳遞到值型別變數
        • 這就是如何傳遞float32型別的值
      • 可以傳遞值型別的陣列
        • 在原生代碼中,您可以使用指標指向第一個物件,然後一個接一個地訪問其他物件
      • String是特殊的,傳遞char陣列 -> 不變的
      • StringBuilder是特殊的,傳遞char陣列 -> 易變的 (需要單獨傳遞長度)
      • 注意:C# bool是8個位元位的,並且不等於Win32的BOOL
      • 佇列:編譯器預設的佇列 (4位元組)
      • Marshal.GetLastWin32Error 支援 GetLastError() 語義
      • 未支援的:
        • MarshalAs: no support for non-blittable types
        • StructLayout: 不能改變外觀
        • Delegates(委託)
        • DateTime
        • Only support default calling convention


    儘量不要嘗試呼叫Windows GetLastError() API,因為CLR呼叫原生代碼時可能會改變last error的程式碼。取而代之的是,使用呼叫的返回值標記錯誤程式碼,再呼叫System.Runtime.InteropServices.Marshal.GetLastWin32Error()方法來獲得錯誤程式碼。
    using System.Runtime.InteropServices;
    
    [DllImport("coredll.dll", SetLastError=true)]
    int myFoo(...);
     
    Foo(...)
    {
        int rc = myFoo(...);
     
        if (rc == false)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error(), "Foo failed");
        }
    }
    


    有限制。.net精簡框架版本1.0的限制為12個。


    通常有三種可能性:
    • 在託管程式碼中的申明不正確
    • .net精簡框架不支援你想做的操作
    • dll的名稱在暴露過程中損壞了

    檢查以下專案: 
    • 有沒有違反.net精簡框架 P/Invoke(呼叫)的限制?
    • 有沒有引數需要預先分配記憶體(如,是不是指標)? 如果是的,您應該傳遞已經存在的變數的參考。
    • 暴露的函式名是否正確? 可以用DUMPBIN.EXE工具來驗證
    • 是不是想嘗試太多的引數?

    例如,針對上面的第二點,RegOpenKey API的最後一個引數HKEY的指標。您應該這樣申明和呼叫:
    //C#
    [DllImport("coredll.dll", SetLastError=true)]
    public static extern long RegOpenKey(
        IntPtr hkey, 
        string lpSubKey, 
        ref IntPtr hkeyResult
    );
    
    public long OpenMySubKey()
    {
        IntPtr hkey = IntPtr.Zero;
        return RegOpenKey(HKEY_CLASSES_ROOT, "MySubKey", ref hkey);
    }
    
    'VB
    <DllImport("coredll.dll", SetLastError:=True)> _
    Public Shared Function RegOpenKey(ByVal hkey As IntPtr, ByVal lpSubKey As String, ByRef hkeyResult As IntPtr) As Long
    End Function
    
    Public Function OpenMySubKey() As Long
        Dim hkey As IntPtr = IntPtr.Zero
        Return RegOpenKey(HKEY_CLASSES_ROOT, "MySubKey", hkey)
    End Function 'OpenMySubKey
    


    有不止一種的方法訪問IntPtr。

    第一種方法,使用非安全程式碼,直接用指標指向byte陣列。
    //C#
    unsafe
    {
        byte[] test = new byte[5];
        fixed (byte* p = &test[0])
        {
            *p = 0xff;
        }
    }
    
    也可以使用GCHandle指向物件。
    //C#
    using System.Runtime.InteropServices;
    
    byte[] test = new byte[5];
    GCHandle hObject = GCHandle.Alloc(test, GCHandleType.Pinned);
    IntPtr pObject = hObject.AddrOfPinnedObject();
    
    if(hObject.IsAllocated)
        hObject.Free();
    
    'VB
    Imports System.Runtime.InteropServices
    
    Dim test(4) As Byte
    Dim hObject As GCHandle = GCHandle.Alloc(test, GCHandleType.Pinned)
    Dim pObject As IntPtr = hObject.AddrOfPinnedObject()
    If hObject.IsAllocated Then
        hObject.Free()
    End If
    
    最後,可以使用LocalAlloc和Marshalling函式複製記憶體塊得到資料塊。
    //C#
    [DllImport("coredll.dll",SetLastError=true)]
    public static extern IntPtr LocalAlloc(uint uFlags, uint uBytes); 
    		
    [DllImport("coredll.dll",SetLastError=true)]
    public static extern IntPtr LocalFree(IntPtr hMem);
    
    [DllImport("coredll.dll",SetLastError=true)]
    public static extern IntPtr LocalReAlloc(IntPtr hMem, uint uBytes, uint fuFlags); 
    
    public const uint LMEM_FIXED = 0;
    public const uint LMEM_MOVEABLE = 2;
    public const uint LMEM_ZEROINIT = 0x0040;
    
    byte[] test = new byte[5];
    IntPtr p = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, (uint)test.Length);
    
    if (p == IntPtr.Zero) 
    {
    	throw new OutOfMemoryException();
    } 
    else 
    {
    	Marshal.Copy(test, 0, p, test.Length);
    }
    
    'VB
    <DllImport("coredll.dll", SetLastError:=True)> _
    Public Shared Function LocalAlloc(ByVal uFlags As UInt32, ByVal uBytes As UInt32) As IntPtr
    End Function
    
    <DllImport("coredll.dll", SetLastError:=True)> _
    Public Shared Function LocalFree(ByVal hMem As IntPtr) As IntPtr
    End Function
    
    <DllImport("coredll.dll", SetLastError:=True)> _
    Public Shared Function LocalReAlloc(ByVal hMem As IntPtr, ByVal uBytes As UInt32, ByVal fuFlags As UInt32) As IntPtr
    End Function
    
    Public Const LMEM_FIXED As Integer = 0
    Public Const LMEM_MOVEABLE As Integer = 2
    Public Const LMEM_ZEROINIT As Integer = &H40
    
    Dim test(4) As Byte
    Dim p As IntPtr = LocalAlloc(Convert.ToUInt32(LMEM_FIXED Or LMEM_ZEROINIT), Convert.ToUInt32(test.Length))
    If p.Equals(IntPtr.Zero) Then
        Throw New OutOfMemoryException
    Else
        Marshal.Copy(test, 0, p, test.Length)
    End If
    


    There are several issues to consider when determining the case of a MissingMethodException. When this exception occurs you should verify the following:

    • If targeting Pocket PC 2003 use Microsoft eMbedded Visual C++ 4.0.
    • If targeting Pocket PC 2000 or 2002 use Microsoft eMbedded Visual Tools 3.0
    • Make sure that the function name is spelled correctly
    • Verify that the DLL is located correctly - Windows or executing folder of target device
    • Verify with DUMPBIN that the names of the functions were not mangled on exporting of the DLL (extern "C" fixes this). More information on this topic can be found in section "6.1. How do I call a function that is in a native DLL?" of this FAQ.
    • Verify that the DLL being imported is not dependant upon other DLLs. A missing dependant DLL will result in the MissingMethodException.
    For the latest Microsoft eMbedded Visual Tools and SDK downloads, visit the MSDN Mobile and Embedded Developer Center "Products & Updates" download page at:
    http://msdn.microsoft.com/mobility/downloads/updates/default.aspx


    You can set the system time by P/Invoking the SetSystemTime function.
    //C#
    using System.Runtime.InteropServices;
    
    public struct SYSTEMTIME 
    {
        public ushort wYear;
        public ushort wMonth; 
        public ushort wDayOfWeek; 
        public ushort wDay; 
        public ushort wHour; 
        public ushort wMinute; 
        public ushort wSecond; 
        public ushort wMilliseconds; 
    }
    
    [DllImport("coredll.dll")]
    public extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime);
    
    [DllImport("coredll.dll")]
    public extern static uint SetSystemTime(ref SYSTEMTIME lpSystemTime);
    
    // Set the clock ahead one hour
    SYSTEMTIME st = new SYSTEMTIME();
    GetSystemTime(ref st);
    st.wHour = (ushort)(st.wHour + 1 % 24);
    SetSystemTime(ref st);
    
    'VB
    Imports System.Runtime.InteropServices
    
    Public Structure SYSTEMTIME
        Public wYear As UInt16
        Public wMonth As UInt16
        Public wDayOfWeek As UInt16
        Public wDay As UInt16
        Public wHour As UInt16
        Public wMinute As UInt16
        Public wSecond As UInt16
        Public wMilliseconds As UInt16
    End Structure
    
    <DllImport("coredll.dll")> _
    Public Shared Sub GetSystemTime(ByRef lpSystemTime As SYSTEMTIME)
    End Sub
    
    <DllImport("coredll.dll")> _
    Public Shared Function SetSystemTime(ByRef lpSystemTime As SYSTEMTIME) As UInt32
    End Function
    
    'Set the clock ahead one hour
    Dim st As New SYSTEMTIME
    GetSystemTime(st)
    st.wHour = Convert.ToUInt16(((Convert.ToInt32(st.wHour) + 1)) Mod 24)
    SetSystemTime(st)
    


    The device can be soft reset through P/Invoking of the KernelIoControl function, as demonstrated in the code below. For more information on how to use the function and extend the functionality of this sample, refer to Visual Studio .NET Help.

    Note: On Smartphone devices, this will only work if you are signed with a privileged certificate.
    //C#
    using System.Runtime.InteropServices;
    
    public const uint FILE_DEVICE_HAL = 0x00000101;
    public const uint METHOD_BUFFERED = 0;
    public const uint FILE_ANY_ACCESS = 0;
    
    public uint CTL_CODE(uint DeviceType, uint Function, uint Method, uint Access)
    {
        return ((DeviceType << 16) | (Access << 14) | (Function << 2) | Method);
    }
    
    [DllImport("Coredll.dll")]
    public extern static uint KernelIoControl
    (
        uint dwIoControlCode,
        IntPtr lpInBuf,
        uint nInBufSize,
        IntPtr lpOutBuf,
        uint nOutBufSize,
        ref uint lpBytesReturned
    );
    
    uint ResetPocketPC()
    {
        uint bytesReturned = 0;
        uint IOCTL_HAL_REBOOT = CTL_CODE(FILE_DEVICE_HAL, 15, 
          METHOD_BUFFERED, FILE_ANY_ACCESS);
        return KernelIoControl(IOCTL_HAL_REBOOT, IntPtr.Zero, 0, 
          IntPtr.Zero, 0, ref bytesReturned);
    }
    
    private void Form1_Load(object sender, System.EventArgs e)
    {
        DialogResult r = MessageBox.Show
        (
            "Are you sure you want to reset?",
            "Test",
            MessageBoxButtons.YesNo,
            MessageBoxIcon.Question,
            MessageBoxDefaultButton.Button2
        );
    
        if (r == DialogResult.Yes)
        {
            ResetPocketPC();
        }
    }
    
    'VB
    Public Const FILE_DEVICE_HAL As Integer = &H101
    Public Const METHOD_BUFFERED As Integer = 0
    Public Const FILE_ANY_ACCESS As Integer = 0
    
    Public Function CTL_CODE( _
      ByVal DeviceType As Integer, _
      ByVal Func As Integer, _
      ByVal Method As Integer, _
      ByVal Access As Integer) As Integer
    
        Return (DeviceType << 16) Or (Access << 14) Or (Func << 2) Or Method
    
    End Function 'CTL_CODE
    
    <DllImport("Coredll.dll")> _
    Public Shared Function KernelIoControl _
    ( _
        ByVal dwIoControlCode As Integer, _
        ByVal lpInBuf As IntPtr, _
        ByVal nInBufSize As Integer, _
        ByVal lpOutBuf As IntPtr, _
        ByVal nOutBufSize As Integer, _
        ByRef lpBytesReturned As Integer _
    ) As Integer
    End Function
    
    Function ResetPocketPC() As Integer
        Dim bytesReturned As Integer = 0
        Dim IOCTL_HAL_REBOOT As Integer = CTL_CODE(FILE_DEVICE_HAL, _
          15, METHOD_BUFFERED, FILE_ANY_ACCESS)
        Return KernelIoControl(IOCTL_HAL_REBOOT, IntPtr.Zero, 0, _
          IntPtr.Zero, 0, bytesReturned)
    End Function 'ResetPocketPC
    
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
      Handles MyBase.Load
        Dim r As DialogResult = MessageBox.Show( _
            "Are you sure you want to reset?", _
            "Test", _
            MessageBoxButtons.YesNo, _
            MessageBoxIcon.Question, _
            MessageBoxDefaultButton.Button2)
    
        If r = DialogResult.Yes Then
            ResetPocketPC()
        End If
    
    End Sub 'Form1_Load
    


    This is not supported with the current version of the .NET Compact Framework. You can, however, P/Invoke Pocket PC's notificaiton system to do this. Refer to the following for more information:

    Sample Code:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnppc2k2/html/ppc_fications.asp

    AYGShell APIs:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceui40/html/_cerefaygshellfunctions.asp

    The native Notification APIs are: SHNotificationAdd, SHNotificationRemove, SHNotificationGetData, and SHNotificationUpdate.



    Refer to the sample in the P/Invoke library.
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/PInvokeLib.asp#PInvokeLib_Topic03



    The Start icon can be hidden using the SHFullScreen API.
    //C#
    const uint SHFS_SHOWTASKBAR = 0x0001;
    const uint SHFS_HIDETASKBAR = 0x0002;
    const uint SHFS_SHOWSIPBUTTON = 0x0004;
    const uint SHFS_HIDESIPBUTTON = 0x0008;
    const uint SHFS_SHOWSTARTICON = 0x0010;
    const uint SHFS_HIDESTARTICON = 0x0020;
    
    [DllImport("aygshell.dll")]
    static extern uint SHFullScreen(IntPtr hwndRequester, uint dwState);
    
    [DllImport("coredll.dll")]
    public static extern IntPtr GetCapture();
    
    private void Form1_Load(object sender, System.EventArgs e)
    {
        Capture = true;
        IntPtr hwnd = GetCapture();
        Capture = false;
        SHFullScreen(hwnd, SHFS_HIDESTARTICON);
    }
    
    'VB
    Const SHFS_SHOWTASKBAR As Integer = &H1
    Const SHFS_HIDETASKBAR As Integer = &H2
    Const SHFS_SHOWSIPBUTTON As Integer = &H4
    Const SHFS_HIDESIPBUTTON As Integer = &H8
    Const SHFS_SHOWSTARTICON As Integer = &H10
    Const SHFS_HIDESTARTICON As Integer = &H20
    
    <DllImport("aygshell.dll")>  _
    Shared Function SHFullScreen(ByVal hwndRequester As IntPtr, ByVal dwState As Integer) As Integer
    End Function
    
    <DllImport("coredll.dll")>  _
    Public Shared Function GetCapture() As IntPtr
    End Function
    
    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    
       Capture = True
       Dim hwnd As IntPtr = GetCapture()
       Capture = False
    
       SHFullScreen(hwnd, SHFS_HIDESTARTICON)
    
    End Sub 'Form1_Load
    


    Refer to the sample:
    http://msdn.microsoft.com/mobility/understanding/articles/default.aspx?pull=/library/en-us/dnnetcomp/html/processmanager.asp



    This sample demonstrates how to P/Invoke numerous useful native functions that are not directly available through the .NET Compact Framework. A test Form is provided that enumerates all available test procedures and allows the user to select and run them:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/PInvokeLib.asp



    Learn how to use the Waveform Audio Interface to record and play ".wav" files:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/WaveInOut.asp

     Top of Page Top of Page

    7. 通用


     

    7. 通用


     


    使用Reflection,應用程式可以確定自己是從哪個目錄啟動的,也可以使用IO.Path名稱空間來修改它。

    //C#
    using System.Reflection;
    using System.IO;
    
    // This is the full directory and exe name
    String fullAppName = Assembly.GetExecutingAssembly().GetName().CodeBase;
    
    // This strips off the exe name
    String fullAppPath = Path.GetDirectoryName(fullAppName);
    
    // This adds a file name to the path
    String splashImageName = Path.Combine(fullAppPath, "myfile.txt");
    
    'VB
    Imports System.IO
    Imports System.Reflection
    
    ' This is the full directory and exe name
    Dim fullAppName As String = [Assembly].GetExecutingAssembly().GetName().CodeBase
    
    ' This strips off the exe name
    Dim fullAppPath As String = Path.GetDirectoryName(fullAppName)
    
    ' This adds a file name to the path
    Dim splashImageName As String = Path.Combine(fullAppPath, "myfile.txt")
    
    學習如何獲得程式執行的當前目錄。在Embedded Visual Basic中,程式執行的當前目錄可以通過App.Path屬性獲得。執行程式的目錄可以通過程式集的AssemblyName物件的獲得,AssemblyName物件包含了程式集的所有描述:
    http://msdn.microsoft.com/library/en-us/dncfhowto/html/HOWTOExecutingAppPath.asp

    這篇快速入門教程告訴您如何獲得您的程式集和資料檔案所在的目錄。Windows CE .NET本身不支援應用程式的當前目錄的設定:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/getappdir.aspx


    一個應用程式有四種方法得到時間間隔:
    • System.Environment.TickCount
      獲得一個帶符號的整型值,表示從機器啟動到呼叫時經過的豪秒數。在.NET精簡框架下,這個值的誤差在0.5秒內,大多情況下會比0.5秒小。
    • GetTickCount()
      屬性Environment.TickCount就是呼叫GetTickCount函式的,所以沒有必要再呼叫原生代碼中的這個方法。
    • Performance Monitor
      可以作為壓力測試用途,但不是為最終使用的應用程式而設計的。如需更多資訊,請檢視本問答的 "7.5. 如何使用效能監視器? " 章節
    • Timers
      使用System.Threading.Timer類,可以線上程內設定計時器,用委託(delegate)指向應用程式 。
    • Performance Counter
      如果OEM廠商支援的話,QueryPerformanceCounter函式能提供最高精度的計時功能。
      請檢視本問答的"6.9. 如何使用效能計數器功能? "章節。


    為了能夠訪問嵌入資源,應用程式只須簡單地引用相關的程式集(assembly)並呼叫GetManifestResourceStream方法。下面這個例子岩石瞭如何從嵌入資源中建立一個位圖:
    //C#
    using System.Reflection;
    
    Assembly asm = Assembly.GetExecutingAssembly();
    Bitmap bmpSprite = new Bitmap(asm.GetManifestResourceStream("AssemblyName.FileName"));
    
    'VB
    Imports System.Reflection
    
    Dim asm As [Assembly] = [Assembly].GetExecutingAssembly()
    Dim bmpSprite As New Bitmap(asm.GetManifestResourceStream("AssemblyName.FileName"))
    
    上面程式碼中, 字串AssemblyName部分可以在執行時通過呼叫asm.GetName().Name得到。

    注意:如果AssemblyName中有空格,它將被下劃線代替,而且必須這樣訪問。


    這是.net精簡框架的BUG。這是由於Windows CE底層的Secure Sockets Layer (SSL)的限制造成的。但是,也偶避免的方法,如果設定 req.AllowWriteStreamBuffering為true,不要設定req.ContentLength屬性,那就不會在發生這個錯誤了。


    效能計數器通過編輯設備註冊表建立:
    1. 建立登錄檔鍵:"HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/.NETCompactFramework/PerfMonitor"
    2. 新建雙位元組項,值就是計數器的名字。
    3. 把Counters的值設定為1表示允許計數器,設定為0表示禁止使用。
    設定了效能計數器後,當程式關閉時,會建立一個文字檔案"mscoree.stat"。這個檔案會存放在裝置的根目錄。這是一個檔案每行的長度是固定的,所以匯入Excel是非常方便的。

    注意: 計數器只能被一個執行著的託管的程式使用。
    注意: 使用效能計數器時,會導致30%的效能下降。


    程式可以過載OnClosing方法,設定CancelEventArgs.Cancel為true就可以取消關閉。
    //C#
    protected override void OnClosing(CancelEventArgs e)
    {
      e.Cancel = true;
    }
    
    'VB
    Protected Overrides Sub OnClosing(ByVal e As CancelEventArgs)
      e.Cancel = True
    End Sub 'OnClosing
    

    您可以呼叫原生代碼的CreateProcess函式開始執行第二個程式。然後呼叫原生代碼的WaitForSingleObject函式暫停呼叫的程式,直到第二個程式執行結束。以下快速入門演示了通過PocketPC模擬器來實現這一操作:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/waitforsingleobject.aspx


    一個.net精簡框架程式最多會產生4個執行緒:
    • 一個主應用程式執行緒。
    • 一個執行緒控制各種時間間隔,時間間隔是供系統和其他應用程式使用的。
    • 一個執行緒跟蹤活動的TCP/IP介面的變化(模擬Windows XP上的媒體動作,Windows CE上是沒有這些操作的)。
    • 一個執行終止物件的執行緒。當第一個被垃圾回收的物件回收時,就被建立了。

    在C++中,通過建造型別(typecasting)可以很方便和有效的儲存一個類或結構體到檔案,並直接從檔案中重構出來。但託管程式碼的本性決定了它不能這樣實現。但還是有辦法實現的,建立一個類,把記憶體中的資料作為它的屬性讓其他類訪問。例如:
    //C#
    public class MyClass
    {
      protected byte[] m_data = null;
      // uint uiDummy
      // short sDummy
      
      // This is a bit unsafe so you should throw an
      // exception or assert if the byte array length is
      // not 6. A safer but less memory efficient approach
      // would be to set m_data = new byte[6] and then copy
      //bytes to m_data.
      public MyClass(byte[] bytes) {m_data = bytes;}
    
      // Get/Set the uint
      public uint uiDummy
      {
        get {return BitConverter.ToUInt32(m_data, 0);}
        set
        {
          Buffer.BlockCopy(BitConverter.GetBytes(value),0,m_data,0,BitConverter.GetBytes(value).Length);
        }
      }
    
      // Get/Set the short
      public short sDummy
      {
        get {return BitConverter.ToInt16(m_data, 4);}
        set
        {
          Buffer.BlockCopy(BitConverter.GetBytes(value),0,m_data,4,BitConverter.GetBytes(value).Length);
        }
      }
    }
    
    byte[] fromFile = {1,1,1,1,2,2};
    MyClass myClass = new MyClass(fromFile);
    
    uint test1 = myClass.uiDummy; // 0x1010101
    short test2 = myClass.sDummy; // 0x202
    
    myClass.sDummy = 0x0505;      // Test setting the short
    uint test4 = myClass.uiDummy; // 0x1010101
    short test5 = myClass.sDummy; // 0x505
    
    'VB
    Public Class ByteClass
      Protected m_data As Byte() = Nothing
    
      ' uint uiDummy
      ' short sDummy
      ' This is a bit unsafe so you should throw an exception
      ' or assert if the byte array length is not 6. A safer
      ' but less memory efficient approach would be to set
      ' m_data = new byte[6] and then copy bytes to m_data.
      Public Sub New(ByVal bytes() As Byte)
        m_data = bytes
      End Sub 'New
    
      ' Get/Set the uint
      Public Property uiDummy() As UInt32
        Get
          Return BitConverter.ToUInt32(m_data, 0)
        End Get
        Set(ByVal Value As System.UInt32)
          Buffer.BlockCopy(BitConverter.GetBytes(Value),0,m_data,0,BitConverter.GetBytes(Value).Length)
        End Set
      End Property
    
      ' Get/Set the short
      Public Property sDummy() As Short
        Get
          Return BitConverter.ToInt16(m_data, 4)
        End Get
        Set(ByVal Value As Short)
          Buffer.BlockCopy(BitConverter.GetBytes(Value),0,m_data,4,BitConverter.GetBytes(Value).Length)
       End Set
      End Property
    End Class 'ByteClass
    
    Dim fromFile As Byte() = {1, 1, 1, 1, 2, 2}
    Dim testClass As New ByteClass(fromFile)
    
    Dim test1 As System.UInt32 = testClass.uiDummy ' 0x1010101 
    Dim test2 As Short = testClass.sDummy          ' 0x202
    testClass.sDummy = &H505                       ' Test short
    Dim test4 As System.UInt32 = testClass.uiDummy ' 0x1010101
    Dim test5 As Short = testClass.sDummy          ' 0x505
    

    不可以。在.net精簡框架中,只有EventHandler方法可以被呼叫。以下程式碼說明啊如何正確使用此方法:
    //C#
    public void HandleMe(object o, EventArgs e) {...}
    form.Invoke(new EventHandler(form.HandleMe));
    
    'VB
    Public Sub HandleMe(o As Object, e As EventArgs)
    End Sub 'HandleMe
    form.Invoke(New EventHandler(AddressOf form.HandleMe))
    
    Although the following will compile, it will not work properly:
    //C#
    public delegate void MyHandler();
    public void HandleMeBadly() {...}
    form.Invoke(new MyHandler(form.HandleMeBadly));
    
    'VB
    Delegate Sub MyHandler()
    Public Sub HandleMeBadly()
    End Sub 'HandleMeBadly
    form.Invoke(New MyHandler(form.HandleMeBadly))
    

    檢視這篇文章,學習如何在基於.net精簡框架的應用程式中訪問電話API:

    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/netcfphoneapi.asp


    Guid.NewGuid方法可以生成新的GUID,但在.net精簡框架中沒有此方法。閱讀這篇文章,學習如何根據GUID規範在PocketPC應用程式中建立GUID物件:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/PPCGuidGen.asp


    這篇文章討論瞭如何使用InTheHand公司的Pocket Outlook .NET元件:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/inthehandpoom.asp


    Visual Studio .NET 2003幫助中的C# Programmer's Reference提供了在.net精簡框架下使用不安全程式碼呼叫GetFileVersionInfo函式。 這個例子帶來的問題是,這個函式是由OEM廠商決定的,並且不保證能返回正確結果。

    使用Reflection可以獲得程式集(Assembly)的版本:
    //C#
    using System.Reflection;
    
    String ver = Assembly.GetExecutingAssembly().GetName().Version.ToString();
    
    'VB
    Imports System.Reflection;
    
    Dim ver As String = [Assembly].GetExecutingAssembly().GetName().Version.ToString()
    

    使用後臺處理,需要注意相關細節並小心設計。這篇文章提供一些關於後臺處理最有用的建議,文中介紹的很多觀點是必須說明的:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/BackgroundProcess.asp


    學習如何在基於.net精簡框架的Windows窗體應用程式中使用多執行緒。
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/netcfmultithreadedapp.asp


    學習如何使用PreEmptive Dotfuscator混淆器保護你的程式碼。
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/netcfobfuscation.asp


    請參考本問答的 "10.5. 寫一個捕捉Pocket PC簽名的程式n ." 部分。


    學習如何使用.net精簡框架獲得Windows CE裝置的裝置號。
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/retrievedeviceid.asp

    你可以呼叫原生代碼的函式來獲得PocketPC的裝置號,也就是序列號。這篇快速入門教程,演示了用MessageBox來顯示裝置號。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/deviceid.aspx


    這篇文章演示瞭如何從基於.net精簡框架的應用程式中傳送短訊息:
    http://msdn.microsoft.com/library/en-us/dnnetcomp/html/netcfsendsms.asp


    這篇文章討論了在.net精簡框架下如何判斷事件的傳送者,.net精簡框架中的控制元件不支援name屬性:
    http://msdn.microsoft.com/library/en-us/dncfhowto/html/HOWTOsenderevent.asp


    在應用程式中使用多執行緒,可以提高使用者介面的效能。基類Control提供Invoke、BeginInvoke和EndInvoke方法在控制元件中來建立執行緒。.net精簡框架不支援非同步的BeginInvoke和EndInvoke呼叫。到現在,也還不支援向同步Invoke呼叫傳遞引數。這篇快速入門教程提供一個自定義類ControlInvoker,可以實現向Invoke方法中傳遞引數:
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/controlinvoker.aspx


    這篇快速入門文章描述了使用P/Invoke定義一個使用者提醒和訂時的應用。您可以計劃提醒視窗彈出的時間。您也可以設定一個應用程式在特定的時間執行或響應某個事件。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/notifications.aspx


    這篇快速入門教程演示了使用P/Invoke播放個短WAV檔案,一個是作為嵌入式資源,另一個作為一個內容。這個窗體包含了兩個按鈕,一個播放嵌入資源的Chimes.wav,另一個按單獨的檔案播放Chord.wav。
    http://samples.gotdotnet.com/quickstart/CompactFramework/doc/playsounds.aspx


    在程式碼中使用命令列引數,只須簡單的定義main方法接受引數並正確處理。以下程式碼演示了這個方法:
    //C#
    static void Main(string[] args)
    {
      // Do your thing here
    }
    
    'VB
    Shared Sub Main(ByVal args() As String)
      'Do your thing here
    End Sub
    
    在Visual Studio 2003中測試的時候,可以通過繼承開發環境設定命令列的引數。主選單,選擇 Project->Project Name Properties.Property Pages對話方塊中,選擇 Configuration Properties->Debugging。Command Line Arguments 中輸入字串:text box.


    完整的.net框架不保證,在訪問一個已經釋放的物件的屬性或方法時能成功。在完整的.net框架下,雖然訪問某些屬性(像Text),經常能成功。根據.net框架和.net精簡框架執行上的區別,在.net精簡框架上訪問已經釋放了的物件的方法或屬性會經常失敗。


    使用KeyDown和KeyUp事件,可以獲得無字元的鍵(像tab)。

    現在,只有一些特定的控制元件支援key的事件(如,form, panel, textbox 和自定義控制元件)。在.net精簡框架SP2版本中,所有控制元件都將支援key事件。

    已經知道的問題,在使用模擬器時,從鍵盤按下tab鍵將不被支援,但從軟鍵盤(SIP)上按下tab時,會有效果。

    這是VS帶的模擬器的原因造成,而不是.net精簡框架的問題。.net精簡框架觸發Key事件當它收到系統傳來的WM_KEY*訊息。當您在模擬器中執行原生代碼程式時,從鍵盤按下tab鍵並不會讓系統觸發WM_KEYDOWN事件。若是通過模擬器的軟鍵盤(SIP),或真正的裝置,則不會有這個問題。


    使用System.IO.FileInfo類訪問檔案的屬性。
    //C#
    System.IO.FileInfo fi = new System.IO.FileInfo("filename");
    // remove readonly attribute
    fi.Attributes -= System.IO.FileAttributes.ReadOnly;
    System.IO.File.Delete("filename");
    
    'VB
    Dim fi As New System.IO.FileInfo("filename")
    'remove readonly attribute
    fi.Attributes -= IO.FileAttributes.ReadOnly
    System.IO.File.Delete("filename")
    



    The existence of a member can be determined at runtime using Reflection. The code below demonstrates how to use Reflection to access the "Width" property and the "GetPixel" method of a Bitmap object. In the case of the "Width" property, the code enumerates all "public" properties with a "get" component and then searches for one named "Width". The "GetPixel" sample demonstrates how one might use Reflection to call a known function where the parameter order is unknown. This sample is set up as if the author knows there is a method named "GetPixel" which takes a pixel location of x,y but does not know the order in which they appear in the parameter list. The sample enumerates the methods and searches for one named "GetPixel" and then enumerates the parameter list to determine whether the first or second parameter is named "X". Keep in mind that, due to the differences in hardware pixel formats, the value returned by GetPixel may be different from that set by SetPixel in this sample.

    Reflection provides many powerful tools for determing functionality at runtime so for information, refer to the documentation regarding System.Type and the namespace System.Reflection.
    //C#
    using System.Reflection;
    using System.Drawing;
    
    Bitmap bm = new Bitmap(200, 100);
    int width = 0;
    
    // Explicitly set one pixel for testing
    int x = 199;
    int y = 20;
    Color pixColor = Color.Black;
    bm.SetPixel(x,y,Color.Magenta);
    
    // Get the "Width" property
    PropertyInfo[] propInfo =
      bm.GetType().GetProperties(BindingFlags.GetProperty | 
      BindingFlags.Public | BindingFlags.Instance);
    
    for (int i = 0; i < propInfo.Length; i++)
    {
        if (propInfo[i].Name == "Width")
        {
            width = (int)propInfo[i].GetValue(bm, null);
            break;
        }
    }
    
    // Call the GetPixel method
    MethodInfo[] methInfo = bm.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance);
    for (int i = 0; i < methInfo.Length; i++)
    {
        if (methInfo[i].Name == "GetPixel")
        {
            ParameterInfo[] paramInfo = methInfo[i].GetParameters();
            if (paramInfo.Length == 2)
        {
    
        object[] xy = new object[2];
        if (paramInfo[0].Name == "x")
        {
            xy[0] = x;
            xy[1] = y;
        }
        else
        {
            xy[1] = x;
            xy[0] = y;
        }
    
        pixColor = (Color)methInfo[i].Invoke(bm, xy);
        break;
    }
    
    'VB
    Imports System.Reflection
    Imports System.Drawing
    
    Dim bm As New Bitmap(200, 100)
    Dim width As Integer = 0
    
    ' Explicitly set one pixel for testing
    Dim x As Integer = 199
    Dim y As Integer = 20
    Dim pixColor As Color = Color.Black
    bm.SetPixel(x, y, Color.Magenta)
    
    ' Get the "Width" property
    Dim propInfo As PropertyInfo() = _
      bm.GetType().GetProperties((BindingFlags.GetProperty Or _
      BindingFlags.Public Or BindingFlags.Instance))
    
    Dim i As Integer
    For i = 0 To propInfo.Length - 1
        If propInfo(i).Name = "Width" Then
            width = Fix(propInfo(i).GetValue(bm, Nothing))
            Exit For
        End If
    Next i
    
    ' Call the SetPixel method
    Dim methInfo As MethodInfo() = bm.GetType().GetMethods((BindingFlags.Public _
      Or BindingFlags.Instance))
    For i = 0 To methInfo.Length - 1
        If methInfo(i).Name = "GetPixel" Then
            Dim paramInfo As ParameterInfo() = methInfo(i).GetParameters()
            If paramInfo.Length = 2 Then
                Dim xy(1) As Object
    
                If paramInfo(0).Name = "x" Then
                    xy(0) = x
                    xy(1) = y
                Else
                    xy(1) = x
                    xy(0) = y
                End If
    
                pixColor = CType(methInfo(i).Invoke(bm, xy), Color)
                Exit For
            End If
        End If
    Next i
    



    The device name can be accessed through the System.Net namespace, as demonstrated by the following code.
    //C#
    String devName = System.Net.Dns.GetHostName();
    
    'VB
    Dim devName As String = System.Net.Dns.GetHostName()
    



    Enter the following commands as single lines (each is broken into two lines for clarity):
    set CFPath=%SystemDrive%/Program Files/Microsoft Visual Studio .NET 2003/
      CompactFrameworkSDK/v1.0.5000/Windows CE
    csc Form1.cs /noconfig /nostdlib /lib:"%CFPath%"  /r:"%CFPath%/system.dll";"%CFPath%/
      system.drawing.dll";"%CFPath%/system.windows.forms.dll";"%CFPath%/mscorlib.dll"
    



    There is no Abort method to the Thread class in the .NET Compact Framework so a thread must be aborted by returning from the executing procedure. Typically, an application will notify threads of a closing event by setting a global variable. The main thread will then wait for worker threads to finish processing before closing the application. The following HOWTO article demonstrates how to accomplish this.

    http://msdn.microsoft.com/library/en-us/dncfhowto/html/stopmt.asp




    Windows Media Player is only available on the Pocket PC 2003 emulator. The Windows Media Player install package is for installation on a hardware device connected through ActiveSync and will not install to the emulator.



    The Active Programs list on the Pocket PC enumerates all open Forms. To stop a Form from being displayed in the list, simply set the Form's caption to be an empty string. The following example shows how to keep only the application name in the list while a Form is displayed from within another Form:
    //C#
    string AppName = "MyApp";
    
    Form1 form1 = new Form1();
    this.Text = "";
    form1.Text = AppName;
    form1.ShowDialog();
    this.Text = AppName;
    
    'VB
    Dim AppName As String = "MyApp"
    
    Dim form1 As New Form1()
    Me.Text = ""
    form1.Text = AppName
    form1.ShowDialog()
    Me.Text = AppName
    



    You can use Reflection to look up a control instance by its name. Here is some sample code:
    //C#
    private void Form1_Load(object sender, System.EventArgs e)
    {
        ComboBox c = (ComboBox)this.ControlFromName("combobox1");
        c.Items.Add("1");
        this.GetControls();
    }
    
    private Control ControlFromName(string name)
    {
        object o = this.GetType().GetField(name,
          System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance |
          System.Reflection.BindingFlags.IgnoreCase).GetValue(this);
    
        return((Control)o);
    }
    
    private void GetControls()
    {
        System.Reflection.FieldInfo[] fis = this.GetType().GetFields
        (
            System.Reflection.BindingFlags.NonPublic | 
            System.Reflection.BindingFlags.Instance |
            System.Reflection.BindingFlags.IgnoreCase
        );
    
        foreach(System.Reflection.FieldInfo fi in fis)
        {
            if (fi.GetValue(this) is Control)
                MessageBox.Show(fi.Name);
        }
    }
    'VB
    Private Function ControlFromName(ByVal name As String) As Control
        Dim o As ObjectDim o As Object
        o = Me.GetType().GetField(name, Reflection.BindingFlags.NonPublic Or _
          Reflection.BindingFlags.Instance Or _
          Reflection.BindingFlags.IgnoreCase).GetValue(Me)
       
        Return (CType(o, Control))
    End Function
    
    Private Sub Form1_Load(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles MyBase.Load
        Dim c As ComboBox
        c = CType(ControlFromName("_combobox1"), ComboBox)
        c.Items.Add("1")
        Me.GetControls()e.GetControls()
    End Sub
    
    Private Sub GetControls()
        Dim fis As System.Reflection.FieldInfo()
    
        fis = Me.GetType().GetFields(Reflection.BindingFlags.NonPublic Or _
          Reflection.BindingFlags.Instance Or _
          Reflection.BindingFlags.IgnoreCase)
    
        For Each fi As Reflection.FieldInfo In fis
            If TypeOf (fi.GetValue(Me)) Is Control Then
                MessageBox.Show(fi.Name)
            End Ifnd If
        Next
    End Sub
    



    The hardware directional pad buttons can be detected through the standard key events by comparing the KeyCode from the KeyEventArgs parameter.
    //C#
    protected override void OnKeyDown(KeyEventArgs e)
    {
        switch (e.KeyCode)
        {
            case Keys.Up:
                MessageBox.Show("Up Key Pressed");
                break;
            case Keys.Down:
                MessageBox.Show("Down Key Pressed");
                break;
            case Keys.Left:
                MessageBox.Show("Left Key Pressed");
                break;
            case Keys.Right:
                MessageBox.Show("Right Key Pressed");
                break;
        }
        base.OnKeyDown (e);
    }
    'VB
    Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
        Select Case e.KeyCode
            Case Keys.Up
                MessageBox.Show("Up Key Pressed")
            Case Keys.Down
                MessageBox.Show("Down Key Pressed")
            Case Keys.Left
                MessageBox.Show("Left Key Pressed")
            Case Keys.Right
                MessageBox.Show("Right Key Pressed")
        End Select
         
        MyBase.OnKeyDown(e)
    
    End Sub 'OnKeyDown
    
    


    See the .NET Compact Framework QuickStarts, Implementing Events topic:

    http://samples.gotdotnet.com/quickstart/compactframework/doc/btndclick.aspx


    See the .NET Compact Framework QuickStarts, OnEnter/OnLeave Functionality:

    http://samples.gotdotnet.com/quickstart/compactframework/doc/enterleave.aspx



    Create a modeless dialog that behaves like a model dialog.
    //C#
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Collections;
    using System.ComponentModel;
    
    public interface IModelessDialogCallback
    {
        void DialogResultCallback(DialogResult result);
    }
    
    publi