最近在做一个三维图形系统,类似3DMAX。我希望我的系统设计成功后,如果用户(有一定计算机水平的)能够通过编写C++程序扩展我的系统的功能。但是要做到,(1)用户编写程序能够同我的图形系统相互调用,控制图形系统中的对象,就象是他们好象写在一个程序里面那样,无缝。
(2)执行用户的程序,只需要我的图形系统装入一下那么方便
(3)用户甚至可以修改我的设计,让我图形系统中的对象行为发生变化,就像修改自己程序那么方便
(4)用户写的对象可以继承图形系统中的对象
(5)当然用户写扩展的程序脚本不能够像写COM那样复杂。一切都要象写一般C++程序那样简单和随心所欲。我设想提出一种:叫做“动态类”(Dynamic Class)的组件对象结构:
(1)一个动态类就是一个DLL和头文件,函数实现和属性数据都在DLL中
(2)头文件负责让用户调用DLL中函数像类的行为一样
(3)要求动态类能实现像类一样的继承,作用域控制,虚函数。同时他又是可以被感知的,也就是我们能够知道图形系统中有那些动态类在运行,他们都有那些方法,他们的数值是那些。
(4)动态类在图形系统中是可以用类似于用VB等等语言编程那样的可视化环境定义属性和可视化引用的。
(5)要能够让用户理解得到整个图形系统是怎样在运行的,就像机器能够看到每一个零件的配合一样。这个想法能不能实现,和怎样实现,希望高人们给点建议。

