Qt에서 ActiveX 서버 구축하기

QAxServer 모듈은 ActiveQt 프레임워크의 일부입니다. 이 모듈은 세 개의 클래스로 구성됩니다:

  • QAxFactory COM 객체 생성을 위한 팩토리를 정의합니다.
  • QAxBindable 는 Qt 위젯과 COM 객체 사이의 인터페이스를 제공합니다.
  • QAxAggregated 추가 COM 인터페이스를 구현하기 위해 서브클래싱할 수 있습니다.

ActiveX 컨트롤과 COM 객체의 몇 가지 구현 예제가 제공됩니다.

라이브러리 사용하기

QAxServer 라이브러리를 사용하여 표준 Qt 애플리케이션을 COM 서버로 전환하려면 .pro 파일의 QT 변수에 axserver 을 추가해야 합니다.

프로세스 외 실행 서버는 이와 같은 .pro 파일에서 생성됩니다:

TEMPLATE = app
QT  += axserver

RC_FILE  = qaxserver.rc
...

처리 중인 서버를 빌드하려면 다음과 같은 .pro 파일을 사용합니다:

TEMPLATE = lib
QT += axserver
CONFIG  += dll

DEF_FILE = qaxserver.def
RC_FILE  = qaxserver.rc
...

qaxserver.rcqaxserver.def 파일은 프레임워크의 일부이며 일반적인 위치( .pro 파일에 경로 지정)에서 사용하거나 프로젝트 디렉터리에 복사할 수 있습니다. 유형 라이브러리 항목으로 파일을 포함하는 한, 즉 버전 정보를 추가하거나 다른 도구 상자 아이콘을 지정할 수 있는 한 이러한 파일을 수정할 수 있습니다.

axserver 모듈을 사용하면 qmake 도구가 필요한 빌드 단계를 빌드 시스템에 추가합니다:

  • 바이너리를 qaxserver.lib 대신 qtmain.lib
  • idc 도구를 호출하여 COM 서버용 IDL 파일을 생성합니다.
  • (컴파일러 설치의 일부인) MIDL 도구를 사용하여 IDL을 타입 라이브러리로 컴파일합니다.
  • 결과 유형 라이브러리를 서버 바이너리에 바이너리 리소스로 첨부합니다(다시 idc 도구를 사용).
  • 서버를 등록합니다. 이 단계는 관리자 권한이 필요할 수 있으며 qaxserver_no_register 구성을 설정하여 건너뛸 수 있습니다.

후처리 단계를 건너뛰려면 qaxserver_no_postlink 구성도 설정하세요.

또한 VERSION 변수를 사용하여 버전 번호를 지정할 수도 있습니다(예: 다음과 같이).

TEMPLATE = lib
VERSION = 2.5
...

지정한 버전 번호는 등록 시 유형 라이브러리 및 서버의 버전으로 사용됩니다.

프로세스 중 대 프로세스 중

COM 서버를 독립 실행형 실행 파일로 실행할지 아니면 클라이언트 프로세스에서 공유 라이브러리로 실행할지는 주로 서버에서 제공하려는 COM 개체 유형에 따라 달라집니다.

실행 파일 서버는 독립 실행형 애플리케이션으로 실행할 수 있다는 장점이 있지만 COM 클라이언트와 COM 개체 간의 통신에 상당한 오버헤드가 추가됩니다. 컨트롤에 프로그래밍 오류가 있는 경우 컨트롤을 실행하는 서버 프로세스만 충돌하고 클라이언트 애플리케이션은 계속 실행될 수 있습니다. 모든 COM 클라이언트가 실행 중인 서버를 지원하는 것은 아닙니다.

인프로세스 서버는 일반적으로 더 작고 시작 시간이 더 빠릅니다. 클라이언트와 서버 간의 통신은 가상 함수 호출을 통해 직접 이루어지며 원격 프로시저 호출에 필요한 오버헤드를 유발하지 않습니다. 그러나 서버가 충돌하면 클라이언트 애플리케이션도 충돌할 수 있으며, 인-프로세스 서버에서 모든 기능을 사용할 수 있는 것은 아닙니다(예: COM의 실행 중인 객체 테이블에 등록).

두 서버 유형 모두 Qt를 공유 라이브러리로 사용하거나 서버 바이너리에 정적으로 링크하여 사용할 수 있습니다.

빌드 후 단계에서 발생하는 일반적인 오류

