How to Call COM Object from Visual Studio C++?


The COM (Component Object Model) has been existent on Windows platform e.g. Win98 for decades. It is powerful technology that allows different programming languages to easily invoke the library written in other languages. On Windows, the COM can be in the form of DLL, OCX etc.

COM Library – WScript.Shell

Microsoft has provided us a WScript.Shell object that can help system administration tasks such as creating a shortcut, print a document. The object has lived in the following system locations for 32-bit and 64-bit respectively:

C:\Windows\SysWOW64\wshom.ocx    # 32 bit
C:\Windows\System32\wshom.ocx    # 64 bit

How to Register/De-register COM?

To register a COM, using the above example, you will need the administration permissions:

1
regsvr32 "wshom.ocx"
regsvr32 "wshom.ocx"

To remove the COM from registry, you then need to add the /u parameter.

1
regsvr32 /u "wshom.ocx"
regsvr32 /u "wshom.ocx"

Example Usages via JScript/VBScript

The easiest ways to invoke the COM is via the inbuilt WSH (Windows Scripting Host), where the Microsoft has provided two scripting languages, the JScript (Bascially, the Microsoft’s Javascript Implementation for WSH, administrative tasks) and the VBScript.

JScript calling wshom.ocx

Using your favorite text editor, type in the following and save as *.js file.

1
2
3
(function(com) {
    com.Popup("Hello from https://helloacm.com");
})(new ActiveXObject("WScript.Shell"));
(function(com) {
	com.Popup("Hello from https://helloacm.com");
})(new ActiveXObject("WScript.Shell"));

Double click the file and this gives you a nice popup dialog. The key here is to create a COM object via the function ActiveXObject, which, however, is also supported on Microsoft Internet Explorer but not on modern browsers such as Chrome.

However, with NodeJs, you will require to install a package before you can call COM objects on windows.

com-jscript-helloacm-wscript.script.popup-example How to Call COM Object from Visual Studio C++? c / c++ COM/OLE tutorial

JScript example of Hello World via WScript.Shell

VBScript calling wshom.ocx

Similarly, the VBScript is very straightforward in creating and calling COM objects:

1
2
Set Wsh = CreateObject("Wscript.Shell")
Wsh.Popup "Hello from https://helloacm.com"
Set Wsh = CreateObject("Wscript.Shell")
Wsh.Popup "Hello from https://helloacm.com"

.NET C# etc

Under .NET managed code e.g. C#, it is also very easy by adding a COM references in the project.

C++ in Visual Studio

Let’s create a simple console application in Visual Studio – unmanaged C++ code (because in managed C++ code, you can easily add References and make calling COM a lot simpler).

Type Library to C++ Header File

Using the following #import, the C++ compiler will generate a C++ header.

1
#import "wshom.ocx" no_namespace, raw_interfaces_only  
#import "wshom.ocx" no_namespace, raw_interfaces_only  

As the wshom.ocx can be found in system path, there is no need to specify the full path. The C++ header file looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Created by Microsoft (R) C/C++ Compiler Version 14.00.23026.0 (25ff6a3b).
//
// c:\users\hp\appdata\local\temp\jetbrains\resharperplatformvs14\v05\tempcaches\solution.consoleapplication1\cppgen\-1303929168\wshom.tlh
//
// C++ source equivalent of Win32 type library wshom.ocx
// compiler-generated file created 12/19/16 at 23:15:35 - DO NOT EDIT!
 
#pragma once
#pragma pack(push, 8)
 
#include <comdef.h>
 
//
// Forward references and typedefs
//
 
struct __declspec(uuid("f935dc20-1cf0-11d0-adb9-00c04fd58a0b"))
/* LIBID */ __IWshRuntimeLibrary;
struct __declspec(uuid("53bad8c1-e718-11cf-893d-00a0c9054228"))
/* dual interface */ ITextStream;
struct __declspec(uuid("f935dc21-1cf0-11d0-adb9-00c04fd58a0b"))
/* dual interface */ IWshShell;
struct __declspec(uuid("f935dc27-1cf0-11d0-adb9-00c04fd58a0b"))
...
...
... 
// Created by Microsoft (R) C/C++ Compiler Version 14.00.23026.0 (25ff6a3b).
//
// c:\users\hp\appdata\local\temp\jetbrains\resharperplatformvs14\v05\tempcaches\solution.consoleapplication1\cppgen\-1303929168\wshom.tlh
//
// C++ source equivalent of Win32 type library wshom.ocx
// compiler-generated file created 12/19/16 at 23:15:35 - DO NOT EDIT!

#pragma once
#pragma pack(push, 8)

#include <comdef.h>

//
// Forward references and typedefs
//

struct __declspec(uuid("f935dc20-1cf0-11d0-adb9-00c04fd58a0b"))
/* LIBID */ __IWshRuntimeLibrary;
struct __declspec(uuid("53bad8c1-e718-11cf-893d-00a0c9054228"))
/* dual interface */ ITextStream;
struct __declspec(uuid("f935dc21-1cf0-11d0-adb9-00c04fd58a0b"))
/* dual interface */ IWshShell;
struct __declspec(uuid("f935dc27-1cf0-11d0-adb9-00c04fd58a0b"))
...
...
... 

This allows you to declare the type directly e.g. IWshShell.

Initialize the COM

We use the CoInitialize or CoInitializeEx (preferred) function to initialize the COM for the current thread, set the threading model etc.

