Skip to content

Commit af2e0bc

Browse files
ExcelPython .xla add-in
1 parent 6cc39a5 commit af2e0bc

File tree

10 files changed

+134
-23
lines changed

10 files changed

+134
-23
lines changed

addin/xlpython.xlam

25.7 KB
Binary file not shown.

addin/xlpython/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
def xlfunc(f = None, **kwargs):
2+
def inner(f):
3+
if f is None:
4+
f = self
5+
self = None
6+
xlfunc = f.__xlfunc__ = {}
7+
xlfunc["name"] = f.__name__
8+
xlfunc["doc"] = f.__doc__ if f.__doc__ is not None else "Python function '" + f.__name__ + "' defined in module '" + f.__module__ + "'."
9+
xlargs = xlfunc["args"] = []
10+
for vname in f.__code__.co_varnames:
11+
xlargs.append({
12+
"name": vname,
13+
"marshal": "value"
14+
})
15+
return f
16+
import inspect
17+
if f is not None and len(kwargs) == 0:
18+
return inner(f)
19+
else:
20+
return inner

.xlpy/xlpython.py renamed to addin/xlpython/xlpyserver.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,23 @@ class XLPythonObject(object):
2323
_public_methods_ = []
2424
def __init__(self, obj):
2525
self.obj = obj
26+
27+
class XLPythonIterator(object):
28+
_public_methods_ = [ "MoveNext", "Current" ]
29+
30+
def __init__(self, obj):
31+
self.iter = obj.__iter__()
32+
self.current = None
33+
34+
def MoveNext(self):
35+
try:
36+
self.current = self.iter.next()
37+
return True
38+
except StopIteration:
39+
return False
40+
41+
def Current(self):
42+
return ToVariant(self.current)
2643

2744
def FromVariant(var):
2845
try:
@@ -34,7 +51,7 @@ def ToVariant(obj):
3451
return win32com.server.util.wrap(XLPythonObject(obj))
3552

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

3956
def ShowConsole(self):
4057
import ctypes
@@ -117,6 +134,14 @@ def Call(self, obj, *args):
117134
else:
118135
return ToVariant(getattr(obj, method)(*pargs, **kwargs))
119136

137+
def Len(self, obj):
138+
obj = FromVariant(obj)
139+
return len(obj)
140+
141+
def Builtins(self):
142+
import __builtin__
143+
return ToVariant(__builtin__)
144+
120145
def GetItem(self, obj, key):
121146
obj = FromVariant(obj)
122147
key = FromVariant(key)
@@ -139,6 +164,15 @@ def SetAttr(self, obj, attr, value):
139164
value = FromVariant(value)
140165
setattr(obj, attr, value)
141166

167+
def HasAttr(self, obj, attr):
168+
obj = FromVariant(obj)
169+
attr = FromVariant(attr)
170+
return hasattr(obj, attr)
171+
172+
def GetIter(self, obj):
173+
obj = FromVariant(obj)
174+
return win32com.server.util.wrap(XLPythonIterator(obj))
175+
142176
def Eval(self, expr, *args):
143177
globals = None
144178
locals = None

.xlpy/xlpython.bas renamed to addin/xlpython/xlpython.bas

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,18 @@ Option Private Module
33
Option Explicit
44

55
#If win64 Then
6-
Const XLPyDLLName As String = "xlpython64-2.0.0.dll"
7-
Declare Function XLPyDLLActivate Lib "xlpython64-2.0.0.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
8-
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
6+
Const XLPyDLLName As String = "xlpython64-2.0.1.dll"
7+
Declare Function XLPyDLLActivate Lib "xlpython64-2.0.1.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
8+
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
99
#Else
10-
Private Const XLPyDLLName As String = "xlpython32-2.0.0.dll"
11-
Private Declare Function XLPyDLLActivate Lib "xlpython32-2.0.0.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
12-
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
10+
Private Const XLPyDLLName As String = "xlpython32-2.0.1.dll"
11+
Private Declare Function XLPyDLLActivate Lib "xlpython32-2.0.1.dll" (ByRef result As Variant, Optional ByVal config As String = "") As Long
12+
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
1313
#End If
1414
Private Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
1515

