8 / 3 / 1
Регистрация: 11.08.2016
Сообщений: 44
1

Простой пример создания COM компонента

15.04.2021, 09:59. Показов 19466. Ответов 8

Студворк — интернет-сервис помощи студентам
Доброго времени суток! В универе по системному программированию дали лабу: написать либу с COM-компонентом и программу, которая бы ее использовала. Не могу никак разобраться, как грамотно, нормально написать простой COM-компонент. В теории я вроде бы разобрался, но на практике ничего не выходит.
На MSDN я нашел пример "простого" COM-компонента с использованием WinRT (https://docs.microsoft.com/ru-... -coclasses), но из того кода я ничего не понял - мои знания C++ на уровне "Си + ООП от C#". Нагуглив несколько старых статей в интернете...
1) https://www.rsdn.org/article/com/introcom.xml
2) http://www.comprice.ru/article... p?ID=42510
...я сумел кое-что написать (библиотеку) и это даже скомпилилось) А вот клиент не компилится. XD Но проблема еще и в том, что я не могу понять, как теперь эту библиотеку использовать: .lib-файл не создается (т.к., насколько я понимаю, нет ни одной переменной, которая бы содержала __declspec(dllexport) ). Объясните, пожалуйста, как решить эту проблему, где у меня ошибки. На данный момент я в тупике, ощущение, что сделал неправильно все, что можно было) XD Но хочу разобраться.

Когда .lib-файл не создался, я попробовал добавить DllExport везде, где можно, но само собой это не сработало)

БИБЛИОТЕКА
framework.h - тут ничего интересного, но все же скину для полноты картины
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
#pragma once
 
#define WIN32_LEAN_AND_MEAN             // Исключите редко используемые компоненты из заголовков Windows
// Файлы заголовков Windows
#include <windows.h>
#include <objbase.h>
 
#define DllExport __declspec(dllexport)

CA.h
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include "pch.h"
#include "IX.h"
#include "IY.h"
 
DllExport class CA : public IX, public IY
{
    // Реализация методов IUnknown
    DllExport virtual HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
    DllExport virtual ULONG __stdcall AddRef();
    DllExport virtual ULONG __stdcall Release();
    // Реализация интерфейса IX
    DllExport virtual void __stdcall Fx();
    // Реализация интерфейса IY
    DllExport virtual void __stdcall Fy();
};

IX.h
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
#pragma once
 
// {23F01AC6-8D3E-4BCD-A33A-4AB364425FEA}
static const GUID IID_IX =
{ 0x23f01ac6, 0x8d3e, 0x4bcd, { 0xa3, 0x3a, 0x4a, 0xb3, 0x64, 0x42, 0x5f, 0xea } };
 
DllExport class IX : IUnknown
{
    DllExport virtual void __stdcall Fx() = 0;
};

IY.h
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
#pragma once
 
// {86E45419-5366-4BBD-9C45-59DE61E432C9}
static const GUID IID_IY =
{ 0x86e45419, 0x5366, 0x4bbd, { 0x9c, 0x45, 0x59, 0xde, 0x61, 0xe4, 0x32, 0xc9 } };
 
interface IY : IUnknown
{
    virtual void __stdcall Fy() = 0;
};

CA.cpp
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
#include "pch.h"
#include "CA.h"
#include <iostream>
 
// класс СОМ-объекта
 
// Реализация методов IUnknown
DllExport ULONG __stdcall CA::AddRef() { return 0; }; // пока ничего не делает
DllExport ULONG __stdcall CA::Release() { return 0; }; // пока ничего не делает
// Реализация интерфейса IX
DllExport void __stdcall CA::Fx() { printf("Fx\n"); }
// Реализация интерфейса IY
DllExport void __stdcall CA::Fy() { printf("Fy\n"); }

Еще есть pch.h и pch.cpp, но их показывать нет смысла.

КЛИЕНТ
В клиенте VS жалуется на строки 16, 30 и 43. Этот пример я взял из интернета.
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#define _CRT_SECURE_NO_WARNINGS
 
#include "CA.h"
#include "IX.h"
#include "IY.h"
 
#include <iostream>
 
IUnknown* CreateInstance()
{
    IUnknown* pI = (IX*)new CA;
    pI->AddRef();
    return pI;
}
void main()
{
    HRESULT hr;
    // Создание объекта
    IUnknown* pIUnknown = CreateInstance();
    IX* pIX = NULL;
    // Получить интерфейс IX
    hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX);
    if (SUCCEEDED(hr))
    { // вызвать метод Fx
        pIX->Fx();
    }
    else printf("Объект не имеет интерфейса \n");
    IY* pIY = NULL;
    // Получить интерфейс IY
    hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY);
    if (SUCCEEDED(hr))
    { // вызвать метод Fy
        pIY->Fy();
    }
    else printf("Объект не имеет интерфейса \n");
    // Получить интерфейс IY через интерфейс IX
    IY* pIY_IX = NULL;
    hr = pIX->QueryInterface(IID_IY, (void**)&pIY_IX);
    if (SUCCEEDED(hr))
    { // вызвать метод Fy
        pIY_IX->Fy();
    }
    else printf("Объект не имеет интерфейса \n");
    // Получить интерфейс IUnknown через интерфейс IY
    IUnknown* pIUnknown_IY = NULL;
    hr = pIY->QueryInterface(IID_IUnknown, (void**)&pIUnknown_IY);
    if (SUCCEEDED(hr))
        if (pIUnknown_IY == pIUnknown)
        {
            printf("указатели совпадают \n");
        }
        else printf("указатели не совпадают \n");
    // Удалить объект
    delete pIUnknown;
}

