TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家提出意见,一起讨论!

 

TCP的码源框架介绍可以参考我的文章: TCPMP之旅(一) TCPMP整体软体框架

最近因为在WINCE播放视频文件,画面很不流畅, 于是想弄个TCPMP来试下效果. 没想到这个X86 平台上的TCPMP网络上还真的是找不到, 只能找到ARM平台的, 一打开就提示不是有效的WINCE应用程序.  于是便自己编译. 说到这个编译过程那可也是一波三折, 具体不再多说啦, 下面我列出所有我在编译过程中的遇到的错误.

 

一、编译

1、 首先是是编译common工程

报以下错误

1>'yasm' 不是内部或外部命令,也不是可运行的程序


解决方法:

从 http://yasm.tortall.net/Download.html 下载得到yasm-1.2.0-win32.exe文件 (我的电脑是32位的,根据实际情况)

TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)_第1张图片

然后把yasm-1.2.0-win32.exe 名字改为 yasm.exe,并把它复制到你VS2005的安装路径下,

eg:

 

之后再编译可能报以下错误:

1>yasm: FATAL: Could not open input file


原因可能有两个:

(1)是不是有中文路径

(2)路径中是否有空格。

 

2、 编译其它工程

2、1  在其它工程出现链接时提示找不到标识符时,在链接lib中加入我们刚刚编译的common.lib。(当然也得加入链接目录)

  

 

2、2 在编译player时会报错:

1>正在链接...
1>main.obj : error LNK2001: 无法解析的外部符号 __tcscpy_s
1>../debug/player.exe : fatal error LNK1120: 1 个无法解析的外部命令


解决方法是将main.c中的  第122行改为 123行.

TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)_第2张图片

 

二、测试

  以下是目标文件:

TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)_第3张图片

效果:

TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)_第4张图片

三、插件的加载过程

补充:附后了interface.plg插件外的插件加载方法。

加载方法同样通过DLLRegister接口。加载插件的过程是在Context_Init函数中的Plugins_Init函数完成的。

void Plugins_Init()
{
	tchar_t Path[MAXPATH];
	array List = {NULL};
	nodedef Def;
	int* i;
	// 查找所有的插件,放到List里
	FindPlugins(&List);

	for (i=ARRAYBEGIN(List,int);i!=ARRAYEND(List,int);++i)
	{
		int Class = *i;
		HKEY Key;
		tchar_t Base[256];

		NodeBase(Class,Base,TSIZEOF(Base));
		if (RegOpenKeyEx(HKEY_ROOT, Base, 0, KEY_READ, &Key) == ERROR_SUCCESS)
		{
			if (LoadValue(Key,NODE_MODULE_PATH,Path,sizeof(Path),TYPE_STRING) && NodeFindModule(Path,0))
			{
				memset(&Def,0,sizeof(Def));
				Def.Class = Class;
				Def.Flags = 0;
				Def.ParentClass = 0;
				Def.Priority = PRI_DEFAULT;
				LoadValue(Key,NODE_PARENT,&Def.ParentClass,sizeof(int),TYPE_INT);
				LoadValue(Key,NODE_PRIORITY,&Def.Priority,sizeof(int),TYPE_INT);
				LoadValue(Key,NODE_FLAGS,&Def.Flags,sizeof(int),TYPE_INT);
				LoadValueString(Key,Class,NODE_NAME);
				LoadValueString(Key,Class,NODE_CONTENTTYPE);
				LoadValueString(Key,Class,NODE_EXTS);
				LoadValueString(Key,Class,NODE_PROBE);
				NodeLoadClass(&Def,Path,0);// 把插件节点注册

			}
			RegCloseKey(Key);
		}
	}

	ArrayClear(&List);
}


 

(1) Plugins_Init先通过FindPlugins(&List);查找exe目录中的所有插件,找到即调用AddModule将

            此节点存到Context  p->NodeModule(用于管理外部插件模块数组)中去。

            并根据注册表判断是否要加载此插件。

(2) 从common.dll目录下增加插件,从注册表中增加插件。