ActiveQt 전용 포스트 프로세싱 단계가 작동하려면 서버가 몇 가지 요구 사항을 충족해야 합니다:

  • 노출된 모든 컨트롤은 QApplication 인스턴스만 있으면 생성할 수 있습니다.
  • 서버의 초기 링크에는 임시 유형 라이브러리 리소스가 포함되어야 합니다.
  • 서버를 실행하는 데 필요한 모든 종속성이 시스템 경로(또는 호출 환경에서 사용하는 경로)에 있을 것. Visual Studio에는 도구|옵션|디렉토리 대화 상자에 나열된 자체 환경 변수 집합이 있습니다.

이러한 요구 사항이 충족되지 않으면 다음 오류 중 하나 이상이 발생할 수 있습니다:

서버 실행 파일 충돌

IDL을 생성하려면 ActiveX 컨트롤로 노출되는 위젯을 인스턴스화해야 합니다(생성자 호출). 이 시점에서는 QApplication 객체만 존재합니다. 위젯 생성자는 생성할 다른 객체에 의존해서는 안 됩니다(예: 널 포인터를 확인해야 함).

서버를 디버깅하려면 -dumpidl 출력 파일로 실행하고 충돌이 발생하는 위치를 확인하세요.

컨트롤의 함수가 호출되지 않는다는 점에 유의하세요.

서버 실행 파일이 유효한 Win32 애플리케이션이 아닙니다.

유형 라이브러리를 첨부하면 서버 바이너리가 손상됩니다. 이는 Windows의 버그이며 릴리스 빌드에서만 발생합니다.

첫 번째 연결 단계에서는 더미 타입 라이브러리를 실행 파일에 연결해야 하며, 나중에 IDC로 대체할 수 있습니다. 예제에 설명된 대로 유형 라이브러리가 포함된 리소스 파일을 프로젝트에 추가합니다.

"DLL을 찾을 수 없음"

빌드 시스템은 인터페이스 정의를 생성하고 서버를 등록하기 위해 서버 실행 파일을 실행해야 합니다. 서버가 링크하는 동적 링크 라이브러리가 경로에 없는 경우 실패할 수 있습니다(예: Visual Studio에서 "디렉터리" 옵션에 지정된 환경 설정을 사용하여 서버를 호출하는 경우). 서버에 필요한 모든 DLL 및 플러그인이 오류 메시지 상자에 인쇄된 경로에 나열된 디렉터리에 있는지 확인하세요( Windows 배포 도구 참조).

"파일을 열 수 없습니다..."

마지막 클라이언트가 사용을 중단했을 때 ActiveX 서버를 제대로 종료하지 못했습니다. 일반적으로 애플리케이션이 종료되는 데 약 2초가 걸리지만, 클라이언트가 컨트롤을 제대로 해제하지 않는 경우와 같이 프로세스를 종료하려면 작업 관리자를 사용해야 할 수도 있습니다(예: 클라이언트가 컨트롤을 제대로 해제하지 않는 경우).

컨트롤을 인스턴스화할 수 없는 경우

이 경우 서버를 관리자로 등록하는 것이 도움이 될 수 있습니다.

컨트롤 구현하기

Qt로 COM 객체를 구현하려면 QObject 또는 기존 QObject 서브클래스의 서브클래스를 만듭니다. 클래스가 QWidget 의 서브 클래스인 경우 COM 객체는 ActiveX 컨트롤이 됩니다.

#include <QWidget>

class MyActiveX : public QWidget
{
    Q_OBJECT

Q_OBJECT 매크로는 위젯에 대한 메타 객체 정보를 ActiveQt 프레임워크에 제공하는 데 필요합니다.

Q_CLASSINFO("ClassID", "{1D9928BD-4453-4bdd-903D-E525ED17FDE5}")
Q_CLASSINFO("InterfaceID", "{99F6860E-2C5A-42ec-87F2-43396F4BE389}")
Q_CLASSINFO("EventsID", "{0A3E9F27-E4F1-45bb-9E47-63099BCCD0E3}")

Q_CLASSINFO() 매크로를 사용하여 COM 객체의 COM 식별자를 지정합니다. ClassIDInterfaceID 는 필수이며, EventsID 는 객체에 신호가 있는 경우에만 필요합니다. 이러한 식별자를 생성하려면 uuidgen 또는 guidgen 과 같은 시스템 도구를 사용합니다.

각 클래스에 대해 추가 속성을 지정할 수 있습니다. 자세한 내용은 클래스 정보 및 조정을 참조하세요.

Q_PROPERTY(int value READ value WRITE setValue)

Q_PROPERTY() 매크로를 사용하여 ActiveX 컨트롤의 속성을 선언합니다.

부모 객체를 취하는 표준 생성자를 선언하고 QObject 하위 클래스처럼 함수, 신호 및 슬롯을 선언합니다.

public:
    MyActiveX(QWidget *parent = 0)
    ...

    int value() const;

public slots:
    void setValue(int v);
    ...

signals:
    void valueChange(int v);
    ...

};