Надеюсь, кто-нибудь сможет грамотно объяснить, как это работает и как все соединить. Заранее спасибо!
0
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
15.04.2021, 09:59
Ответы с готовыми решениями:

простой пример использования компонента TrackBar
люди, здравствуйте! помогите плиз с вопросом! мне нужно к курсовому прикрепить немного теории и...

Простой пример создания нового потока с интерфейсом Runnable
public class theThread implements Runnable{ Thread t; theThread(){ t = new Thread(this,...

Привидите простой пример создания Dll файла, но чтобы в самом Dll находился файл.
Пожалуйста привидите простой пример создания Dll файла, но чтобы в самом Dll находился файл (к...

Простой вариант дублирования компонента
К примеру - есть панель с компонентами и со свойством Visible:=false; (Используем эту панель как...

8
2350 / 810 / 309
Регистрация: 10.02.2018
Сообщений: 1,903
15.04.2021, 18:07 2
Цитата Сообщение от Ivanshka Посмотреть сообщение
Но проблема еще и в том, что я не могу понять, как теперь эту библиотеку использовать: .lib-файл не создается (т.к., насколько я понимаю, нет ни одной переменной, которая бы содержала __declspec(dllexport) ). Объясните, пожалуйста, как решить эту проблему, где у меня ошибки. На данный момент я в тупике, ощущение, что сделал неправильно все, что можно было) XD Но хочу разобраться.
Библиотека должна экспортировать функции DllGetClassObject и DllCanUnloadNow. А в клиентской части нужно создавать объект с помощью специальных функций, например CoCreateInstance.
1
Модератор
3382 / 2154 / 352
Регистрация: 13.01.2012
Сообщений: 8,364
16.04.2021, 13:57 3
Ivanshka, https://www.cyberforum.ru/com-... ost3538077
0
8 / 3 / 1
Регистрация: 11.08.2016
Сообщений: 44
30.04.2021, 21:05  [ТС] 4
Цитата Сообщение от Ygg Посмотреть сообщение
Библиотека должна экспортировать функции DllGetClassObject и DllCanUnloadNow. А в клиентской части нужно создавать объект с помощью специальных функций, например CoCreateInstance.
Разобрался получше, сумел создать и зарегистрировать COM-компонент, написал клиент и вроде как все работает. Но возникли 2 другие проблемы.
Компонент "по классике жанра" - MathComponent, но с двумя интерфейсами (+ и -, * и /). Проблемы:
1) Клиент вылетает почти в самом конце при вызове
C++
1
((Math*)pAdder)->Release();
2) Не могу понять почему, но методы IMultiplier->Mul() и IMultiplier->Div() вместо * и / делают + и - соответственно. Клиент вызывает Mul и Div, но в библиотеке буквально вызывается Add и Sub соответственно, я даже через отладчик проверял.
3) По диаграмме последовательности COM, которую я прикрепил, видно, что у нас должна быть возможность получить один интерфейс компонента через другой, но почему-то я так сделать не могу - функции QueryInterface в моем интерфейсе нет, хотя они и наследуются от IUnknown.

Клиент:
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// ТЕОРИЯ И ПРИМЕРЫ
// https://www.rsdn.org/article/com/introcom.xml#EDHAE
// http://www.comprice.ru/articles/detail.php?ID=42510
// ГОДНОЕ ОПИСАНИЕ МОДИФИКАТОРОВ __stdcall и его аналогов:
// https://www.rsdn.org/forum/cpp/159835.all
 
#define _CRT_SECURE_NO_WARNINGS
 
#include <initguid.h>
#include "Math.h"
#include "MathClassFactory.h"
#include "IAdder.h"
#include "IMultiplier.h"
#include <stdio.h>
 
int main(int argc, char* argv[])
{
    CoInitialize(NULL);
    IClassFactory *pClassFactory;
    HRESULT hr = CoGetClassObject(CLSID_Math,
        CLSCTX_INPROC_SERVER, NULL,
        IID_IClassFactory, (void**)&pClassFactory);
 
    if (SUCCEEDED(hr)) {
        printf("ok!\n");
    }
    else {
        switch (hr){
        case E_NOINTERFACE: printf("no interface\n"); return -1;
        case REGDB_E_CLASSNOTREG: printf("class not registered\n"); return -1;
        case E_FAIL: printf("fail: unspecified error\n"); return -1;
        default: printf("unknown error %x (%d)\n", hr, hr); return -1;
        }
    }
 
    // Используя интерфейс фабрики классов, создаем экземпляр 
    // компонента Math
    IUnknown* pMathComponent = nullptr;
    hr = pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pMathComponent);
 
    // Освободить интерфейс фабрики классов
    pClassFactory->Release();
    
    IAdder* pAdder = nullptr;
    IMultiplier* pMultiplier = nullptr;
    hr = pMathComponent->QueryInterface(IID_IAdder, (LPVOID*)&pAdder);
    hr = pMathComponent->QueryInterface(IID_IMultiplier, (LPVOID*)&pMultiplier);
    pMathComponent->Release();
 
    double result;
    hr = pAdder->Add(1000, 8, result);
    printf("1000 + 8 = %f\n", result);
    hr = pAdder->Sub(1000, 333, result);
    printf("1000 - 333 = %f\n", result);
 
