//通过 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 #include #include #include #include #include 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 #include class CEnumString : public IEnumString { private: LONG m_cRef; std::vector 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(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& 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 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"' /*指定库目录*/ )