ActiveQt 프레임워크는 프로퍼티와 공용 슬롯을 ActiveX 프로퍼티 및 메서드로, 시그널을 ActiveX 이벤트로 노출하고 Qt 데이터 유형과 동등한 COM 데이터 유형 간에 변환합니다.

데이터 유형

프로퍼티에 지원되는 Qt 데이터 유형은 다음과 같습니다:

Qt 데이터 타입COM 속성
boolVARIANT_BOOL
QStringBSTR
intint
uint부호 없는 int
doubledouble
qlonglongCY
qulonglongCY
QColorOLE_COLOR
QDateDATE
QDateTimeDATE
QTimeDATE
QFontIFontDisp*
QPixmapIPictureDisp*
QVariantVARIANT
QVariantList ( QList<QVariant>와 동일)safearray(variant)
QStringListsafearray(bstr)
QByteArraysafearray(바이트)
QRect사용자 정의 유형
QSize사용자 정의 타입
QPoint사용자 정의 타입

신호와 슬롯의 파라미터에 지원되는 Qt 데이터 타입은 다음과 같습니다:

Qt 데이터 타입COM 파라미터
bool[in] VARIANT_BOOL
bool&[in, out] VARIANT_BOOL*
QString, const QString&[in] BSTR
QString&[in, out] BSTR*
QString&[in, out] BSTR*
int[in] int
int&[in,out] int
uint[in] 부호 없는 int
uint&[in, out] 부호 없는 int*
double[in] double
double&[in, out] double*
QColor, const QColor&[in] OLE_COLOR
QColor&[in, out] OLE_COLOR*
QDate, const QDate&[in] DATE
QDate&[in, out] DATE*
QDateTime, const QDateTime&[in] DATE
QDateTime&[in, out] DATE*
QFont, const QFont&[in] IFontDisp*
QFont&[in, out] IFontDisp**
QPixmap, const QPixmap&[in] IPictureDisp*
QPixmap&[in, out] IPictureDisp**
QList<QVariant>, const QList<QVariant>&[in] SAFEARRAY(VARIANT)
QList<QVariant>&[in, out] SAFEARRAY(VARIANT)*
QStringList, const QStringList&[in] SAFEARRAY(BSTR)
QStringList&[in, out] SAFEARRAY(BSTR)*
QByteArray, const QByteArray&[in] SAFEARRAY(BYTE)
QByteArray&[in, out] SAFEARRAY(BYTE)*
QObject*[in] IDispatch*
QRect& [in, out] 구조체 QRect (사용자 정의)
QSize&[in, out] 구조체 QSize (사용자 정의)
QPoint&[in, out] 구조체 QPoint (사용자 정의)

내보낸 열거형과 플래그도 지원됩니다( Q_ENUM() 및 Q_FLAG() 참조). 인-파라미터 유형도 반환값으로 지원됩니다.

다른 데이터 유형을 사용하는 매개변수가 있는 프로퍼티와 시그널/슬롯은 ActiveQt 프레임워크에서 무시됩니다.

하위 개체

COM 객체는 COM 객체의 하위 요소를 나타낼 수 있는 여러 하위 객체를 가질 수 있습니다. 예를 들어 다중 문서 스프레드시트 애플리케이션을 나타내는 COM 개체는 각 스프레드시트에 대해 하나의 하위 개체를 제공할 수 있습니다.

QObject 하위 클래스는 QAxFactory 에 알려진 한 ActiveX에서 하위 개체의 유형으로 사용할 수 있습니다. 그런 다음 해당 유형을 속성에서 사용하거나 슬롯의 반환 유형 또는 매개 변수로 사용할 수 있습니다.

속성 알림

프로퍼티를 ActiveX 클라이언트에 바인딩 가능하게 만들려면 QAxBindable 클래스에서 다중 상속을 사용합니다:

#include <QAxBindable>
#include <QWidget>

