How do I run a COM (.ocx) object in a C++ command line interface program. (VS2017)

0

How do I run a COM (.ocx) object in a C++ command line interface program. (VS2017)

After many hours of research I have the following. I think the COM object is loading as trio is populated. But I do not know how to run it successfully. It may need to be attached to a CWND or something.

I have this code, which may be the wrong rabbit hole. It crashes horribly.

  HRESULT hr;
   hr = CoInitialize(0);
   assert(SUCCEEDED(hr));
   {
      static CLSID const clsid
         = { 0xf1933967, 0x74b0, 0x11d3,{ 0x8a, 0x13, 0x0, 0x40, 0x33, 0x93, 0xb2, 0x36 } };
      //CLSID ClassID;
      //hr = CLSIDFromProgID(OLESTR("TrioPCLib.TrioPC"), &ClassID);
      assert(SUCCEEDED(hr)); 
      TrioPCLib::_DTrioPCPtr trio;
      IID iid = TrioPCLib::_DTrioPCPtr::GetIID();
      hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, reinterpret_cast<void**>(&trio));
      assert(SUCCEEDED(hr));
      trio->Release();

   }
   CoUninitialize();

The COM object has GUI which I do not need to use it. I just want to call the API.

UPDATE: The ActiveX is loaded and I can call, for example, AboutBox() and it is displayed. It crashes on the CoUninitialize() with an exception...

Unhandled exception at 0x779CA899 (ntdll.dll) in TestVpu.exe: 0xC0000374: A heap has been corrupted (parameters: 0x77A05910).

c++
visual-studio
com
ocx
asked on Stack Overflow Apr 17, 2018 by William J Bagshaw • edited Apr 17, 2018 by William J Bagshaw

2 Answers

3

I'd be willing to bet that the exception is actually happening at the close brace, not the CoUninitialize call. It looks like you might be releasing the COM object twice, once explicitly and once implicitly.

You are explicitly calling trio->Release(). You are also using a TrioPCLib::_DTrioPCPtr, which is probably a COM smart pointer produced by the Visual C++ compiler encountering an #import. This class automatically calls Release on the referenced object when it goes out of scope.

You should either use a TrioPCLib::_DTrioPC* or you should not call trio->Release(). (FWIW: I would prefer to use TrioPCLib::_DTrioPCPtr and not explicitly call trio->Release().)

answered on Stack Overflow Apr 17, 2018 by Michael Gunter • edited Apr 20, 2018 by Michael Gunter
-1

The following appears to be much more stable. It uses pointers and calls the Release. The import has some extra params. The import generates code that you can look at. The class is derived from IDispatch and calls are made using _com_dispatch_method(). Up to now I have not needed to worry about a message pump.

#include "stdafx.h"
#include <iostream>
using namespace std;
#import "C:/Users/*****/Documents/Trio Motion Solutions/TrioMCTools/Debug/Win32/TrioPC.ocx" named_guids no_namespace

int main(int, char**)
{
    CoInitialize(0);
    {
        bool inOK = false;
        int length = 3;

        _DTrioPC* pitd = 0;
        HRESULT hr = CoCreateInstance(CLSID_TrioPC, 0, CLSCTX_ALL, DIID__DTrioPC, reinterpret_cast<void**>(&pitd));
        if (SUCCEEDED(hr)) cout << "ok" << endl; else cout << "nok" << endl;

        pitd->SetHost("192.168.0.250");
        bool openOK = pitd->Open(2, 0);
        if (openOK) cout << "ok" << endl; else cout << "nok" << endl;


        SAFEARRAY *data = SafeArrayCreateVectorEx(VT_R8, 0, length, NULL);

        if (data)
        {
            double *safe_data;
            hr = SafeArrayAccessData(data, (void **)&safe_data);
            if (SUCCEEDED(hr))
            {
                int i;
                for (i = 0; i < length; i++)
                    safe_data[i] = i+532;
                SafeArrayUnaccessData(data);

                VARIANT arg;
                VariantInit(&arg);
                arg.vt = VT_ARRAY | VT_R8;
                arg.parray = data;

                inOK = pitd->SetTable(1000, 3, &arg);
            }
        }
        pitd->Release();
    }
    CoUninitialize();

    return 0;
}
answered on Stack Overflow Apr 19, 2018 by William J Bagshaw

User contributions licensed under CC BY-SA 3.0