    // эти 2 метода не работают
    hr = pMultiplier->Mul(1000, 8, result);
    printf("1000 * 8 = %f\n", result);
    hr = pMultiplier->Div(1000, 333, result);
    printf("1000 / 333 = %f\n", result);
 
    ((Math*)pAdder)->Release();
 
    CoUninitialize();
    return 0;
}


Библиотека:
Laba4COM.def
Кликните здесь для просмотра всего текста
Код
;
; Laba4Com.def : Объявляем параметры модуля для DLL.
;

LIBRARY      "Laba4COM"

EXPORTS
    ; Имена точек входа для внешнего пользования помещаются здесь
    DllCanUnloadNow     PRIVATE
    DllGetClassObject   PRIVATE
    DllInstall          PRIVATE
    DllRegisterServer   PRIVATE
    DllUnregisterServer PRIVATE

pch.h
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
#ifndef PCH_H
#define PCH_H
 
// Добавьте сюда заголовочные файлы для предварительной компиляции
#include <objbase.h>
#include <assert.h>
#endif //PCH_H

Laba4COM.cpp
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "pch.h"
#include <assert.h>
 
#include "registration.h"
 
#include "Math.h"               // компонент
#include "MathClassFactory.h"   // фабрика компонента
#include "IAdder.h"             // интерфейс компонента
#include "IMultiplier.h"        // интерфейс компонента
 
// информация о компоненте
#define VersionIndependentProgID L"Laba4COM.MathComponent"
#define FriendlyName L"Laba4COM.Math.Component.COM"
#define ProgramID L"Laba4COM.MathComponent.1"
 
HMODULE hmodule;
long g_lObjs = 0;
long g_lLocks = 0;
 
BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        hmodule = hModule; // сохраняем дескриптор библиотеки для возможности зарегистрировать ее через regsvr32.exe
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
 
// ЭКСПОРТИРУЕМЫЕ ФУНКЦИИ ДЛЯ УПРАВЛЕНИЯ БИБЛИОТЕКОЙ (регистрация / дерегистрации)
 
// Возвращает фабрику классов для компонента заданного типа
HRESULT __stdcall DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
    HRESULT hr;
    MathClassFactory* pCF;
 
    pCF = 0;
 
    // Make sure the CLSID is for our Expression component
    if (rclsid != CLSID_Adder && rclsid != CLSID_Multiplier && rclsid != CLSID_Math)
        return(E_FAIL);
 
    pCF = new MathClassFactory();
 
    if (pCF == 0)
        return(E_OUTOFMEMORY);
 
    hr = pCF->QueryInterface(riid, ppv);
 
    // Check for failure of QueryInterface
    if (FAILED(hr))
    {
        delete pCF;
        pCF = 0;
    }
 
    return hr;
}
 
STDAPI DllCanUnloadNow(void)
{
    if (g_lObjs || g_lLocks)
        return(S_FALSE);
    else
        return(S_OK);
}
 
STDAPI DllInstall(BOOL b, PCWSTR s) {
    return S_OK;
}
 
STDAPI DllRegisterServer() {
    HRESULT result = RegisterServer(hmodule, CLSID_Math, FriendlyName, VersionIndependentProgID, ProgramID);
    return result;
}
 
STDAPI DllUnregisterServer() {
    HRESULT result = UnregisterServer(CLSID_Math, VersionIndependentProgID, ProgramID);
    return result;
}

Math.h
Кликните здесь для просмотра всего текста
C++
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
#pragma once
#include "pch.h"
#include "IAdder.h"
#include "IMultiplier.h"
 
// {740B73EF-83ED-4EC7-9833-341B3E0D8FA7}
static const GUID CLSID_Math =
{ 0x740b73ef, 0x83ed, 0x4ec7, { 0x98, 0x33, 0x34, 0x1b, 0x3e, 0xd, 0x8f, 0xa7 } };
 
extern long g_lObjs; // кол-во экземпляров компонента
extern long g_lLocks; // кол-во вызовов LockServer
 
class Math : public IAdder, public IMultiplier
{
protected:
    long m_lRef; // счетчик ссылок на компонент
public:
    // Конструктор / деструктор
    Math();
    ~Math();
    // Реализация методов IUnknown
    STDMETHOD(QueryInterface(const IID& iid, void** ppv)); // разворачивается в virtual HRESULT CALLBACK QueryInterface(const IID& iid, void** ppv);
    STDMETHOD_(ULONG, AddRef());
    STDMETHOD_(ULONG, Release());
    // Реализация интерфейса IAdder
    STDMETHOD(Add(const double x, const double y, double& z));
    STDMETHOD(Sub(const double x, const double y, double& z));
    // Реализация интерфейса IMultiplier
    STDMETHOD(Mul(const double x, const double y, double& z));
    STDMETHOD(Div(const double x, const double y, double& z));
};

Math.cpp
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include "pch.h"
#include "Math.h"
#include "IAdder.h"
#include "IMultiplier.h"
 
#include <iostream>
 
// класс СОМ-объекта
 
Math::Math()
{
    m_lRef = 0;
 
    // Увеличить значение внешнего счетчика объектов
    InterlockedIncrement(&g_lObjs);
}
 
Math::~Math()
{
    // Уменьшить значение внешнего счетчика объектов
    InterlockedDecrement(&g_lObjs);
}
 
// Метод выбора интерфейса для работы с компонентом
STDMETHODIMP Math::QueryInterface(const IID& iid, void** ppv)
{
    *ppv = 0;
 
    if (iid == IID_IUnknown || iid == IID_IAdder || iid == IID_IMultiplier)
        *ppv = this;
 
    if (*ppv)
    {
        AddRef();
        return(S_OK);
    }
    return (E_NOINTERFACE);
}
 
