目次

Toymath3

作成日: 2023-09-02 (土)

第5回 VBAでCOMを使う

そのうち説明を追記予定。

test_Toymath3.xls

Sheet1 (Sheet1)

Sub test_Toymath3()
    'late-binding
    'Dim Calc As ToymathLib3.Calculator
    'Set Calc = CreateObject("Toymath3.Calculator.1")

    'early-binding
    Dim Calc As New ToymathLib3.Calculator

    X = Calc.Add(100, 200)
    Debug.Print X

    Debug.Assert Calc.Add(10, 20) = 30
    Debug.Assert Calc.Sub(10, 20) = -10
    Debug.Assert Calc.Mul(10, 20) = 200
    Debug.Assert Calc.Div(10, 20) = 0
End Sub

Toymath3

Makefile

MODNAME = toymath3
DLLFILE = ${MODNAME}.dll
IDLSRC = ${MODNAME}.idl
DEFFILE = ${MODNAME}.def
TLBFILE = ${MODNAME}.tlb
IDLOUT = ${MODNAME}.h ${MODNAME}_i.c ${MODNAME}_p.c ${TLBFILE} dlldata.c
RCSRC = ${MODNAME}.rc
RCBIN = ${MODNAME}.res
OUTDIR = output
SRCS = \
	calculator.cpp \
	calculatorclass.cpp \
	modulelock.cpp \
	exports.cpp
OBJS = ${SRCS:%.cpp=${OUTDIR}/%.o}
DEPS = ${SRCS:%.cpp=${OUTDIR}/%.d}
CPPFLAGS =
CXXFLAGS = -Wall -std=c++20 -fPIC
LDFLAGS =
LDLIBS = -lole32 -loleaut32 -luuid

.PHONY: all
all: ${OUTDIR} ${IDLOUT} ${OUTDIR}/${DLLFILE}

${OUTDIR}/${DLLFILE}: ${OBJS} ${OUTDIR}/${RCBIN} ${OUTDIR}/${MODNAME}_i.o
	${CXX} -shared -o $@ $^ ${DEFFILE} ${LDFLAGS} ${LDLIBS}

${OUTDIR}/%.o: %.cpp
	${CXX} -c ${CXXFLAGS} -o $@ $< -MMD -MP ${CPPFLAGS}

-include ${DEPS}

${IDLOUT}: ${IDLSRC}
	midl $<
    
${OUTDIR}/${RCBIN}: ${RCSRC} ${TLBFILE}
	windres $< -O COFF -o $@

${OUTDIR}/${MODNAME}_i.o: ${MODNAME}_i.c
	gcc -c -o $@ $<

${OUTDIR}:
	mkdir -p ${OUTDIR}

.PHONY: clean
clean:
	${RM} ${IDLOUT}
	${RM} -R ${OUTDIR}

.PHONY: install
install: all
	cp -f ${OUTDIR}\${DLLFILE} C:\code\lib

calculator.h

#ifndef CALCULATOR_H
#define CALCULATOR_H

#include "toymath3.h"

class Calculator : public ICalculator {
public:
    Calculator(void);

protected:
    virtual ~Calculator(void);

public:
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    
    STDMETHODIMP Add(long x, long y, long *result);
    STDMETHODIMP Sub(long x, long y, long *result);
    STDMETHODIMP Mul(long x, long y, long *result);
    STDMETHODIMP Div(long x, long y, long *result);
    
private:
    LONG m_cRef;
};

#endif // CALCULATOR_H

calculator.cpp

#include "calculator.h"

#include "modulelock.h"
#include <objbase.h>

extern IID const IID_ICalculator;

Calculator::Calculator(void) : m_cRef{0} {}

Calculator::~Calculator(void) {}