1616
Private Function XLPyFolder() As String
17-
XLPyFolder = ThisWorkbook.Path + "\.xlpy"
17+
XLPyFolder = ThisWorkbook.Path + "\xlpython"
1818
End Function
1919

2020
Function XLPyConfig() As String

.xlpy/xlpython.cfg renamed to addin/xlpython/xlpython.cfg

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@
2727
# The value of variable xxx if it has been set, otherwise an empty string - does not raise an error
2828

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

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

3535
# Optionally redirect stdout and stderr
3636
RedirectOutput = $(ConfigDir)\$(ConfigName).log

xlpython/config.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -324,9 +324,8 @@ void Config::ActivateRPCServer()
324324
if(!CreateProcessA(NULL, cmdLine, NULL, NULL, TRUE, 0, envStr.p, workingDir.c_str(), &si, &pi))
325325
{
326326
formatted_exception e;
327-
e << "Could not create Python process.\n\n";
328-
if(this->HasValue("RedirectOutput"))
329-
e << "Try consulting '" << this->GetValue("RedirectOutput") << "'.\n\n";
327+
e << "Could not create Python process.\n";
328+
e << "Error message: " << GetLastErrorMessage() << "\n";
330329
e << "Command: " << cmdLine << "\nWorking Dir: " << workingDir;
331330
throw e;
332331
}
@@ -348,9 +347,10 @@ void Config::ActivateRPCServer()
348347
if(dwExitCode != STILL_ACTIVE)
349348
{
350349
formatted_exception e;
351-
e << "Python process exited before it was possible to create the interface object.\n\n";
350+
e << "Python process exited before it was possible to create the interface object.";
352351
if(this->HasValue("RedirectOutput"))
353-
e << "Try consulting " << this->GetValue("RedirectOutput") << ".\n\n";
352+
e << " Try consulting '" << this->GetValue("RedirectOutput") << "'.";
353+
e << "\n";
354354
e << "Command: " << cmdLine << "\nWorking Dir: " << workingDir;
355355
throw e;
356356
}

xlpython/dispatch.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,15 @@ HRESULT __stdcall CDispatchWrapper::Invoke(DISPID dispIdMember, REFIID riid, LCI
6565
VariantInit(&result);
6666
HRESULT hRet = pDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult == NULL ? &result : pVarResult, pExcepInfo, puArgErr);
6767
VariantClear(&result);
68+
69+
//if(FAILED(hr))
70+
//{
71+
// BSTR bstrOld = pExcepInfo->bstrDescription;
72+
// std::string
73+
74+
75+
// SysFreeString(bstrOld);
76+
//}
77+
6878
return hRet;
6979
}

xlpython/utils.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,49 @@ void ToStdString(const wchar_t* ws, std::string& str)
2828
delete narrow;
2929
}
3030

