怎么引出com的那份虚函数的作用表文件呢

COM组件简介_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
评价文档:
喜欢此文档的还喜欢
COM组件简介
阅读已结束,如果下载本文需要使用
想免费下载本文?
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
你可能喜欢10193人阅读
&&在网上看到一个非常热的帖子,里面是这样的一个问题:
#include &iostream&
class Base
virtual void f() { cout && "Base::f" && }
virtual void g() { cout && "Base::g" && }
class Derive : public Base
typedef void(*Fun)(void);
void main()
Base *d = new D
Fun pFun = (Fun)*((int*)*(int*)(d)+0);
printf("&(Base::f): 0x%x /n", &(Base::f));
printf("&(Base::g):0x%x /n", &(Base::g));
printf("pFun: 0x%x /n", pFun);
&& & 在打印的时候发现pFun的地址和 &(Base::f)的地址竟然不一样太奇怪了?经过一番深入研究,终于把这个问题弄明白了。下面就来一步步进行剖析。
&& & &根据VC的虚函数的布局机制,上述的布局如下:
&& & & &然后我们再细细的分析第一种方式:
&& & & & Fun pFun = (Fun)*((int*)*(int*)(d)+0);
d是一个类对象的地址。而在32位机上指针的大小是4字节,因此*(int*)(&d)取得的是vfptr,即虚表的地址。从而*((int*)*(int*)(&d)+0)是虚表的第1项,也就是Base::f()的地址。事实上我们得到了验证,程序运行结果如下:
这说明虚表的第一项确实是虚函数的地址,上面的VC虚函数的布局也确实木有问题。
&& & &但是,接下来就引发了一个问题,为什么&(Base::F)和PFun的值会不一样呢?既然PFun的值是虚函数f的地址,那&(Base::f)又是什么呢?带着这个问题,我们进行了反汇编。
printf("&(Base::f): 0x%x /n", &(Base::f));
&& & &mov & & & & edi,dword ptr [__imp__printf (4020D4h)]&
&& &0040106E &push & & & &offset Base::`vcall'{0}' (4013A0h)&
&& & &push & & & &offset string "&(Base::f): 0x%x /n" (40214Ch)&
&& & &call & & & &edi &
printf("&(Base::g): 0x%x /n", &(Base::g));
&& &0040107A &push & & & &offset Base::`vcall'{4}' (4013B0h)&
&& &0040107F &push & & & &offset string "&(Base::g): 0x%x /n" (402160h)&
&& & &call & & & &edi &
那么从上面我们可以清楚的看到:
&& &&Base::f 对应于Base::`vcall'{0}' (4013A0h)&
&& & Base::g对应于Base::`vcall'{4}' (4013B0h)
那么Base::`vcall'{0}'和Base::`vcall'{4}'到底是什么呢,继续进行反汇编分析
Base::`vcall'{0}':
&& & &mov & & & & eax,dword ptr [ecx]&
&& & &jmp & & & & dword ptr [eax]&&
&& & &......
Base::`vcall'{4}':
&& & &mov & & & & eax,dword ptr [ecx]&
&& & &jmp & & & & dword ptr [eax+4]&
&& & 第一句中, 由于ecx是this指针, 而在VC中一般虚表指针是类的第一个成员, 所以它是把vfptr, 也就是虚表的地址存到了eax中. 第二句
相当于取了虚表的某一项。对于Base::f跳转到Base::`vcall'{0}',取了虚表的第1项;对于Base::g跳转到Base::`vcall'{4}',取了虚表第2项。由此都能够正确的获得虚函数的地址。
&& & &由此我们可以看出,vc对此的解决方法是由编译器加入了一系列的内部函数"vcall". 一个类中的每个虚函数都有一个唯一与之对应的vcall函数,通过特定的vcall函数跳转到虚函数表中特定的表项。
&& & 更深一步的进行讨论,考虑多态的情况,将代码改写如下:
#include &iostream&
class Base
virtual void f() { cout && "Base::f" && }
virtual void g() { cout && "Base::g" && }
class Derive : public Base{
virtual void f() { cout && "Derive::f" && }
virtual void g() { cout && "Derive::g" && }
typedef void(*Fun)(void);
void main()
Base *d = new D
Fun pFun = (Fun)*((int*)*(int*)(d)+0);
printf("&(Base::f): 0x%x /n", &(Base::f));
printf("&(Base::g): 0x%x /n", &(Base::g));
printf("&(Derive::f): 0x%x /n", &(Derive::f));
printf("&(Derive::g): 0x%x /n", &(Derive::g));
printf("pFun: 0x%x /n", pFun);
打印的时候表现出来了多态的性质:
分析可知原因如下:
&& & &这是因为类Derive的虚函数表的各项对应的值进行了改写(rewritting),原来指向Based::f()的地址变成了指向Derive::f(),原来指向Based::g()的地址现在编变成了指向Derive::g()。
反汇编代码如下:
printf("&(Derive::f): 0x%x /n", &(Derive::f));
&& & &push & & & &offset Base::`vcall'{0}' (4013B0h)&
&& &0040108B &push & & & &offset string "&(Derive::f): 0x%x /n" (40217Ch)&
&& & &call & & & &esi &
printf("&(Derive::g): 0x%x /n", &(Derive::g));
&& & &push & & & &offset Base::`vcall'{4}' (4013C0h)&
&& & &push & & & &offset string "&(Derive::g): 0x%x /n" (402194h)&
&& &0040109C &call & & & &esi&
&& & &因此虽然此时Derive::f依然对应Base::`vcall'{0}',而&Derive::g依然对应Base::`vcall'{4}',但是由于每个类有一个虚函数表,因此跳转到的虚表的位置也发生了改变,同时因为进行了改写,虚表中的每个slot项的值也不一样。
稍微总结一下:
在VC中有两种方法调用虚函数,一种是通过虚表,另外一种是通过vcall thunk的方式
通过虚表的方式:
&&&&&& base *d = new D
&&&&& &d-&f();
&&&&&&&&&&&&& &004115FA& mov&&&&&&&& eax,dword ptr [d] &&&&&&&&&&&&& 004115FD& mov&&&&&&&& edx,dword ptr [eax]&&&&&&&&&&&&&&&004115FF& mov&&&&&&&& esi,esp&&&&&&&&&&&&&&&& mov&&&&&&&& ecx,dword ptr [d] &&&&&&&&&&&&& & mov&&&&&&&& eax,dword ptr [edx] &&&&&&&&&&&&& & call&&&&&&& eax& &&&&&&&&&&&&& & cmp&&&&&&&& esi,esp &&&&&&&&&&&&& 0041160A& call&&&&&&& @ILT+470(__RTC_CheckEsp) (4111DBh)
&这种方式的应用环境是通过类对象的指针或引用来调用虚函数
通过vcall thunk的方式:
&&&&&& typedef void (Base::* func1)( void );
&&&&&&&base *d = new D
&&&&&& func1 pFun1 = &Base::f;
&&&&&& (d-&*pFun1)();
&&&&&&&&&&&&&& && mov&&&&&&&& dword ptr [pFun1],offset Base::`vcall'{0}' (4110C3h) &&&&&&&&&&&&&&& & mov&&&&&&&& esi,esp &&&&&&&&&&&&&&& & lea&&&&&&&&& ecx,[d] &&&&&&&&&&&&&&& & call&&&&&&&& &dword ptr [pFun1] &&&&&&&&&&&&&&& & cmp&&&&&&&& esi,esp &&&&&&&&&&&&&&& 004115BA& call&&&&&&& @ILT+460(__RTC_CheckEsp) (4111D1h)&
& 这种方式对应的应用环境是通过类成员函数的指针来调用虚函数
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:144146次
积分:2643
积分:2643
排名:第6490名
原创:98篇
评论:265条
(33)(2)(3)(1)(1)(1)(3)(5)(13)(4)(2)(2)(4)(5)(1)(1)(9)(6)(3)(1)(3)(2)当前位置:
利用 DirectShow 开发自己的 Filter
利用 DirectShow 开发自己的 Filter
发布日期: 15:03
浏览次数:9213次
标  签:图形
文章评分:5.0
操  作:
称号:未设置简介:...
文章概要:
学习directshow已经有几天了,下面将自己的学习心得写下来,希望对其他的人有帮助。 Filter实质是个COM组件,所以学习开发Filter之前你应该对com的知识有点了解。Com组件的实质是一个实现了纯虚指针接口的C++对象。 关于com的东西,这里不多讲。
学习directshow已经有几天了,下面将自己的学习心得写下来,希望对其他的人有帮助。 Filter实质是个COM组件,所以学习开发Filter之前你应该对com的知识有点了解。Com组件的实质是一个实现了纯虚指针接口的C++对象。关于com的东西,这里不多讲。
一、给vc配置DirectShow的开发环境
无论开发Filter还是开发Dshow的应用程序都要配置一下开发环境的,其实就是包含一下dshow用到的头文件和动态库。 选择Tools菜单下面的Options。在弹出的Option对话框配置如下:
图1 添加头文件
选择动态库文件添加到工程中
图2 添加动态库
二、创建工程以及Filter的入口函数
创建工程:
一般情况下,创建Filter使用一个普通的Win32 DLL项目。而且,一般Filter项目不使用MFC。这时,应用程序通过CoCreateInstance函数Filter实例; Filter与应用程序在二进制级别的协作。另外一种方法,也可以在MFC的应用程序项目中创建Filter。
在vc里新建一个工程,选择win32动态库,如下图
这样生成了一个简单的DLL,只有一个Dllmain入口函数。下面我要给这个filter添加入口函数了。 Filter是个基于DLL的com组件,所以一般的Filter都要实现下面几个入口函数:
DllGetClassObject
DllCanUnloadNow
DllRegisterServer
DllUnregisterServer
首先定义导出函数:
要导出这些函数有两种方法,一是在定义函数时使用导出关键字_declspec(dllexport),另外一种方法是在创建DLL文件时使用模块定义文件.Def。使用导出函数关键字_declspec(dllexport)创建MyDll.dll就是在 .h文件中定义定义函数如下:
extern "C" _declspec(dllexport)BOOL DllRegisterS 等等
为了用.def文件创建DLL,往该工程中加入一个文本文件,命名为MyDll.def,再在该文件中加入如下代码:
LIBRARY MyFilter.ax
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
其中LIBRARY语句说明该def文件是属于相应DLL的,EXPORTS语句下列出要导出的函数名称。我们可以在.def文件中的导出函数后加@n,如Max@1,Min@2,表示要导出的函数顺序号,在进行显式连时可以用到它。该DLL编译成功后,打开工程中的Debug目录,同样也会看到MyDll.dll和MyDll.lib文件。
然后要定义这些函数的实现了,其实这些工作dshow的基类里都已经替我们做好了,我们所要做的就拿来用就是了,最重要的三个函数的实现一般如下 
STDAPI DllRegisterServer()
return AMovieDllRegisterServer2(TRUE);
STDAPI DllUnregisterServer()
return AMovieDllRegisterServer2(FALSE);
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY DllMain(HANDLE hModule, DWORD
LPVOID lpReserved)
return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved);
其中DllEntryPoint 是在C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\dllentry.cpp定义的,如果感兴趣我们可以去看看它的定义。 AMovieDllRegisterServer2函数是在下面 C:\DX90SDK\Samples\C++\DirectShow\BaseClasses\dllsetup.cpp这个文件定义的,具体实现可以自己看看。
到了这里你恐怕要做点工作,还是要设置一下你的项目环境,否则恐怕你编译是通不过的,因为你用到了基类的一些东西,所以你要将你的dshow基类的定义和库文件包含进来。首先包含:
#include Streams.h
其次在Project –Setting菜单下配置自己的Filter输出的名字和连接的lib文件
其中library modules里的包含的动态库如下
c:\DX90SDK\Samples\C++\DirectShow\BaseClasses\debug\strmbasd.lib msvcrtd.lib quartz.lib vfw32.lib winmm.lib kernel32.lib advapi32.lib version.lib largeint.lib user32.lib gdi32.lib comctl32.lib ole32.lib olepro32.lib oleaut32.lib uuid.lib
此时你编译一下,好像还是通不过,它提示有一个全局的用于实现COM接口的变量没有定义,不着急,下面我们就开始实现Filter的com接口。
三、如何实现Filter 的类厂对象
我们知道一个Filter是一个com组件,所以它com特性的实现其实在其基类中实现的,比如IUnknown接口,我们直接从基类派生出我们的Filter后,它就支持com接口了,它就是一个com组件了。
所有的com组件为了实现二进制的封装,所以连创建的接口都封装了,因此每个com对象都有个类对象(也叫类厂对象,本身也是com对象,用来创建com组件)来创建com组件。
下面温习一下com组件的创建过程,其中涉及到几个函数:
1.当客户端要创建一个com组件时,它通过底层的COM API函数 CoGetClassObject()使用SCM的服务,这个函数请SCM把一个指针绑定到客户端请求的com组件的类对象上,其实在CoGetClassObject()里它装载了该DLL的库,通过该dll的导出函数DllGetClassObject();DllGetClassObject根据客户端提供的com组件CLASSID,返回该com组件类对象的指针。下面com组件的创建和SCM无关了。
2.客户端利用组件的类对象(类厂对象)的IClassFactory::CreateInstance方法创建com组件。
Filter在这里使用了一个类厂模板类来当作Filter的类厂对象。下面看看类厂在DShow是怎么工作的。  
类厂对象也是一个com组件。本来DllGetClassObject是应该由我们自己完成一个函数,在directshow基类里已经完成了,我们不用管它了。它的功能就是来寻找这个DLL中的类厂对象,看是否有符合客户端请求的类厂对象。
DLL里声明了一个全局的类厂模板数组,当DllGetClassObject请求类厂对象的时候,它就搜索这个数组,看是否有和CLSID匹配的类厂对象。当它找到一个匹配的CLSID,它就创建一个类厂对象,然后讲类厂指针返回给CoGetClassObject,然后客户端可以根据返回去的类厂指针,调用 IClassFactory::CreateInstance方法创建组件,类厂就根据数组里定义的方法创建com组件。
factory template包含下列变量:
const WCHAR * m_N // Name
const CLSID * m_ClsID; // CLSID
LPFNNewCOMObject m_lpfnN // Function to create an instance of the component
LPFNInitRoutine m_lpfnI // Initialization function (optional)
const AMOVIESETUP_FILTER * m_pAMovieSetup_F // Set-up information (for filters)
其中的两个函数指针m_lpfnNew and m_lpfnInit使用下面的定义:
typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);
你可以参照如下的方式定义你的类厂对象:
CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
CMyFilter *pFilter = new CMyFilter(NAME("my Filter"), pUnk, pHr);
if (pFilter== NULL)
*pHr = E_OUTOFMEMORY;
你可以声明自己的类厂数组如下:
CFactoryTemplate g_Templates[1] =
L"my filter",
&CLSID_MYFilter,
CMyFilter::CreateInstance,
// Method to create an instance of MyComponent
// Initialization function
&sudInfTee
// Set-up information (for filters)
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
如果在这个com组件中你要支持多个filter,你可以在这个数组中继续添加就是了。
四、如何实现自己的 Filter
在这里就要讲如何创建自己的Filter了,下面我们以写一个CTransformFilter为例:
1、选择一个基类,声明自己的类。
创建filter很简单,你只要根据自己的需要选择不同的基类Filter派生出自己的Filter,它就已经支持com特性了。
从逻辑上考虑,在写Filter之前,选择一个合适的Filter基类是至关重要的。为此,你必须对几个Filter的基类有相当的了解。在实际应用中,Filter的基类并不总是选择CBaseFilter的。相反,因为我们绝大部分写的都是中间的传输Filter(Transform Filter),所以基类选择CTransformFilter和CTransInPlaceFilter的居多。如果我们写的是源Filter,我们可以选择CSource作为基类;如果是Renderer Filter,可以选择CBaseRenderer或CBaseVideoRenderer等。
总之,选择好Filter的基类是很重要的。当然,选择Filter的基类也是很灵活的,没有绝对的标准。能够通过CTransformFilter实现的Filter当然也能从CBaseFilter一步一步实现。
下面笔者就从本人的实际经验出发,对Filter基类的选择提出几点建议供大家参考。首先,你必须明确这个Filter要完成什么样的功能,即要对Filter项目进行需求分析。请尽量保持Filter实现的功能的单一性。如果必要的话,你可以将需求分解,由两个(或者更多的)功能单一的Filter去实现总的功能需求。
其次,你应该明确这个Filter大致在整个Filter Graph的位置,这个Filter的输入是什么数据,输出是什么数据,有几个输入Pin、几个输出Pin等等。你可以画出这个Filter的草图。弄清这一点十分重要,这将直接决定你使用哪种“模型”的Filter。比如,如果Filter仅有一个输入Pin和一个输出Pin,而且一进一处的媒体类型相同,则一般采用CTransInPlaceFilter作为Filter的基类;如果媒体类型不一样,则一般选择CTransformFilter作为基类。
再者,考虑一些数据传输、处理的特殊性要求。比如Filter的输入和输出的Sample并不是一一对应的,这就一般要在输入Pin上进行数据的缓存,而在输出Pin上使用专门的线程进行数据处理。这种情况下,Filter的基类选择CSource为宜(虽然这个Filter并不是源Filter)。当Filter的基类选定了之后,Pin的基类也就相应选定了。接下去,就是Filter和Pin上的代码实现了。有一点需要注意的是,从软件设计的角度上来说,应该将你的逻辑类代码同Filter的代码分开。下面,我们一起来看一下输入Pin的实现。你需要实现基类所有的纯虚函数,比如CheckMediaType等。在CheckMediaType内,你可以对媒体类型进行检验,看是否是你期望的那种。因为大部分Filter采用的是推模式传输数据,所以在输入Pin上一般都实现了Receive方法。有的基类里面已经实现了Receive,而在Filter类上留一个纯虚函数供用户重载进行数据处理。这种情况下一般是无需重载Receive方法的,除非基类的实现不符合你的实际要求。而如果你重载了Receive方法,一般会同时重载以下三个函数EndOfStream、BeginFlush和EndFlush。我们再来看一下输出Pin的实现。一般情况下,你要实现基类所有的纯虚函数,除了CheckMediaType进行媒体类型检查外,一般还有DecideBufferSize以决定Sample使用内存的大小,GetMediaType提供支持的媒体类型。
最后,我们看一下Filter类的实现。首先当然也要实现基类的所有纯虚函数。除此之外,Filter还要实现CreateInstance以提供COM的入口,实现NonDelegatingQueryInterface以暴露支持的接口。如果我们创建了自定义的输入、输出Pin,一般我们还要重载GetPinCount和GetPin两个函数。
这里我主要为了举例,所以简单写的filter没有Pin接口,但在我的demo里的Filter,却是有个out pin和一个input pin。我的Filter类的定义如下:
class CMyFilter :
public CCritSec, public CBaseFilter
CMyFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *hr);
virtual ~CMyFilter();
static CUnknown * WINAPI CreateInstance(LPUNKNOWN pUnk, HRESULT *phr);
CBasePin *GetPin(int n);
int GetPinCount();
注:因为基类是一个纯虚的基类,所以在你的filter一定要派生一个其中的纯虚函数,否则编译器会提示你的派生类也是一个纯虚类,你在创建这个com组件对象的时候,纯虚类是没法创建对象的。
2、给自己的Filter生成一个CLSID
你可以用Guidgen or Uuidgen给自己的Filter生成一个128位的ID号,然后利用DEFINE_GUID宏在Filter的头文件声明该Filter的CLSID;
[myFilter.h]
// {-02AA-415f-890F-76D94C85AAF1}
DEFINE_GUID(CLSID_MYFilter,
0x, 0x2aa, 0x415f, 0x89, 0xf, 0x76, 0xd9, 0x4c, 0x85, 0xaa, 0xf1);
这个CLSID_MYFilter在类厂数组用到,在注册Filter时也要用到。
3、CMyFilter类的简单实现
这个类纯粹为了演示用,所以特别简单,你可以参考我的demo,那个filter写的功能比较全。
CMyFilter::CMyFilter(TCHAR *pName,LPUNKNOWN pUnk,HRESULT *hr)
:CBaseFilter(NAME("my filter"), pUnk, this, CLSID_MYFilter)
CMyFilter::~CMyFilter()
// Public method that returns a new instance.
CUnknown * WINAPI CMyFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr)
CMyFilter *pFilter = new CMyFilter(NAME("my Filter"), pUnk, pHr);
if (pFilter== NULL)
*pHr = E_OUTOFMEMORY;
CBasePin * CMyFilter::GetPin(int n)
return NULL;
int CMyFilter::GetPinCount()
这样基本上就实现了一个filter,但是这个filter没有与之相联系的PIN,但是实现Filter的基本过程就时这样了,至于逻辑上的东西,比如Filter和pin如何连接,数据流是如何流动的,你都要去看看sdk了,按照上面的步骤你就可以写一个Filter的框架出来。
下面我们总结一下写一个Filter至少需要那些东西。
1、Filter的实现类
在这里就是CMyFilter类,在这个类里你可以实现自己的逻辑上的功能,包括定义你的filter的特性,给你的filter配备pin接口等。
2 com组件的引出函数,五个全局函数:
DllMain //dll的入口函数
DllGetClassObject //获得com组件的类厂对象
DllCanUnloadNow //com组件是否可以卸载
DllRegisterServer //注册com组件
DllUnregisterServer //卸载com组件
其中DllGetClassObject 已经由基类完成,你自己只要完成三个函数即可,
DllMain,DllRegisterServer,DllUnregisterServer。
3、com组件的类厂对象
类厂对象是用来生成Filter对象的,用的模板类定义了一个全局的模板类对象数组,一般格式如下:
CFactoryTemplate g_Templates[1] =
L"my filter",
&CLSID_MYFilter,
CMyFilter::CreateInstance,
// Method to create an instance of MyComponent
// Initialization function
&sudInfTee
// Set-up information (for filters)
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
4、关于你自己定义的Filter以及Pin的信息
这些是一个全局的结构变量,用于描述你的Filter和你定义的pin,在注册Filter的时候会用到,如下:
AMOVIESETUP_FILTER 描述一个Filter
AMOVIESETUP_PIN 描述pin
AMOVIESETUP_MEDIATYPE 描述数据类型
下面的代码描述了一个Filter带有一个output PIN:
static const WCHAR g_wszName[] = L"Some Filter";
AMOVIESETUP_MEDIATYPE sudMediaTypes[] = {
{ &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24 },
{ &MEDIATYPE_Video, &MEDIASUBTYPE_RGB32 },
AMOVIESETUP_PIN sudOutputPin = {
// Obsolete, not used.
// Is this pin rendered?
// Is it an output pin?
// Can the filter create zero instances?
// Does the filter create multiple instances?
&GUID_NULL,
// Obsolete.
// Obsolete.
// Number of media types.
sudMediaTypes
// Pointer to media types.
AMOVIESETUP_FILTER sudFilterReg = {
&CLSID_SomeFilter,
// Filter CLSID.
g_wszName,
// Filter name.
MERIT_NORMAL,
// Number of pin types.
&sudOutputPin
// Pointer to pin information.
最后如果你还是调试通不过,看看你是否包含了下面的头文件:
#include & streams.h&
#include & initguid.h&
#include & tchar.h&
#include & stdio.h&
智慧的鱼 aoosang
非常感谢。拿到DirectShow不知从何入手,这个是非常好的入门教程,一步一步的引导,非常实用,谢谢了!
13:11 发表
最多还可以输入100字
【VIP年会员制套餐】
【C/C++软件工程师实战能力集训大纲】
VC知识库发布了C/C++业界的“本草纲目”
【牛人都在千人一号群! 加群三步走!!!】
第一步:请必须加VC知识库QQ: 为好友;
第二步:请必须关注本站微博:
第三步:申请加入群:.(必须将关注微博截屏发到QQ方可通过!)
【最新2013:】
全部100% VC++源码提供: E-Form++全新大型SCADA & HMI解决方案源码、CAD解决方案源码、Gis解决方案源码 、电力石油化工仿真与图形建模解决方案源码、大量其他高级制图VC++源码下载!
【 新视频发布】
o o o o o o o o o
结合如何用window API实现matlab屏幕抓取...
在VC环境中除了我们所常用的Dialog、Menu和Bitmap等标准资源类型之外,它还支持自定义资源类型(Custom Resource),我们自定义的资源类型能做些什么呢?呵呵,用处多多。...
本文介绍了套接字编程的基本知识。...com_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
文档贡献者
评价文档:
&&¥2.00
喜欢此文档的还喜欢
把文档贴到Blog、BBS或个人站等:
普通尺寸(450*500pix)
较大尺寸(630*500pix)
大小:12.69KB
登录百度文库,专享文档复制特权,财富值每天免费拿!
你可能喜欢

我要回帖

更多关于 虚函数的作用 的文章

 

随机推荐