static void FindPlugins(array* List)
{
	tchar_t* s;
	tchar_t Name[64];
	tchar_t Path[MAXPATH];
	tchar_t Path2[MAXPATH];
	DWORD RegSize,RegType,NameSize,Disp;
	HKEY Key,KeyStamp;
	int No,Class;

	NodeBase(0,Path,TSIZEOF(Path));
	tcscat_s(Path,TSIZEOF(Path),T("\\DLLStamp"));
	if (RegCreateKeyEx(HKEY_ROOT, Path, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &KeyStamp, &Disp) != ERROR_SUCCESS)
		KeyStamp = NULL;

	// 在exe目录中增加插件
	GetModulePath(Path,NULL);
	FindFiles(Path,T("*.plg"),AddModule,&KeyStamp);

	// 可能exe是在和common.dll 不同的目录,所以去common.dll目录下查找其他插件
	GetModulePath(Path2,T("common.dll"));
	if (tcsicmp(Path2,Path)!=0)
		FindFiles(Path2,T("*.plg"),AddModule,&KeyStamp);

	// additional plugins
	NodeBase(0,Path,TSIZEOF(Path));
	tcscat_s(Path,TSIZEOF(Path),T(".Plugins"));
	if (RegOpenKeyEx(HKEY_ROOT, Path, 0, KEY_READ, &Key) == ERROR_SUCCESS)
	{
		for (No=0;;++No)
		{
			NameSize = TSIZEOF(Name);
			RegSize = sizeof(Path);
			if (RegEnumValue(Key,No,Name,&NameSize,NULL,&RegType,(LPBYTE)Path,&RegSize)!=ERROR_SUCCESS)
				break;
			if (RegType == REG_SZ)
				AddModule(Path,&KeyStamp);
		}
		RegCloseKey(Key);
	}

	if (KeyStamp)
	{
		// delete unused stamps
		ArrayClear(List);
		RegSize = TSIZEOF(Path);
		for (No=0;RegEnumValue(KeyStamp,No,Path,&RegSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS;++No)
		{
			if (!NodeFindModule(Path,0))
				ArrayAppend(List,Path,(tcslen(Path)+1)*sizeof(tchar_t),256);
			RegSize = TSIZEOF(Path);
		}

		for (s=ARRAYBEGIN(*List,tchar_t);s!=ARRAYEND(*List,tchar_t);s+=tcslen(s)+1)
			RegDeleteValue(KeyStamp,s);

		RegCloseKey(KeyStamp);
	}

	ArrayClear(List);
	NodeBase(0,Path,TSIZEOF(Path));
	if (RegOpenKeyEx(HKEY_ROOT, Path, 0, KEY_READ, &Key) == ERROR_SUCCESS)
	{
		RegSize = TSIZEOF(Name);
		for (No=0;RegEnumKeyEx(Key,No,Name,&RegSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS;++No)
		{
			if (tcslen(Name)==4)
			{
				Class = StringToFourCC(Name,0);
				ArrayAppend(List,&Class,sizeof(Class),128);
			}
			RegSize = TSIZEOF(Name);
		}
		RegCloseKey(Key);
	}
}


 

(3) 把插件节点注册。即把节点加入到Context p->NodeClass中

        

void NodeLoadClass(const nodedef* Def,const tchar_t* Module,int ModuleId)
{
	context* p = Context();
	int ModuleNo = FindModule(p,Module,ModuleId);
	if (ModuleNo>=0)
		Register(p,Def,0,ModuleNo); // 注册节点
}

 

四、Interface工程源码解读

    Interface插件功能模块包含在interface文件夹下,其中的interface.c实现图形界面的大部分功能,

还有小部分功能在win_win32.c文件中实现.程序运行过程基本有下面几个步骤:

  1.  多媒体播放器运行程序为player.exe,工程中main.c文件的WinMain函数为程序开始的第一步。

它先加载e:\Au1380_forVolo\tcpmp_from_xianshengWin32\debug\ilayer.exe  ,假如它不存在就加载 interface.plg。

然后转到interface.plg的main函数中。

#if !defined(TARGET_WINCE) && defined(UNICODE)
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hParent,LPSTR CmdA,int CmdShow)
{
	WCHAR Cmd[2048];
	mbstowcs(Cmd,CmdA,sizeof(Cmd)/sizeof(WCHAR)); //!!!
#else
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hParent,TCHAR* Cmd,int CmdShow)
{
#endif

#ifndef NDEBUG
//	DLLTest(); // just to help debugging plugins. comment out if not needed
	Context();
#endif

#if defined(TARGET_WINCE) && defined(ARM)
	if (ProgramId == 2)
	{
		OSVERSIONINFO Ver;
		Ver.dwOSVersionInfoSize = sizeof(Ver);
		GetVersionEx(&Ver);
		if (Ver.dwMajorVersion*100 + Ver.dwMinorVersion >= 421)
		{
			// old shell menu not supported after WM2003SE
			MessageBox(NULL,T("This ARM_CE2 version of the player is not compatible with this device. Please install ARM_CE3 version."),NULL,MB_OK|MB_ICONERROR); 
			return 1; 
		}
	}
#endif

#ifdef NDEBUG
	if (!FindRunning(Cmd))
	{
		HANDLE Handle = CreateMutex(NULL,FALSE,ProgramName);
		if (GetLastError() != ERROR_ALREADY_EXISTS)
#endif
		{
#ifndef NO_PLUGINS
			HMODULE Module;
			SetCursor(LoadCursor(NULL, IDC_WAIT));
			Module = Load(T("interface.plg"));
			if (Module)
			{
				void (*Main)(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine);
				*(FARPROC*)&Main = GetProcAddress(Module,TWIN("Main"));
				if (!Main)
					*(FARPROC*)&Main = GetProcAddress(Module,TWIN("_Main@16"));
				if (Main)
					Main(ProgramName,ProgramVersion,ProgramId,Cmd);
				FreeLibrary(Module);
			}
#else
			Main(ProgramName,ProgramVersion,ProgramId,Cmd);
#endif
#ifdef NDEBUG
			CloseHandle(Handle);
#endif
			SetCursor(LoadCursor(NULL, IDC_ARROW));
		}
#ifdef NDEBUG
	}
#endif
	return 0;
}


 

   2.  common工程是核心模块

是一个开放的集数据输入、转换、音/视频解码、信号输出等功能为一体的完整的多媒体播放框架。WinMain后调用 common.dll中的Contxt_Init函数,完成各个功能模块ID的注册工作.除Interface功能外,其他功能模块的注册基本在 Contxt_Init中完成.

DLLEXPORT void Main(const tchar_t* Name,const tchar_t* Version,int Id,const tchar_t* CmdLine)
{
	SAFE_BEGIN
	// 完成各个功能模块ID的注册工作.除Interface功能外,其他功能模块的注册基本在 Contxt_Init中完成.
	if (Context_Init(Name,Version,Id,CmdLine,NULL))
	{
		WaitEnd();
		// 在这里才把界面显示出来
		WinPopupClass(INTERFACE_ID,NULL);
		Context_Done();
	}
	SAFE_END
}


 

   3.   在调用完Context_Init后调用了WinPopupClass(INTERFACE_ID,NULL);
int WinPopupClass(int Class,win* Parent)
{
	int Result = 0;
	// 创建节点
	node* p = NodeCreate(Class);
	// 是WIN_CLASS类
	if (p && NodeIsClass(p->Class,WIN_CLASS))
	{
		// 创建窗口
		Result = ((win*)p)->Popup((win*)p,Parent);
		NodeDelete(p);
	}
	return Result;
}


NodeCreate完成创建节点功能,它是common.lib中的接口。此接口不仅被interface模块调用,也被很多模块调用。

node* NodeCreate(int ClassId)
{
	context* p = Context();
	// 从类中创建节点 
	return NodeCreateFromClass(p,FindClass(p,ClassId));
}

NodeCreateFromClass调用interface.plg中DLLRegister函数。

通过DLLRegister函数对INTREFACE_ID对象进行注册,对象注册完成创建和内存分配等功能.

====================补充   start  ==================================================

interface插件的注册过程( 其实与其它插件的注册过程一致,都是通过调用common.dll库完成的):

(1)NodeCreateFromClass中有LoadModule,LoadModule中有NodeLoadModule,

          它寻找对应于ID为INTREFACE_ID的插件对应的plg,从plg中找到DLLRegister接口,来注册自己。

      

void* NodeLoadModule(const tchar_t* Path,int* Id,void** AnyFunc,void** Db)
{
	HMODULE Module;
	int Error = 0;

#if !defined(TARGET_WINCE)
	int OldMode = SetErrorMode(SEM_NOOPENFILEERRORBOX| SEM_FAILCRITICALERRORS);
#endif

	Module = LoadLibrary(Path);
	if (!Module)
	{
		Error = GetLastError();
		if (Error == ERROR_NOT_ENOUGH_MEMORY || Error == ERROR_OUTOFMEMORY)
		{
			NodeHibernate();
			Module = LoadLibrary(Path);
			if (!Module)
				Error = GetLastError();
		}
	}

	Context()->LoadModule = Module;

#if !defined(TARGET_WINCE)
	SetErrorMode(OldMode);
#endif

	if (Module)
	{
		FARPROC Func = GetProcAddress(Module,TWIN("DLLRegister"));
		if (!Func)
			Func = GetProcAddress(Module,TWIN("_DLLRegister@4"));

		if (Func)
		{
			int Result;
			if (AnyFunc)
				*(FARPROC*)AnyFunc = Func; // set before calling DLLRegister
			// DLLRegister注册自己.
			Result = ((int(*)(int))Func)(CONTEXT_VERSION);
			if (Result != ERR_NONE)
			{
				Func = NULL;
				if (AnyFunc)
					*(FARPROC*)AnyFunc = NULL;
				if (Result == ERR_NOT_COMPATIBLE)
					PluginError(Path);
			}
		}

		if (!Func)
		{
			FreeLibrary(Module);
			Module = NULL;
		}
	}
	else
	if (Error == ERROR_NOT_ENOUGH_MEMORY || Error == ERROR_OUTOFMEMORY)
		ShowOutOfMemory();
	else
		PluginError(Path);

	return Module;
}


 

====================补充   end==================================================

 

static node* NodeCreateFromClass(context* p, nodeclass* Class)
{
	node **Empty = NULL;
	node **i;
	nodemodule* Module;
	node* Node;
	int Size;
	nodeclass *j;

	if (!Class || (Class->Def.Flags & CF_ABSTRACT))
		return NULL;

	Size = 0;
	for (j=Class;j;j=j->Parent)
		if (Size < (j->Def.Flags & CF_SIZE))
			Size = (j->Def.Flags & CF_SIZE);
	if (!Size)
		return NULL;

	LockEnter(p->NodeLock);

	if ((Class->Def.Flags & CF_GLOBAL) && 
		(Module = LoadModule(p,Class->ModuleNo))!=NULL &&
		(Node = NodeEnumObject(NULL,Class->Def.Class))!=NULL)
	{
		++Module->ObjectCount;
		LockLeave(p->NodeLock);
		return Node;
	}

	for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
		if (!*i)
		{
			Empty = i;
			break;
		}

	if (!Empty)
	{
		if (!ArrayAppend(&p->Node,NULL,sizeof(node**),256))
		{
			LockLeave(p->NodeLock);
			return NULL;
		}
		Empty = ARRAYEND(p->Node,node*)-1;
	}

	*Empty = Node = (node*) malloc(Size);
	if (Node)
	{
		memset(Node,0,Size);
		Node->Class = Class->Def.Class;
		// 创建 Node节点
		if (CallCreate(p,Node,Class,(Class->Def.Flags & CF_GLOBAL)!=0) != ERR_NONE)
		{
			for (i=ARRAYBEGIN(p->Node,node*);i!=ARRAYEND(p->Node,node*);++i)
				if (*i == Node)
					*i = NULL;
			free(Node);
			Node = NULL;
		}
		else
		{
#ifndef REGISTRY_GLOBAL
			if (Class->Def.Flags & CF_GLOBAL)
				NodeRegLoad(Node);
#endif
			Node->Set(Node,NODE_SETTINGSCHANGED,NULL,0); // should be after NodeRegLoad
		}
	}

	LockLeave(p->NodeLock);
	return Node;
}

上面的代码主要调用CallCreate函数.

 

4.  注册后interface.c中Create函数对INTREFACE_ID对象intface* p进行初始化,完成界面状态的最初设置.
WinPopupClass(INTERFACE_ID,NULL);

 

 5.主窗口创建与弹出,win_win32.c文件中WinPopupClass函数调用Popup函数,Popup实现窗口创建功能.

	// 创建窗口
		Result = ((win*)p)->Popup((win*)p,Parent);

 

 6.Popup函数中调用HandleMessage(p,&Msg),该函数为消息响应函数,Popup完成主窗口创建的后给HandleMessage函数发送WM_CREATE消息.

static int Popup(win* p,win* Parent)
{
	HWND Wnd;
	MSG Msg;
	int Style = WS_VISIBLE;
	int ExStyle = 0;
	int x,y;
	int Width,Height;
	int Priority;

	// we need higher priority in main window for better user interface responses
	// but open dialog and other parts can't have that, because of some buggy keyboard drivers...
	Priority = ThreadPriority(NULL,Parent?0:-1);

	p->Result = 0;
	p->Parent = Parent;

	p->BitmapNo = 0;
	p->AygShellTB = 0;

#if defined(TARGET_WINCE)
	if (AygShell && Parent)
		ExStyle |= WS_EX_CAPTIONOKBTN;

	if (Parent)
		Style |= WS_POPUP;

	{
		RECT r;
		GetWorkArea(p,&r);
		x = r.left;
		y = r.top;
		Width = r.right - r.left;
		Height = r.bottom - r.top;
	}
#else
	Style |= WS_OVERLAPPEDWINDOW;
	y = x = CW_USEDEFAULT;
	Width = WinUnitToPixelX(p,p->WinWidth);
	Height = WinUnitToPixelY(p,p->WinHeight);
#endif
	// 创建窗口
	Wnd = CreateWindowEx(ExStyle,WinClass.lpszClassName,LangStr(p->Node.Class,NODE_NAME),Style,x,y,Width,Height,
		Parent?Parent->Wnd:NULL,NULL,WinClass.hInstance,p);

	if (Wnd)
	{
		if (p->Parent)
		{
			p->Parent->Child = p;
			EnableWindow(p->Parent->Wnd,0);
		}
		else
			Main = p;

		while (p->Wnd && GetMessage(&Msg, NULL, 0, 0)) 
			HandleMessage(p,&Msg);// HandleMessage(p,&Msg),该函数为消息响应函数,Popup完成主窗口创建的后给HandleMessage函数发送WM_CREATE消息

		if (Main == p)
			Main = NULL;
	}
	
	ThreadPriority(NULL,Priority);
	return p->Result;
}


 

 7.   WM_CREATE消息响应

分两部分:

   win_win32.c中的CALLBACK Proc首先响应,完成窗口工具栏的创建;

   

static LRESULT CALLBACK Proc(HWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	int Result = 0;
	win* p = (win*)GetWindowLong(Wnd,GWL_USERDATA);

	switch (Msg)
	{
#if defined(TARGET_WINCE)
	case WM_HOTKEY:
		if (p->Parent && HIWORD(lParam)==VK_ESCAPE)
		{
			HWND Focus = GetFocus();
			wincontrol* i = (wincontrol*)GetWindowLong(Focus,GWL_USERDATA);
			if (FuncSHSendBackToFocusWindow && ((VALIDCONTROL(i) && i->Editor) || (!VALIDCONTROL(i) && i)))
				FuncSHSendBackToFocusWindow(Msg,wParam,lParam);
			else
			{
				p->HotKeyEscape = !p->HotKeyEscape;
				if (!p->HotKeyEscape)
					PostMessage(p->Wnd,WM_CLOSE,0,0);
			}
			return 0;
		}
		break;
#endif
	// 完成工具栏中系统菜单,进度条,播放控件和声音控制的创建
	case WM_CREATE:

		p = (win*)((CREATESTRUCT*)lParam)->lpCreateParams;
		p->Wnd = Wnd;
		p->Module = ((CREATESTRUCT*)lParam)->hInstance;
		p->Result = 1;
		p->Focus = NULL;
		p->Closed = 0;
		
		UpdateDPI(p);

#if defined(TARGET_WINCE)
		p->Activate = malloc(sizeof(SHACTIVATEINFO));
		if (p->Activate)
		{
			memset(p->Activate,0,sizeof(SHACTIVATEINFO));
			((SHACTIVATEINFO*)p->Activate)->cbSize = sizeof(SHACTIVATEINFO);
		}
#endif

		if (p->Proc)
			p->Proc(p,MSG_PREPARE,0,0,&Result);
		CreateToolBar(p);

		SetWindowLong(Wnd,GWL_USERDATA,(LONG)p); // only after CreateToolBar (WM_MOVE)

		if (p->Flags & WIN_DIALOG)
		{
			RECT r;
			GetClientRect(p->Wnd,&r);
			CreateWindow(DialogClass.lpszClassName,NULL,WS_CLIPCHILDREN|WS_CHILD|WS_VISIBLE,
				0,p->ToolBarHeight,r.right,r.bottom-p->ToolBarHeight,p->Wnd,NULL,DialogClass.hInstance,p);
		}

		if (p->Init)
			p->Init(p);

		if (!p->Focus)
			WinNext(p,0);
		break;


 

    然后interface.c中的bool_t Proc函数再响应完成工具栏中系统菜单,进度条,播放控件和声音控制的创建.

   TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)_第5张图片

8.  MSG_INIT消息响应

     主要是在interface.c中的Proc 函数中.

   TCPMP之旅(二) TCPMP 在win32下的编译过程+插件的加载过程(VS2005+XP)_第6张图片

       TCPMP包含的内容比较丰富,要研究透彻还需要花很多功夫,继续努力中...


 

你可能感兴趣的:(Module,XP,null,Path,interface,WinCE)