// Реализация методов IUnknown
 
// Увеличивает внутренний счетчик ссылок
STDMETHODIMP_(ULONG) Math::AddRef() { return InterlockedIncrement(&m_lRef); };
 
// Уменьшает внутренний счетчик ссылок и удаляет компонент, если ссылок не осталось
STDMETHODIMP_(ULONG) Math::Release() {
    if (InterlockedDecrement(&m_lRef) == 0)
    {
        delete this;
        return 0;
    }
 
    return m_lRef;
};
 
// Реализация интерфейса IAdder
 
STDMETHODIMP Math::Add(const double x, const double y, double& z) {
    z = x + y;
    return S_OK;
}
 
STDMETHODIMP Math::Sub(const double x, const double y, double& z) {
    z = x - y;
    return S_OK;
}
 
// Реализация интерфейса IMultiplier
 
STDMETHODIMP Math::Mul(const double x, const double y, double& z) {
    z = x * y;
    return S_OK;
}
 
STDMETHODIMP Math::Div(const double x, const double y, double& z) {
    z = x / y;
    return S_OK;
}

MathClassFactory.h
Кликните здесь для просмотра всего текста
C++
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
#pragma once
 
// {F959D111-FD7D-4145-BDAB-0202FB48A880}
static const GUID CLSID_MathClassFactory =
{ 0xf959d111, 0xfd7d, 0x4145, { 0xbd, 0xab, 0x2, 0x2, 0xfb, 0x48, 0xa8, 0x80 } };
 
// фабрика объектов. отвечает за подсчет ссылок на себя и выбор нужного интерфейса
class MathClassFactory : public IClassFactory
{
protected:
    // Счетчик обращений к экземпляру ClassFactory
    long m_lRef;
public:
    MathClassFactory();
    ~MathClassFactory();
 
    // реализация интерфейса IUnknown 
    STDMETHOD(QueryInterface(REFIID riid, void** ppv)); // REFIID расширяется в const IID&
    STDMETHOD_(ULONG, AddRef());
    STDMETHOD_(ULONG, Release());
 
    // реализация интерфеса  IClassFactory
    STDMETHOD(CreateInstance(LPUNKNOWN pUnk, REFIID riid, void** ppv));
    STDMETHOD(LockServer(BOOL fLock));
};

MathClassFactory.cpp
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "pch.h"
#include "Math.h"
#include "MathClassFactory.h"
 
MathClassFactory::MathClassFactory() { m_lRef = 0; }
 
MathClassFactory::~MathClassFactory() { }
 
STDMETHODIMP_(ULONG) MathClassFactory::AddRef() { return InterlockedIncrement(&m_lRef); }
 
STDMETHODIMP_(ULONG) MathClassFactory::Release()
{
    if (InterlockedDecrement(&m_lRef) == 0)
    {
        delete this;
        return 0;
    }
 
    return m_lRef;
}
 
STDMETHODIMP MathClassFactory::QueryInterface(REFIID riid, void** ppv)
{
    *ppv = 0;
 
    if (riid == IID_IUnknown || riid == IID_IClassFactory)
        *ppv = this;
 
    if (*ppv)
    {
        AddRef();
        return S_OK;
    }
 
    return(E_NOINTERFACE);
}
 
// Метод для запроса у фабрики ссылки на компонент
STDMETHODIMP MathClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj)
{
    Math* pMath;
    HRESULT hr;
 
    // Инициализация возвращаемого значения
    // на случай возникновения проблем
    *ppvObj = 0;
 
    // Создание нового экземпляра объекта Math
    pMath = new Math();
    if (pMath == 0)
        return(E_OUTOFMEMORY);
 
    // Получить у созданного объекта указатель
    // на запрашиваемый интерфейс
    hr = pMath->QueryInterface(riid, ppvObj);
    if (FAILED(hr))
        delete pMath;
 
    return hr;
}
 
STDMETHODIMP MathClassFactory::LockServer(BOOL fLock)
{
    if (fLock)
        InterlockedIncrement(&g_lLocks);
    else
        InterlockedDecrement(&g_lLocks);
 
    return S_OK;
}

registration.h
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
// вспомогательные методы для регистрации / дерегистрации библиотеки
 
const int CLSID_STRING_SIZE = 39;
 
BOOL setKeyAndValue(const WCHAR* szKey,         // smw:const char* szKey
    const WCHAR* szSubkey,                      // smw:const char* szSubkey,
    const WCHAR* szValue);                      // smw: const char* szValue
 
void CLSIDtochar(const CLSID& clsid,            // Convert a CLSID into a char string.
    WCHAR* szCLSID,                             // smw:char* szCLSID,
    int length);
 
// Delete szKeyChild and all of its descendents.
LONG recursiveDeleteKey(HKEY hKeyParent, const WCHAR* szKeyChild);
 
 
HRESULT RegisterServer(HMODULE hModule,            // DLL module handle
    const CLSID& clsid,         // Class ID
    const WCHAR* szFriendlyName, // Friendly Name
    const WCHAR* szVerIndProgID, // Programmatic
    const WCHAR* szProgID);      //   IDs
 
HRESULT UnregisterServer(const CLSID& clsid,
    const WCHAR* szVerIndProgID,
    const WCHAR* szProgID);
 
