Yin Zhu
Yin Zhu

Reputation: 17119

Embed a Python interpreter in a (Windows) C++ application

I am building a window application written in C++. I'd like to utilize several python libraries.

I don't need any fancy Python interop here. My method is like this:

This method is dirty, but it will work for a lot of interpreter-like environments, e.g. gnuplot, lua.

My question is that what kind of API are there for me to use for this task. Maybe I need some Win32 API?

EDIT: I don't need any Python specific. I really want the general method. So that my application could also work with gnuplot, etc..

Upvotes: 4

Views: 1914

Answers (4)

Stephen Quan
Stephen Quan

Reputation: 26179

I've put together a "Hello World" IActiveScript C++ ATL console application that:

  • Implements a new class called CSimpleScriptSite
  • Derives from IActiveScriptSite and IActiveScriptSiteWindow
  • Calls CoCreateInstance on the Python engine with the IActiveSite interface
  • Executes the Python statement print 'Hello World. 5 squared is: ' + str(5 * 5)
  • Has no error handling for bad Python. You should consult MSDN IActiveScriptError
  • Has functions with a stub return S_OK; implementation
  • I have Python 2.6 installed, but you can use any Python interpreter.
  • I used the Python for Windows extensions which has a ProgID of Python which gives the Python interpreter IActiveScript wrappers
  • However, you can make the code work with any Python interpreter that supports IActiveScript, you just need to update the ProgID (e.g. Python.AXScript.2)

Here's the Python Hello World sample:

#include <atlbase.h>
#include <activscp.h>

#define CHECKHR(stmt) \
    { \
        HRESULT hr = S_OK; \
        if (FAILED(hr = (stmt))) { return hr; } \
    }

class CSimpleScriptSite :
    public IActiveScriptSite,
    public IActiveScriptSiteWindow
{
public:
    CSimpleScriptSite() : m_cRefCount(1), m_hWnd(NULL) { ZeroMemory(&m_clsidScriptEngine, sizeof(m_clsidScriptEngine)); }

    // IUnknown

    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
    STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject);

    // IActiveScriptSite

    STDMETHOD(GetLCID)(LCID *plcid){ *plcid = 0; return S_OK; }
    STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppiunkItem, ITypeInfo **ppti) { return TYPE_E_ELEMENTNOTFOUND; } 
    STDMETHOD(GetDocVersionString)(BSTR *pbstrVersion) { *pbstrVersion = SysAllocString(L"1.0"); return S_OK; }
    STDMETHOD(OnScriptTerminate)(const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) { return S_OK; }
    STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState) { return S_OK; }
    STDMETHOD(OnScriptError)(IActiveScriptError *pIActiveScriptError) { return S_OK; }
    STDMETHOD(OnEnterScript)(void) { return S_OK; }
    STDMETHOD(OnLeaveScript)(void) { return S_OK; }

    // IActiveScriptSiteWindow

    STDMETHOD(GetWindow)(HWND *phWnd) { *phWnd = m_hWnd; return S_OK; }
    STDMETHOD(EnableModeless)(BOOL fEnable) { return S_OK; }

    // Miscellaneous

    HRESULT CloseScriptEngine();
    HRESULT Evaluate(LPCOLESTR szScript, VARIANT *pResult, LPCOLESTR strItemName);
    HRESULT Execute(LPCOLESTR szScript, LPCOLESTR strItemName);
    HRESULT OpenScriptEngine(CLSID &rclsid);
    HRESULT OpenScriptEngine(LPCOLESTR szScriptEngine);
    HRESULT SetWindow(HWND hWnd) { m_hWnd = hWnd; }

private:
    CComPtr<IActiveScript> m_ptrIActiveScript;
    CLSID                  m_clsidScriptEngine;
    ULONG                  m_cRefCount;
    HWND                   m_hWnd;
};

STDMETHODIMP_(ULONG) CSimpleScriptSite::AddRef()
{
    return InterlockedIncrement(&m_cRefCount);
}

STDMETHODIMP_(ULONG) CSimpleScriptSite::Release()
{
    if (!InterlockedDecrement(&m_cRefCount))
    {
        delete this;
        return 0;
    }
    return m_cRefCount;
}