STDMETHODIMP Calculator::QueryInterface(REFIID riid, void **ppv) {
    if (riid == IID_IUnknown) {
        *ppv = static_cast<IUnknown *>(this);
    } else if (riid == IID_ICalculator) {
        *ppv = static_cast<ICalculator *>(this);
    } else {
        *ppv = nullptr;
        return E_NOINTERFACE;
    }
    
    reinterpret_cast<IUnknown *>(*ppv)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) Calculator::AddRef(void) {
    if (m_cRef == 0) {
        LockModule();
    }
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) Calculator::Release(void) {
    LONG res = InterlockedDecrement(&m_cRef);
    if (res == 0) {
        delete this;
        UnlockModule();
    }
    return res;
}

STDMETHODIMP Calculator::Add(long x, long y, long *result) {
    *result = x + y;
    return S_OK;
}

STDMETHODIMP Calculator::Sub(long x, long y, long *result) {
    *result = x - y;
    return S_OK;
}

STDMETHODIMP Calculator::Mul(long x, long y, long *result) {
    *result = x * y;
    return S_OK;
}

STDMETHODIMP Calculator::Div(long x, long y, long *result) {
    *result = x / y;
    return S_OK;
}

calculatorclass.h

#ifndef CALCULATORCLASS_H
#define CALCULATORCLASS_H

#include <objbase.h>

class CalculatorClassObject : public IClassFactory {
public:
    CalculatorClassObject(void);
    
protected:
    virtual ~CalculatorClassObject(void);

public:
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    
    STDMETHODIMP CreateInstance(IUnknown *pUnknownOuter, IID const &iid, void **ppv);
    STDMETHODIMP LockServer(BOOL bLock);

private:
    LONG m_cRef;
};

#endif // CALCULATORCLASS_H

calculatorclass.cpp

#include "calculatorclass.h"

#include "calculator.h"
#include "modulelock.h"

CalculatorClassObject::CalculatorClassObject(void) : m_cRef{0} {}

CalculatorClassObject::~CalculatorClassObject(void) {}

STDMETHODIMP CalculatorClassObject::QueryInterface(REFIID riid, void **ppv) {
    if (riid == IID_IUnknown) {
        *ppv = static_cast<IUnknown *>(this);
    } else if (riid == IID_IClassFactory) {
        *ppv = static_cast<IClassFactory *>(this);
    } else {
        *ppv = nullptr;
        return E_NOINTERFACE;
    }
    
    reinterpret_cast<IUnknown *>(*ppv)->AddRef();
    return S_OK;
}

