您的位置:首页 > 编程语言

关于exe形式编程的一点心得,希望对大家有所帮助

2010-04-15 17:34 513 查看
其实我也只是一个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。

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);


6. 如何在手机上运行exe程序


以有几种方法,一种是通过文件管理器直接执行这个exe,这种方法通常在开发阶段使用,因为要让用户这么操作,用户会觉得很不方便;第二种是通过app来
调用exe;第三种是利用mdl在开机阶段就调用exe。后两种的方法是类似的,都是通过CApaCommandLine来实现,具体的代码可以参考:

http://discussion.forum.nokia.com/f...ead.php?t=66477

7. 如何终止exe程序

开发阶段,我们可以借助TaskSpy等工具,当然也可以通过我们的App来终止exe进程。具体的方法如下:首先要找到符合名称得进程,然后将其kill

Code:
_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;

}

}


8. 新的调试方法


在手机上调试
exe程序还是一个比较麻烦的事情,前面介绍了console,但是有时候不方便用console,这时候就要换一种方法来记录信息,比较简单的就是用文
件记录,这个方法的缺点是不能实时察看,另外一个缺点就是要消耗较多的时间,别看这点时间,有时候就会掩盖一些问题。我就遇到过,不过可以通过其他的办法
来解决。

这里给出一段我用的代码供大家参考。因为是调试代码,所以写的并不是很完善,要求字符串不能含中文:

Code:
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中添加一行字符串。你可以在手机上用记事本直接打开察看。

前面提到的时间占用问题如何解决呢?

也很简单,在你对时间有要求的地方定义一片缓存,把信息先放到缓存里,等到过了这个地方,再将缓存里的数据写入文件。用这个方法还可以写入数字等内容。

代码类似于:

Code:
TBuf<100> tempstr;

......

tempstr.Append(myinfo);

tempstr.Append(_L("/r/n"));  // 换行

......

tempstr.AppendFormat(_L("a=%d, b=%d/r/n"), a, b);

......

......

WriteTestInfoL(tempstr);


通过这个方法调试还是帮我解决了不少问题的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