class MyActiveX : public QWidget, public QAxBindable
{
    Q_OBJECT

속성 쓰기 함수를 구현할 때 QAxBindable 클래스의 requestPropertyChange() 및 propertyChanged() 함수를 사용하여 ActiveX 클라이언트가 컨트롤 속성에 바인딩할 수 있도록 하세요.

컨트롤 서비스

COM 시스템에서 COM 서버를 사용할 수 있게 하려면 5개의 고유 식별자를 사용하여 시스템 레지스트리에 등록해야 합니다. 이러한 식별자는 guidgen 또는 uuidgen 과 같은 도구에서 제공됩니다. 등록 정보를 통해 COM은 요청된 ActiveX 컨트롤을 제공하는 바이너리를 지역화하고, 컨트롤에 대한 원격 프로시저 호출을 마샬링하며, 컨트롤에 의해 노출된 메서드 및 속성에 대한 유형 정보를 읽을 수 있습니다.

클라이언트가 요청할 때 COM 개체를 생성하려면 서버는 QAxFactory 의 구현을 내보내야 합니다. 가장 쉬운 방법은 매크로 집합을 사용하는 것입니다:

QAXFACTORY_BEGIN("{ad90301a-849e-4e8b-9a91-0a6dc5f6461f}",
                 "{a8f21901-7ff7-4f6a-b939-789620c03d83}")
    QAXCLASS(MyWidget)
    QAXCLASS(MyWidget2)
    QAXTYPE(MySubType)
QAXFACTORY_END()

이렇게 하면 MyWidgetMyWidget2 을 COM 클라이언트에서 만들 수 있는 COM 개체로 내보내고 MySubTypeMyWidgetMyWidget2 의 속성 및 매개 변수에 사용할 수 있는 유형으로 등록합니다.

QAxFactory class documentation 에서 이 매크로를 사용하는 방법과 사용자 지정 팩토리를 구현하고 사용하는 방법을 설명합니다.

프로세스 외 실행 서버의 경우, 일반적인 Qt 애플리케이션처럼 QApplication 객체를 인스턴스화하고 이벤트 루프로 진입하는 main() 함수를 구현할 수 있습니다. 기본적으로 애플리케이션은 표준 Qt 애플리케이션으로 시작되지만, 명령줄에 -activex 을 전달하면 ActiveX 서버로 시작됩니다. QAxFactory::isServer ()를 사용하여 표준 애플리케이션 인터페이스를 생성 및 실행하거나 독립 실행형 실행을 방지할 수 있습니다:

#include <QApplication>
#include <QAxFactory>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    if (!QAxFactory::isServer()) {
        // create and show main window
    }
    return app.exec();
}

그러나 ActiveQt는 주요 함수의 기본 구현을 제공하므로 이 작업은 필요하지 않습니다. 기본 구현은 QAxFactory::startServer()를 호출하고 QApplication 인스턴스를 생성한 다음 exec()를 호출합니다.

ActiveX 서버 실행 파일을 빌드하려면 qmake 을 실행하여 make파일을 생성하고 다른 Qt 애플리케이션과 마찬가지로 컴파일러의 make 도구를 사용합니다. 또한 make 프로세스는 -regserver 명령줄 옵션으로 결과 실행 파일을 호출하여 시스템 레지스트리에 컨트롤을 등록합니다.

ActiveX 서버가 실행 파일인 경우 다음 명령줄 옵션이 지원됩니다:

옵션결과
-regserver시스템 레지스트리에 서버를 등록합니다.
-regserverperuser현재 사용자의 시스템 레지스트리에 서버를 등록합니다(5.14 이후).
-unregserver시스템 레지스트리에서 서버를 등록 해제합니다.
-unregserverperuser현재 사용자의 시스템 레지스트리에서 서버를 등록 해제합니다(5.14 이후).
-activex애플리케이션을 ActiveX 서버로 시작합니다.
-dumpidl <file> -version x.y서버의 IDL을 지정된 파일에 씁니다. 유형 라이브러리의 버전은 x.y입니다.

모든 Windows 시스템에서 사용할 수 있는 regsvr32 도구를 사용하여 인프로 서버를 등록할 수 있습니다.

일반적인 컴파일 시간 문제

나열된 컴파일러/링커 오류는 Microsoft Visual C++ 6.0 컴파일러에서 발생하는 오류를 기반으로 합니다.

"오버로드된 함수에 2개의 매개변수가 없습니다."

QAXCLASS() 또는 QAXFACTORY_DEFAULT() 매크로를 사용하는 코드에서 오류가 발생하는 경우 위젯 클래스에 기본 팩토리에서 사용할 수 있는 생성자가 없는 경우입니다. 표준 위젯 생성자를 추가하거나 생성자가 필요하지 않은 사용자 정의 팩토리를 구현하세요.

QAXFACTORY_EXPORT() 매크로를 사용하는 코드에서 오류가 발생하는 경우 QAxFactory 하위 클래스에 적절한 생성자가 없습니다. 팩토리 클래스에 다음과 같은 공용 클래스 생성자를 제공하세요.

MyFactory(const QUuid &, const QUuid &);

와 같은 공용 클래스 생성자를 제공하세요.

"구문 오류: 숫자에 잘못된 접미사"

고유 식별자가 QAXFACTORY_EXPORT(), QAXFACTORY_BEGIN() 또는 QAXFACTORY_DEFAULT() 매크로에 문자열로 전달되지 않았습니다.

