转载请标明是引用于 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位的,根据实际情况)
然后把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行.
二、测试
以下是目标文件:
效果:
三、插件的加载过程
补充:附后了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工程源码解读
还有小部分功能在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);
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函数再响应完成工具栏中系统菜单,进度条,播放控件和声音控制的创建.
8. MSG_INIT消息响应
主要是在interface.c中的Proc 函数中.