31+
void ToStdString(BSTR bs, std::string& str)
32+
{
33+
BOOL bUsedDefaultChar;
34+
int len = (int) SysStringLen(bs);
35+
AutoArrayDeleter<char> narrow(new char[len+1]);
36+
WideCharToMultiByte(CP_ACP, 0, bs, len, narrow.p, len+1, "?", &bUsedDefaultChar);
37+
narrow.p[len] = 0;
38+
str = narrow.p;
39+
}
40+
41+
void ToBStr(const std::string& str, BSTR& bs)
42+
{
43+
int sz = (int) str.length() + 1;
44+
OLECHAR* wide = new OLECHAR[sz];
45+
MultiByteToWideChar(CP_ACP, 0, str.c_str(), sz * sizeof(OLECHAR), wide, sz);
46+
bs = SysAllocString(wide);
47+
delete[] wide;
48+
}
49+
50+
std::string GetLastErrorMessage()
51+
{
52+
DWORD dwError = GetLastError();
53+
char* lpMsgBuf;
54+
55+
if(0 == FormatMessageA(
56+
FORMAT_MESSAGE_ALLOCATE_BUFFER |
57+
FORMAT_MESSAGE_FROM_SYSTEM |
58+
FORMAT_MESSAGE_IGNORE_INSERTS,
59+
NULL,
60+
dwError,
61+
0,
62+
(LPSTR) &lpMsgBuf,
63+
0,
64+
NULL))
65+
{
66+
return "Could not get error message: FormatMessage failed.";
67+
}
68+
69+
std::string ret = lpMsgBuf;
70+
LocalFree(lpMsgBuf);
71+
return ret;
72+
}
73+
3174
const char* GetDLLPath()
3275
{
3376
static bool initialized = false;

xlpython/utils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
void ToVariant(const char* str, VARIANT* var);
22
void ToVariant(const std::string& str, VARIANT* var);
33
void ToStdString(const wchar_t* ws, std::string& str);
4+
void ToStdString(BSTR bs, std::string& str);
5+
void ToBstr(const std::string& str, BSTR& bs);
46

57
class formatted_exception : public std::exception
68
{
@@ -41,6 +43,8 @@ void NewGUID(GUID& guid);
4143

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

46+
std::string GetLastErrorMessage();
47+
4448
static inline std::string strlower(std::string& s)
4549
{
4650
std::transform(s.begin(), s.end(), s.begin(), std::tolower);

xlpython/xlpython.vcxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,25 +66,25 @@
6666
<LinkIncremental>true</LinkIncremental>
6767
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
6868
<IntDir>$(Platform)\$(Configuration)\</IntDir>
69-
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
69+
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
7070
</PropertyGroup>
7171
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
7272
<LinkIncremental>true</LinkIncremental>
7373
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
7474
<IntDir>$(Platform)\$(Configuration)\</IntDir>
75-
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
75+
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
7676
</PropertyGroup>
7777
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
7878
<LinkIncremental>false</LinkIncremental>
7979
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
8080
<IntDir>$(Platform)\$(Configuration)\</IntDir>
81-
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
81+
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
8282
</PropertyGroup>
8383
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
8484
<LinkIncremental>false</LinkIncremental>
8585
<OutDir>$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>
8686
<IntDir>$(Platform)\$(Configuration)\</IntDir>
87-
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.0</TargetName>
87+
<TargetName>$(ProjectName)$(PlatformArchitecture)-2.0.1</TargetName>
8888
</PropertyGroup>
8989
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
9090
<ClCompile>
@@ -100,7 +100,7 @@
100100
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
101101
</Link>
102102
<PostBuildEvent>
103-
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
103+
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
104104
<Message>Copying dll to bin folder</Message>
105105
</PostBuildEvent>
106106
</ItemDefinitionGroup>
@@ -118,7 +118,7 @@
118118
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
119119
</Link>
120120
<PostBuildEvent>
121-
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
121+
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
122122
<Message>Copying dll to bin folder</Message>
123123
</PostBuildEvent>
124124
</ItemDefinitionGroup>
@@ -140,7 +140,7 @@
140140
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
141141
</Link>
142142
<PostBuildEvent>
143-
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
143+
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
144144
<Message>Copying dll to bin folder</Message>
145145
</PostBuildEvent>
146146
</ItemDefinitionGroup>
@@ -162,7 +162,7 @@
162162
<ModuleDefinitionFile>xlpython.def</ModuleDefinitionFile>
163163
</Link>
164164
<PostBuildEvent>
165-
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir).xlpy"</Command>
165+
<Command>xcopy /y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)addin\xlpython"</Command>
166166
<Message>Copying dll to bin folder</Message>
167167
</PostBuildEvent>
168168
</ItemDefinitionGroup>

0 commit comments

Comments
 (0)