• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Khan's Notebook GCC/GNU/Linux Delphi/Window Java/Anywhere

            路漫漫,長修遠,我們不能沒有錢
            隨筆 - 173, 文章 - 0, 評論 - 257, 引用 - 0
            數據加載中……

            關于exe形式編程的一點心得,希望對大家有所幫助(轉載自nokia forum)

            其實我也只是一個Symbian的初學者,我能深刻的感受到一個初學者在探索新的開發平臺時的坎坷。以下的心得是我經過一段很長時間的探索才得到的,這其 中走了很多彎路,也得到了很多人的幫助?,F在我將其整理了一下貼出來,希望能給尋求相關知識的朋友一些幫助,以便于大家少走一些彎路。
            當然我的水平有限,理解不深,錯誤在所難免,希望大家發現后能及時指正。

            1.為什么要用exe形式的程序?
            相信絕大部分人做Symbian程序都是從app開始的,app的例子非常多,很容易上手。但是有些需求在用app實現中出現了一些問題,假設我們要做一 個來電檢測程序,把所有來電號碼都記錄在一個文件中。如果用app做當然可以實現,但是問題是這個app是有窗口界面的,但這個窗口對使用者來說毫無價 值,白白浪費了一大塊資源,但是又不能把這個窗口關掉,一旦關掉,app就終止運行了,來電檢測也就無法實現了。類似的程序的最佳解決方案就是做成exe 形式。
            通常exe程序是用來做后臺服務的,對使用者來說他是不可見的,通常沒有界面,這樣既節省了資源,有不會因為使用者不小心關閉了程序而導致功能無法實現。

            2.exe程序的框架
            exe的例子也有一些,大家可以參考那些例子來建立mmp文件以及程序基礎框架,這里就不多說了。
            exe總是從E32Main函數開始執行的,我是用如下的E32Main代碼的:
            GLDEF_C TInt E32Main()
            {
              CTrapCleanup* cleanup = CTrapCleanup::New();

              RUNMAIN(); // 宏

              _LIT(KMsgPanicEpoc32ex,"EPOC32EX");
              __ASSERT_ALWAYS(!error,User::Panic(KMsgPanicEpoc32ex,error));
              delete cleanup;
              return 0;
            }
            這里的處理程序 RUNMAIN 其實是我定義的一個宏,通常情況下,他是:
            #define RUNMAIN() TRAPD(error, MainL());
            實際上就是去掉用MainL。為什么這么做,后面會提到。
            MainL的代碼如下,構建了CActiveScheduler,然后就是具體的處理了

            void MainL()
            {
              CActiveScheduler* scheduler = new(ELeave) CActiveScheduler();
              CleanupStack::PushL(scheduler);
              CActiveScheduler::Install(scheduler);

              // 具體的處理
              // ......

              CleanupStack::PopAndDestroy(scheduler);
            }

            3.如何調試exe
            又了上面的代碼,這個exe已經可以編譯和運行了,雖然他什么實質的事情都沒做,但是他的的確確已經是一個合格的exe了。如果你把編譯好的程序放到手機 上運行(用文件管理器打開),你會發現什么都沒有發生,你無法判斷它是否執行了,執行到哪里了。這就帶來一個調試的問題。
            首先說說如何在VC6環境中調試,這個比較簡單,調試app的時候,我們是指定vc6運行那個模擬器程序的,而調試exe你只要指定vc6調試時運行你生 成的那個exe就可以了,當然這個exe是wins編碼的,不能是手機上運行的armi編碼。調試運行后也會顯示手機模擬器的界面,不過沒有9宮格主菜單 了。
            比較麻煩的是手機上的執行調試,如前面提到的那樣,我們可能什么都看不到,那么如何讓exe顯示一些信息呢?這里就需要用到控制臺Console。Console就如Windows上的dos窗口,是純文本的,對付信息顯示是綽綽有余。
            Console的用法也非常簡單,先構造CConsoleBase,然后就可以用它的Printf函數在控制臺上輸出數據了。
            我把Console單獨放在一組cpp/h文件中,如下:

            // ================= Start of console.h =======================
            #ifndef __CONSOLE_H__
            #define __CONSOLE_H__

            #include <e32base.h>
            #include <e32cons.h>
            #include <e32std.h>

            #define _DEBUG_CONSOLE_

            #ifdef _DEBUG_CONSOLE_
            extern CConsoleBase* gConsole;
            extern void ConsoleMainL();
            #define RUNMAIN() TRAPD(error, ConsoleMainL());
            // 顯示字符串
            #define CONSOLEPRINTINFO(infostr) gConsole->Printf(infostr);gConsole->Printf(_L("\n"));
            // 先是數字
            #define CONSOLEPRINTNUM(num) gConsole->Printf(_L("%d\n"), num);
            #else // _DEBUG_CONSOLE_
            #define RUNMAIN() TRAPD(error, MainL());
            #define CONSOLEPRINTINFO(infostr)
            #define CONSOLEPRINTNUM(num)
            #endif // _DEBUG_CONSOLE_

            #endif //__CONSOLE_H__

            // ================= End of console.h =========================

            // ================= Start of console.cpp =======================
            #include "console.h"
            #ifdef _DEBUG_CONSOLE_
            extern void MainL();
            CConsoleBase* gConsole;
            void ConsoleMainL()
            {
              gConsole = Console::NewL(_L("MyExe"), TSize(KConsFullScreen, KConsFullScreen));
              MainL();
              delete gConsole;
            }
            #endif
            // ================= End of console.cpp =========================

            這樣一來我只要將"#define _DEBUG_CONSOLE_"這一行去掉就可以編譯生成不含Console的最終代碼了,而加上這一行就可以顯示調試信息。在主代碼中只要調用 CONSOLEPRINTINFO 和 CONSOLEPRINTNUM 兩個宏來分別顯示字符串和數字,而不用再考慮是否define了_DEBUG_CONSOLE_。
            看到這里的 RUNMAIN 宏了嗎,他的作用就是在調試的時候去執行ConsoleMainL,而不是MainL,兩者的區別就是ConsoleMainL先建立了一個Console,最后再將其釋放。
            放到手機上運行一下吧,運行后會出現一個全屏的白色窗口,其實你可能看不清這個窗口,因為它是一閃而過的。怎么會這樣,呵呵,因為我們的MainL()函數里面什么都沒做,exe程序當然就立即結束了。你可以嘗試在MainL()函數“具體的處理”這部分加上兩句話:
            CONSOLEPRINTINFO(_L("Hello World!"));
            CActiveScheduler::Start();
            第一句是在控制臺上顯示Hello World;第二句開始檢測CActive事件,這里用這個只是為了能讓程序保持住,而不會立即結束。在手機上運行后,你會發現一個白色窗口,上面顯示 Hello World。新的問題又來了,這個程序現在總也結束不了了,這時候需要一個線程管理工具來終止這個exe,這樣的工具有AppMan和TaskSpy。
            注意你的線程名稱,當你是用了Console時,線程名稱就是控制臺名稱“MyExe”,當不用Console時,線程名稱就是那個exe的名字,這點對下一段很有用。為了保持一致,建議大家將控制臺名稱設定為exe程序的名稱。
            另外你可以根據你的需要定義你的顯示宏,而不一定是我這里的 CONSOLEPRINTINFO 和 CONSOLEPRINTNUM。

            4.如何防止exe運行多個實例
            和app不同的是,exe可以運行多個實例,在某些情況下,這是有用的。但是如果我們不需要這個特性,那么如何才能阻止exe運行多個實例以減少資源占用呢?這就需要用TFindProcess。
            將MainL寫成:

            _LIT(KPROCESSNAME, "MyExe*"); // 線程名稱
            void MainL()
            {
              CActiveScheduler* scheduler = new(ELeave) CActiveScheduler();
              CleanupStack::PushL(scheduler);
              CActiveScheduler::Install(scheduler);

              // 尋找符合條件的線程
              TInt pcount = 0;
              TFullName processName;
              TFindProcess findProcess(KPROCESSNAME);
              while (ETrue)
              {
                findProcess.Next(processName);
                if (processName != KNullDesC)
                {
                  pcount ++;
                  CONSOLEPRINTINFO(processName);
                } else
                  break;
              }

              if (pcount <= 1) // 只有本線程運行
              {
                // 具體的處理
                // ......
              }

              CONSOLEPRINTINFO(_L("Exe End"));
              CleanupStack::PopAndDestroy(scheduler);
            }

            注意,KPROCESSNAME是線程主名稱,后面*是一個通佩符,因為具體的線程名稱后面還跟著一串數字,我們只要定位前面的關鍵字就可以了。
            這里判斷線程的數量用了 if (pcount <= 1),而不是<1,因為當前在做判斷的線程也算一個。
            當發現有其他相同的線程在運行時,本線程就跳過具體的處理,直接結束了。這樣就達到了我們的目的。


            5. 如何讓exe程序顯示信息窗口
            exe是后臺的程序,通常是沒有窗口界面的,但是我們有時候需要讓使用者獲得一些信息。比如一個鬧鐘提醒程序,平時在后臺運行,到時間后除了要播放鬧鈴,可能還需要在屏幕上顯示一些用戶預先設置的提示信息,如"XXX生日"之類的。這時候就需要來構造一個窗口。

            // ================= Start of Window.h =======================
            //

            #if !defined(__MY_WINDOW_H__)
            #define __MY_WINDOW_H__

            class CWindow;

            /////////////////////////////////////////////////////////////////////////
            ////////////////////// Declaration of CWsClient /////////////////////////
            /////////////////////////////////////////////////////////////////////////

            // Base class for all windows
            class CWsClient : public CActive
            {
            protected:
              //construct
              CWsClient(const TRect& aRect);

            public:
              static CWsClient* NewL(const TRect& aRect);
              void ConstructL();
              // destruct
              ~CWsClient();

            public:
              // terminate cleanly
              void Exit();
              // active object protocol
              void IssueRequest(); // request an event
              void DoCancel(); // cancel the request

              virtual void RunL(); // handle completed request

            private:
              CWsScreenDevice* iScreen;
              CWindowGc* iGc;

              CWindow *iWindow;

              RWsSession iWs;
              RWindowGroup iGroup;

              const TRect& iRect;

              friend class CWindow; // needs to get at session
            };

            //////////////////////////////////////////////////////////////////////////////
            ///////////////////////// CWindow declaration ////////////////////////////////
            //////////////////////////////////////////////////////////////////////////////

            class CWindow : public CBase
            {
            public:
              CWindow(CWsClient* aClient);
              void ConstructL (const TRect& aRect);
              ~CWindow();

            public:
              // access
              RWindow& Window(); // our own window
              // drawing
              void Draw(const TRect& aRect);

            private:
              CWindowGc* SystemGc(); // system graphics context

            private:
              RWindow iWindow; // window server window
              TRect iRect; // rectangle re owning window
            private:
              CWsClient* iClient; // client including session and group
            };

            #endif // __MY_WINDOW_H__

            // ================= End of Window.h =======================



            // ================= Start of Window.cpp =======================
            // Window.cpp
            //

            #include <w32std.h>
            #include <coedef.h>
            #include "Window.h"

            ///////////////////////////////////////////////////////////////////////////////
            ////////////////////////// CWindow implementation /////////////////////////////
            ///////////////////////////////////////////////////////////////////////////////

            CWindow::CWindow(CWsClient* aClient): iClient(aClient)
            {
            }

            void CWindow::ConstructL (const TRect& aRect)
            {
              // Use the window group for parent window
              RWindowTreeNode* parent= &(iClient->iGroup);
              iWindow=RWindow(iClient->iWs); // use app's session to window server
              User::LeaveIfError(iWindow.Construct(*parent,(TUint32)this));
              iRect = aRect;
              iWindow.SetExtent(iRect.iTl, iRect.Size()); // set extent relative to group coords
              iWindow.Activate(); // window is now active
            }

            CWindow::~CWindow()
            {
              iWindow.Close(); // close our window
            }

            RWindow& CWindow::Window()
            {
              return iWindow;
            }

            CWindowGc* CWindow::SystemGc()
            {
              return iClient->iGc;
            }

            /****************************************************************************\
            | Function: CWindow::Draw
            | Purpose: Redraws the contents of CSmallWindow within a given
            | rectangle. CSmallWindow displays a square border around
            | the edges of the window, and two diagonal lines between the
            | corners.
            | Input: aRect Rectangle that needs redrawing
            | Output: None
            \****************************************************************************/
            void CWindow::Draw(const TRect& aRect)
            {
              // Drawing to a window is done using functions supplied by
              // the graphics context (CWindowGC), not the window.
              CWindowGc* gc = SystemGc(); // get a gc
              gc->SetClippingRect(aRect); // clip outside this rect
              gc->Clear(aRect); // clear
              TSize size=iWindow.Size();
              TInt width=size.iWidth;
              TInt height=size.iHeight;
              // Draw a square border
              gc->DrawLine(TPoint(0,0),TPoint(0,height-1));
              gc->DrawLine (TPoint (0, height-1), TPoint (width-1, height-1));
              gc->DrawLine(TPoint(width-1,height-1),TPoint(width-1,0));
              gc->DrawLine (TPoint (width-1, 0), TPoint (0, 0));
              // Draw a line between the corners of the window
              gc->DrawLine(TPoint(0,0),TPoint(width, height));
              gc->DrawLine (TPoint (0, height), TPoint (width, 0));
            }


            /////////////////////////////////////////////////////////////////////////////////////
            /////////////////////////// CWsClient implementation ////////////////////////////////
            /////////////////////////////////////////////////////////////////////////////////////
            CWsClient* CWsClient::NewL(const TRect& aRect)
            {
              // make new client
              CWsClient* client=new (ELeave) CWsClient(aRect);
              CleanupStack::PushL(client); // push, just in case
              client->ConstructL(); // construct and run
              CleanupStack::Pop();
              return client;
            }

            CWsClient::CWsClient(const TRect& aRect)
            : CActive(CActive::EPriorityHigh),
            iRect(aRect)
            {
            }

            void CWsClient::ConstructL()
            {
              // add ourselves to active scheduler
              CActiveScheduler::Add(this);
              // get a session going
              User::LeaveIfError(iWs.Connect());
              // construct our one and only window group
              iGroup=RWindowGroup(iWs);
              User::LeaveIfError(iGroup.Construct(2,ETrue)); // meaningless handle; enable focus
              // construct screen device and graphics context
              iScreen=new (ELeave) CWsScreenDevice(iWs); // make device for this session
              User::LeaveIfError(iScreen->Construct()); // and complete its construction
              User::LeaveIfError(iScreen->CreateContext(iGc));// create graphics context

              iWindow = new (ELeave) CWindow (this);
              iWindow->ConstructL(iRect);

              // 窗口始終在最上層
              iGroup.SetOrdinalPosition(0, ECoeWinPriorityAlwaysAtFront);
              // 禁止接受焦點
              iGroup.EnableReceiptOfFocus(EFalse);
              // Set the window is non-fading
              iGroup.SetNonFading(ETrue);

              // 將窗口提到前面
              TApaTask task(iWs);
              task.SetWgId(iGroup.Identifier());
              task.BringToForeground();

              // request first event and start scheduler
              IssueRequest();
            }

            CWsClient::~CWsClient()
            {
              // neutralize us as an active object
              Deque(); // cancels and removes from scheduler
              // get rid of everything we allocated
              delete iGc;
              delete iScreen;
              delete iWindow;
             
              // destroy window group
              iGroup.Close();
              // finish with window server
              iWs.Close();
            }

            void CWsClient::IssueRequest()
            {
              iWs.RedrawReady(&iStatus); // request redraw
              SetActive(); // so we're now active
            }

            void CWsClient::DoCancel()
            {
              iWs.RedrawReadyCancel(); // cancel redraw request
            }

            /****************************************************************************\
            | Function: CWsClient::RunL()
            | Called by active scheduler when an even occurs
            | Purpose: do Redraw
            \****************************************************************************/
            void CWsClient::RunL()
            {
              // find out what needs to be done
              TWsRedrawEvent redrawEvent;
              iWs.GetRedraw(redrawEvent); // get event
              CWindow* window=(CWindow*)(redrawEvent.Handle()); // get window
              if (window)
              {
                TRect rect=redrawEvent.Rect(); // and rectangle that needs redrawing
                // now do drawing
                iGc->Activate(window->Window());
                window->Window().BeginRedraw(rect);
                window->Draw(rect);
                window->Window().EndRedraw();
                iGc->Deactivate();
              }
              // maintain outstanding request
              IssueRequest(); // maintain outstanding request
            }
            // ================= End of Window.cpp =======================

            上面的代碼只是一個最基礎的框架,你可以自己添更多的東西。比如顯示一些文字。不過要顯示文字就要先設定字體,具體操作如下:
            先要建立一個CWsScreenDevice:
            iScreen = new (ELeave) CWsScreenDevice(iWs);

            然后可以用GetNearestFontInTwips通過字體名字獲得CFont:
            _LIT(FONT_CH16, "CombinedChinesePlain16");
            TFontSpec myFontSpec(FONT_CH16, 200);
            iScreen->GetNearestFontInTwips(iFont, myFontSpec);



            再補充兩點:
            1. 如何在手機上運行exe程序
            可以有幾種方法,一種是通過文件管理器直接執行這個exe,這種方法通常在開發階段使用,因為要讓用戶這么操作,用戶會覺得很不方便;第二種是通過app 來調用exe;第三種是利用mdl在開機階段就調用exe。后兩種的方法是類似的,都是通過CApaCommandLine來實現,具體的代碼可以參考:
            http://discussion.forum.nokia.com/forum/showthread.php?t=66477

            2. 如何終止exe程序
            開發階段,我們可以借助TaskSpy等工具,當然也可以通過我們的App來終止exe進程。具體的方法如下:首先要找到符合名稱得進程,然后將其kill

            _LIT(KPROCESSNAME, "myexe*"); // 進程名稱,別忘了最后面的匹配字符*
            void KillExeL()
            {
              TInt Err;
              TFullName processName;
              TFindProcess findProcess(KPROCESSNAME);
              while (ETrue)
              {
                findProcess.Next(processName);
                if (processName != KNullDesC) // 找到符合條件的進程
                {
                  RProcess aProcess;
                  Err = aProcess.Open(findProcess, EOwnerProcess);
                  if (Err == KErrNone)
                  {
                    aProcess.Kill(0); // kill該進程
                  }
                  aProcess.Close();
                } else
                  break;
              }
            }

            1. 新的調試方法
            在手機上調試exe程序還是一個比較麻煩的事情,前面介紹了console,但是有時候不方便用console,這時候就要換一種方法來記錄信息,比較簡 單的就是用文件記錄,這個方法的缺點是不能實時察看,另外一個缺點就是要消耗較多的時間,別看這點時間,有時候就會掩蓋一些問題。我就遇到過,不過可以通 過其他的辦法來解決。
            這里給出一段我用的代碼供大家參考。因為是調試代碼,所以寫的并不是很完善,要求字符串不能含中文:
            void WriteTestInfoL(const TDesC& infostr)
            {
              RFs aSession;
              aSession.Connect();

              TFileName *fname = new (ELeave) TFileName;
              CleanupStack::PushL(fname);
              fname->Copy(_L("c:\\testinfo.txt"));

              RFile aTestFile;
              TInt err = aTestFile.Open(aSession, *fname, EFileWrite);
              if (err == KErrNotFound) // 沒有此文件
              {
                err = aTestFile.Create(aSession, *fname, EFileWrite);
              }
              CleanupStack::PopAndDestroy();

              if (err == KErrNone)
              {
                TInt pos = 0;
                aTestFile.Seek(ESeekEnd, pos); // 在最后添加
                TBuf8<50> aText;
                for (TInt i=0; i<infostr.Length(); i++)
                {
                  aText.Append(infostr[i] & 0xFF); // 16bit簡單轉8bit
                }

                aTestFile.Write(aText, aText.Size());
                aTestFile.Write(_L8("\r\n"), 2); // 添加一個換行
              }
              aTestFile.Close();
              aSession.Close();
            }

            這段代碼每調用一次,就會向c:\testinfo.txt中添加一行字符串。你可以在手機上用記事本直接打開察看。
            前面提到的時間占用問題如何解決呢?
            也很簡單,在你對時間有要求的地方定義一片緩存,把信息先放到緩存里,等到過了這個地方,再將緩存里的數據寫入文件。用這個方法還可以寫入數字等內容。
            代碼類似于:
            TBuf<100> tempstr;
            ......
            tempstr.Append(myinfo);
            tempstr.Append(_L("\r\n")); // 換行
            ......
            tempstr.AppendFormat(_L("a=%d, b=%d\r\n"), a, b);
            ......
            ......
            WriteTestInfoL(tempstr);


            通過這個方法調試還是幫我解決了不少問題的。



            posted on 2007-08-27 17:46 Khan 閱讀(2629) 評論(2)  編輯 收藏 引用 所屬分類: GCC/G++

            評論

            # re: 關于exe形式編程的一點心得,希望對大家有所幫助(轉載自nokia forum)  回復  更多評論   

            謝謝這篇文章,在我最困惑的時候幫了我。
            2007-12-29 15:51 | andy liu

            # re: 關于exe形式編程的一點心得,希望對大家有所幫助(轉載自nokia forum)  回復  更多評論   

            感覺文章作者的功力很深厚
            2008-08-29 13:50 | juniorhope
            狠狠人妻久久久久久综合蜜桃| 久久综合九色综合欧美就去吻| 久久久久免费精品国产| 中文字幕乱码人妻无码久久| 国产人久久人人人人爽| 99久久精品免费看国产免费| 亚洲乱码日产精品a级毛片久久| 亚洲中文字幕久久精品无码APP | 97r久久精品国产99国产精| 亚洲国产精品人久久| 性做久久久久久久久| 国产V亚洲V天堂无码久久久| 中文字幕久久精品| 久久精品男人影院| 亚洲国产精品一区二区久久hs| 国产福利电影一区二区三区久久老子无码午夜伦不 | 2020久久精品亚洲热综合一本| 久久精品国产久精国产果冻传媒| 久久99精品国产99久久6男男| 国产精品久久久久久久app| 国产成人精品久久亚洲高清不卡 | 热99RE久久精品这里都是精品免费| 青青青国产精品国产精品久久久久| 久久精品国产亚洲av麻豆蜜芽 | 久久综合88熟人妻| 欧美久久一级内射wwwwww.| 亚洲国产成人久久综合碰碰动漫3d| 少妇人妻88久久中文字幕| 国产精品久久婷婷六月丁香| 九九久久精品无码专区| 91久久九九无码成人网站| 久久国产乱子伦免费精品| 性高湖久久久久久久久| 日本五月天婷久久网站| 狠狠色丁香久久婷婷综合图片| 久久精品国产第一区二区| 国产精品免费久久久久久久久| 国产成人精品久久综合| 国产精品伦理久久久久久| 国产精品内射久久久久欢欢| 国产亚洲精久久久久久无码AV|