// Convert a CLSID to a char string
void CLSIDtochar(const CLSID& clsid,
    WCHAR* szCLSID,          // smw:char* szCLSID,
    int length);
 
// Parent of key to delete
LONG recursiveDeleteKey(HKEY hKeyParent,
    const WCHAR* lpszKeyChild);  // Key to delete
 
BOOL setKeyAndValue(const WCHAR* szKey,         // smw:const char* szKey
    const WCHAR* szSubkey,      // smw:const char* szSubkey,
    const WCHAR* szValue);       // smw: const char* szValue

registration.cpp
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "pch.h"
#include "registration.h"
 
HRESULT RegisterServer(HMODULE hModule, const CLSID& clsid, const WCHAR* szFriendlyName, const WCHAR* szVerIndProgID, const WCHAR* szProgID)
{
 
    WCHAR szModule[512];
    DWORD dwResult = GetModuleFileName(hModule, szModule, sizeof(szModule) / sizeof(WCHAR));
    assert(dwResult != 0);
 
    WCHAR szCLSID[CLSID_STRING_SIZE];
    CLSIDtochar(clsid, szCLSID, sizeof(szCLSID));
 
    WCHAR szKey[264];      //smw szKey[64]
    wcscpy_s(szKey, L"CLSID\\");
    wcscat_s(szKey, szCLSID);
 
    setKeyAndValue(szKey, NULL, szFriendlyName);
    setKeyAndValue(szKey, L"InprocServer32", szModule);
    setKeyAndValue(szKey, L"ProgID", szProgID);
    setKeyAndValue(szKey, L"VersionIndependentProgID", szVerIndProgID);
    setKeyAndValue(szVerIndProgID, NULL, szFriendlyName);
    setKeyAndValue(szVerIndProgID, L"CLSID", szCLSID);
    setKeyAndValue(szVerIndProgID, L"CurVer", szProgID);
    setKeyAndValue(szProgID, NULL, szFriendlyName);
    setKeyAndValue(szProgID, L"CLSID", szCLSID);
 
    return S_OK;
}
 
HRESULT UnregisterServer(const CLSID& clsid, const WCHAR* szVerIndProgID, const WCHAR* szProgID)
{
    WCHAR szCLSID[CLSID_STRING_SIZE];
    CLSIDtochar(clsid, szCLSID, sizeof(szCLSID));
 
    WCHAR szKey[64];
    wcscpy_s(szKey, L"CLSID\\");
    wcscat_s(szKey, szCLSID);
 
    LONG lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szKey);
    assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND)); // Subkey may not exist.
    lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szVerIndProgID);
    assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND)); // Subkey may not exist.
    lResult = recursiveDeleteKey(HKEY_CLASSES_ROOT, szProgID);
    assert((lResult == ERROR_SUCCESS) || (lResult == ERROR_FILE_NOT_FOUND)); // Subkey may not exist.
 
    return S_OK;
}
 
void CLSIDtochar(const CLSID& clsid, WCHAR* szCLSID, int length)
{
    assert(length >= CLSID_STRING_SIZE);
 
    LPOLESTR wszCLSID = NULL;
    HRESULT hr = StringFromCLSID(clsid, &wszCLSID);
    assert(SUCCEEDED(hr));
    wcscpy_s(szCLSID, CLSID_STRING_SIZE, wszCLSID);
    CoTaskMemFree(wszCLSID);
 
}
 
LONG recursiveDeleteKey(HKEY hKeyParent, const WCHAR* lpszKeyChild)
{
    HKEY hKeyChild;
    LONG lRes = RegOpenKeyEx(hKeyParent, lpszKeyChild, 0,
        KEY_ALL_ACCESS, &hKeyChild);
    if (lRes != ERROR_SUCCESS)
    {
        return lRes;
    }
 
    FILETIME time;
    WCHAR szBuffer[256];
    DWORD dwSize = 256;
    while (RegEnumKeyEx(hKeyChild, 0, szBuffer, &dwSize, NULL,
        NULL, NULL, &time) == S_OK)
    {
        lRes = recursiveDeleteKey(hKeyChild, szBuffer);
        if (lRes != ERROR_SUCCESS)
        {
            RegCloseKey(hKeyChild);
            return lRes;
        }
        dwSize = 256;
    }
 
    RegCloseKey(hKeyChild);
    return RegDeleteKey(hKeyParent, lpszKeyChild);
}
 
