Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • Open a thread to run Python interpreter.

  • Send commands from C++ to the Python interpreter. The C++ may need to write some intermediate files for the interop.

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..

like image 660
Yin Zhu Avatar asked Jan 21 '23 19:01

Yin Zhu


2 Answers

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

like image 98
bgporter Avatar answered Jan 23 '23 07:01

bgporter


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;
}
like image 23
Stephen Quan Avatar answered Jan 23 '23 07:01

Stephen Quan