请教COM, IDispatch相关问题! 
我得到一个COM的组件, 其中有:
/* interface IDZCommABS */
/* [unique][helpstring][nonextensible][oleautomation][uuid][object] */ 
    MIDL_INTERFACE("59313063-9D27-4F30-ACB6-4E662FFFA9EF")
    IDZCommABS : public IUnknown
    {
    public:
.....
....
/* dispinterface _IDZCommABSEvents */
/* [helpstring][uuid] */ 
EXTERN_C const IID DIID__IDZCommABSEvents;#if defined(__cplusplus) && !defined(CINTERFACE)    MIDL_INTERFACE("B031A9E6-858C-48A7-B7B9-0FEB0B6DA398")
    _IDZCommABSEvents : public IDispatch
    {
    };
当我在VC6中用:
    hr = CoCreateInstance( IID_IDZCommABS, NULL, CLSCTX_INPROC,
        IID_IUnknown, ( LPVOID* )&lpUnknown );
成功,但是:
    hr = lpUnknown->QueryInterface( IID_IDispatch, (LPVOID*)&m_pDisp );
时,就失败了.提示不支持该接口, 为什么呢?

解决方案 »

  1.   

    从上面的信息看,IDZCommABS直接继承自IUnknown,所以当然不支持IDispatch。
    倒是连接点事件_IDZCommABSEvents继承自IDispatch。
      

  2.   

    那请问我要如何得到IID_IDispatch呢。如果的不到,我又如何象IDispatch::Invoke一样利用函数的名来调用函数。
      

  3.   

    对于直接继承自IUnknown的接口,就可以不用IDispatch::Invoke,直接通过接口指针就可以调用,如:
    IDZCommABS *p = NULL;
    CoCreateInstance( CLSID_DZCommABS, NULL, CLSCTX_INPROC, IID_IDZCommABS, (LPVOID *)&p );
    p->SomeMethod();
    p->Release();
    p = NULL;当然,以上的CLSID和IID是我杜撰的。
      

  4.   

    我现在需要的是写一个函数, 通过传入要调用的函数名来调用指定的函数,并返回调用的结果,所以需要象Invoke一样的函数。有什么方法吗?
      

  5.   

    这个COM组件是你写的吗?如果是你写的,那么可以修改它使之支持接口调度(IDispatch)。如果不是你写的,那么你可以在客户端采用类似这样的方法:
    HRESULT InvokeByName( IDZCommABS *p, LPCTSTR lpszMethod )
    {
        if ( lstrcmp( lpszMethod, _T("SomeMethod") ) == 0 )
            return p->SomeMethod();
        else if ( lstrcmp( lpszMethod, _T("AnotherMethod") ) == 0 )
            return p->AnotherMethod();
        // ...
    }
      

  6.   

    不是我写的,我现在的问题是,我连这个COM里有的方法都不知道,只能靠外部传入的参数来调用。不知道如何实现。
      

  7.   

    那个COM有tlb吗?如果有的话,可以通过tlb察看调用信息。
    然后通过CreateStdDispatch取得IDispatch接口。
      

  8.   

    那个com不是你写的,也是有办法的.他不是继承自IDispatch, 那么那个Invoke肯定是不能用了.----------------------------
    自己写一个包装类
    提供一个方法
    比如:bool CallFunction(CString funname,...){
          if(funname="fun1"){
               //调用fun1方法
          }elseif(funname="fun2"){
              //调用fun2方法
          }}
    -------------------------
    为什么可以这样,这样可行马?1 其他的路绝对走不通.
    2 com你了解.而且你可以得到一个确切的实例.
      invoke用在你对你要用的那个对象不大了解的时候
    3 那个com的函数种类有限,你不许花费多大的精力,可行!
      

  9.   

    即使没有IDispatch接口,只要注册了TLB,也是可以动态调用的。
    你可以用Ole View看一下,是不是有TLB。也可以用VB试一下(VB也支持非IDispatch接口,只要有TLB)。如果没有TLB,那除非你有它的interface定义头文件,否则肯定是没办法的了。你可以查一下关于TypeLib的文档,如果搞不懂,可以再问。
      

  10.   

    楼上请注意.动态调用和能用Invoke调用是两码事.在vb中 createobject在幕后做的工作不一定是用了invoke
    如果是dim obj as Object
    set obj =createobject("progid")
    obj.method他是调用了 Invoke如果是
    dim obj as IDog
    set obj=createobject("progid")
    obj.method他就不是调用了invoke,这种情况没有继承IDispath也是可以的.上次关于插件的讨论,我最后就是用第二种方法实现的.
    我写了一个idl,定义了插件接口,没有继承IDispatch,
    用midl编译得到tlb后,在activedll工程里引用他.
    添加了一个类实现了tlb中定义的tlb.
    在;另一个工程中使用createojbect创建了他的对象.
    但是这样并使用了IDispatch.你用vc试一下把.怎么样才能得到一个
       指向
           没有实现IDispatch接口的对象
       的IDispatch指针.得不到这个IDisatch指针,你又如何调用Invoke呢?建议看
    <Com+ 与 Visual Basic6> 讲vb中实现com的原理和背后做的工作
    <com 本质论> 讲 com 原理.
      

  11.   

    TaiJi1985(太极_实践是检验真理的唯一标准,请没有试过的人保持沉):楼主只要通过函数名调用,又不是一定非要通过Invoke。所以只要能动态调用就可以了。
    你推荐的书我一定会找来看看。
    不过,碰巧,不久前我刚刚做过类似的事情,并不是很难的。建议你也试试看。
      

  12.   

    我用了另外的方法:ITypeInfo::Invoke方法。
    COM有如下定义:
        IDZCommABS : public IUnknown
        {
        public:
            virtual /* [helpstring][id] */ HRESULT STDMETHODCALLTYPE GetNewID( 
                /* [out] */ LONG *lplReturnCode,
                /* [in] */ ULONGLONG lProjectID,
                /* [out] */ ULONGLONG *lpullConnectID) = 0;
    ....     
        };
    我利用如下语句调用:
        LONG *lpOut = new LONG();    
        ULONGLONG *lpOut2 = new ULONGLONG();    vArgs[0].vt = VT_PTR;
        vArgs[0].plVal = lpOut;
        vArgs[1].vt = VT_I8;
        vArgs[1].ullVal = 1;
        vArgs[2].vt = VT_PTR;
        vArgs[2].pullVal = lpOut2;    dispparams.cArgs = 3;
        dispparams.cNamedArgs = 0;
        dispparams.rgvarg = vArgs;
        
        hr = m_lpTInfo->Invoke( (PVOID *)lpIUnk, pFuncDesc->memid, DISPATCH_METHOD, &dispparams, 
            NULL, NULL, NULL );为何总提示参数不正确?应如何调用?
      

  13.   

    哈哈,晕死了,哪有c++调用组件非要用IDispatch接口调用的,进程内调用慢太多了。
      

  14.   

    是这样得到的:
    hr = LoadTypeLib( L"e:\\DZCommABS.dll", &m_lpITypeLib );

    hr = m_lpITypeLib->GetTypeInfo( i, &m_lpTInfo );
      

  15.   

    1. 你要确认i是正确的。每个TypeLib中有很多TypeInfo,你要取得正确的Interface的TypeInfo。
    否则Invoke会失败。2. m_lpTInfo->Invoke( (PVOID *)lpIUnk, ... 中的lpIUnk应该是对应Interface的指针,不能是单纯的IUnknown*。可以通过QueryInterface取得正确的Interface指针。3. 确保Invoke中的其它参数是正确的,这和IDispatch::Invoke是一样的。下面代码给你参考:这是我写的测试用代码,完全可以运行的。const GUID g_LibID = ...;                               // LIB ID
    const OLECHAR * const g_pClassName = OLESTR("...");     // COM Class Name
    const OLECHAR * const g_pFuncName = OLESTR("...");      // Function Name// TypeAttrWrap wraps the TYPEATTR pointer, release the structure after uesd.
    class TypeAttrWrap
    {
    private:
    ITypeInfoPtr m_pTypeInfo;
    TYPEATTR * m_pTypeAttr;public:
    TypeAttrWrap(ITypeInfo * pTypeInfo) : m_pTypeAttr(NULL), m_pTypeInfo(pTypeInfo)
    {
    } ~TypeAttrWrap()
    {
    if (m_pTypeAttr)
    {
    m_pTypeInfo->ReleaseTypeAttr(m_pTypeAttr);
    }
    } HRESULT GetInfo()
    {
    return m_pTypeInfo->GetTypeAttr(& m_pTypeAttr);
    } operator BOOL () const
    {
    return (m_pTypeAttr != NULL);
    } const TYPEATTR * operator -> () const
    {
    return m_pTypeAttr;
    } TYPEATTR * operator -> ()
    {
    return m_pTypeAttr;
    }
    };int main(int argc, char* argv[])
    {
    if (FAILED(CoInitialize(NULL)))
    {
    cout << "Error" << endl;
    return 1;
    } try
    {
    HRESULT hr; // 1. Load Type Library
    ITypeLibPtr pTypeLib;
    hr = LoadRegTypeLib(
    g_LibID, // Type Library's GUID
    1, 0, // Version
    GetThreadLocale(), // LCID
    &pTypeLib); // Result
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 2. Get coclass's Type Infomation
    ITypeInfoPtr pTypeInfo;
    MEMBERID memId;
    USHORT uFound = 1;
    CComBSTR bstrTypeName(g_pClassName);
    hr = pTypeLib->FindName(
    bstrTypeName, // coclass Name
    0, // Hash Value, use 0 as the default value.
    &pTypeInfo, // Returned ITypeInfo * Array.
    &memId, // Returned Member ID Array.
    &uFound); // in/out The Array Size.
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    }
    if (uFound != 1)
    {
    _com_issue_error(E_INVALIDARG);
    } // 3. Get coclass's Main Interface's TypeInfo
    TypeAttrWrap pTypeAttr(pTypeInfo);
    hr = pTypeAttr.GetInfo();
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 3.1. Get Implemented Type Number
    USHORT nImplTypes = pTypeAttr->cImplTypes; // 3.2. Find The Default Interface From The Implemented Types
    USHORT nImplTypeIndex;
    for (nImplTypeIndex = 0; nImplTypeIndex < nImplTypes; ++ nImplTypeIndex)
    {
    // 3.2.1. Get Implemented Type's Flag
    int TypeFlags;
    hr = pTypeInfo->GetImplTypeFlags(nImplTypeIndex, &TypeFlags);
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 3.2.2. End The Loop While We Find The Default Interface
    if (TypeFlags == IMPLTYPEFLAG_FDEFAULT)
    {
    break;
    }
    } // 3.3. If It Is Not Found, ...
    if (nImplTypeIndex >= nImplTypes)
    {
    _com_issue_error(E_INVALIDARG);
    } // 3.4. Get The RefType's Handle
    HREFTYPE hRefType;
    hr = pTypeInfo->GetRefTypeOfImplType(nImplTypeIndex, &hRefType);
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 3.5. Get The ITypeInfo
    ITypeInfoPtr pInterfaceInfo;
    hr = pTypeInfo->GetRefTypeInfo(hRefType, &pInterfaceInfo);
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 4. Get Default Interface's GUID
    TypeAttrWrap pInterfaceAttr(pInterfaceInfo);
    hr = pInterfaceAttr.GetInfo();
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } GUID guidInterface = pInterfaceAttr->guid; // 5. Create Instance And Get The Default Interface
    IUnknownPtr pObj;
    hr = pTypeInfo->CreateInstance(NULL, guidInterface, (void **) &pObj);
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 6. Get Function's DISPID By The Name
    MEMBERID FuncId;
    CComBSTR bstrFuncName(g_pFuncName);
    hr = pInterfaceInfo->GetIDsOfNames(&bstrFuncName, 1, &FuncId);
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 7. Call The Function
    CComVariant Params[2];
    Params[0] = 10L;
    Params[1] = 20L;
    DISPPARAMS dispParams;
    dispParams.rgvarg = Params;
    dispParams.rgdispidNamedArgs = NULL;
    dispParams.cArgs = 2;
    dispParams.cNamedArgs = 0;
    CComVariant Result;
    EXCEPINFO ExceptionInfo;
    UINT ArgErr;
    hr = pInterfaceInfo->Invoke(pObj, FuncId, DISPATCH_METHOD, &dispParams, &Result, &ExceptionInfo, &ArgErr);
    if (FAILED(hr))
    {
    _com_issue_error(hr);
    } // 8. Write The Result
    cout << Result.lVal << endl;
    }
    catch (_com_error& e)
    {
    CHAR * pMsg = e.Description();
    if (pMsg)
    {
    cout << "Error : " << pMsg << endl;
    }
    else
    {
    cout << "Error : 0x" << hex << e.Error() << endl;
    }
    } CoUninitialize(); return 0;
    }
      

  16.   

    啊,我现在又出现了一个新问题,调用Invoke没出现错误,但是,发现并没有真正的进入所调用的程序中,是不是没有实现IDispatch接口的COM,这样调用不对呢。但是肯定有方法的。