• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
    • 軟件測試技術
    • 軟件測試博客
    • 軟件測試視頻
    • 開源軟件測試技術
    • 軟件測試論壇
    • 軟件測試沙龍
    • 軟件測試資料下載
    • 軟件測試雜志
    • 軟件測試人才招聘
      暫時沒有公告

    字號: | 推薦給好友 上一篇 | 下一篇

    .NET Compact Framework 的實時行為

    發布: 2007-6-13 16:09 | 作者: 網絡轉載 | 來源: 網絡 | 查看: 215次 | 進入軟件測試論壇討論

    領測軟件測試網

    適用于:
        Microsoft® Windows® CE .NET
        Microsoft Visual Studio® .NET
        Microsoft Visual Basic® .NET
        Microsoft Visual C#®
        Microsoft .NET Compact Framework

    下載 CFInRT_used_for_actual_measurements.exe。

    下載 RTCF.exe。

    下載 Win32CEAppInRT.exe。

    摘要:Visual Studio .NET 2003 的到來為智能設備可編程性提供了集成的支持,從而可以使用托管代碼為多種設備開發應用程序。軟件開發人員現在可以在設備開發過程中使用象 Visual Basic .NET 和 Visual C# 這樣的新型語言。盡管這聽起來很令人鼓舞,但仍有一個問題需要回答:使用托管代碼為嵌入式設備編寫應用程序時,是否可以利用 Windows CE .NET 的實時功能?本文將回答這個問題,并提出一種可能的方案,將實時行為與 Microsoft .NET 功能結合起來。

    目錄

    托管環境和非托管環境

    象 Microsoft® 公共語言運行庫這樣的托管環境的某些優勢(例如,編寫更安全且平臺獨立的軟件)在實時環境中可能會成為劣勢。一般來說,您不能在使用一種方法之前等待實時 (JIT) 編譯器編譯這種方法,也不能等待內存回收器通過刪除不使用的資源來清除以前分配的內存。而這兩種特性都會影響確定性的系統行為?梢酝ㄟ^調用 GC.Collect() 來強制內存回收器履行其職責。但您希望內存回收器能夠自己執行任務,因為它經過了高度優化。為實現真正的實時行為,如果有一種方法可以將用本機或非托管 Microsoft Win32® 代碼編寫的真正的實時功能與用托管代碼編寫的其他功能區分開,那就太好了。利用平臺調用 (P/Invoke),您就可以做到這一點。

    有效的平臺調用

    根據 MSDN® 幫助,平臺調用是公共語言運行庫提供的功能,它使托管代碼能夠調用非托管的本機動態鏈接庫 (DLL) 入口點。換句話說,平臺調用提供了一條從托管 Microsoft .NET 代碼到非托管 Win32 代碼的切換路徑。為了能夠在 Microsoft Windows® CE .NET 內使用此機制,必須將要調用的本機 Win32 函數在 DLL 中定義為外部公開。由于托管 .NET 環境不知道 C++ 名稱混成的任何情況,因此從托管應用程序內調用的函數還應具有 C 命名規則。為了能夠使用 DLL 中的功能,需要在托管應用程序內的功能入口點周圍構建一個包裝類。列表 1 顯示了一個小型非托管 DLL 的示例。列表 2 顯示如何從托管代碼中調用非托管 DLL。由于此機制適用于所有輸出的 DLL 函數,而且幾乎所有 Win32 API 都被輸出到 coredll.dll 中,因此此機制也提供了一種方法,用來調用幾乎所有的 Win32 API。我們在測試中使用了平臺調用,以便從托管應用程序中調用非托管實時線程。

    // 這就是函數 GetTimingInfo,位于
    // 非托管 Win32 DLL 中。此函數需要一些信息,
    // 這些信息來自同一 DLL 中的一個中斷服務
    // 線程。請求托管應用程序時,使用
    // 雙緩沖機制來復制計時信息。
    
    RTCF_API DWORD GetTimingInfo(LPDWORD lpdwAvgPerfTicks,
        LPDWORD lpdwMax,
        LPDWORD lpdwMin,
        LPDWORD lpdwDeltaMax,
        LPDWORD lpdwDeltaMin)
    {
        g_bRequestData = TRUE;
        if (WaitForSingleObject(g_hNewDataEvent,
            1000)==WAIT_OBJECT_0)
        {
            *lpdwAvgPerfTicks = g_dwBufferedAvgPerfTicks;
            *lpdwMax = g_dwBufferedMax;
            *lpdwMin = g_dwBufferedMin;
            *lpdwDeltaMax = g_dwBufferedDeltaMax;
            *lpdwDeltaMin = g_dwBufferedDeltaMin;
            return 1;
        }
        else
            return 0;
    }
    
    // GetTimingInfo 原型
    #ifdef RTCF_EXPORTS
    #define RTCF_API __declspec(dllexport)
    #else
    #define RTCF_API __declspec(dllimport)
    #endif
    
    extern "C"
    {
        RTCF_API BOOL Init();
        RTCF_API BOOL DeInit();
        RTCF_API DWORD GetTimingInfo(LPDWORD lpdwAvgPerfTicks,
            LPDWORD lpdwMax,
            LPDWORD lpdwMin,
            LPDWORD lpdwDeltaMax,
            LPDWORD lpdwDeltaMin);
    }
    

    列表 1:要從托管代碼中調用的 Win32 DLL

    // 能夠平臺調用到 DLL 中的包裝類
    // DLL 中的輸出函數由此包裝類
    // 導入。請注意,如何使用編譯器屬性來識別
    // 集成了輸出函數的實際 DLL。
    using System;
    using System.Runtime.InteropServices;
    
    namespace CFinRT
    {
        public class WCEThreadIntf
        {
            [DllImport("RTCF.dll")]
            public static extern bool Init();
            [DllImport("RTCF.dll")]
            public static extern bool DeInit();
            [DllImport("RTCF.Dll")]
            public static extern uint GetTimingInfo(
                ref uint perfAvg,
                ref uint perfMax,
                ref uint perfMin,
                ref uint perfTickMax,
                ref uint perfTickMin);
        }
    }
    
    // 從托管代碼中調用非托管函數
    public void CollectValue() 
    {
        if (WCEThreadIntf.GetTimingInfo(ref aveSleepTime,
            ref maxSleepTime,
            ref minSleepTime,
            ref curMaxSleepTime,
            ref curMinSleepTime) != 0) 
        {
            curMaxSleepTime = (uint)(float)((curMaxSleepTime *
                scaleValue) / 1.19318);
            curMinSleepTime = (uint)(float)((curMinSleepTime *
                scaleValue) / 1.19318);
            aveSleepTime = (uint)(float)((aveSleepTime *
                scaleValue) / 1.19318);
            maxSleepTime = (uint)(float)((maxSleepTime *
                scaleValue) / 1.19318);
            minSleepTime = (uint)(float)((minSleepTime *
                scaleValue) / 1.19318);
        } 
    
        StoreValue();
        counter = (counter + 1) % samplesInMinute;
    }
    

    列表 2:調用非托管代碼

    實時方案

    系統需要真正的實時功能從外部數據源檢索信息。信息存儲在系統中,并且會以某種圖形化的形式向用戶顯示。圖 1 顯示了解決此問題的一種可能的方案。

    圖 1:使用托管和非托管代碼的實時方案

    位于本機 Win32 DLL 中的實時線程接收外部數據源的中斷。該線程會處理中斷并存儲要向用戶顯示的相關信息。在右邊,用托管代碼編寫的單獨 UI 線程將讀取實時線程以前存儲的信息。由于在進程之間切換環境的代價非常大,因此您希望讓整個系統位于同一進程內。如果通過將實時功能放到 DLL 中并在該 DLL 與系統的其他部分之間提供接口,把實時功能與用戶界面功能分開,這樣就實現了用單一進程來處理系統的所有部分的目標。UI 線程與實時 (RT) 線程之間的通信是通過使用平臺調用進入本機 Win32 代碼來實現的。

    實際測試

    您希望使測試具有代表性,但要盡可能簡單,以便使它能夠很容易地在其他系統上重復。為此,可以下載源代碼來親自進行實驗。 此測試需要提供一種向系統通知中斷的方法,還要求能輸出探測來測量系統性能。 使用由信號生成器生成的方波來通知系統。 當然,Windows CE .NET 操作系統應能夠集成 .NET Compact Framework。 Paul Yao 寫的一篇文章中提到應使用哪些 Windows CE .NET 模塊和組件來運行托管應用程序。 請參閱適用于 Windows CE .NET 的 Microsoft .NET Compact Framework。 測試的目的不只是具有代表性和可重復性,而且也包括為輸入找到適當的中斷源。 列表 3 顯示了如何將物理中斷掛接到中斷服務線程上。

    RTCF_API BOOL Init()
    {
        BOOL bRet = FALSE;
        DWORD dwIRQ = IRQ;    // 在我們的例子中,IRQ = 5
    
        // 獲取指定 IRQ 的 SysIntr
        if (KernelIoControl(IOCTL_HAL_TRANSLATE_IRQ,
            &dwIRQ,
            sizeof(DWORD),
            &g_dwSysIntr,
            sizeof(DWORD),
            NULL))
        {
            // 創建一個事件來激活 IST
            g_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
    
            if (g_hEvent)
            {
                // 將中斷連接到事件,并
                // 創建中斷服務線程。
                // 實際的 IST 顯示在列表 4 中
                InterruptDisable(g_dwSysIntr);
    
                if (InterruptInitialize(g_dwSysIntr,
                    g_hEvent, NULL, 0))
                {
                    g_bFinish = FALSE;
                    g_hThread = CreateThread(NULL,
                        0,
                        IST,
                        NULL,
                        0,
                        NULL);
                    if (g_hThread)
                    {
                        bRet = TRUE;
                    }
                    else
                    {
                        InterruptDisable(g_dwSysIntr);
                        CloseHandle(g_hEvent);
                        g_hEvent = NULL;
                    }
                }
            }
        }
        return bRet;
    }
    

    列表 3:將物理中斷連接到中斷服務線程

    為了利用托管代碼和 .NET Compact Framework 來測試應用程序的實時行為,我們基于 Standard SDK 創建了 Windows CE .NET 平臺。我們還在平臺中包含了 .NET Compact Framework 的 RTM 版本。操作系統在頻率為 300 MHz 的 Geode GX1 上運行。通知系統時使用方波,它會立即連接到 PC104 總線(第 23 針)上的 IRQ5 線。方波的頻率為 10 kHz。在上升的側面,會生成一個中斷。該中斷由中斷服務線程 (IST) 處理。在 IST 中,我們將探測脈沖發送到并行端口,以便查看輸出信號。還利用高精度 QueryPerformanceCounter API 來存儲激活 IST 的時間。為了能夠測量較長時間段內的計時信息,除了平均時間以外,我們還存儲了最長和最短時間。從中斷發生到探測輸出的這段時間表示 IRQ - IST 滯后時間。高精度計時器獲得的計時信息指示激活 IST 的時間。理想情況下,對于 10 kHz 的中斷頻率,此值應該為 100 微秒。所有計時信息均按照固定的時間間隔傳遞到圖形用戶界面。

    由于 .NET Compact Framework 本身并不能在真正實時的情況(如前所述)下使用,因此,我們決定將其僅用于顯示目的,而對于所有實時功能,則使用由嵌入式 Microsoft Visual C++® 4.0 編寫的 DLL。為了在 DLL 與 .NET Compact Framework 圖形用戶界面 (GUI) 之間進行通信,我們結合使用了雙緩沖機制和平臺調用。GUI 利用 System.Threading.Timer 對象,按照固定的時間間隔來請求新的計時信息。DLL 決定何時有時間將信息傳遞給 GUI。數據準備好之前會禁用 GUI。GUI 中顯示的信息的刷新率可由用戶選擇。在我們的測試中,使用了 50 毫秒的刷新率。

    以下偽代碼解釋了 IST 的操作以及 GUI 檢索本機 Win32 DLL 中存儲的信息的機制。

    Interrupt Service Thread:
    Wait
    On IRQ 5 send probe pulse to the parallel port
    Measure time with QueryPerformanceCounter
    Store measured time (min, max, current, average) locally
    if (userInterfaceRequestsData) {
        copy measured time information
        reset statistic measure values
        set dataReady event
        userInterfaceRequestsData = false
    }  

    托管代碼定期更新顯示數據:

    disable timer     // 請參閱“缺陷”
    call with P/Invoke into the DLL
    // 以下代碼在 DLL 中實現
    userInterfaceRequestsData = true
    wait for dataReady event
    return measured values
    draw measured values on the display, each time using new graphics objects
    update marker    // 顯示屏上的滾動垂直條
    enable timer
    

    在測試過程中,我們掛接了一個示波器,并在實驗中安排了 10 分鐘,同時打印輸出范圍和 Windows CE .NET 圖形顯示。圖 2 顯示了使用示波器測量的中斷滯后時間。在最佳情況下,滯后時間為 14.0 微秒,在最差情況下,滯后時間為 54.4 微秒,即抖動為 40.4 微秒。圖 3 顯示了激活 IST 的周期。此圖是實際用戶界面的屏幕快照。理想情況下,IST 應該每 100 微秒運行一次,即我們測量過程中的平均時間(中間的藍線)。除了 50 毫秒的采樣周期(白色方塊)內的最短和最長時間以外,我們還測量了總體最短(綠色)和最長(紅色)時間。測試周期內的偏差不超過 ±40 微秒。

    圖 2:托管應用程序:IRQ.IST 滯后時間

    圖 3:托管應用程序:運行 10 分鐘后 IST 激活的次數

    結果

    我們用了較長的時間進行測量,以確保內存回收器和 JIT 編譯器經常處于活動狀態。感謝 Microsoft 人員提供了性能計數器的注冊項,使我們能夠監視 .NET Compact Framework 的行為。使用此注冊項,可以在 .NET Compact Framework 中激活多個性能計數器。我們主要使用了此性能信息來驗證確實運行了 JIT 編譯器和內存回收器。此性能信息還明確顯示出測試過程中使用的對象數目。

    // 要用來收集新數據和
    // 刷新屏幕的定期計時器方法
    private void OnTimer(object source) 
    {
        // 臨時停止計時器,以防止
        // 調用全部的 OnTimer
        if (theTimer != null) 
        {
            theTimer.Change(Timeout.Infinite, dp.Interval);
        }
        Pen blackPen = new Pen(Color.Black);
        Pen yellowPen = new Pen(Color.Yellow);
        Graphics gfx = CreateGraphics();
    
        td.SetTimePointer(dp.CurrentSample, gfx, blackPen);
    
        for (int i = 0; i < dp.SamplesPerMeasure; i++) 
        {
            td.ShowValue(dp.CurrentSample, dp[i], gfx, i);
        }
    
        dp.CollectValue();
        td.SetTimePointer(dp.CurrentSample, gfx, yellowPen);
    
        gfx.Dispose();
        yellowPen.Dispose();
        blackPen.Dispose();
    
        // 為下一次更新重新啟動計時器
        if (theTimer != null) 
        {
            theTimer.Change(dp.Interval, dp.Interval);
        }
    }
    

    列表 4:在托管環境中處理計時器消息

    如列表 4 所示,每當周期性地更新屏幕時,都會實例化多個對象。這些對象(兩個筆對象和一個圖形對象)是在每次屏幕更新期間創建的。函數 td.ShowValuetd.SetTimerPointer 還會創建畫筆。由于每次屏幕更新時,td.SetTimerPointer 都會被調用兩次,因此每次屏幕更新期間共創建六個對象。由于每 50 毫秒更新一次屏幕,因此每秒創建 120 個對象。在 10 分鐘的執行時間里,共創建 72,000 個對象。所有這些對象都可能由內存回收器管理。在表 1 中,已分配對象的數目大致等于這些理論值。

    計數器 n 平均值 最小值 最大值
    程序總運行時間 603752 0 0 0 0
    已分配的最大字節數 1115238 0 0 0 0
    已分配的對象數 66898 0 0 0 0
    已分配的字節數 1418216 66898 21 8 24020
    簡單回收數 0 0 0 0 0
    按簡單回收收集的字節數 0 0 0 0 0
    簡單回收后使用的字節數 0 0 0 0 0
    簡單回收時間 0 0 0 0 0
    精簡回收數 1 0 0 0 0
    按精簡回收收集的字節數 652420 1 652420 652420 652420
    精簡回收后使用的字節數 134020 1 134020 134020 134020
    精簡回收時間 357 1 357 357 357
    完整回收數 0 0 0 0 0
    按完整回收收集的字節數 0 0 0 0 0
    完整回收后使用的字節數 0 0 0 0 0
    完整回收時間 0 0 0 0 0
    由應用程序導致的回收的 GC 數 0 0 0 0 0
    GC 滯后時間 357 1 357 357 357
    抖動的字節數 14046 259 54 1 929
    抖動的本機字節數 70636 259 272 35 3758
    抖動的方法數 259 0 0 0 0
    間隔的字節數 0 0 0 0 0
    間隔的方法數 0 0 0 0 0
    異常數 0 0 0 0 0
    調用數 3058607 0 0 0 0
    虛擬調用數 1409 0 0 0 0
    虛擬調用緩存命中數 1376 0 0 0 0
    平臺調用數 176790 0 0 0 0
    回收后使用的總字節數 421462 1 421462 421462 421462

    表 1:測試運行五分鐘后 .NET Compact Framework 的性能結果

    結果中分別包含了運行 10 分鐘和運行 100 分鐘的性能計數器結果。此數據是在實際測試過程中記錄的?梢钥闯,運行 10 分鐘后,發生了內存回收,且性能沒有明顯下降。表 2 顯示了運行大約 100 分鐘后的性能計數器。此次運行過程中發生了完整內存回收。在此次運行過程中,僅創建了 461,499 個對象,而不是預期的 720,000 個。這比預期對象數大約少 35%。此差異很可能是由于性能計數器所致。按照 Microsoft 的測試結果,在托管應用程序中,性能計數器會導致大約 30% 的性能損失。但是系統的實時行為未受到影響,如圖 4 所示。

    計數器 n 平均值 最小值 最大值
    執行引擎啟動時間 478 0 0 0 0
    程序總運行時間 5844946 0 0 0 0
    已分配的最大字節數 1279678 0 0 0 0
    已分配的對象數 461499 0 0 0 0
    已分配的字節數 8975584 461499 19 8 24020
    簡單回收數 0 0 0 0 0
    按簡單回收收集的字節數 0 0 0 0 0
    簡單回收后使用的字節數 0 0 0 0 0
    簡單回收時間 0 0 0 0 0
    精簡回收數 11 0 0 0 0
    按精簡回收收集的字節數 8514912 11 774082 656456 786476
    精簡回收后使用的字節數 1679656 11 152696 147320 153256
    精簡回收時間 5395 0 490 436 542
    完整回收數 2 0 0 0 0
    按完整回收收集的字節數 397428 2 198714 1916 395512
    完整回收后使用的字節數 79924 2 39962 17328 62596
    完整回收時間 65 2 32 2 63
    由應用程序導致的回收的 GC 數 0 0 0 0 0
    GC 滯后時間 5460 13 420 2 542
    抖動的字節數 19143 356 53 1 929
    抖動的本機字節數 95684 356 268 35 3758
    抖動的方法數 356 0 0 0 0
    間隔的字節數 85304 326 261 35 3758
    間隔的方法數 385 0 0 0 0
    異常數 0 0 0 0 0
    調用數 21778124 0 0 0 0
    虛擬調用數 1067 0 0 0 0
    虛擬調用緩存命中數 1029 0 0 0 0
    平臺調用數 1996991 0 0 0 0
    回收后使用的總字節數 5632119 13 433239 84637 493054

    表 2:測試運行 100 分鐘后 .NET Compact Framework 的性能結果

    圖 4:托管應用程序:運行 100 分鐘后 IST 激活的次數

    遠程進程查看器提供了多種證據,證明內存回收器和 JIT 編譯器不影響實時行為。圖 5 顯示了用于托管應用程序的遠程進程查看器的屏幕轉儲。應用程序中的所有線程(優先級為 0 的實時線程除外)都以正常優先級 (251) 運行。在我們的測量中,沒有發現 JIT 編譯器和內存回收器需要內核阻斷才能執行任務。

    圖 5:顯示托管應用程序的遠程進程查看器

    缺陷

    在測試過程中,提高方波的頻率會在托管應用程序中產生意外的結果。尤其是當某些屏幕區域無效而需要頻繁重畫時,應用程序會隨機地掛起系統。進一步調查顯示,此問題是由于經驗豐富的 Win32 程序員的疏忽造成的。在 Win32 應用程序中,每當計時器到期時,使用計時器都會產生一條 WM_TIMER 消息。但在消息隊列中,WM_TIMER 消息的優先級很低,因此只有在不需要處理其他優先級較高的消息時,才會發布這些消息。由于 CreateTimer 不提供用于開始計時的精確計時器,因此此行為可能會導致丟失計時器觸發。但這不是問題,尤其是在計時器用于更新圖形用戶界面 (GUI) 的情況下。不過,在托管應用程序中,我們使用 System.Threading.Timer 對象來創建計時器。每次計時器到期時,都會調用一個委托。委托是從某個線程池中的單獨線程內進行調用的。如果系統正在忙于處理其他活動(例如,重畫整個屏幕),則在完成前面激活的委托之前,會激活更多的計時器委托,而且每個委托位于單獨的線程中。這可能會導致耗盡線程池中的所有可用線程,并使系統掛起。列表 4 中提供了用于防止這種情況的解決方案。每次激活一個計時器委托時,我們都通過調用 Timer 對象的 Change 方法來停止計時器對象,以表明在處理完當前計時器消息之前,我們不需要下一條消息。這可能會導致計時器時間間隔不精確。在我們的例子中,計時器只用于刷新屏幕,因此計時不精確算不上問題。

    結果證明

    為了能夠將我們的實驗結果與相同設置中的典型結果進行比較,我們還編寫了一個 Win32 應用程序,該應用程序調用具有實時功能的同一個 DLL。Win32 應用程序在功能上與托管應用程序相同。它可以為系統提供一個圖形用戶界面,計時信息顯示在其中的一個窗口中。當收到 WM_TIMER 消息時,該應用程序僅利用 Win32 API 繪制計時結果。在性能方面,我們沒有發現任何明顯的差異,如圖 6 和圖 7 所示。在圖 6 中,使用示波器再次測量中斷滯后時間。對于 Win32 應用程序,滯后時間為 14.4 微秒,在最差的情況下,滯后時間為 55.2 微秒,即抖動為 40.8 微秒。這些結果與使用 .NET Compact Framework 托管應用程序測試的結果大致相等。

    圖 6:Win32 應用程序:運行 10 分鐘后 IST 激活的次數

    在圖 7 中,當激活 IST 時,顯示周期時間(對于 Win32 應用程序也如此)。同樣,這些結果與使用 .NET Compact Framework 托管應用程序的結果也相同。托管應用程序和 Win32 應用程序的源代碼都可以通過下載得到。

    圖 7:Win32 應用程序:運行 10 分鐘后 IST 激活的次數

    小結

    我們并非建議將 .NET Compact Framework 單獨用于某項實時工作,而是希望能將其用作表示層,這一點很重要。在這樣一個系統中,.NET Compact Framework 可與實時功能“和平共存”,而不會影響 Windows CE .NET 的實時行為。在本文中,我們沒有對 .NET Compact Framework 的圖形功能進行基準測試。在我們的測試中,沒有發現完全用 Win32 編寫的應用程序與部分在托管環境中用 C# 編寫的應用程序之間有任何明顯差別。.NET Compact Framework 可以提高程序員的工作效率,并且可以提供豐富的功能,因此用托管代碼編寫表示層、并用非托管代碼編寫絕對實時功能具有很多優勢。這些不同類型的功能之間的明顯區別可以通過此方法來消除。

    致謝

    我們已經考慮了很久,希望在實時情況下測試 .NET Compact Framework 的可用性。但是此測試需要與能夠提供所需硬件和測量設備的人員和公司共同完成。因此,我們要感謝 Getronics 的 Willem Haring 在此項目中為我們提供了支持、意見和熱情的招待。我們還要感謝 Delem 的人員對我們的熱情招待,以及為我們提供了測試所需的設備。

    關于作者

    Michel Verhagen 在荷蘭的 PTS Software 工作。Michel 是一名 Windows CE .NET 顧問,在 Windows CE 方面已經積累了四年經驗。他的主要專長在 Platform Builder 領域。

    Maarten Struys 也在 PTS Software 工作,負責實時和嵌入式方面的內容。Maarten 是一名經驗豐富的 Windows (CE) 開發人員,從推出 Windows (CE) 時起,就開始從事 Windows CE 方面的工作。自 2000 年以來,Maarten 開始在 .NET 環境中使用托管代碼。他還是荷蘭有關嵌入式系統開發領域的兩家權威雜志的自由撰稿人。他最近開設了一個 Web 站點,用來提供有關嵌入式環境中 .NET 的信息。

    其他資源

    有關 Windows CE .NET 的詳細信息,請參閱 Windows Embedded Web 站點(英文)。

    有關 Windows CE .NET 中包含的聯機文檔和上下文相關幫助,請參閱 Windows CE .NET 產品文檔(英文)。

    有關 Microsoft Visual Studio® .NET 的詳細信息,請參閱 Visual Studio Web 站點(英文)。

    延伸閱讀

    文章來源于領測軟件測試網 http://www.kjueaiud.com/

    TAG: compact framework net 實時 行為


    關于領測軟件測試網 | 領測軟件測試網合作伙伴 | 廣告服務 | 投稿指南 | 聯系我們 | 網站地圖 | 友情鏈接
    版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
    北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
    技術支持和業務聯系:info@testage.com.cn 電話:010-51297073

    軟件測試 | 領測國際ISTQBISTQB官網TMMiTMMi認證國際軟件測試工程師認證領測軟件測試網

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>