====== Toymath3 ====== 作成日: 2023-09-02 (土) [[https://youtu.be/ZVBSrgK-9Lg|第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 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(this); } else if (riid == IID_ICalculator) { *ppv = static_cast(this); } else { *ppv = nullptr; return E_NOINTERFACE; } reinterpret_cast(*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 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(this); } else if (riid == IID_IClassFactory) { *ppv = static_cast(this); } else { *ppv = nullptr; return E_NOINTERFACE; } reinterpret_cast(*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 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 // #include // #include // #include 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; //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"