aardio 文档

aardio 范例: 通过 C++ 调用 IAutoComplete 接口

//通过 C++ 调用 IAutoComplete 接口
/*
aardio 支持很多不同的接口,
例如下面就演示了使用 aardio 调用 C++ 的接口函数。
再用一句代码就将 C++/MFC 实现的 IDispatch 对象转换为了 aardio 对象。
使用 VC6 编译生成的 DLL 非常小只有 24 KB。
这是因为 VC6 的运行库是所有 Windows 系统自带的运行库,
可以动态连接系统库,从而可以生成更小的 DLL 文件。
*/
//用法请参考标准库 com.autoComplete 的源码。
import console.init;
import vc6; 
var vc = vc6( "~/lib/com/autoComplete/.res/" )  

/*
输入 C++ 源码,自动存为 autoComplete.cpp

- C++ 头文件的顺序不能错,afxwin.h,afxdisp.h 一定要最先导入
- 之后再导入 initguid.h ,然后再是 shlguid.h 等以最先初始化 GUID
- 然后再是导入其他头文件
- 仍然报错缺少的 CLSID_AutoComplete,IID_IAutoComplete 用 DEFINE_GUID 手动定义

这么做的好处是只要一个文件,生成极小的 DLL。
*/
vc.autoComplete  = /****** 
#include <afxwin.h>
#include <afxdisp.h>
#include <initguid.h>
#include <shlguid.h>
#include <shldisp.h> 
#include <shlobj.h> 

DEFINE_GUID(CLSID_AutoComplete,0x00bb2763,0x6a77,0x11d0,0xa5,0x35,0x00,0xc0,0x4f,0xd7,0xd0,0x62);
DEFINE_GUID(IID_IAutoComplete,0x00bb2762,0x6a77,0x11d0,0xa5,0x35,0x00,0xc0,0x4f,0xd7,0xd0,0x62);
DEFINE_GUID(IID_IAutoComplete2,0xeac04bc0,0x3791,0x11d2,0xbb,0x95,0x00,0x60,0x97,0x7b,0x46,0x4c);

#include <vector>
#include <string>

class CEnumString : public IEnumString
{
private:
    LONG m_cRef;
    std::vector<std::wstring> m_strings;
    size_t m_nCurrent;

public:
    CEnumString() : m_cRef(1), m_nCurrent(0) {}

    virtual ~CEnumString() {}

    // IUnknown methods
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
    {
        if (riid == IID_IUnknown || riid == IID_IEnumString)
        {
            *ppvObject = static_cast<IEnumString*>(this);
            AddRef();
            return S_OK;
        }
        *ppvObject = NULL;
        return E_NOINTERFACE;
    }

    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHOD_(ULONG, Release)()
    {
        LONG cRef = InterlockedDecrement(&m_cRef);
        if (cRef == 0)
            delete this;
        return cRef;
    }

    // IEnumString methods
    STDMETHOD(Next)(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
    {
        ULONG cFetched = 0;

        while (m_nCurrent < m_strings.size() && cFetched < celt)
        {
            size_t len = (m_strings[m_nCurrent].length() + 1) * sizeof(WCHAR);
            rgelt[cFetched] = (LPOLESTR)CoTaskMemAlloc(len);
            if (rgelt[cFetched])
            {
                wcscpy(rgelt[cFetched], m_strings[m_nCurrent].c_str());
                cFetched++;
                m_nCurrent++;
            }
            else
            {
                return E_OUTOFMEMORY;
            }
        }

        if (pceltFetched)
            *pceltFetched = cFetched;

        return (cFetched == celt) ? S_OK : S_FALSE;
    }

    STDMETHOD(Skip)(ULONG celt)
    {
        m_nCurrent += celt;
        return (m_nCurrent <= m_strings.size()) ? S_OK : S_FALSE;
    }

    STDMETHOD(Reset)()
    {
        m_nCurrent = 0;
        return S_OK;
    }

    STDMETHOD(Clone)(IEnumString **ppenum)
    {
        CEnumString *pNew = new CEnumString();
        if (!pNew)
            return E_OUTOFMEMORY;

        pNew->m_strings = m_strings;
        pNew->m_nCurrent = m_nCurrent;
        *ppenum = pNew;
        return S_OK;
    }

    void SetStrings(const std::vector<std::wstring>& strings)
    {
        m_strings = strings;
        m_nCurrent = 0;
    }
};

class CAutoCompleteObject : public CCmdTarget
{
    DECLARE_DYNCREATE(CAutoCompleteObject)

public:
    CAutoCompleteObject();
    virtual ~CAutoCompleteObject();

protected:
    HWND m_hwndEdit;
    IAutoComplete2* m_pAutoComplete;  // 改为原生指针
    CEnumString* m_pEnumString;
    bool m_bInitialized;
    DWORD m_dwOptions;

public:
    BOOL Initialize(HWND hwndEdit, DWORD dwOptions,LPCWSTR formatString);

    afx_msg void SetStrings(const VARIANT& items);
    afx_msg void SetOption(DWORD dwOptions);
    afx_msg void Enable(BOOL bEnable);
    afx_msg void Delete();

    DECLARE_DISPATCH_MAP()
    DECLARE_INTERFACE_MAP()
};

IMPLEMENT_DYNCREATE(CAutoCompleteObject, CCmdTarget)

CAutoCompleteObject::CAutoCompleteObject() 
    : m_hwndEdit(NULL), m_pAutoComplete(NULL), m_pEnumString(NULL), m_bInitialized(false), m_dwOptions(0)
{
    EnableAutomation();
    AfxOleLockApp();
}

CAutoCompleteObject::~CAutoCompleteObject()
{
    Delete();
    AfxOleUnlockApp();
}

BOOL CAutoCompleteObject::Initialize(HWND hwndEdit, DWORD dwOptions,LPCWSTR formatString)
{
    if (!IsWindow(hwndEdit))
        return FALSE;

    m_hwndEdit = hwndEdit;
    m_dwOptions = dwOptions;

    HRESULT hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
                                   IID_IAutoComplete2, (void**)&m_pAutoComplete);

    if (FAILED(hr))
        return FALSE;

    m_pEnumString = new CEnumString();
    if (!m_pEnumString)
    {
        m_pAutoComplete->Release();  // 手动释放
        m_pAutoComplete = NULL;
        return FALSE;
    }

    hr = m_pAutoComplete->Init(m_hwndEdit, m_pEnumString, NULL, formatString);
    if (FAILED(hr))
    {
        m_pEnumString->Release();
        m_pEnumString = NULL;
        m_pAutoComplete->Release();  // 手动释放
        m_pAutoComplete = NULL;
        return FALSE;
    }

    m_pAutoComplete->SetOptions(m_dwOptions);
    m_pAutoComplete->Enable(TRUE);

    m_bInitialized = true;
    return TRUE;
}

void CAutoCompleteObject::SetStrings(const VARIANT& items)
{
    if (!m_bInitialized || !m_pEnumString)
        return;

    std::vector<std::wstring> stringList;
    if ((items.vt & VT_ARRAY) && (items.vt & VT_BSTR))  // VT_ARRAY | VT_BSTR
    {
        SAFEARRAY* psa = items.parray;
        LONG lBound, uBound;
        SafeArrayGetLBound(psa, 1, &lBound);
        SafeArrayGetUBound(psa, 1, &uBound);

        VARTYPE vtElement;
        SafeArrayGetVartype(psa, &vtElement);

        if (vtElement == VT_BSTR)
        {
             for (LONG i = lBound; i <= uBound; i++)
             {
                BSTR bstr;
                SafeArrayGetElement(psa, &i, &bstr);
                if (bstr)
                {
                    stringList.push_back(std::wstring(bstr, SysStringLen(bstr)));
                    SysFreeString(bstr);
                }
             }
        }
        else if (vtElement == VT_VARIANT)
        {
            for (LONG i = lBound; i <= uBound; i++)
            {
                VARIANT var;
                VariantInit(&var);
                SafeArrayGetElement(psa, &i, &var);

                // 尝试转换为字符串
                VARIANT varStr;
                VariantInit(&varStr);
                if (SUCCEEDED(VariantChangeType(&varStr, &var, 0, VT_BSTR)))
                {
                    stringList.push_back(std::wstring(varStr.bstrVal, SysStringLen(varStr.bstrVal)));
                    VariantClear(&varStr);
                }
                VariantClear(&var);
            }
        }
    }

    m_pEnumString->SetStrings(stringList);

    if (m_pAutoComplete)
    {
        m_pEnumString->Reset();
    }
}

void CAutoCompleteObject::SetOption(DWORD dwOptions)
{
    if (!m_bInitialized || !m_pAutoComplete)
        return;

    m_dwOptions = dwOptions;
    m_pAutoComplete->SetOptions(m_dwOptions);
}

void CAutoCompleteObject::Enable(BOOL bEnable)
{
    if (!m_bInitialized || !m_pAutoComplete)
        return;

    m_pAutoComplete->Enable(bEnable);
}

void CAutoCompleteObject::Delete()
{
    if (m_pAutoComplete)
    {
        m_pAutoComplete->Enable(FALSE);
        m_pAutoComplete->Release();  // 手动释放
        m_pAutoComplete = NULL;
    }

    if (m_pEnumString)
    {
        m_pEnumString->Release();
        m_pEnumString = NULL;
    }

    m_bInitialized = false;
}

BEGIN_DISPATCH_MAP(CAutoCompleteObject, CCmdTarget)
    DISP_FUNCTION(CAutoCompleteObject, "setStrings", SetStrings, VT_EMPTY, VTS_VARIANT)
    DISP_FUNCTION(CAutoCompleteObject, "setOption", SetOption, VT_EMPTY, VTS_I4)
    DISP_FUNCTION(CAutoCompleteObject, "enable", Enable, VT_EMPTY, VTS_BOOL)
    DISP_FUNCTION(CAutoCompleteObject, "delete", Delete, VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

BEGIN_INTERFACE_MAP(CAutoCompleteObject, CCmdTarget)
    INTERFACE_PART(CAutoCompleteObject, IID_IDispatch, Dispatch)
END_INTERFACE_MAP()

extern "C" __declspec(dllexport) LPDISPATCH __cdecl CreateIDispatchObject(HWND hwndEdit, DWORD dwOptions/*,LPCWSTR formatString*/)
{
    if (!IsWindow(hwndEdit))
        return NULL;

    CAutoCompleteObject* pObj = new CAutoCompleteObject();
    if (!pObj){
        return NULL;
    }

    if (!pObj->Initialize(hwndEdit, dwOptions,NULL))
    {
        delete pObj;
        return NULL;
    }

    LPDISPATCH pDispatch = pObj->GetIDispatch(FALSE);
    return pDispatch;
}