"해결되지 않은 외부 심볼 _ucm_instantiate"

서버가 QAxFactory. 프로젝트의 구현 파일 중 하나에서 QAXFACTORY_EXPORT() 매크로를 사용하여 팩토리를 인스턴스화하고 내보내거나 QAXCLASS() 또는 QAXFACTORY_DEFAULT() 매크로를 사용하여 기본 팩토리를 사용하세요.

"_ucm_initialize 이미 정의된 ..."

서버가 QAxFactory 의 구현을 두 개 이상 내보내거나 동일한 구현을 두 번 내보냅니다. 기본 팩토리를 사용하는 경우 QAXFACTORY_BEGIN() 또는 QAXFACTORY_DEFAULT() 매크로는 프로젝트에서 한 번만 사용해야 합니다. 서버가 여러 ActiveX 컨트롤을 제공하는 경우 사용자 지정 QAxFactory 구현과 QAXFACTORY_EXPORT() 매크로를 사용합니다.

QAxServer 바이너리 배포하기

Qt로 작성된 ActiveX 서버는 Qt를 공유 라이브러리로 사용하거나 바이너리에 정적으로 링크된 Qt를 사용할 수 있습니다. 두 가지 방법 모두 다소 큰 패키지를 생성합니다(서버 바이너리 자체가 커지거나 Qt DLL을 제공해야 합니다).

독립형 서버 설치하기

ActiveX 서버를 독립 실행형 애플리케이션으로도 실행할 수 있는 경우, 대상 시스템에 실행 파일을 설치한 후 -regserver 명령줄 매개 변수를 사용하여 서버 실행 파일을 실행합니다. 그러면 서버에서 제공하는 컨트롤을 ActiveX 클라이언트에서 사용할 수 있습니다.

인프로세스 서버 설치

ActiveX 서버가 설치 패키지의 일부인 경우 Microsoft에서 제공하는 regsvr32 도구를 사용하여 대상 시스템에 컨트롤을 등록합니다. 이 도구가 없는 경우 설치 프로그램 프로세스에 DLL을 로드하고 DllRegisterServer 기호를 확인한 후 함수를 호출합니다:

HMODULE dll = LoadLibrary("myserver.dll");
typedef HRESULT(__stdcall *DllRegisterServerProc)();
DllRegisterServerProc DllRegisterServer =
    (DllRegisterServerProc)GetProcAddress(dll, "DllRegisterServer");

HRESULT res = E_FAIL;
if (DllRegisterServer)
    res = DllRegisterServer();
if (res != S_OK)
    // error handling

인터넷을 통해 서버 배포하기

웹 페이지에서 서버의 컨트롤을 사용하려면 페이지를 보는 데 사용되는 브라우저에서 서버를 사용할 수 있도록 설정해야 하며 페이지에서 서버 패키지의 위치를 지정해야 합니다.

서버 위치를 지정하려면 웹 사이트의 OBJECT 태그에 CODEBASE 속성을 사용합니다. 이 값은 서버 파일 자체, 서버에 필요한 다른 파일(예: Qt DLL)이 나열된 INF 파일 또는 압축된 CAB 아카이브를 가리킬 수 있습니다.

INF 및 CAB 파일은 ActiveX 및 COM 프로그래밍에 관한 거의 모든 서적과 MSDN 라이브러리 및 기타 다양한 온라인 리소스에 문서화되어 있습니다. 예제에는 CAB 아카이브를 빌드하는 데 사용할 수 있는 INF 파일이 포함되어 있습니다:

[version]
    signature="$CHICAGO$"
    AdvancedINF=2.0
 [Add.Code]
    simpleax.exe=simpleax.exe
 [simpleax.exe]
    file-win32-x86=thiscab
    clsid={DF16845C-92CD-4AAB-A982-EB9840E74669}
    RegisterServer=yes

Microsoft의 CABARC 도구를 사용하면 CAB 아카이브를 쉽게 생성할 수 있습니다:

cabarc N simpleax.cab simpleax.exe simple.inf

INF 파일은 Qt의 정적 빌드를 가정하므로 다른 DLL에 대한 종속성이 INF 파일에 나열되지 않습니다. DLL에 따라 ActiveX 서버를 배포하려면 종속성을 추가하고 라이브러리 파일에 아카이브를 제공해야 합니다.

컨트롤 사용

웹 페이지에 포함시키는 등 ActiveX 컨트롤을 사용하려면 <object> HTML 태그를 사용합니다.

<object ID="MyActiveX1" CLASSID="CLSID:ad90301a-849e-4e8b-9a91-0a6dc5f6461f">
   ...
<\object>

컨트롤의 속성을 초기화하려면 다음을 사용합니다.

<object ID=...>
    <param name="name" value="value">