BOOL setKeyAndValue(const WCHAR* szKey, const WCHAR* szSubkey, const WCHAR* szValue)
{
    HKEY hKey;
    WCHAR szKeyBuf[1024];
 
    wcscpy_s(szKeyBuf, szKey);
 
    if (szSubkey != NULL)
    {
        wcscat_s(szKeyBuf, L"\\");
        wcscat_s(szKeyBuf, szSubkey);
    }
    long lResult = RegCreateKeyEx(HKEY_CLASSES_ROOT,
        szKeyBuf,
        0, NULL, REG_OPTION_NON_VOLATILE,
        KEY_ALL_ACCESS, NULL,
        &hKey, NULL);
    if (lResult != ERROR_SUCCESS)
    {
        return FALSE;
    }
 
    if (szValue != NULL)
    {
        RegSetValueEx(hKey, NULL, 0, REG_SZ,
            (BYTE*)szValue,
            2 * wcslen(szValue) + 1);
    }
 
    RegCloseKey(hKey);
    return TRUE;
}
Миниатюры
Простой пример создания COM компонента  
0
2350 / 810 / 309
Регистрация: 10.02.2018
Сообщений: 1,903
01.05.2021, 00:38 5
Цитата Сообщение от Ivanshka Посмотреть сообщение
Клиент вылетает почти в самом конце при вызове Release...
Клиент вызывает Mul и Div, но в библиотеке буквально вызывается Add и Sub соответственно...
Вы как-то не правильно интерфейсы реализуете и вызываете их то же не очень правильно.
IAdder и IMultiplier - это два экспортируемых из библиотеки интерфейса (кода вы не дали, но я так предполагаю).
Math - это уже не интерфейс, это внутренний библиотечный класс, реализующий оба вышеозначенных интерфейса.
Это весьма спорное решение и, как видно из вашего кода, вы не совсем понимаете, что с этим нужно делать дальше.
Самым простым и наглядным примером было бы написать две разные реализации интерфейсов, одну для IAdder, вторую для IMultiplier. Добавить ещё два GUID-а, по одному на каждый интерфейс. Из клиента создать интерфейсы двумя вызовами CreateInstance с указанием соответствующих GUID-ов. Каждый вызов CreateInstance будет создавать новый объект реализующий соответствующий интерфейс. Два интерфейса - два объекта.
Если же вы решили делать один объект, который будет поддерживать два интерфейса, то нужно писать соответствующий обработчик QueryInterface, который будет грамотно преобразовывать указатель на this. На разные клиентские запросы нужно возвращать (IAdder*)this или (IMultiplier*)this.

Общие замечания к вашему коду.
1) #include "Math.h" внутри клиента - это нонсенс, как и преобразование ((Math*)pAdder). Клиент оперирует только интерфейсами и GUID-ами.
2) Каждый QueryInterface требует последующего вызова Release. Вы дважды в клиентском коде запрашиваете интерфейсы и только один раз делаете освобождение.
0
8 / 3 / 1
Регистрация: 11.08.2016
Сообщений: 44
02.05.2021, 23:32  [ТС] 6
Цитата Сообщение от Ygg Посмотреть сообщение
IAdder и IMultiplier - это два экспортируемых из библиотеки интерфейса (кода вы не дали, но я так предполагаю).
Блин, точно) XD Я и не заметил, что коды интерфейсов не скинул) На всякий случай скину)
IAdder.h
Кликните здесь для просмотра всего текста
C++
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
#pragma once
 
#include "pch.h"
 
// {23F01AC6-8D3E-4BCD-A33A-4AB364425FEA}
static const GUID CLSID_Adder =
{ 0x23f01ac6, 0x8d3e, 0x4bcd, { 0xa3, 0x3a, 0x4a, 0xb3, 0x64, 0x42, 0x5f, 0xea } };
// {23F01AC6-8D3E-4BCD-A33A-4AB364425FEA}
static const GUID IID_IAdder =
{ 0x23f01ac6, 0x8d3e, 0x4bcd, { 0xa3, 0x3a, 0x4a, 0xb3, 0x64, 0x42, 0x5f, 0xea } };
 
class IAdder : IUnknown
{
public:
    // STDMETHOD расширяется в virtual HRESULT __stdcall method(args) = 0
    // суммирование
    STDMETHOD(Add(const double x,                // [in]
        const double y,                          // [in]
        double& z                                // [out]
    )) PURE;
 
    // вычитание
    STDMETHOD(Sub(const double x,                // [in]
        const double y,                          // [in]
        double& z                                // [out]
    )) PURE;
};

IMultiplier.h
Кликните здесь для просмотра всего текста
C++
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
#pragma once
 
#include "pch.h"
 
// {86E45419-5366-4BBD-9C45-59DE61E432C9}
static const GUID CLSID_Multiplier =
{ 0x86e45419, 0x5366, 0x4bbd, { 0x9c, 0x45, 0x59, 0xde, 0x61, 0xe4, 0x32, 0xc9 } };
// {86E45419-5366-4BBD-9C45-59DE61E432C9}
static const GUID IID_IMultiplier =
{ 0x86e45419, 0x5366, 0x4bbd, { 0x9c, 0x45, 0x59, 0xde, 0x61, 0xe4, 0x32, 0xc9 } };
 
class IMultiplier : IUnknown
{
public:
    // умножение
    STDMETHOD(Mul(const double x,                // [in]
        const double y,                          // [in]
        double& z                                // [out]
    )) PURE;
 
    // деление
    STDMETHOD(Div(const double x,                // [in]
        const double y,                          // [in]
        double& z                                // [out]
    )) PURE;
};

Цитата Сообщение от Ygg Посмотреть сообщение
Если же вы решили делать один объект, который будет поддерживать два интерфейса, то нужно писать соответствующий обработчик QueryInterface, который будет грамотно преобразовывать указатель на this. На разные клиентские запросы нужно возвращать (IAdder*)this или (IMultiplier*)this.
Да, именно так и требуется сделать.
Цитата Сообщение от Ygg Посмотреть сообщение
#include "Math.h" внутри клиента - это нонсенс, как и преобразование ((Math*)pAdder). Клиент оперирует только интерфейсами и GUID-ами.
Да, я это понимаю, просто сделал так на автомате, когда потребовалось добавить GUID в проект клиента. Позже просто скопирую нужные GUID'ы, а эту чушь удалю.
Цитата Сообщение от Ygg Посмотреть сообщение
Каждый QueryInterface требует последующего вызова Release. Вы дважды в клиентском коде запрашиваете интерфейсы и только один раз делаете освобождение.
Точно( Не заметил, когда экспериментировал с интерфейсами, выявляя причину ошибки.
Буду дальше исправлять косяки, большое спасибо!

Добавлено через 17 минут
Цитата Сообщение от Ygg Посмотреть сообщение
Если же вы решили делать один объект, который будет поддерживать два интерфейса, то нужно писать соответствующий обработчик QueryInterface, который будет грамотно преобразовывать указатель на this. На разные клиентские запросы нужно возвращать (IAdder*)this или (IMultiplier*)this.
Точно, сделал приведение типов, где нужно было - и проблема с вызовами функций исчезла)
Math.cpp
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include "pch.h"
#include "Math.h"
 