1
2
CoInitialize(nullptr);  // depreciated 
CoInitializeEx(nullptr, COINIT_MULTITHREADED);   // preferred, puts the calling thread into a shared multi-threaded apartment (MTA)
CoInitialize(nullptr);	// depreciated 
CoInitializeEx(nullptr, COINIT_MULTITHREADED);	 // preferred, puts the calling thread into a shared multi-threaded apartment (MTA)

And you will need to reference the header file Objbase.h in order to use one of these functions.

Finalization of the COM object

After you finish the COM, you need to free all its resources e.g. unload DLL.

1
CoUninitialize();
CoUninitialize();

This forces all connections to the COM close. And you also need to reference Objbase.h header.

Creating the COM object in Unmanaged C++

First, you will need to get the CLSID (Class ID) from the library name i.e. WScript.Shell. You can do this via the following C++ code:

1
2
CLSID clsid;
CLSIDFromProgID(OLESTR("WScript.Shell"), &clsid);   
CLSID clsid;
CLSIDFromProgID(OLESTR("WScript.Shell"), &clsid);	

Because the C++ type library conversion from wshom.ocx has been successful, so you can declare the COM object via:

1
IWshShell *pApp = nullptr;
IWshShell *pApp = nullptr;

The next step is to call Win32 API CoCreateInstance that actually creates the COM object:

1
2
HRESULT hr;
hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWshShell), reinterpret_cast<LPVOID *>(&pApp));
HRESULT hr;
hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWshShell), reinterpret_cast<LPVOID *>(&pApp));

The Microsoft C++ Compiler will look for the UUID of the specified object during compiler time (it is not a function). The CLSCTX_INPROC_SERVER tells that the COM is actually a DLL object that runs in the same process of the caller process.

If sucessful, the pApp object will be set and you should always check for that:

1
2
3
if (FAILED(hr) || pApp == nullptr) {
    throw "Cannot Create COM Object";
}
if (FAILED(hr) || pApp == nullptr) {
	throw "Cannot Create COM Object";
}

Calling the COM functions in Unmanaged C++ code

We need to reference Propvarutil.h in order to use a few helper functions that convert the primitive data types e.g. int, double to VARIANT type, which is widely used data type in COM.

1
2
3
4
5
6
7
8
9
int out; // function returns value
VARIANT s;
InitVariantFromInt32(0, &s);
VARIANT title;
InitVariantFromString(PCWSTR(L"title"), &title);
VARIANT type;
InitVariantFromInt32(4096, &type); // Modal Dialog
BSTR msg = ::SysAllocString(L"Hello from https://helloacm.com");
pApp->Popup(msg, &s, &title, &type, &out);
int out; // function returns value
VARIANT s;
InitVariantFromInt32(0, &s);
VARIANT title;
InitVariantFromString(PCWSTR(L"title"), &title);
VARIANT type;
InitVariantFromInt32(4096, &type); // Modal Dialog
BSTR msg = ::SysAllocString(L"Hello from https://helloacm.com");
pApp->Popup(msg, &s, &title, &type, &out);

As we can see, it is not so convenient to call COM functions using unmanaged C++ code.

Complete C++ Code that calls the COM Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <objbase.h>
#include <unknwn.h>
#include <Propvarutil.h>
#import "wshom.ocx" no_namespace, raw_interfaces_only  
 
using namespace std;
 
int main() {
    HRESULT hr;
    CLSID clsid;
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);  
    CLSIDFromProgID(OLESTR("WScript.Shell"), &clsid);   
    IWshShell *pApp = nullptr;
    //REFIID IID_IUnknown = { 0x00000000, 0x0000, 0x0000,{ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
    hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWshShell), reinterpret_cast<LPVOID *>(&pApp));
    if (FAILED(hr) || pApp == nullptr) {
        throw "Cannot Create COM Object";
    }
    int out;
    VARIANT s;
    InitVariantFromInt32(0, &s);
    VARIANT title;
    InitVariantFromString(PCWSTR(L"title"), &title);
    VARIANT type;
    InitVariantFromInt32(4096, &type);
    BSTR msg = ::SysAllocString(L"Hello from https://helloacm.com");
    pApp->Popup(msg, &s, &title, &type, &out);
    CoUninitialize();
    cout << "Out = " << out;
    return 0;
}
#include <iostream>
#include <objbase.h>
#include <unknwn.h>
#include <Propvarutil.h>
#import "wshom.ocx" no_namespace, raw_interfaces_only  

using namespace std;

int main() {
	HRESULT hr;
	CLSID clsid;
	CoInitializeEx(nullptr, COINIT_MULTITHREADED);	
	CLSIDFromProgID(OLESTR("WScript.Shell"), &clsid);	
	IWshShell *pApp = nullptr;
	//REFIID IID_IUnknown = { 0x00000000, 0x0000, 0x0000,{ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } };
	hr = CoCreateInstance(clsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IWshShell), reinterpret_cast<LPVOID *>(&pApp));
	if (FAILED(hr) || pApp == nullptr) {
		throw "Cannot Create COM Object";
	}
	int out;
	VARIANT s;
	InitVariantFromInt32(0, &s);
	VARIANT title;
	InitVariantFromString(PCWSTR(L"title"), &title);
	VARIANT type;
	InitVariantFromInt32(4096, &type);
	BSTR msg = ::SysAllocString(L"Hello from https://helloacm.com");
	pApp->Popup(msg, &s, &title, &type, &out);
	CoUninitialize();
	cout << "Out = " << out;
	return 0;
}

Useful Resources

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
1542 words
Last Post: Coding considerations for online gaming
Next Post: What is the Probability of Two Sharing Birthday?

The Permanent URL is: How to Call COM Object from Visual Studio C++?

Leave a Reply