<\object>

웹 브라우저에서 스크립팅을 지원하는 경우 JavaScript, VBScript 및 양식을 사용하여 컨트롤을 스크립팅합니다. ActiveQt 예제에는 예제 컨트롤에 대한 데모 HTML 페이지가 포함되어 있습니다.

지원 및 지원되지 않는 ActiveX 클라이언트

다음은 주로 ActiveX 컨트롤 및 클라이언트 애플리케이션에 대한 자체 경험을 바탕으로 한 것이며, 결코 완전하지 않습니다.

지원되는 클라이언트

이러한 표준 애플리케이션은 ActiveQt로 개발된 ActiveX 컨트롤과 함께 작동합니다. 일부 클라이언트는 인프로세스 컨트롤만 지원한다는 점에 유의하세요.

  • Internet Explorer
  • Microsoft ActiveX 컨트롤 테스트 컨테이너
  • Microsoft Visual Studio 6.0
  • Microsoft Visual Studio.NET/2003
  • Microsoft Visual Basic 6.0
  • MFC 및 ATL 기반 컨테이너
  • Sybase 파워빌더
  • ActiveQt 기반 컨테이너

Microsoft Office 애플리케이션이 지원되지만 컨트롤을 "삽입 가능" 개체로 등록해야 합니다. QAxFactory::registerClass 을 다시 구현하여 COM 클래스에 이 속성을 추가하거나 Q_CLASSINFO 매크로를 사용하여 클래스의 "삽입 가능" 클래스 정보를 "yes"로 설정하세요.

지원되지 않는 클라이언트

다음과 같은 클라이언트 애플리케이션에서 ActiveQt 기반 COM 객체가 작동하도록 만들지 못했습니다.

  • 볼랜드 C++ 빌더(버전 5 및 6)
  • 볼랜드 델파이

일반적인 런타임 오류

서버가 응답하지 않음

시스템이 서버를 시작할 수 없는 경우(서버가 프로세스를 실행하는지 작업 관리자에게 확인), 서버가 종속하는 DLL이 시스템 경로에서 누락되지 않았는지(예: Qt DLL!) 확인합니다. 종속성 워커를 사용하여 서버 바이너리의 모든 종속성을 확인합니다.

서버가 실행되는 경우(예: 작업 관리자에 프로세스가 나열됨) 다음 섹션에서 서버 디버깅에 대한 정보를 참조하세요.

개체를 만들 수 없음

빌드 프로세스 중에 서버를 빌드하고 올바르게 등록할 수 있지만 OLE/COM 객체 뷰어 애플리케이션 등으로 객체를 초기화할 수 없는 경우 서버가 종속하는 DLL이 시스템 경로에서 누락되지 않았는지(예: Qt DLL) 확인합니다. 종속성 워커를 사용하여 서버 바이너리의 모든 종속성을 확인합니다.

서버가 실행되는 경우 다음 섹션에서 서버 디버깅에 대한 정보를 참조하세요.

COM 서버를 언로드 및 재로드할 때 발생하는 충돌

Active Qt COM 서버가 Qt Base에 있는 모듈 이외의 Qt 모듈을 사용하는 경우, COM 서버를 프로세스 외 COM 서버로 활성화해야 합니다. Qt Quick 와 같은 모듈을 포함하는 프로세스 중인 COM 서버를 활성화하려고 시도하면 COM 서버를 언로드한 후 충돌이 발생할 수 있습니다.

발신 COM 호출 중 충돌 또는 예기치 않은 동작 발생

처리 중이 아닌 COM 서버는 클라이언트에 대한 발신 호출을 수행하는 동안 메시지 큐를 처리한다는 점에 유의하세요. 이로 인해 클라이언트가 동시에 서버를 호출하는 경우 예기치 않은 동작이나 충돌이 발생할 수 있습니다. 이러한 상황에서는 발신 호출이 반환되기 전에 수신 호출이 서버에서 실행됩니다. 특히 컨트롤이 클라이언트로 다시 호출하는 동안 클라이언트가 ActiveX 컨트롤을 닫으면 충돌이 발생할 수 있습니다. 이러한 재진입 문제는 메시지 필터(IMessageFilter 및 CoRegisterMessageFilter)를 사용하여 완화할 수 있습니다.

런타임 오류 디버깅

Visual Studio에서 처리 중인 서버를 디버깅하려면 서버 프로젝트를 활성 프로젝트로 설정하고 프로젝트 설정에서 클라이언트 "디버그 세션용 실행 파일"을 지정하세요(예: ActiveX 테스트 컨테이너 사용). 코드에 중단점을 설정할 수 있으며, 디버그 버전을 설치한 경우 ActiveQt 및 Qt 코드에 들어갈 수도 있습니다.