#include <iostream>
 
// класс СОМ-объекта
 
Math::Math()
{
    m_lRef = 0;
 
    // Увеличить значение внешнего счетчика объектов
    InterlockedIncrement(&g_lObjs);
}
 
Math::~Math()
{
    // Уменьшить значение внешнего счетчика объектов
    InterlockedDecrement(&g_lObjs);
}
 
// Метод выбора интерфейса для работы с компонентом
STDMETHODIMP Math::QueryInterface(const IID& iid, void** ppv)
{
    *ppv = 0;
 
    if (iid == IID_IUnknown)
        *ppv = this;
    else if (iid == IID_IAdder)
        *ppv = (IAdder*)this;
    else if (iid == IID_IMultiplier)
        *ppv = (IMultiplier*)this;
 
 
    if (*ppv)
    {
        AddRef();
        return(S_OK);
    }
    return (E_NOINTERFACE);
}
 
// Реализация методов IUnknown
 
// Увеличивает внутренний счетчик ссылок
STDMETHODIMP_(ULONG) Math::AddRef() { return InterlockedIncrement(&m_lRef); };
 
// Уменьшает внутренний счетчик ссылок и удаляет компонент, если ссылок не осталось
STDMETHODIMP_(ULONG) Math::Release() {
    if (InterlockedDecrement(&m_lRef) == 0)
    {
        delete this;
        return 0;
    }
 
    return m_lRef;
};
 
// Реализация интерфейса IAdder
 
STDMETHODIMP Math::Add(const double x, const double y, double& z) {
    z = x + y;
    return S_OK;
}
 
STDMETHODIMP Math::Sub(const double x, const double y, double& z) {
    z = x - y;
    return S_OK;
}
 
// Реализация интерфейса IMultiplier
 
STDMETHODIMP Math::Mul(const double x, const double y, double& z) {
    z = x * y;
    return S_OK;
}
 
STDMETHODIMP Math::Div(const double x, const double y, double& z) {
    z = x / y;
    return S_OK;
}

Но как быть с вызовом Release()? Я понимаю, что приводить тип интерфейса к типу класса Math для вызова Release() - полный бред, но что нужно исправить, чтобы можно было из самого интерфейса вызывать функцию? Вот это я понять никак не могу...(
0
2350 / 810 / 309
Регистрация: 10.02.2018
Сообщений: 1,903
03.05.2021, 10:16 7
Лучший ответ Сообщение было отмечено Ivanshka как решение

Решение

Цитата Сообщение от Ivanshka Посмотреть сообщение
Но как быть с вызовом Release()?
Просто вызывайте функцию у нужного интерфейса и всё.
Запросили интерфейс сложения, поработали с ним - сделайте его освобождение.
Запросили интерфейс умножения, поработали с ним - сделайте его освобождение.

Что такое интерфейс? Интерфейс - это табличка в памяти, которая содержит адреса всех функций перечисленных в описании интерфейса. Получая интерфейс - вы получаете указатель на начало соответствующей таблички. Вызовы функций интерфейса выполняются через вызовы адресов из таблички.

Класс, который наследуется от интерфейса, содержит в себе помимо переменных ещё и табличку функций интерфейса. Это значит, что когда вы создаёте экземпляр класса, то выделяется память, в которой размещаются все переменные класса и табличка виртуальных функций. Когда вы переопределяете виртуальную функцию на новую, то её новый адрес помещается в старую табличку.

Если же вы решили сделать класс, который наследуется от двух интерфейсов, то внутри такого класса размещаются две таблички функций, по одной на каждый интерфейс. Именно к этим табличкам вы приводите указатель this, когда клиент запрашивает соответствующие интерфейсы.

Каждый ваш интерфейс ещё унаследован от IUnknown. Это означает, что табличка функций интерфейса содержит как собственные функции, так и функции базового интерфейса. Как я уже говорил, при создании класса, который наследуется от двух ваших интерфейсов, получаются две таблички функций. Каждая табличка будет содержать три функции IUnknown. Нужно, что бы функции IUnknown каждой таблички ссылались на общую реализацию сделанную для класса. Тогда не важно какой интерфейс вы освобождаете, всё равно должна быть вызвана единая общая функция.

Добавлено через 50 минут
Наврал малость по табличкам, но не принципиально. В экземпляре класса хранится не сама табличка, а адрес таблички. Важно, что нужно вызывать единую функцию Release (AddRef, QueryInterface). А в разных табличках интерфейсов одного класса могут быть указаны одинаковые адреса функций.
2
8 / 3 / 1
Регистрация: 11.08.2016
Сообщений: 44
09.05.2021, 16:55  [ТС] 8
Цитата Сообщение от Ygg Посмотреть сообщение
Просто вызывайте функцию у нужного интерфейса и всё.
Да, я знаю, но у меня почему-то не получалось вызвать Release у интерфейса - я просто не мог вызвать функции интерфейса IUnknown через указатель интерфейса. Как оказалось, интерфейс IUnknown нужно было наследовать другими интерфейсами с модификатором public:
C++
1
class IAdder : public IUnknown { }
.
Только так я могу вызывать его функции в клиенте.
Что касается теории по интерфейсам, то это я знаю, и все же спасибо - освежил память) :-)
Теперь я до конца понял, как что делать, и все работает.
Большое спасибо за помощь!)

