Question

I've followed a number of detailed tutorials on how to use a COM object in C++. I'm using VS 2010 pro. I made a new solution called TestComInterop. Made a C# project called TestMath. Made it com visible by selecting the option in properties->Assembly Information->Make assembly COM-visible. I then went to the Signing property, signed the Assembly called MyMathCom.snk (no password). I then used the GUID generator and made 2 GUID's. Then put this code into my program and compiled. (success)

using System.Runtime.InteropServices;
namespace TestMath
{
    [Guid("599AD473-B0A9-4A6E-B260-CF6FDEBF151B"),InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IClass1
    {
        void AddNumbers(byte[] array);
    }
    [Guid("62FBC3A9-E2C0-4B53-9BF3-FDE22AA0CFF2"),ClassInterface(ClassInterfaceType.None)]
    public class Class1 : IClass1
    {

        public void AddNumbers(byte[] array)
        {
            ulong number = 0;
            foreach (var item in array)
            {
                number += item;
            }
            System.Console.WriteLine("The answer is {0}", number);
            System.Windows.Forms.MessageBox.Show("DOrk");
        }
    }
}

I then made a C++ project for Console App. Allowed MFC.

Then i added a Typelib MFC class. I was able to use the drop down box to find TestMath<1.0> and it had my iClass1. I selected that and it made the header file for me

// Machine generated IDispatch wrapper class(es) created with Add Class from Typelib Wizard

#import "C:\\Users\\rsny\\Desktop\\TestComInterop\\TestComInterop\\TestMath\\bin\\Debug\\TestMath.tlb" no_namespace
// CClass1 wrapper class

class CClass1 : public COleDispatchDriver
{
public:
    CClass1(){} // Calls COleDispatchDriver default constructor
    CClass1(LPDISPATCH pDispatch) : COleDispatchDriver(pDispatch) {}
    CClass1(const CClass1& dispatchSrc) : COleDispatchDriver(dispatchSrc) {}

    // Attributes
public:

    // Operations
public:


    // IClass1 methods
public:
    void AddNumbers(SAFEARRAY * array)
    {
        static BYTE parms[] = {VTS_NONE} ;
        InvokeHelper(0x60020000, DISPATCH_METHOD, VT_EMPTY, NULL, parms, array);
    }

    // IClass1 properties
public:

};

compiled, and it made the tlh and tli files for me.. success..

So then last step is to run my code. Opened up TestComInterop.cpp and this is were I can't find a "standard" way of doing it. I've tried various different things, but wasn't sure what to put in... here is my code for that

// TestComInterop.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "TestComInterop.h"
#include "CClass1.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(NULL);

    if (hModule != NULL)
    {
        // initialize MFC and print and error on failure
        if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
        {
            // TODO: change error code to suit your needs
            _tprintf(_T("Fatal Error: MFC initialization failed\n"));
            nRetCode = 1;
        }
        else
        {
            // TODO: code your application's behavior here.
        }
    }
    else
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
        nRetCode = 1;
    }
    CClass1* myMath = new CClass1;
    myMath->CreateDispatch("62FBC3A9-E2C0-4B53-9BF3-FDE22AA0CFF2");
    //bool result = myMath.
    if (myMath)
        cout << "AWESOME" << endl;
    else
        cout << "LAME" << endl;

    unsigned char numbers[5] = {0x01,0x02,0x03,0x04,0x05};
    myMath->AddNumbers((SAFEARRAY*)numbers);
    delete myMath;
    getchar();
    return nRetCode;
}

Now I'm expecting it to paste the answer to my console.. but nothing. I'm also expecting it to display a message box... nothing. to say the least I'm a newb when it comes to COM objects. So far making this hasn't been too difficult with all the tools and so forth... but for the life of me I can't get this to work.

Just in case it's needed here are my tlh and tli files.

// Created by Microsoft (R) C/C++ Compiler Version 10.00.30319.01 (e323d9ba).
//
// c:\users\rsny\desktop\testcominterop\testcominterop\testcominterop\debug\testmath.tli
//
// Wrapper implementations for Win32 type library C:\\Users\\rsny\\Desktop\\TestComInterop\\TestComInterop\\TestMath\\bin\\Debug\\TestMath.tlb
// compiler-generated file created 09/14/12 at 12:08:08 - DO NOT EDIT!

#pragma once

//
// interface IClass1 wrapper method implementations
//

inline HRESULT IClass1::AddNumbers ( SAFEARRAY * array ) {
    HRESULT _hr = raw_AddNumbers(array);
    if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
    return _hr;
}

// Created by Microsoft (R) C/C++ Compiler Version 10.00.30319.01 (e323d9ba).
//
// c:\users\rsny\desktop\testcominterop\testcominterop\testcominterop\debug\testmath.tlh
//
// C++ source equivalent of Win32 type library C:\\Users\\rsny\\Desktop\\TestComInterop\\TestComInterop\\TestMath\\bin\\Debug\\TestMath.tlb
// compiler-generated file created 09/14/12 at 12:08:08 - DO NOT EDIT!

#pragma once
#pragma pack(push, 8)

#include <comdef.h>

//
// Forward references and typedefs
//

struct __declspec(uuid("d29ff1b5-bf10-4bbe-9bd9-cb5346f4bfaf"))
/* LIBID */ __TestMath;
struct __declspec(uuid("599ad473-b0a9-4a6e-b260-cf6fdebf151b"))
/* dual interface */ IClass1;
struct /* coclass */ Class1;

//
// Smart pointer typedef declarations
//

_COM_SMARTPTR_TYPEDEF(IClass1, __uuidof(IClass1));

//
// Type library items
//

struct __declspec(uuid("599ad473-b0a9-4a6e-b260-cf6fdebf151b"))
IClass1 : IDispatch
{
    //
    // Wrapper methods for error-handling
    //

    HRESULT AddNumbers (
        SAFEARRAY * array );

    //
    // Raw methods provided by interface
    //

      virtual HRESULT __stdcall raw_AddNumbers (
        /*[in]*/ SAFEARRAY * array ) = 0;
};

struct __declspec(uuid("62fbc3a9-e2c0-4b53-9bf3-fde22aa0cff2"))
Class1;
    // interface _Object
    // [ default ] interface IClass1

//
// Wrapper method implementations
//

#include "c:\users\rsny\desktop\testcominterop\testcominterop\testcominterop\debug\testmath.tli"

#pragma pack(pop)
Was it helpful?

Solution

I don't know why you use COleDispatchDriver derived class.

As you mention .tlh/tli files, I would assume that you have already imported your TLB file.

SO all you need to use it is something like this (writing straight from head so please ignore possible errors):

// prepare values
unsigned char numbers[] = {0x01,0x02,0x03,0x04,0x05};
SAFEARRAY* sa = SafeArrayCreateVector(VT_UI1, 0, 5);
char* data;
SafeArrayAccessData(sa, (void**)&data);
memcpy(data, numbers, 5)
SafeArrayUnaccessData(sa);

// instantiate COM object and call the method
IClass1Ptr obj(_uuidof(Class1));
obj->AddNumbers(sa);  

// clean up
SafeArrayDestroy(sa);

If you use ATL, I suggest using CComSafeArray as it takes away a lot of pain from working with SAFEARRAYs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top