class CAutoCompleteApp : public CWinApp
{
public:
    virtual BOOL InitInstance()
    {
        CWinApp::InitInstance();

        // 在 aardio 里不需要这句
        // if (FAILED(CoInitialize(NULL))) return FALSE;

        return TRUE;
    }

    virtual int ExitInstance()
    {
        //在 aardio 里不需要这句
        //CoUninitialize();

        return CWinApp::ExitInstance();
    }
};

CAutoCompleteApp theApp;
******/ 

//编译生成DLL 
vc.exec(
    'cl autoComplete.cpp'
    ,'/W3' /*警告等级*/
    ,'/MD' /*使用多线程动态运行库*/
    ,'/O2 /Ot /EHsc' /*代码优化选项*/
    ,'/D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "_AFXDLL" ' /*定义常数和宏*/
    ,'/I"./INCLUDE"'/*指定头文件目录*/
    ,'kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib shlwapi.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib' /*导入库*/
    ,'/link /SUBSYSTEM:WINDOWS /MACHINE:X86' /*后面是链接参数 */
    ,'/out:autoComplete.dll'/*输出文件名*/
    ,'/dll' /*输出DLL*/ 
    ,'/LIBPATH:".\LIB" /LIBPATH:".\LIB2"' /*指定库目录*/
)

Markdown 格式