실행 서버를 디버깅하려면 디버거에서 애플리케이션을 실행하고 명령줄 매개변수 -activex 로 시작합니다. 그런 다음 클라이언트를 시작하고 ActiveX 컨트롤의 인스턴스를 만듭니다. COM은 다음 번에 ActiveX 컨트롤을 만들려고 하는 클라이언트에 대해 기존 프로세스를 사용합니다.

클래스 정보 및 튜닝

각 COM 클래스에 대한 속성을 제공하려면 Qt XML의 메타 객체 시스템의 일부인 Q_CLASSINFO 매크로를 사용합니다.

Key값의 의미
Version클래스의 버전(기본값은 1.0)
Description클래스를 설명하는 문자열입니다.
ClassID클래스 ID입니다. 지정하지 않은 경우 QAxFactory::classID 을 다시 구현해야 합니다.
InterfaceID인터페이스 ID입니다. 지정하지 않은 경우 QAxFactory::interfaceID 을 다시 구현해야 합니다.
EventsID이벤트 인터페이스 ID입니다. 지정하지 않으면 신호가 COM 이벤트로 노출되지 않습니다.
DefaultProperty지정된 속성은 이 클래스의 기본 속성을 나타냅니다. 즉, 푸시 버튼의 기본 속성은 "텍스트"입니다.
DefaultSignal지정된 신호는 이 클래스의 기본 신호를 나타냅니다. 즉, 푸시 버튼의 기본 신호는 "클릭"입니다.
LicenseKey객체를 생성하려면 지정된 라이선스 키가 필요합니다. 라이선스 키를 비워두면 라이선스가 있는 머신이 필요할 수 있습니다. 기본적으로 클래스는 라이선스가 부여되지 않습니다. 다음 섹션도 참조하세요.
StockEvents값이 "yes"인 경우 개체는 주식 이벤트를 노출합니다. QAxFactory::hasStockEvents ()를 참조하세요.
ToSuperClass객체는 값에 포함된 클래스 이름까지 모든 슈퍼클래스의 기능을 노출합니다. QAxFactory::exposeToSuperClass () 참조
Insertable값이 "yes"이면 클래스가 "삽입 가능"으로 등록되고 OLE 2 컨테이너(예: Microsoft Office)에 나열됩니다. 이 속성은 기본적으로 설정되지 않습니다.
집계 가능값이 "아니오"인 경우 클래스는 집계를 지원하지 않습니다. 기본적으로 집계가 지원됩니다.
생성 가능값이 "아니요"이면 클라이언트가 클래스를 만들 수 없으며 다른 클래스의 API를 통해서만 사용할 수 있습니다(즉, 클래스가 하위 유형인 경우).
RegisterObject값이 "yes"이면 이 클래스의 객체가 OLE에 등록되고 실행 중인 객체 테이블에서 액세스할 수 있습니다(즉, 클라이언트는 이 클래스의 이미 실행 중인 인스턴스에 연결할 수 있습니다). 이 속성은 프로세스 외 서버에서만 지원됩니다.
MIME객체는 값에 지정된 형식의 데이터와 파일을 처리할 수 있습니다. 값의 형식은 mime:extension:description입니다. 여러 형식은 세미콜론으로 구분합니다.
CoClassAlias생성된 IDL과 레지스트리에서 사용되는 클래스 이름입니다. 이것은 네임스페이스에 있는 C++ 클래스에 특히 유용합니다 - 기본적으로 ActiveQt는 IDL을 컴파일하기 위해 "::"를 제거합니다.
구현된 카테고리쉼표로 구분된 카테고리 ID(CATID) UUID의 목록입니다. "제어", "삽입 가능" 등의 추가 컨테이너 기능을 지정하기 위한 일반적인 메커니즘입니다. 일반적인 CATID로는 CATID_InternetAware ("{0DE86A58-2BAA-11CF-A229-00AA003D7352}"), CATID_SafeForScripting ("{7DD95801-9882-11CF-9FA9-00AA006C42C4}")와 사용자 정의 CATID 값 등이 있습니다.

키와 값 모두 대소문자를 구분합니다.

다음은 자체 API만 노출하는 클래스의 버전 2.0을 선언하며, Microsoft Office 애플리케이션의 '개체 삽입' 대화 상자에서 사용할 수 있습니다.