STDMETHODIMP CSimpleScriptSite::QueryInterface(REFIID riid, void **ppvObject)
{
    if (riid == IID_IUnknown || riid == IID_IActiveScriptSiteWindow)
    {
        *ppvObject = (IActiveScriptSiteWindow *) this;
        AddRef();
        return NOERROR;
    }
    if (riid == IID_IActiveScriptSite)
    {
        *ppvObject = (IActiveScriptSite *) this;
        AddRef();
        return NOERROR;
    }
    return E_NOINTERFACE;
}

HRESULT CSimpleScriptSite::OpenScriptEngine(CLSID &rclsid)
{
    m_ptrIActiveScript = NULL;
    CHECKHR(CoCreateInstance(rclsid, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void **) &m_ptrIActiveScript));
    CHECKHR(m_ptrIActiveScript->SetScriptSite(this));
    CComPtr<IActiveScriptParse> ptrIActiveScriptParse;
    CHECKHR(m_ptrIActiveScript->QueryInterface(IID_IActiveScriptParse, (void **) &ptrIActiveScriptParse));
    CHECKHR(ptrIActiveScriptParse->InitNew());
    m_clsidScriptEngine = rclsid;
    return S_OK;
}

HRESULT CSimpleScriptSite::OpenScriptEngine(LPCOLESTR szScriptEngine)
{
    CLSID clsid;
    CHECKHR(CLSIDFromProgID(szScriptEngine, &clsid));
    return OpenScriptEngine(clsid);
}

HRESULT CSimpleScriptSite::CloseScriptEngine()
{
    if (!m_ptrIActiveScript) { return S_OK; }
    CHECKHR(m_ptrIActiveScript->SetScriptState(SCRIPTSTATE_CLOSED));
    m_ptrIActiveScript = NULL;
    return S_OK;
}

HRESULT CSimpleScriptSite::Evaluate(LPCOLESTR szScript, VARIANT *pResult, LPCOLESTR strItemName)
{
    if (!m_ptrIActiveScript) { return E_POINTER; }
    if (!pResult) { return E_INVALIDARG; }
    EXCEPINFO ei = {0};
    CComPtr<IActiveScriptParse> ptrIActiveScriptParse;
    CHECKHR(m_ptrIActiveScript->QueryInterface(IID_IActiveScriptParse, (void **) &ptrIActiveScriptParse));
    CHECKHR(ptrIActiveScriptParse->ParseScriptText(szScript, strItemName, NULL, NULL, 0, 0, SCRIPTTEXT_ISEXPRESSION, pResult, &ei));
    return m_ptrIActiveScript->SetScriptState(SCRIPTSTATE_CONNECTED);
}

HRESULT CSimpleScriptSite::Execute(LPCOLESTR szScript, LPCOLESTR strItemName)
{
    if (!m_ptrIActiveScript) { return E_POINTER; }
    EXCEPINFO ei = {0};
    CComPtr<IActiveScriptParse> ptrIActiveScriptParse;
    CHECKHR(m_ptrIActiveScript->QueryInterface(IID_IActiveScriptParse, (void **) &ptrIActiveScriptParse));
    CHECKHR(ptrIActiveScriptParse->ParseScriptText(szScript, strItemName, NULL, NULL, 0, 0, 0L, NULL, &ei));
    return m_ptrIActiveScript->SetScriptState(SCRIPTSTATE_CONNECTED);
}

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr = S_OK;
    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    CSimpleScriptSite *pScriptSite = new CSimpleScriptSite();
    hr = pScriptSite->OpenScriptEngine(OLESTR("Python"));
    hr = pScriptSite->Execute(OLESTR("print 'Hello World. 5 squared is: ' + str(5 * 5)"), NULL);
    hr = pScriptSite->CloseScriptEngine();
    hr = pScriptSite->Release();
    ::CoUninitialize();
    return 0;
}

Upvotes: 3

bcsanches
bcsanches

Reputation: 2372

You can also use boost Python: http://www.boost.org/doc/libs/1_45_0/libs/python/doc/index.html

Upvotes: 0

Seva Alekseyev
Seva Alekseyev

Reputation: 61388

ActivePython (http://www.activestate.com/activepython/downloads) installs itself as an ActiveScript engine.The ProgID is Python.AXScript.2 . So you can use it with COM via the Windows standard IActiveScript interface. Read up on it.

Distribution is another matter. Either you require that customers have it, or you could try and extract the juicy bits from the ActiveState's package, or maybe there's an official way to do unattended setup...

Upvotes: 2

bgporter
bgporter

Reputation: 36554

If you have the source distribution of Python, you can look in the Demo/embed directory for samples. The pertinent documentation is here.

Upvotes: 4

Related Questions