Skip to content

Commit

Permalink
ExcelPython .xla add-in
Browse files Browse the repository at this point in the history
  • Loading branch information
ericremoreynolds committed Jul 18, 2014
1 parent 6cc39a5 commit af2e0bc
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 23 deletions.
Binary file added addin/xlpython.xlam
Binary file not shown.
20 changes: 20 additions & 0 deletions addin/xlpython/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
def xlfunc(f = None, **kwargs):
def inner(f):
if f is None:
f = self
self = None
xlfunc = f.__xlfunc__ = {}
xlfunc["name"] = f.__name__
xlfunc["doc"] = f.__doc__ if f.__doc__ is not None else "Python function '" + f.__name__ + "' defined in module '" + f.__module__ + "'."
xlargs = xlfunc["args"] = []
for vname in f.__code__.co_varnames:
xlargs.append({
"name": vname,
"marshal": "value"
})
return f
import inspect
if f is not None and len(kwargs) == 0:
return inner(f)
else:
return inner
36 changes: 35 additions & 1 deletion .xlpy/xlpython.py → addin/xlpython/xlpyserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ class XLPythonObject(object):
_public_methods_ = []
def __init__(self, obj):
self.obj = obj

class XLPythonIterator(object):
_public_methods_ = [ "MoveNext", "Current" ]

def __init__(self, obj):
self.iter = obj.__iter__()
self.current = None

def MoveNext(self):
try:
self.current = self.iter.next()
return True
except StopIteration:
return False

def Current(self):
return ToVariant(self.current)

def FromVariant(var):
try:
Expand All @@ -34,7 +51,7 @@ def ToVariant(obj):
return win32com.server.util.wrap(XLPythonObject(obj))

class XLPython(object):
_public_methods_ = [ 'Module', 'Tuple', 'Dict', 'List', 'Obj', 'Str', 'Var', 'Call', 'GetItem', 'SetItem', 'GetAttr', 'SetAttr', 'Eval', 'Exec', 'ShowConsole', 'Reload', 'AddPath' ]
_public_methods_ = [ 'Module', 'Tuple', 'Dict', 'List', 'Obj', 'Str', 'Var', 'Call', 'GetItem', 'SetItem', 'GetAttr', 'SetAttr', 'HasAttr', 'Eval', 'Exec', 'ShowConsole', 'Reload', 'AddPath', 'Builtins', 'Len', 'GetIter' ]

def ShowConsole(self):
import ctypes
Expand Down Expand Up @@ -117,6 +134,14 @@ def Call(self, obj, *args):
else:
return ToVariant(getattr(obj, method)(*pargs, **kwargs))

def Len(self, obj):
obj = FromVariant(obj)
return len(obj)

def Builtins(self):
import __builtin__
return ToVariant(__builtin__)

def GetItem(self, obj, key):
obj = FromVariant(obj)
key = FromVariant(key)
Expand All @@ -139,6 +164,15 @@ def SetAttr(self, obj, attr, value):
value = FromVariant(value)
setattr(obj, attr, value)

def HasAttr(self, obj, attr):
obj = FromVariant(obj)
attr = FromVariant(attr)
return hasattr(obj, attr)

def GetIter(self, obj):
obj = FromVariant(obj)
return win32com.server.util.wrap(XLPythonIterator(obj))

def Eval(self, expr, *args):
globals = None
locals = None
Expand Down
14 changes: 7 additions & 7 deletions .xlpy/xlpython.bas → addin/xlpython/xlpython.bas
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,18 @@ Option Private Module
Option Explicit

#If win64 Then
Const XLPyDLLName As String = "xlpython64-2.0.0.dll"
Declare Function XLPyDLLActivate Lib "xlpython64-2.0.0.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
Declare Function XLPyDLLNDims Lib "xlpython64-2.0.0.dll" (ByRef src As Variant, ByRef dims As Long, ByRef transpose As Boolean, ByRef dest As Variant) As Long
Const XLPyDLLName As String = "xlpython64-2.0.1.dll"
Declare Function XLPyDLLActivate Lib "xlpython64-2.0.1.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
Declare Function XLPyDLLNDims Lib "xlpython64-2.0.1.dll" (ByRef src As Variant, ByRef dims As Long, ByRef transpose As Boolean, ByRef dest As Variant) As Long
#Else
Private Const XLPyDLLName As String = "xlpython32-2.0.0.dll"
Private Declare Function XLPyDLLActivate Lib "xlpython32-2.0.0.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
Private Declare Function XLPyDLLNDims Lib "xlpython32-2.0.0.dll" (ByRef src As Variant, ByRef dims As Long, ByRef transpose As Boolean, ByRef dest As Variant) As Long
Private Const XLPyDLLName As String = "xlpython32-2.0.1.dll"
Private Declare Function XLPyDLLActivate Lib "xlpython32-2.0.1.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
Private Declare Function XLPyDLLNDims Lib "xlpython32-2.0.1.dll" (ByRef src As Variant, ByRef dims As Long, ByRef transpose As Boolean, ByRef dest As Variant) As Long
#End If
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long

Private Function XLPyFolder() As String
XLPyFolder = ThisWorkbook.Path + "\.xlpy"
XLPyFolder = ThisWorkbook.Path + "\xlpython"
End Function

Function XLPyConfig() As String
Expand Down
4 changes: 2 additions & 2 deletions .xlpy/xlpython.cfg → addin/xlpython/xlpython.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
# The value of variable xxx if it has been set, otherwise an empty string - does not raise an error