STDMETHODIMP_(ULONG) CalculatorClassObject::AddRef(void) {
    if (m_cRef == 0) {
        LockModule();
    }
    return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CalculatorClassObject::Release(void) {
    LONG res = InterlockedDecrement(&m_cRef);
    if (res == 0) {
        delete this;
        UnlockModule();
    }
    return res;
}

STDMETHODIMP CalculatorClassObject::CreateInstance(IUnknown *pUnknownOuter, IID const &iid, void **ppv) {
    if (pUnknownOuter != nullptr) {
        return CLASS_E_NOAGGREGATION;
    }
    
    Calculator *pCalculator = nullptr;
    try {
        pCalculator = new Calculator;
    } catch (...) {
        return E_OUTOFMEMORY;
    }
    
    pCalculator->AddRef();
    HRESULT hr = pCalculator->QueryInterface(iid, ppv);
    pCalculator->Release();
    return hr;
}

STDMETHODIMP CalculatorClassObject::LockServer(BOOL bLock) {
    //if (bLock) {
    //    LockModule();
    //} else {
    //    UnlockModule();
    //}
    return S_OK;
}

modulelock.h

#ifndef MOUDLELOCK_H
#define MODULELOCK_H

#include <objbase.h>

void LockModule();
void UnlockModule();
LONG GetModuleLockCount();

#endif // MODULELOCK_H

modulelock.cpp

#include "modulelock.h"

static LONG g_cLocks = 0;

void LockModule() {
    InterlockedIncrement(&g_cLocks);
}

void UnlockModule() {
    InterlockedDecrement(&g_cLocks);
}

LONG GetModuleLockCount() {
    return g_cLocks;
}

exports.cpp

#include "modulelock.h"
#include "calculatorclass.h"
// #include <olectl.h>
// #include <winreg.h>
// #include <string>
// #include <format>

extern IID const LIBID_ToymathLib3;
extern CLSID const CLSID_Calculator;

static WCHAR const *g_wszModulePath = L"C:\\code\\lib\\toymath3.dll";
static WCHAR const *g_wszProgId = L"Toymath3.Calculator.1";

using RegKV = std::pair<std::wstring, std::wstring>;

//static HRESULT RegisterServerWriteRegistry(RegKV const &kv) {
//    HKEY hkey;
//    if (long err = RegCreateKeyW(HKEY_CLASSES_ROOT, kv.first.c_str(), &hkey); err == ERROR_SUCCESS) {
//        err = RegSetValueW(hkey, nullptr, REG_SZ, kv.second.c_str(), 0 /* this parameter is ignored */);
//        RegCloseKey(hkey);
//        if (err == ERROR_SUCCESS) {
//            return S_OK;
//        }
//    }
//    return SELFREG_E_CLASS;
//}

STDAPI DllGetClassObject(CLSID const &clsid, IID const &iid, void **ppv) {
    if (clsid != CLSID_Calculator) {
        return CLASS_E_CLASSNOTAVAILABLE;
    }
    
    CalculatorClassObject *pCalculatorClass = nullptr;
    try {
        pCalculatorClass = new CalculatorClassObject;
    } catch (...) {
        return E_OUTOFMEMORY;
    }
    
    pCalculatorClass->AddRef();
    HRESULT hr = pCalculatorClass->QueryInterface(iid, ppv);
    pCalculatorClass->Release();
    return hr;
}

STDAPI DllCanUnloadNow() {
    if (GetModuleLockCount() == 0) {
        return S_OK;
    } else {
        return S_FALSE;
    }
}

STDAPI DllRegisterServer(void) {
    return E_NOTIMPL;
    
    // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
    // この関数では、レジストリにモジュールの情報を登録します。
    // レジストリの操作は、ちょっとしたミスでシステムを破壊してしまう危険が伴います。
    // 何が行われるかを完全に理解していない限り実行をしないでください。
    // もし試すのであれば、壊れても被害の少ない実験用の環境で試すことを強くおすすめします。
    // 必ずトラブルが発生しても復旧できるように復元ポイントを作成しておいてください。
    //    
    // サンプルをあげておきます。
    // 正しく動作することは一切保証できません。
    // 不用意に実行しないでください。
    // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
    
//    LPOLESTR lpstrClsid;
//    StringFromCLSID(CLSID_Calculator, &lpstrClsid);
//    RegKV clsid = {std::format(L"CLSID\\{}", lpstrClsid), L"Toymath3 Calculator class"};
//    RegKV inproc = {std::format(L"CLSID\\{}\\InprocServer32", lpstrClsid), g_wszModulePath};
//    RegKV progid = {std::format(L"CLSID\\{}\\ProgId", lpstrClsid), g_wszProgId};
//    RegKV hkcrProgid = {std::format(L"{}", g_wszProgId), lpstrClsid};
//    RegKV hkcrProgidClsid = {std::format(L"{}\\CLSID", g_wszProgId), lpstrClsid};
//    CoTaskMemFree(lpstrClsid);
//    
//    if (HRESULT hr = RegisterServerWriteRegistry(clsid); FAILED(hr)) {
//        return hr;
//    }
//    if (HRESULT hr = RegisterServerWriteRegistry(inproc); FAILED(hr)) {
//        return hr;
//    }
//    if (HRESULT hr = RegisterServerWriteRegistry(progid); FAILED(hr)) {
//        return hr;
//    }
//    // HKCR\Toymath3.Calculator.1
//    if (HRESULT hr = RegisterServerWriteRegistry(hkcrProgid); FAILED(hr)) {
//        return hr;
//    }
//    // HKCR\Toymath3.Calculator.1\CLSID
//    if (HRESULT hr = RegisterServerWriteRegistry(hkcrProgidClsid); FAILED(hr)) {
//        return hr;
//    }
//    
//    ITypeLib *ptlib;
//    HRESULT hr = LoadTypeLib(g_wszModulePath, &ptlib);
//    if (SUCCEEDED(hr)) {
//        hr = RegisterTypeLib(ptlib, g_wszModulePath, nullptr);
//        ptlib->Release();
//    }
//    return hr;
}

STDAPI DllUnregisterServer(void) {
    return E_NOTIMPL;

    // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
    // この関数では、レジストリから登録されているモジュールの情報を削除します。
    // レジストリの操作は、ちょっとしたミスでシステムを破壊してしまう危険が伴います。
    // 何が行われるかを完全に理解していない限り実行をしないでください。
    // もし試すのであれば、壊れても被害の少ない実験用の環境で試すことを強くおすすめします。
    // 必ずトラブルが発生しても復旧できるように復元ポイントを作成しておいてください。
    //
    // サンプルをあげておきます。
    // 正しく動作することは一切保証できません。
    // 不用意に実行しないでください。
    // ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️
    
//    HRESULT hr = UnRegisterTypeLib(LIBID_ToymathLib3, 1, 0, 0, SYS_WIN32);
//
//    LPOLESTR lpstrClsid;
//    StringFromCLSID(CLSID_Calculator, &lpstrClsid);
//    std::wstring keyClsid = std::format(L"CLSID\\{}", lpstrClsid);
//    std::wstring keyInproc = std::format(L"CLSID\\{}\\InprocServer32", lpstrClsid);
//    std::wstring keyProgId = std::format(L"CLSID\\{}\\ProgId", lpstrClsid);
//    std::wstring keyHkcrProgid = std::format(L"{}", g_wszProgId);
//    std::wstring keyHkcrProgidClsid = std::format(L"{}\\CLSID", g_wszProgId);
//    CoTaskMemFree(lpstrClsid);
//    
//    if (RegDeleteKeyW(HKEY_CLASSES_ROOT, keyHkcrProgidClsid.c_str()) != ERROR_SUCCESS) {
//        hr = E_FAIL;
//    }
//    if (RegDeleteKeyW(HKEY_CLASSES_ROOT, keyHkcrProgid.c_str()) != ERROR_SUCCESS) {
//        hr = E_FAIL;
//    }
//    if (RegDeleteKeyW(HKEY_CLASSES_ROOT, keyProgId.c_str()) != ERROR_SUCCESS) {
//        hr = E_FAIL;
//    }
//    if (RegDeleteKeyW(HKEY_CLASSES_ROOT, keyInproc.c_str()) != ERROR_SUCCESS) {
//        hr = E_FAIL;
//    }
//    if (RegDeleteKeyW(HKEY_CLASSES_ROOT, keyClsid.c_str()) != ERROR_SUCCESS) {
//        hr = E_FAIL;
//    }
//
//    return hr;
}

toymath3.idl

import "unknwn.idl";

[object, uuid(1dc508ef-d0eb-420a-b213-dd16aa0dfee0)]
interface ICalculator : IUnknown {
    HRESULT Add([in] long x, [in] long y, [out, retval] long *result);
    HRESULT Sub([in] long x, [in] long y, [out, retval] long *result);
    HRESULT Mul([in] long x, [in] long y, [out, retval] long *result);
    HRESULT Div([in] long x, [in] long y, [out, retval] long *result);
}

[
    uuid(f90bdc34-89b3-46dd-b262-edf4aba3a935),
    lcid(0),
    version(1.0),
    helpstring("Toymath Library 3")
]
library ToymathLib3 {
    importlib("stdole32.tlb");

    [uuid(957E455A-8FD1-4D33-8057-462897B00888)]
    coclass Calculator {
        [default] interface ICalculator;
    }
}

toymath3.rc

1   TYPELIB "toymath3.tlb"