解决方案 »

  1.   

    activex script 技术好好研究一下,可以解决你的问题
      

  2.   

    定义DLL入口点、DLL中回掉函数。
    下面是X-SCAN的插件函数
    /*****************************************************************
      Filename    : sample.cpp
      Description : check xxxxxx vulnerable
      Author      : glacier
      Update      : 2002-07-09  Copyright (C) 2002 http://www.xfocus.org   All Rights Reserved
     *****************************************************************///-----------------------------------------------------------------
    #include <stdio.h>
    #include <winsock.h>
    #include <string.h>#pragma comment(lib, "ws2_32.lib")#define PLUGIN_PARAMS_1     1
    #define VULN_MEMBER_NAME    "Sample"
    #define MAXLEN              256
    /*
     -  AlertUser()
     -
     *  Function:
     *    form of string output:
     *    "[szHostName]: find [szVulnName] vulmerabilities-[szLine]"
     *    when szVlunName==NULL, form is as following:
     *    "[szHostName]: [szLine]"
     *
     *  Parameters:
     *    IN szHostName - file name
     *    IN szVulnName - vulnerabilities name
     *    IN szLine     - strings which will be inserted files
     *
     *  Returned data:
     *    [nothing]
     *
     */
    typedef VOID (CALLBACK *PALERT_USER) (
        char *szHostName,
        char *szVulnName,
        char *szLine);
    /*
     -  LogToFile()
     -    
     *  Function:
     *    insert strings into LOG file and  with "<BR>" automatically
     *    allow strings carrying other HTML sentinels
     *
     *  Parameters:
     *    IN szLogFileName - LOG file name
     *    IN sLine         - strings waiting to be inserted files
     *
     *  Returned data:
     *    [nothing]
     *
     */
    typedef VOID (CALLBACK *PLOG_TO_FILE) (
        char *szLogFileName,
        char *szLine);
    /*
     -  AddToTreeView()
     -
     *  Function:
     *    add nodes to TreeView in the window of X-Scan-GUI
     *
     *  Parameters:
     *    IN szLine       - complete path of nodes and the list separator 
     *                      should be "\n" (for example "127.0.0.1\nPort\n80") 
     *    IN szImageFile  - node ico file (size of this ico file must be 16*16,
     *                      .bmp type) 
     *
     *  Returned data
     *    [nothing]
     *
     */
    typedef VOID (CALLBACK *PADD_TO_TREEVIEW) (
        char *szLine,
        char *szImageFile);
      

  3.   

    /* plug-in basic information */
    typedef struct _PLUGIN_INFO {
        char    szClassName[50];        /* type of plug-in */
        char    szMemberName[50];       /* name of plug-in components */
        char    szVersion[10];          /* plug-in version */
        char    szFileName[50];         /* original file name of plug-in */
        char    szParamsRequest[50];    /* choose the parameters,such as"-cgi" */
        char    szPrompt[200];          /* explanation of parameter, such as "-cgi: check HTTP vulnerability" */
        int     nSingle;                /* If it's the only vulnerability(the number of vulnerability can be 1 or 0, such as Netbios information) */
        char    szAuthorName[50];       /* Author of the plug-ins */
        char    szRisk[10];             /* vulnerability risk ranking */
        char    szDescription[300];     /* vulnerability description in plug-ins */
        char    szAdvice[300];          /* suggestion about vulnerability in plug-ins */
        DWORD   dwTimeOut;              /* time-out limitation, "0" stands for no limitation */
        int     nParamsType;            /* function type(PLUGIN_PARAMS_1) of plug-in export function "PluginFunc()" */
        int     nMark;                  /* identify whether this plug-in has been selected default */
        char    szImageFile[50];        /* 16*16.bmp ico file (locate in images direcotory without including path) */
    } PLUGIN_INFO;/* plug-in running basic parameters */
    typedef struct _SCAN_PARAMS_0 {
        char    szHostName[MAXLEN];     /* host address */
        char    szParams[MAXLEN];       /* user input parameters */
        int     nMaxThread;             /* max thread number */
        int     *pnThreadCount;         /* number of total active thread */
        int     *pnPluginThreadCount;   /* number of active thread what was created by current plug-in */
        char    *szCurrentSchedule;     /* current schedule */
        DWORD   *plTimeOut;             /* limitation of timeout, and "0" means no limit */
        int     nVerbose;               /* if display the detail information */
        char    szLogFileName[MAXLEN];  /* log file name(including the path) */
        char    szResultBuf[MAXLEN];    /* dynamical returned information */
        int     nVulnCount;             /* number of vulnerabilities */
    } SCAN_PARAMS_0;/* plug-in running parameters */
    typedef struct _SCAN_PARAMS_1 {
        SCAN_PARAMS_0       stBaseParams;   /* basic parameter (SCAN_PARAMS_0) */
        PALERT_USER         pAlertUser;     /* call-back function "AlertUser()" address */
        PLOG_TO_FILE        pLogToFile;     /* call-back function "LogToFile()" address */
        PADD_TO_TREEVIEW    pAddToTreeView; /* call-back function "AddToTreeView()" address */
    } SCAN_PARAMS_1;
                      /* This function is quoted when initializing plug-ins, which is used to acquire the plug-in basic information.
       Returned value: TRUE -- plug-in initialized successfully; FALSE -- plug-in initialization failed */
    extern "C" __declspec(dllexport) BOOL GetPluginInfo(PLUGIN_INFO*);/* This function is quoted when scanning host. Input the host information and return the scanning result.
       Returned value: TRUE -- there are some vulnerabilities; FALSE -- there's no vulnerabilities */
    extern "C" __declspec(dllexport) BOOL PluginFunc(SCAN_PARAMS_1*);
    static PALERT_USER      pAlertUser      = NULL;
    static PLOG_TO_FILE     pLogToFile      = NULL;
    static PADD_TO_TREEVIEW pAddToTreeView  = NULL;
    int CheckVuln(char *szHostName, char *szLogFileName)
    {
        char szLine[MAXLEN] = {0};
        char szVulnName[MAXLEN] = {0};
        char szVulnContent[MAXLEN] = {0};
        int nRtn = 0;    // ...
        
        nRtn++;
        strcpy(szVulnName, VULN_MEMBER_NAME);
        strcpy(szVulnContent, "vuln_1");    pAlertUser(szHostName, szVulnName, szVulnContent);
        sprintf(szLine, "Found %s", szVulnContent);
        pLogToFile(szLogFileName, szLine);
        sprintf(szLine, "%s\n%s\n%s", szHostName, szVulnName, szVulnContent);
        pAddToTreeView(szLine, "alert.bmp");    // ...    nRtn++;
        strcpy(szVulnContent, "vuln_2");    pAlertUser(szHostName, szVulnName, szVulnContent);
        sprintf(szLine, "Found %s", szVulnContent);
        pLogToFile(szLogFileName, szLine);
        sprintf(szLine, "%s\n%s\n%s", szHostName, szVulnName, szVulnContent);
        pAddToTreeView(szLine, "alert.bmp");    return nRtn;
    }/*
     -  GetPluginInfo()
     -
     *  Function:
     *    The function is quoted when initializing plug-ins,
     *    initializing plug-ins,
     *    which is used to acquire the plug-in basic information.
     *
     *  Parameter:
     *    OUT pstPluginInfo   -  plug-in basic information
     *    IN
     *
     *  Result:
     *    TRUE                -  plug-in initialized successfully
     *    FALSE               -  plug-in initialization failed
     */
    __declspec(dllexport) BOOL GetPluginInfo(PLUGIN_INFO *pstPluginInfo)
    {
        strcpy(pstPluginInfo->szClassName, "SAMPLE");
        strcpy(pstPluginInfo->szMemberName, VULN_MEMBER_NAME);
        strcpy(pstPluginInfo->szVersion, "1.0");
        strcpy(pstPluginInfo->szFileName, "sample.dll");
        strcpy(pstPluginInfo->szParamsRequest, "-sample");
        strcpy(pstPluginInfo->szPrompt, "-sample: check xxxxxx");
        pstPluginInfo->nSingle = 0;
        strcpy(pstPluginInfo->szAuthorName, "glacier");
        strcpy(pstPluginInfo->szRisk, "high");
        strcpy(pstPluginInfo->szDescription, "");
        strcpy(pstPluginInfo->szAdvice, "");
        pstPluginInfo->dwTimeOut = 0;
        pstPluginInfo->nParamsType = PLUGIN_PARAMS_1;
        pstPluginInfo->nMark = 1;
        strcpy(pstPluginInfo->szImageFile, "alert.bmp");    return TRUE;
    }
    /*
     -  PluginFunc()
     -
     *  Function:
     *    This function is quoted when scanning host.
     *    Input the host information and return the scanning result.
     *
     *  Parameter:
     *    OUT IN pstScanParams    -  plug-in running parameters
     *
     *  Result:
     *    TRUE                    -  there are some vulnerabilities
     *    FALSE                   -  there's no vulnerabilities
     */
    __declspec(dllexport) BOOL PluginFunc(SCAN_PARAMS_1 *pstScanParams_1)
    {
        char    szHostName[MAXLEN] = {0};
        char    szLogFileName[MAXLEN] = {0};
        int     nVerbose = 0;
        int     nVulnCount = 0;
        int     nMaxThread = 0;
        int     *pnThreadCount = NULL;
        int     *pnPluginThreadCount = NULL;    BOOL    bResult = FALSE;    strcpy(szHostName, pstScanParams_1->stBaseParams.szHostName);
        strcpy(szLogFileName, pstScanParams_1->stBaseParams.szLogFileName);
        nVerbose = pstScanParams_1->stBaseParams.nVerbose;
        nMaxThread = pstScanParams_1->stBaseParams.nMaxThread;
        pnThreadCount = pstScanParams_1->stBaseParams.pnThreadCount;
        pnPluginThreadCount = pstScanParams_1->stBaseParams.pnPluginThreadCount;
        pAlertUser = pstScanParams_1->pAlertUser;
        pLogToFile = pstScanParams_1->pLogToFile;
        pAddToTreeView = pstScanParams_1->pAddToTreeView;
        pstScanParams_1->stBaseParams.nVulnCount = 0;    while ((*pnThreadCount) >= nMaxThread) {
            Sleep(100);
        }    (*pnThreadCount)++;
        (*pnPluginThreadCount)++;    strcpy(pstScanParams_1->stBaseParams.szCurrentSchedule, "Checking xxxxxx ...");    if (nVerbose) {
            pAlertUser(szHostName, NULL, "Checking xxxxxx ...");
        }    __try {
            nVulnCount = CheckVuln(szHostName, szLogFileName);
        }
        __except (EXCEPTION_EXECUTE_HANDLER) {
            __try {
                pAlertUser(NULL, NULL, "Function \"CheckVuln()\" causes an exception.");
            }
            __except (EXCEPTION_EXECUTE_HANDLER) {
                printf("%s\n", "Function \"CheckVuln()\" causes an exception, failed to alert user.");
            }
        }    (*pnThreadCount)--;
        (*pnPluginThreadCount)--;    pstScanParams_1->stBaseParams.nVulnCount = nVulnCount;
        if (nVulnCount > 0) {
            bResult = TRUE;
        }    pAlertUser(szHostName, NULL, "xxxxxx scan complete.");    return bResult;
    }
      

  4.   

    /*
     -  DllEntryPoint()
     -
     *  Function:
     *            Dll entry point
     *
     *  Parameter:
     *            OUT
     *            IN
     *
     *  Result:
     *
     */
    int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void*)
    {
        return 1;
    }
    //---------------------------------------------------------------------------
      

  5.   

    实际上这用COM来实现最好,我原来就做过一个,主框架和Plug in各提供相应接口,互相调用,很方便。现在我发现在.NET上更容易实现,正在写。
      

  6.   

    楼主,我觉得,你还是做成Com的可执行程序比较好,然后向外公布你的接口,这样大家就可以为你写一些AddIn或者Plug_in.
      

  7.   

    Plug in
    用不用COM写倒是不重要,如果你要给VB用或者其它用最好是写成COM
      

  8.   

    进一步设想
    X3D实现细节—动态类DRE_CLASS方法
    什么是动态类(Dynamic Runtime Extending Class)
    A. 动态类是把一个类的所有的方法,属性都实现在动态连接库DLL中。
    B. 支持软件在运行状态下面时候进行功能扩展和二次开发的方法,不需要从新编译系统的程序。
    C. 动态类支持一个系统全局的动态类组件管理
    D. 动态类应该具有类的所有行为,包括:封装,继承,访问控制
    E. 动态类应该包括获取自己成员函数的帮助信息
    F. 动态类应该能为动态类程序的继承开发环境IDE提供一定开发辅助信息的能力。
    为什么要在X3D系统中实现动态类体系结构
    A.逻辑动画图形系统要实现的功能的潜在需求:
    (A).三维图形系统都应该给用户提供二次开发的能力
    (B).这要求支持运行时候系统的控制,也就是能够访问到系统中所有的系统变量,对象,函数和方法。能够管理事件触发。
    (C).这样设计将极大的减少后期进行逻辑关系开发的工作量。并保证系统稳定
    (D).实现用X3D来进行游戏开发。所需要的编程控制能力。
    (E).让X3D成为了最优秀的终极图形系统,因为它具有无限扩充性
    B.组件对象模型使用起来太烦琐,不利于用户掌握,而且缺乏类的很多能力,对于一个常常要使用到类层次结构的图形系统而言非常的不方便
    C.便于修改维护和实现最小的投入最大的功能能力
    如何实现动态类结构?
    A. 规范DLL的布局
    1. 一个DLL就是一个类
    2. DLL里面实现一个接口统一的类创建函数DREC_CLASS_CREAT返回动态类指针
    3. DLL里面实现一个DLL的引用记数
    4. DLL里面实现一个寻找类函数的方法DREC_CALL,他允许去寻找到虚函数和父类函数
    5. DLL中实现一个初试化方法DREC_CLASS_INITENV,负责在动态类管理环境中装如动态类运行的环境
    6. 实现寻找到成员变量环境,并把成员变量环境记录到正确的动态类对象的方法DREC_SET_VALUE,DREC_GET_VALUE
    7. 支持消息调用和字符串调用的能力
    8. 建立数据结构记录动态类的实例对象
    9. 尽量用宏和模版来封装让用户使用开发方便
    10. 实现动态类继承的DREC_CLASS_EXTENDED方法,供动态类管理环境初始话调用
    11. 实现DREC_THIS方法获取指向动态类实例的指针
    12. 实现DREC_SUPER方法获取把当前动态类实例父类化后的指针
    13. 实现一个动态类访问信息数据结构描述动态类成员的访问控制DREC_PURVIEW
    14. 实现一个动态类实例子的构造,析构函数DREC_CLASS_INIT,DREC_CLASS_FINAL
    B. 实现动态类管理环境
    C. 实现动态类的头文件和辅助文件自动生成编译工具
      

  9.   

    看了你的描述,我还是觉的COM是最好的选择,你的所有功能都可以用COM实现,你研究一下,OFFICE和VC++的开发环境就可以发现.他们都是支持你所描述的功能的,你可以找一点VC++的AddIn的开发代码或者是Office的AddIn开发代码,你就能够有更清楚的了解.
      

  10.   

    你可以使用ActiveX自动化编程,曝露一些自动化接口,同时使用ActiveX Script,这样别人就能使用 VBS、JAVA Script等对你的应用进行二次开发了。另外,我发现你的蓝图有些不太现实,什么是无限扩充性??看你的蓝图就知道你从未做过这方面的实践,研究,只是纸上谈兵而已。
    一旦一个应用程序编译好,他的功能无论如何都只能是有限的,它如果想实现所有可能的功能而又自己不用重写重编译,那它就只能是最好什么都别干,那这样的系统又有什么实际价值,因为这就跟把不同的功能分开来写成独立的应用没有什么两样。而且你又如何预测得到所有的用户需求?
    再者,二次开发对原系统在运行时的控制应该是严格控制的,而不是“...支持运行时候系统的控制,也就是能够访问到系统中所有的系统变量,对象,函数和方法...”,这样才能保证系统的稳定,想像一下,如果不论谁对系统进行二次开发都能随意访问到系统的所有的系统变量,对象,函数和方法,这样的系统有什么稳定性可言?
      

  11.   

    COM是我借鉴的标准之一,但是我觉得qureyinterface比较麻烦吗,而且GUID也不好记忆,另外就是需要注册组件后使用。最关键一点他只支持包容和聚合,但是无法作到虚函数和非虚函数方式的继承。另外写IDL不太容易为用户接受,在我见过的国人写的程序很少开发成com的,因为com不好使用。
    无限扩充性指的是用户可以方便的修改我的图形系统,其思想就是组件话编程。用户可以改写继承我以前的动态类,而不用重新编译指的是,不用整个系统从新编译,而用户写的程序当然需要编译,只是编译后就放入了组件库修改动态库管理器中的访问路由脚本。由于程序都是动态装入,并可以替换修改,当然具有无限扩充性。只是这种扩充比其他的扩充更有优势,优势是可以利用图形系统的编辑器把一些工作可视化。比如,设计一个用于编游戏的图形系统。你可以利用图形系统的功能来画图,但是游戏的逻辑你得靠编程实现,你怎么做呢?
    是只利用图形系统编辑角色,存盘,然后从头写一个图形环境在你的程序中来使用方便呢,还是就直接在图形编辑器添加一定的逻辑代码,比如碰撞时间的处理逻辑,来得方便呢。
    特别是对于开发3D的图形系统而言,你难道需要从头写灯光,贴图,摄像机,等等程序用程序中的变量来定位角色,而通过多次运行调试才使得你装入的各个图元之间的位置关系最漂亮,仅仅只是为了装如显示控制某个角色?
    我设计X3D图形系统的一个目的就是为了让游戏制作更加的方便。用户即可以用我的编辑环境来编辑3D图元,同时又可以写一段对事件处理的程序来设定3D图元的一些自动行为。而这些要求不重新编译整个环境来做到,如何做呢?难道让所有对象,包括运行时候用户通过编辑建立的对象都可访问的这种方法来得更友好吗?
      

  12.   

    为什么我不用COM来设计呢?
    1.COM的使用方法:
    使用COM需要首先CoCreateInstance然后用QuerryInterface来得到类实例指针.但是你必须知道他们的GUID所以我的设计中要添加罗列函数的方法,和不用GUID而用字符串来访问
    COM访问控制的能力很低下
    另外就是我如何访问已经存在在内存中的COM对象呢,再无法获取到他的指针的情况下面?
    因为plugin不是原来的程序,他如何去同原来内存中的COM实例通信呢?
    所以我要为COM加上一些RTTI功能,用一个系统对象表来获取内存中的对象实例指针。
    最好能够舍弃COM的使用中需要一个头文件来描述接口类结构的做法(COM没有头文件得到的接口就是一个无用的指针)2.COM的编写方法
    编写包容和聚合的COM是件不容易的事情
    写IDL非常麻烦
    所以我要更方便的写COM程序,要求如同写面向对象程序一样的简单。并且在对象继承的功能上面要更加的强大。3.跨平台的考虑,我要求以后改写成LINUX上面的so文件也很方便。而LINUX上面是没有注册Reg机制的。