[轉貼]關於exe形式編程的一點心得,希望對大家有所幫助

關鍵字: exe, 後臺程序, 調試, Console, 多個實例, 窗口, 字體

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

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

2.exe程序的框架
exe的例子也有一些,大家可以參考那些例子來建立mmp文件以及程序基礎框架,這裏就不多說了。
exe總是從E32Main函數開始執行的,我是用如下的E32Main代碼的:
Code:
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,然後就是具體的處理了
Code:
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文件中,如下:
Code:
// ================= 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。

Old



4.如何防止exe運行多個實例
和app不同的是,exe可以運行多個實例,在某些情況下,這是有用的。但是如果我們不需要這個特性,那麼如何才能阻止exe運行多個實例以減少資源佔用呢?這就需要用TFindProcess。
將MainL寫成:
Code:
_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生日"之類的。這時候就需要來構造一個窗口。
Code:
// ================= 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:
Code:
iScreen = new (ELeave) CWsScreenDevice(iWs);
然後可以用GetNearestFontInTwips通過字體名字獲得CFont:
Code:
_LIT(FONT_CH16, "CombinedChinesePlain16");
TFontSpec myFontSpec(FONT_CH16, 200);
iScreen->GetNearestFontInTwips(iFont, myFontSpec);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章