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).

2 Answers 2

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().)

Sign up to request clarification or add additional context in comments.

2 Comments

well once the heap is shite then when the CoUnitialize is called it all goes southwards. But I agree with your assessment that it probably is due to multiple releases. That said I think that is just the beginning of his worries since he doesnt have a message pump (seemingly)
@AndersK. I have added an answer that includes working code that I have been using without issues. I too was concerned that the original code was a little light on what is required to call and active x control. However the code is working well without the need for me to add a message pump. It uses the #import directive, which, under the hood, creates glue code which calls _com_dispatch_method(). It would appear that this is enough to call get a working solution, in my case. Thanks for your input on this and your contribution to the solution that works for me.
-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;
}

2 Comments

Downvoting this answer because: 1) It solves the original problem by using one of the suggestions I made in my own answer 2 days prior to this. And 2) This answer has 21 lines of code (all the SetHost/Open/SafeArray/SetTable stuff) that have nothing to do with the original question. By having this answer here, it obscures the issue.
Voting this down is backwards. I ask a question, and then to support the question provide example code that crashes. The previous answer that you give answers why the example code is crashing, not the question about how to run an active x from a command line application. I hope my answer is giving back to the community the accumulated knowledge that has lead to code that works well for me and is based on many fragments of information I have accumulated, including your input on why the code fragment was crashing.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.