正在COM入门,试验中遇到难题--
    我用Visual FoxPro9.0将一个项目Project1中的类TEST(类中只有一个自定义方法opentable)编译成一个COM,生成的COM部件名是Project1.TEST,并且在Visual FoxPro9.0中测试,调用类TEST的方法opentable运行正确。
   但是,用VC编写的客户程序里不知该怎么创建这个COM对象,好像需要调用API:
   WINOLEAPI CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter,
                    DWORD dwClsContext, REFIID riid, LPVOID FAR* ppv);
   其中有一项参数‘REFIID riid’应该是‘方法opentable’所对应的接口IID吧,那么,如何得到呢?尝试了下面的途径:
   首先通过注册表查找:打开注册表,只看到Project1.TEST的接口CLSID,却没有标出其中的‘方法opentable’的接口IID;
   然后用VC6的OLE/COM Object View查看:打开VC6的OLE/COM Object View,找到COM部件Project1.TEST,双击打开,内容如下
// Generated .IDL file (by the OLE/COM Object Viewer)
// typelib filename: Project1.dll
[uuid(3472BE1C-760A-4309-B3E6-A64503420B27),
  version(1.0),
  helpstring("\xFFFFFFCF\xFFFFFFEE目1 Type Libra")]
library \xFFFFFFCF\xFFFFFFEE?
{
    // TLib :     // TLib : OLE Automation : {00020430-0000-0000-C000-000000000046}
    importlib("stdole2.tlb");
    // Forward declare all types defined in this typelib
    interface Itest;
    [odl,
      uuid(B39D2C1A-B151-45E5-89C8-82BF55E7869B),
      helpstring("Project1.test"),
      hidden,
      dual,
      nonextensible,
      oleautomation ]
    interface Itest : IDispatch {
        [id(00000000), propget, helpstring("指定用户自定义类库的文件名,该类库包含对象的类。")]
        HRESULT ClassLibrary([out, retval] BSTR* ClassLibrary);
        [id(0x00000001)]
        HRESULT opentable([out, retval] VARIANT* RetVal);
        [id(0x00000002), propget, helpstring("指定在代码中用以引用对象的名称。")]
        HRESULT Name([out, retval] BSTR* Name);
        [id(0x00000002), propput, helpstring("指定在代码中用以引用对象的名称。")]
        HRESULT Name([in] BSTR Name);
        ······//有很多类似的,在这里我就省略了。
        HRESULT SetAll(
                        BSTR cProperty, 
                        VARIANT Value, 
                        BSTR cClass, 
                        [out, retval] VARIANT* RetVal);    };
    [uuid(5CB425B4-DD80-4750-AE5E-D757EAF56947),
      helpstring("Project1.test")    ]    
    coclass test {
        [default] interface Itest;    };};我想,其中有一句‘opentable([out, retval] VARIANT* RetVal);[id(0x00000002)’中的id(0x00000002)是不是‘方法opentable’的接口IID呢?怎么用呢?我试着将它加上Project1.TEST的接口CLSID作为‘方法opentable’的接口IID放到CoCreateInstance调用,程序如下
#include "windows.h"
#include <stdio.h>
#include <comutil.h>
void main()
{
 CoInitialize(NULL);
static const GUID TESTclsid={0x3472be1c,0x760a,0x4309,
{0xb3,0xe6,0xa6,0x45,0x03,0x42,0x0b,0x27}};
//Project1.TEST的接口CLSID
static const GUID TESTopeniid={0x3472be1e,0x760a,0x4309,
{0xb3,0xe6,0xa6,0x45,0x03,0x42,0x0b,0x27}};
//我是用那个id(0x00000002)加上上面的Project1.TEST的接口CLSID,得到TESTopeniid,作为Project1.TEST的‘方法opentable’的接口IID,不知对不?IUnknown *pUnknown;CoCreateInstance(TESTclsid, NULL, CLSCTX_INPROC_SERVER,TESTopeniid, (void **)&pUnknown);printf("%s",pUnknown->opentable );
}
结果有错误提示 error C2039: 'opentable' : is not a member of 'IUnknown'
由于时间较紧张,COM方面的学习只想面向应用,即主要着重于掌握如何在客户程序中创建和使用COM对象,而不是掌握如何开发一个COM组件,很多COM的书都是大篇幅介绍如何开发COM组件,而对创建和使用COM对象的叙述显得有些散乱,特别是VC++中创建和使用COM对象很复杂,要求客户程序中首先#include接口类定义文件,可是Visual FoxPro9.0生成COM部件时只生成了两个文件,一个是Project1.dll,一个是Project1.tlb,哪里有什么接口类定义文件哟,没有接口类定义文件,VC中好像就无法创建COM对象了,
不像VB、VFP那样创建一个COM对象只需一句CREATEOBJECT就搞掂了,所以希望大家在这一方面给予热心指点,非常感谢!

解决方案 »

  1.   

    TESTopeniid 应该等于 B39D2C1A-B151-45E5-89C8-82BF55E7869B,这是接口ID(IID),跟类型库ID(CLSID)是完全不同的,两者没有任何关联,也不能进行算术操作
      

  2.   

    B39D2C1A-B151-45E5-89C8-82BF55E7869B应该是接口ID。5CB425B4-DD80-4750-AE5E-D757EAF56947是你的clsid,good luck.有些知识还是需要补充的。即使你解决了这个问题。
      

  3.   

    CLSID是Class ID,就是表示一个COM对象
    IID是Interface ID,表示一个COM接口
    因此可以某一个CLSID1的对象实现了IID1,CLSID2也实现了IID1
    也可以CLSID1实现了IID1,同时也实现了IID2
    所以CLSID跟IID没什么关系,体现其关系的仅仅是利用CLSID调用了CoCreateInstance拿到IUnkown接口后,QueryInterface一次就知道它有没有实现IID了没有。用COM其实很简单的:
    Class Wizard->Add Class->From a type library
    它就会生成相应的包装类,你直接用就可以了
      

  4.   

    提醒一下,包装类使用之前记得先调用AttachDispatch
      

  5.   

    谢谢大家,我也做了试验,使用组件封装类的方法,但不成功,如下
    Ivfpoper oper;// Ivfpoper是封装类名
    int a=oper. test ();//这里调用组件的内部所实现的方法函数test (),它只是简单的返回一个整数5.
    编译报错:第二句代码error C2440: 'initializing' : cannot convert from 'struct tagVARIANT' to 'int'
    于是改为
    Ivfpoper oper;
    VARIANT var;
    var=oper. test ();
    int a=var.lVal;
       顺利通过编译,但是,在调试器中跟踪,发现,运行到最后,变量a中的值是空的(-858993460),并不是组件所返回的5.
      为此,再次调试跟踪,发现执行var=oper. test ();后,var中根本没有得到组件所返回的5. 
       打开封装类Ivfpoper的test(),代码如下
    VARIANT Ivfpoper::test()
    {
    VARIANT result;
    InvokeHelper(0x1, DISPATCH_METHOD, VT_VARIANT, (void*)&result, NULL);
    return result;
    }
       于是单步跟踪进入oper. test ()中,发现,它返回的VARIANT类型的变量result确实什么数据都没有,难道是这个组件没有实现“返回5”的功能?可是,这个组件在visual foxpro中测试,确实实现了“返回5”这个功能,那么,为什么在VC中这个功能没有实现呢?
      

  6.   

    1.使用__uuidof(expression),这个宏可以根据导入的类型库,组件,接口的类型,来获得它们的uuid。
    导入语句:#import "msxml4.dll"2.如果你的组件没有实现IDisptach接口:
    可以使用
    CoCreateInstance得查询接口,然后使用QueryInterface接口查询当前组件的其它。3.如果你的组件实现了IDispatch接口,可以这么来使用接口:
    OleInitialize(NULL); //这个必须的,这样一来,当前的线程只能套间,而不是自由线程。
    IDispatch* pIDispatch=NULL;
    CLSID clsID;
    //将组件的ProgID转化成它的CLSID
    HRESULT hr=CLSIDFromProgID(_T("Aggregate221.CA"),&clsID);
    if(FAILED(hr))
    {
    cout<<"could not find the prog ID"<<endl;
    return -1;
    }
    //查询IDispatch接口
    hr=CoCreateInstance(clsID,NULL,CLSCTX_LOCAL_SERVER,IID_IDispatch,(void**)&pIDispatch);
    if(SUCCEEDED(hr))
    {
            //方法名称
            OLECHAR* szMethodName1=L"FxInString";

    //得到方法的DISPID
    hr=pIDispatch->GetIDsOfNames(IID_NULL,&szMethodName1,1,GetUserDefaultLCID(),&dispID);
    if(FAILED(hr))
    {
    cout<<"could not find FxInString method"<<endl;
    pIDispatch->Release();
    return -4;
    } WCHAR szHello[100]=L"My life for this";
    BSTR bstrHello=SysAllocString(szHello);
    VARIANTARG variant1;
    VariantInit(&variant1);
    variant1.vt=VT_BSTR;
    variant1.bstrVal=bstrHello;
    DISPPARAMS params;
    params.cArgs=1;
    params.cNamedArgs=0;
    params.rgdispidNamedArgs=NULL;
    params.rgvarg=&variant1;
                    //调用调度方法
    hr=pIDispatch->Invoke(dispID,IID_NULL,GetUserDefaultLCID(),DISPATCH_METHOD,&params,NULL,NULL,NULL);
    if(FAILED(hr))
    {
    cout<<"could not invoke FxInString method"<<endl;
    SysFreeString(bstrHello);
    pIDispatch->Release();
    return -5;
    }
    SysFreeString(bstrHello);
    }
    OleUninitialize();
    return 0;5.使用__uuid宏可以偷赖一下:
    #import "msxml.dll"
    ::CoInitialize(NULL);
    MSXML2::IXMLDOMDocument2Ptr xml; 
    xml.CreateInstance(__uuidof(MSXML2::DOMDocument)); 
    if(!xml->load(_bstr_t(m_sFilePath)))
    return false;int i= 1; 
    bool bNode = TRUE; 
    MSXML2::IXMLDOMNodePtr node; 
    node = xml->selectSingleNode(_bstr_t(strRoot)); 
    ::CoUninitialize();
    其中:MSXML2是命名空间
      

  7.   

    记得AttachDispatch,用CoCreateInstance创建你这个CLSID的对象,然后用QueryInterface查询Ivfpoper接口
    然后用AttachDispatch把查询到的接口附加到上面