Добавлено через 30 минут
Цитата Сообщение от Ygg Посмотреть сообщение
#include "Math.h" внутри клиента - это нонсенс, как и преобразование ((Math*)pAdder). Клиент оперирует только интерфейсами и GUID-ами.
Теперь только осталось убедиться, что исправил эту мелочь правильно. Значит, include интерфейсов из библиотеки в клиент - это нормально, но include самого класса компонента - нет, все верно?
Кликните здесь для просмотра всего текста
C++
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// ТЕОРИЯ И ПРИМЕРЫ
#define _CRT_SECURE_NO_WARNINGS
 
#include <initguid.h>
#include "MathClassFactory.h"
#include "IAdder.h"
#include "IMultiplier.h"
#include <stdio.h>
 
// {740B73EF-83ED-4EC7-9833-341B3E0D8FA7}
static const GUID CLSID_Math =
{ 0x740b73ef, 0x83ed, 0x4ec7, { 0x98, 0x33, 0x34, 0x1b, 0x3e, 0xd, 0x8f, 0xa7 } };
 
int main(int argc, char* argv[])
{
    CoInitialize(NULL);
    IClassFactory *pClassFactory;
    HRESULT hr = CoGetClassObject(CLSID_Math,
        CLSCTX_INPROC_SERVER, NULL,
        IID_IClassFactory, (void**)&pClassFactory);
 
    if (SUCCEEDED(hr)) {
        printf("ok!\n");
    }
    else {
        switch (hr){
        case E_NOINTERFACE: printf("no interface\n"); return -1;
        case REGDB_E_CLASSNOTREG: printf("class not registered\n"); return -1;
        case E_FAIL: printf("fail: unspecified error\n"); return -1;
        default: printf("unknown error %x (%d)\n", hr, hr); return -1;
        }
    }
 
    // Используя интерфейс фабрики классов, создаем экземпляр 
    // компонента Math
    IUnknown* pMathComponent = nullptr;
    hr = pClassFactory->CreateInstance(NULL, IID_IUnknown, (void**)&pMathComponent);
 
    // Освободить интерфейс фабрики классов
    pClassFactory->Release();
    
    IAdder* pAdder = nullptr;
    IMultiplier* pMultiplier = nullptr;
    hr = pMathComponent->QueryInterface(IID_IAdder, (LPVOID*)&pAdder);
    hr = pMathComponent->QueryInterface(IID_IMultiplier, (LPVOID*)&pMultiplier);
    pMathComponent->Release();
 
    double result;
    hr = pAdder->Add(1000, 8, result);
    printf("1000 + 8 = %f\n", result);
    hr = pAdder->Sub(1000, 333, result);
    printf("1000 - 333 = %f\n", result);
 
    hr = pMultiplier->Mul(1000, 8, result);
    printf("1000 * 8 = %f\n", result);
    hr = pMultiplier->Div(1000, 333, result);
    printf("1000 / 333 = %f\n", result);
 
    pAdder->Release();
    pMultiplier->Release();
 
    CoUninitialize();
    return 0;
}
0
2350 / 810 / 309
Регистрация: 10.02.2018
Сообщений: 1,903
11.05.2021, 11:12 9
Цитата Сообщение от Ivanshka Посмотреть сообщение
Значит, include интерфейсов из библиотеки в клиент - это нормально, но include самого класса компонента - нет, все верно?
Да, верно. В целом, код клиента стал вполне рабочим.
Могу сделать пару замечаний, но они скорее косметические.
1. MathClassFactory, это стандартный интерфейс фабрики. Инклудить его имеет смысл только ради CLSID. С другой стороны, вы явно определяете в коде клиента CLSID_Math. Получается некоторая избыточность, нужно либо одно, либо другое. Обычно GUID-ы подключают инклудами, а не копипастой констант.
2. Я ни разу не встречал создание интерфейса/объекта через запрос IID_IUnknown (37 строка). Обычно есть имя интерфейса и к нему прилагается два GUID-а: CLSID_<имя интерфейса> и IID_<имя интерфейса>.
3. Я не встречал ручное создание фабрики. Для создания интерфейса обычно используется CoCreateInstance. Эта функция сама создает фабрику, создает фабрикой объект и удаляет фабрику. Такой алгоритм создания объектов универсален, нет смысла каждый раз заново прописывать лишний однотипный код в новом приложении.
0
11.05.2021, 11:12
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
11.05.2021, 11:12
Помогаю со студенческими работами здесь

Пример внешенего компонента на Delphi
У кого есть или поделитесь литературой

Момент создания компонента
на форме лежит TreeView и pageControl. на странице в педжконтроле в свойстве Onshow вот такой код -...

Ошибка создания компонента
На форму кидаю любой компонент и вылетает вот такая ошибка, может кто знает как с ней бороться?

Проблема создания компонента
Создаю новый UserControl, помещаю на него ComboBox и через мастер компонентов, наследую новому...

Простой пример
Доброе время суток! Вот написал на html-е пример меню. Можете помочь через JQ сделать эффект...

Простой пример на С++
a - типа int задаем


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2023, CyberForum.ru