Preliminary exploration of vs2019 COM component – realizing the CreateObject function logic of vbs

Time:2021-9-14

Preliminary exploration of vs2019 COM component – simple COM writing and cross language call

The last article realized how to write COM based on IDispatch interface and how VBS calls com

This time is mainly to realize the logic of VBS CreateObject function, that is, how to call COM based on idispathc interface without knowing the class name

 

prerequisite

1. Master the basic syntax of C + +

2. Platform installation vs2019

3. The local platform is windows 10 1909 x64

4. Basic DLL programming knowledge (not required)

 

This goal

1. Create a DLL and implement the CreateObject function

2. Write a demo that calls the DLL

 

1. Create a DLL and implement the CreateObject function

  

First, create a dynamic link library through vs

  

 

  

Before writing, sort out the execution process of the program

Initialize com Library

Get function pointer

Incoming parameters

Calling function pointer

Uninstall com Library

 

 

   

Then start writing our DLL

After vs2019 creates a DLL project, the system will add more header files by default

      

 

And source files

      

 

We open the PCH. H header file to define our function declaration

    

Parameter is   COM component progid, function name, number of parameters, variable length parameters

Extern “C” is defined as C

_ Declspec (dllimport) defines this function as the function to export

    

  Create a new cominit. H to define the initialization and unloading library functions of the com library

1 // ComInit.h
 2 
 3 #pragma once
 4 static bool _init = false;
 5 
 6 // initialization
 7 bool Init();
 8 
 9 // end initialization
10 void Release();

 

Create a cominit.cpp to implement the init and release functions

// ComInit.cpp

#include "pch.h"
#include "ComInit.h"

bool Init()
{
    if (_init == true)
    {
        return _init;
    }
    else
    {
        if (S_OK == CoInitialize(NULL))
            _init = true;
        else
            _init = false;

        return _init;
    }

    return false;
}

void Release()
{
    if (true == _init)
    {
        CoUninitialize();
        _init = false;
    }
}

 

Then open pch.cpp to implement the CreateObject function

1 #include "pch.h"
 2 #include "ComStart.h"
 3 #include 
 4 #include 
 5 
 6 // error macro
 7 #define ASSERT(s) if((s) == true)
 8 
 9 // COM class name, function name, number of parameters passed in, variable length parameters
10 VARIANT CreateObject(const WCHAR* __comname,const WCHAR* __funcname,int __count, ...)
11 {
12 / * com is used after registering with the system*/
13    
14 // is initialization successful
15     if (true == Init())
16     {
17 // progid value storage
18         CLSID clsid;
19 
20 // obtain the CLSID of the component through progid
21 // the CLSID value is stored in the registry HKEY_ CLASSES_ Root [take _comname plus. 1 as the key value (mycom. Firstclass. 1)]
22         HRESULT hr = ::CLSIDFromProgID(__comname, &clsid);
23 
24         ASSERT(S_OK != hr)
25             assert(hr != S_OK);
26 
27 // obtain iunknow through smart pointer
28         CComPtrspUnk;
29 
30         /* 
31         *   CoCreateInstance
32 * value obtained by clsidfromprogid
33 * pointer to interface IUnknown
34 * context for running executable code [clsctx_all is all]
35         *       IID_ IUnknown is the return type
36 * a pointer variable used to receive the address of the COM object interface
37         */
38 // get iunknow content 
39         hr = ::CoCreateInstance(clsid, NULL, CLSCTX_ALL, IID_IUnknown, (LPVOID*)&spUnk);
40 
41         ASSERT(S_OK != hr)
42             assert(hr != S_OK);
43         
44 // declare a new IDispatch smart pointer through IUnknown smart pointer
45         CComDispatchDriver spDisp(spUnk);
46 
47 // parameter array
48         VARIANT* __args = new VARIANT[__count];
49 
50 // variable length parameter variable
51         va_list ap;
52 
53 // locate the variable length parameter of the first function
54         va_start(ap, __count);
55 
56 // loop to obtain variable length parameters, convert them to variant type, and put them in__ Args variable
57         for (auto i = 0; i < __count; i++)
58             __args[i] = va_arg(ap, VARIANT);
59 
60 // end variable length parameter
61         va_end(ap);
62 
63 // store the returned value of COM function
64         VARIANT __ret;
65 
66 // execute com function
67 
68         /*
69         *   [InvokeN]
70 * function name
71 * function parameters
72 * number of functions
73 * return value storage
74         */
75         hr = spDisp.InvokeN((LPCOLESTR)__funcname, __args, __count, &__ret);
76        
77         ASSERT(S_OK != hr)
78             assert(hr != S_OK);
79 
80 // memory recycling
81         delete[] __args;
82 
83 // uninstall the com Library
84         Release();
85 
86 // return value
87         return __ret;
88     }
89 
90     assert(_init == false);
91 }

   

After completion, compile (Ctrl + b) to obtain the new DLL and lib files (x64) and the PCH. H header file of the project

    

    

 

2. Write a demo that calls the DLL

 

Vs2019 create a new console based project

  

 

Move DLL, lib and PCH. H files to the new project directory, and add code to the PCH. H file

// pch.h

#pragma once
#include 

//Newly added code
#pragma comment(lib,"ComPack.lib")

extern "C" _declspec(dllimport) VARIANT CreateObject(const WCHAR * __comname, const WCHAR * __funcname, int __count, ...);

 

Find the main function and write the calling code

#include 
#include "pch.h"

int main()
{
    //Parameter type must be variant
    VARIANT __param1;

    //The parameter type is long
    __param1.vt = VT_I4;

    //The parameter value is 2
    __param1.lVal = 2;


    //Get comtest.temp and call the number function. The number of parameters is 1. Pass in the parameters to the number function__ param1
    VARIANT __ret = CreateObject(L"ComTest.Temp", L"Number",1, __param1);

    std::cout << __ret.lVal << std::endl;
}

 

  Execute and run and display the execution results

  

 

 There is an error during operation. Check whether the called com has been registered

I talked about how to register in the last article

  

Next, modify the code and try to call the run function in Wscript. Shell

#include 
#include "pch.h"


int main()
{
    VARIANT __param1;

    //The parameter type is BSTR
    __param1.vt = VT_BSTR;

    //Create a string in BSTR format
    __param1.bstrVal = SysAllocString(L"notepad.exe");

    //Call the function and release the BSTR
    VARIANT __ret = CreateObject(L"Wscript.shell", L"run",1, __param1);
    SysFreeString(__param1.bstrVal);
}

It is worth mentioning that the strings of COM components are different from previous strings, and the creation and destruction methods are also different

Sysallocstring creates a BSTR string for

Sysfreestring is a free BSTR string

 

The running result shows that the system command has been successfully executed and a notepad has been opened

  

 

 

matters needing attention:

Com can only be called based on IDispatch interface

Com must be registered with the system (beware of accidental deletion or moving path)

The uninstall DLL function is    Regsvr32.exe – UI [unknown DLL]

The corresponding version of DLL shall be consistent as far as possible

 

GitHub source code:

  3065190005/ComTest: ComTest Code (github.com)

 

Recommended Today

The selector returned by ngrx store createselector performs one-step debugging of fetching logic

Test source code: import { Component } from ‘@angular/core’; import { createSelector } from ‘@ngrx/store’; export interface State { counter1: number; counter2: number; } export const selectCounter1 = (state: State) => state.counter1; export const selectCounter2 = (state: State) => state.counter2; export const selectTotal = createSelector( selectCounter1, selectCounter2, (counter1, counter2) => counter1 + counter2 ); // […]