class MyActiveX : public QWidget
{
    Q_OBJECT
    Q_CLASSINFO("Version", "2.0")
    Q_CLASSINFO("ClassID", "{7a4cffd8-cbcd-4ae9-ae7e-343e1e5710df}")
    Q_CLASSINFO("InterfaceID", "{6fb035bf-8019-48d8-be51-ef05427d8994}")
    Q_CLASSINFO("EventsID", "{c42fffdf-6557-47c9-817a-2da2228bc29c}")
    Q_CLASSINFO("Insertable", "yes")
    Q_CLASSINFO("ToSuperClass", "MyActiveX")
    Q_PROPERTY(...)

public:
    MyActiveX(QWidget *parent = 0);

    ...
};

라이선스가 있는 컴포넌트 개발

컴포넌트를 개발하는 경우 해당 컴포넌트를 인스턴스화할 수 있는 사용자를 제어하고 싶을 수 있습니다. 서버 바이너리를 모든 클라이언트 컴퓨터에 제공하고 등록할 수 있으므로 누구나 자신의 소프트웨어에서 해당 구성 요소를 사용할 수 있습니다.

예를 들어 컨트롤을 생성하는 코드가 라이선스 키를 제공하거나 컨트롤이 실행될 머신에 라이선스를 부여하는 등 다양한 기법을 사용하여 컴포넌트 라이선스를 부여할 수 있습니다.

Qt 클래스를 라이센스가 있는 것으로 표시하려면 Q_CLASSINFO() 매크로를 사용하여 "LicenseKey"를 지정합니다.

class MyLicensedControl : public QWidget
{
    Q_OBJECT
    Q_CLASSINFO("LicenseKey", "<key string>")
    ...
};

이 키는 라이선스가 부여되지 않은 컴퓨터에서 MyLicensedControl 인스턴스를 생성할 수 있어야 합니다. 이제 라이선스가 있는 개발자는 자신의 애플리케이션과 함께 서버 바이너리를 재배포할 수 있으며, 애플리케이션 사용자는 라이선스 키가 없으면 컨트롤을 만들 수 없지만 "LicenseKey" 값을 사용하여 컨트롤을 생성할 수 있습니다.

컨트롤에 대한 단일 라이선스 키가 충분하지 않은 경우(즉, 다른 개발자가 다른 라이선스 키를 받기를 원하는 경우) 빈 키를 지정하여 컨트롤에 라이선스가 필요함을 표시하고 QAxFactory::validateLicenseKey()를 다시 구현하여 시스템에 라이선스가 있는지(즉, 라이선스 파일을 통해) 확인할 수 있습니다.

기타 인터페이스

ActiveQt 서버에서 제공하는 ActiveX 컨트롤은 OLE 사양을 구현하기 위한 최소한의 COM 인터페이스 세트를 지원합니다. ActiveX 클래스가 QAxBindable 클래스를 상속하면 추가 COM 인터페이스를 구현할 수도 있습니다.

QAxAggregated 의 새 하위 클래스를 만들고 다중 상속을 사용하여 추가 COM 인터페이스 클래스를 하위 클래스화합니다.

class AxImpl : public QAxAggregated, public ISomeCOMInterface
{
public:
    AxImpl() {}

    long queryInterface(const QUuid &iid, void **iface);

    // IUnknown
    QAXAGG_IUNKNOWN

    // ISomeCOMInterface
    ...
}

QAxAggregated::queryInterface() 함수를 다시 구현하여 추가 COM 인터페이스를 지원합니다.

long AxImpl::queryInterface(const QUuid &iid, void **iface)
{
    *iface = 0;
    if (iid == IID_ISomeCOMInterface)
        *iface = (ISomeCOMInterface *)this;
    else
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}

ISomeCOMInterfaceIUnknown 의 서브클래스이므로 QueryInterface(), AddRef(), Release() 함수를 구현해야 합니다. 이를 위해 클래스 정의에서 QAXAGG_IUNKNOWN 매크로를 사용합니다. IUnknown 함수를 수동으로 구현하는 경우 QAxAggregated::controllingUnknown() 함수가 반환하는 인터페이스 포인터에 대한 호출을 위임하세요.

HRESULT AxImpl::QueryInterface(REFIID iid, void **iface)
{
    return controllingUnknown()->QueryInterface(iid, iface);
}

queryInterface() 구현에서 IUnknown 인터페이스 자체를 지원하지 마세요.

COM 인터페이스의 메서드를 구현하고, 컨트롤을 구현하는 QObject 서브클래스를 호출해야 하는 경우 QAxAggregated::object()를 사용합니다.

QAxBindable 서브클래스에서 QAxBindable::createAggregate()을 구현하여 QAxAggregated 서브클래스의 새 객체를 반환합니다.

class MyActiveX : public QWidget, public QAxBindable
{
    Q_OBJECT

public:
    MyActiveX(QWidget *parent);

    QAxAggregated *createAggregate()
    {
        return new AxImpl();
    }
};

ActiveQt 프레임워크도참조하십시오 .

© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.