# The CLSID of the object which will get created
CLSID = {C2922F29-0F9F-4A12-8E11-294A961423A1}
CLSID = $(RandomGUID)

# The command line used to launch the COM server
Command = pythonw.exe -u "$(ConfigDir)\xlpython.py" $(CLSID)
Command = pythonw.exe -u "$(ConfigDir)\xlpyserver.py" $(CLSID)

# Optionally redirect stdout and stderr
RedirectOutput = $(ConfigDir)\$(ConfigName).log
Expand Down
10 changes: 5 additions & 5 deletions xlpython/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,8 @@ void Config::ActivateRPCServer()
if(!CreateProcessA(NULL, cmdLine, NULL, NULL, TRUE, 0, envStr.p, workingDir.c_str(), &si, &pi))
{
formatted_exception e;
e << "Could not create Python process.\n\n";
if(this->HasValue("RedirectOutput"))
e << "Try consulting '" << this->GetValue("RedirectOutput") << "'.\n\n";
e << "Could not create Python process.\n";
e << "Error message: " << GetLastErrorMessage() << "\n";
e << "Command: " << cmdLine << "\nWorking Dir: " << workingDir;
throw e;
}
Expand All @@ -348,9 +347,10 @@ void Config::ActivateRPCServer()
if(dwExitCode != STILL_ACTIVE)
{
formatted_exception e;
e << "Python process exited before it was possible to create the interface object.\n\n";
e << "Python process exited before it was possible to create the interface object.";
if(this->HasValue("RedirectOutput"))
e << "Try consulting " << this->GetValue("RedirectOutput") << ".\n\n";
e << " Try consulting '" << this->GetValue("RedirectOutput") << "'.";
e << "\n";
e << "Command: " << cmdLine << "\nWorking Dir: " << workingDir;
throw e;
}
Expand Down
10 changes: 10 additions & 0 deletions xlpython/dispatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,15 @@ HRESULT __stdcall CDispatchWrapper::Invoke(DISPID dispIdMember, REFIID riid, LCI
VariantInit(&result);
HRESULT hRet = pDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult == NULL ? &result : pVarResult, pExcepInfo, puArgErr);
VariantClear(&result);

//if(FAILED(hr))
//{
// BSTR bstrOld = pExcepInfo->bstrDescription;
// std::string


// SysFreeString(bstrOld);
//}

return hRet;
}
43 changes: 43 additions & 0 deletions xlpython/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,49 @@ void ToStdString(const wchar_t* ws, std::string& str)
delete narrow;
}

void ToStdString(BSTR bs, std::string& str)
{
BOOL bUsedDefaultChar;
int len = (int) SysStringLen(bs);
AutoArrayDeleter<char> narrow(new char[len+1]);
WideCharToMultiByte(CP_ACP, 0, bs, len, narrow.p, len+1, "?", &bUsedDefaultChar);
narrow.p[len] = 0;
str = narrow.p;
}

void ToBStr(const std::string& str, BSTR& bs)
{
int sz = (int) str.length() + 1;
OLECHAR* wide = new OLECHAR[sz];
MultiByteToWideChar(CP_ACP, 0, str.c_str(), sz * sizeof(OLECHAR), wide, sz);
bs = SysAllocString(wide);
delete[] wide;
}

std::string GetLastErrorMessage()
{
DWORD dwError = GetLastError();
char* lpMsgBuf;

if(0 == FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dwError,
0,
(LPSTR) &lpMsgBuf,
0,
NULL))
{
return "Could not get error message: FormatMessage failed.";
}

std::string ret = lpMsgBuf;
LocalFree(lpMsgBuf);
return ret;
}

const char* GetDLLPath()
{
static bool initialized = false;
Expand Down
4 changes: 4 additions & 0 deletions xlpython/utils.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
void ToVariant(const char* str, VARIANT* var);
void ToVariant(const std::string& str, VARIANT* var);
void ToStdString(const wchar_t* ws, std::string& str);
void ToStdString(BSTR bs, std::string& str);
void ToBstr(const std::string& str, BSTR& bs);

class formatted_exception : public std::exception
{
Expand Down Expand Up @@ -41,6 +43,8 @@ void NewGUID(GUID& guid);

void GetLastWriteTime(const char* path, FILETIME* pFileTime);

std::string GetLastErrorMessage();

static inline std::string strlower(std::string& s)
{
std::transform(s.begin(), s.end(), s.begin(), std::tolower);
Expand Down
16 changes: 8 additions & 8 deletions xlpython/xlpython.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,25 @@
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
<IntDir>$(Platform)\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
Expand All @@ -100,7 +100,7 @@
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
</Link>
<PostBuildEvent>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
<Message>Copying dll to bin folder</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
Expand All @@ -118,7 +118,7 @@
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
</Link>
<PostBuildEvent>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
<Message>Copying dll to bin folder</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
Expand All @@ -140,7 +140,7 @@
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
</Link>
<PostBuildEvent>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
<Message>Copying dll to bin folder</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
Expand All @@ -162,7 +162,7 @@
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
</Link>
<PostBuildEvent>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
<Message>Copying dll to bin folder</Message>
</PostBuildEvent>
</ItemDefinitionGroup>
Expand Down

0 comments on commit af2e0bc

Please sign in to comment.