大家都知道COM接口,COM就是一个虚函数表指针,VB6虽然支持COM的thiscall调用,可必需通过引用,而如果一些COM接口没有给基于VB6的引用,那VB6就残了,比如IStream之类的,这次的ITaskbarList3也是一样。
我们先看看看一个COM接口在内存中的结构,这里我用VB6的方式写,大家理解起来会很简单。
如果我们声明一个变量,这个变量是一个COM对象,然后调用xx函数真正初始化了这个对象,COM指针就指向一个虚函数表,此时COM在Win32内存中是这样的,伪代码如下:
Type Com
Type IUnknown
QueryInterface + 0
AddRef + 4
Release + 8
End Type
Function1 + 12
Function2 + 16
Function3 + 20
···
End Type
恩,可以理解为COM指针就是一个表的内存区域,然后这个内存区域里面就是这样一张虚函数表,调用时,编译器根据索引*4取得需要调用的虚函数的地址,然后进行调用。
我们再追寻下一个COM对象的诞生过程:
声明COM对象变量(x86下这是一个4字节的指针)
调用**函数初始化这个变量,**函数进行一个new class操作,IUnknown引用+1,初始化class内的各种此对象需要内部变量之类的等等,这就是为什么call这个COM成员函数时需要压入COM指针,因为那就是一个伪class指针,记录了一些这个class需要使用的数据。
此时一个新的class数据被分配出来,建立虚函数表,把class的基地址写入4字节的程序内变量。
程序调用class的某个成员函数,根据虚函数表取到函数地址,压入class基地址,函数代码内从这个class提取相关变量等,执行代码。
我们再来看看一个COM函数的原形:
void test(int a,int b);
真正的原形是:
void test(this,int a,int b);
this即为class指针,这是对程序员不可见的。我们称此为thiscall,也就是stdcall的一个变种。(可以去看看cdecl、stdcall、thiscall的文章)
好了,理解了这些基本的东西,我们就可以动手了。
ITaskbarList3这个COM对象的声明在Windows 7 SDK的Shobjidl.h文件中,C style如下:
SetProgressValue为第10个函数,那函数地址就是(10-1)*4,为了更易懂,我们可以把一个COM函数的Vtbl列成VB6里面的Enum。
- typedef struct ITaskbarList3Vtbl
- {
- BEGIN_INTERFACE
- HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in REFIID riid,/* [annotation][iid_is][out] */
- __RPC__deref_out void **ppvObject);
- ULONG ( STDMETHODCALLTYPE *AddRef )(
- __RPC__in ITaskbarList3 * This);
- ULONG ( STDMETHODCALLTYPE *Release )(
- __RPC__in ITaskbarList3 * This);
- HRESULT ( STDMETHODCALLTYPE *HrInit )(
- __RPC__in ITaskbarList3 * This);
- HRESULT ( STDMETHODCALLTYPE *AddTab )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwnd);
- HRESULT ( STDMETHODCALLTYPE *DeleteTab )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwnd);
- HRESULT ( STDMETHODCALLTYPE *ActivateTab )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwnd);
- HRESULT ( STDMETHODCALLTYPE *SetActiveAlt )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwnd);
- HRESULT ( STDMETHODCALLTYPE *MarkFullscreenWindow )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwnd,/* [in] */ BOOL fFullscreen);
- HRESULT ( STDMETHODCALLTYPE *SetProgressValue )(
- __RPC__in ITaskbarList3 * This,/* [in] */ ULONGLONG ullCompleted,/* [in] */ ULONGLONG ullTotal);
- HRESULT ( STDMETHODCALLTYPE *SetProgressState )(
- __RPC__in ITaskbarList3 * This,/* [in] */ TBPFLAG tbpFlags);
- HRESULT ( STDMETHODCALLTYPE *RegisterTab )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwndTab,/* [in] */ __RPC__in HWND hwndMDI);
- HRESULT ( STDMETHODCALLTYPE *UnregisterTab )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwndTab);
- HRESULT ( STDMETHODCALLTYPE *SetTabOrder )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwndInsertBefore);
- HRESULT ( STDMETHODCALLTYPE *SetTabActive )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HWND hwndMDI,/* [in] */ DWORD dwReserved);
- HRESULT ( STDMETHODCALLTYPE *ThumbBarAddButtons )(
- __RPC__in ITaskbarList3 * This,/* [in] */ UINT cButtons,/* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton);
- HRESULT ( STDMETHODCALLTYPE *ThumbBarUpdateButtons )(
- __RPC__in ITaskbarList3 * This,/* [size_is][in] */ __RPC__in_ecount_full(cButtons) LPTHUMBBUTTON pButton);
- HRESULT ( STDMETHODCALLTYPE *ThumbBarSetImageList )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in_opt HIMAGELIST himl);
- HRESULT ( STDMETHODCALLTYPE *SetOverlayIcon )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in HICON hIcon,/* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszDescription);
- HRESULT ( STDMETHODCALLTYPE *SetThumbnailTooltip )(
- __RPC__in ITaskbarList3 * This,/* [string][unique][in] */ __RPC__in_opt_string LPCWSTR pszTip);
- HRESULT ( STDMETHODCALLTYPE *SetThumbnailClip )(
- __RPC__in ITaskbarList3 * This,/* [in] */ __RPC__in RECT *prcClip);
- END_INTERFACE
- } ITaskbarList3Vtbl;
添加一个Module1,复制代码:
再添加一个Form1,一个Command1,复制代码:
- Private Declare Function CallWindowProcW& Lib "user32" (ByVal lpPrevWndFunc As Long,ByVal hWnd As Long,ByVal Msg As Long,ByVal wParam As Long,ByVal lParam As Long)
- Private Declare Function LocalAlloc& Lib "kernel32" (ByVal f&,ByVal s&)
- Private Declare Function LocalFree& Lib "kernel32" (ByVal m&)
- Private Declare Sub PutMem1 Lib "msvbvm60" (ByVal Ptr As Long,ByVal NewVal As Byte)
- Private Declare Sub PutMem2 Lib "msvbvm60" (ByVal Ptr As Long,ByVal NewVal As Integer)
- Private Declare Sub PutMem4 Lib "msvbvm60" (ByVal Ptr As Long,ByVal NewVal As Long)
- Private Declare Sub PutMem8 Lib "msvbvm60" (ByVal Ptr As Long,ByVal NewVal As Currency)
- Public Function CallCOMInterface&(ByVal CComPtr&,ByVal dwMemberIndex&,ParamArray pParam())
- Dim i%,offset&
- Dim hMem&
- hMem = LocalAlloc(0,((UBound(pParam) + 2) * 5) + 5 + 6 + 1) '//申请代码内存
- offset = hMem
- For i = UBound(pParam) To 0 Step -1 '//压入参数
- PutMem1 offset,&H68 'push Param
- offset = offset + 1
- PutMem4 offset,pParam(i)
- offset = offset + 4
- Next
- PutMem1 offset,&H68 'push COM point,压入COM指针
- PutMem4 offset + 1,CComPtr
- offset = offset + 5
- PutMem1 offset,&HA1 'mov eax,dword ptr ds:CComPtr,eax=CComPtr指针第一个函数地址
- PutMem4 offset + 1,&HFF 'call dword ptr ds:eax + dwMemberIndex * 4,根据Win32下COM表结构,一个函数地址长度4字节
- PutMem1 offset + 1,&H90
- PutMem4 offset + 2,dwMemberIndex * 4
- offset = offset + 6
- PutMem1 offset,&HC3 'retn
- PutMem1 offset + 1,&H90 '//nop一行代码
- CallCOMInterface = CallWindowProcW(hMem,0) 'call
- LocalFree hMem '//释放内存
- End Function
点击Command1测试效果。 这代码就是根据ITaskbarList3的CLSID和IID通过CoCreateInstance创建出来一个COM对象指针,然后调用里面的虚函数,实现Windows7下任务栏进度条效果。 关于ITaskbarList3的其他函数说明自己看MSDN就行了,理解套用代码就一样调用,其他的COM接口也一样。比如什么XMLLite的那些东西。。。 有人会问,上面的SetProgressValue不是3个参数么,为什么我们调用的时候传入了5个参数?! 看原形,SetProgressValue是一个dword的hwnd,2个ULONGULONG的进度参数,ULONGULONG这个玩意是8字节,压栈的时候分2个4字节压,所以我们就写成了4个参数,也就是分开了。就像那个FILETIME差不多。再简单一点,一个ULONGULONG=2个ULONG=2个dword 好了、长篇大论就写到这里,撸管睡觉去。
- Private i&
- Private Enum ITaskbarList3
- QueryInterface
- AddRef
- Release
- 'IUnknown
- HrInit
- AddTab
- DeleteTab
- ActivateTab
- SetActiveAlt
- MarkFullscreenWindow
- SetProgressValue
- SetProgressState
- RegisterTab
- UnregisterTab
- SetTabOrder
- SetTabActive
- ThumbBarAddButtons
- ThumbBarUpdateButtons
- ThumbBarSetImageList
- SetOverlayIcon
- SetThumbnailTooltip
- SetThumbnailClip
- End Enum
- Private Type GUID
- Data1 As Long
- Data2 As Integer
- Data3 As Integer
- Data4(7) As Byte
- End Type
- Private Declare Function IIDFromString& Lib "ole32 " (ByVal ID As Long,ByVal IDs As Long)
- Private Declare Function CLSIDFromString& Lib "ole32 " (ByVal ID As Long,ByVal IDs As Long)
- Private Declare Function CoCreateInstance& Lib "ole32 " (ByVal CLSID As Long,ByVal Outer As Long,ByVal Context As Long,ByVal IID As Long,Obj As Any)
- Private Function CreateW7Task&()
- Dim CID As GUID,IID As GUID,objW7Task&
- CLSIDFromString StrPtr("{56FDF344-FD6D-11d0-958A-006097C9A090}"),VarPtr(CID)
- IIDFromString StrPtr("{EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF}"),VarPtr(IID)
- CoCreateInstance VarPtr(CID),1,VarPtr(IID),objW7Task
- CreateW7Task = objW7Task
- End Function
- Private Sub Command1_Click()
- Dim j&
- For j = 0 To 10000
- Me.Caption = CallCOMInterface(i,SetProgressValue,Me.hWnd,j,10000,0)
- Next
- End Sub
- Private Sub Form_Load()
- i = CreateW7Task
- End Sub