1 /**
2 	Code from generic.h and generic.d
3 	Ported to the D Programming Language by Laeeth Isharc (2015)
4 	This module provides the ceremony that must be done for every XLL.
5 	At least one module must be linked to this one implementing the
6 	getWorkSheetFunctions function so that they can be registered
7 	with Excel.
8 */
9 
10 module xlld.xll;
11 
12 import xlld: WorksheetFunction, LPXLOPER12;
13 version(unittest) import unit_threaded;
14 
15 
16 alias AutoCloseFunc = void delegate() nothrow;
17 
18 private AutoCloseFunc[] gAutoCloseFuncs;
19 
20 /**
21    Registers a delegate to be called when the XLL is unloaded
22 */
23 void registerAutoCloseFunc(AutoCloseFunc func) nothrow {
24     gAutoCloseFuncs ~= func;
25 }
26 
27 /**
28    Registers a function to be called when the XLL is unloaded
29 */
30 void registerAutoCloseFunc(void function() nothrow func) nothrow {
31     gAutoCloseFuncs ~= { func(); };
32 }
33 
34 private void callRegisteredAutoCloseFuncs() nothrow {
35     foreach(func; gAutoCloseFuncs) func();
36 }
37 
38 @("registerAutoClose delegate")
39 unittest {
40     int i;
41     registerAutoCloseFunc({ ++i; });
42     callRegisteredAutoCloseFuncs();
43     i.shouldEqual(1);
44 }
45 
46 @("registerAutoClose function")
47 unittest {
48     const old = gAutoCloseCounter;
49     registerAutoCloseFunc(&testAutoCloseFunc);
50     callRegisteredAutoCloseFuncs();
51     (gAutoCloseCounter - old).shouldEqual(1);
52 }
53 
54 version(Windows) {
55     version(exceldDef)
56         enum dllMain = false;
57     else version(unittest)
58         enum dllMain = false;
59     else
60         enum dllMain = true;
61 } else
62       enum dllMain = false;
63 
64 
65 static if(dllMain) {
66 
67     import core.sys.windows.windows;
68 
69     extern(Windows) BOOL DllMain( HANDLE hDLL, DWORD dwReason, LPVOID lpReserved )
70     {
71         import core.runtime;
72         import std.c.windows.windows;
73         import core.sys.windows.dll;
74         switch (dwReason)
75         {
76         case DLL_PROCESS_ATTACH:
77             Runtime.initialize();
78             dll_process_attach( hDLL, true );
79             break;
80         case DLL_PROCESS_DETACH:
81             Runtime.terminate();
82             dll_process_detach( hDLL, true );
83             break;
84         case DLL_THREAD_ATTACH:
85             dll_thread_attach( true, true );
86             break;
87         case DLL_THREAD_DETACH:
88             dll_thread_detach( true, true );
89             break;
90         default:
91             break;
92         }
93         return true;
94     }
95 }
96 
97 // this function must be defined in a module compiled with
98 // the current module
99 // It's extern(C) so that it can be defined in any module
100 extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow;
101 
102 extern(Windows) int xlAutoOpen() {
103     import core.runtime:rt_init;
104 
105     rt_init(); // move to DllOpen?
106     registerAllWorkSheetFunctions;
107 
108     return 1;
109 }
110 
111 private void registerAllWorkSheetFunctions() {
112     import xlld.memorymanager: allocator;
113     import xlld.framework: Excel12f, freeXLOper;
114     import xlld.xlcall: xlGetName, xlfRegister, XLOPER12;
115     import xlld.wrap: toXlOper;
116     import std.algorithm: map;
117     import std.array: array;
118 
119     // get name of this XLL, needed to pass to xlfRegister
120     static XLOPER12 dllName;
121     Excel12f(xlGetName, &dllName);
122 
123     foreach(strings; getWorksheetFunctions.map!(a => a.toStringArray)) {
124         auto opers = strings.map!(a => a.toXlOper(allocator)).array;
125         scope(exit) foreach(ref oper; opers) freeXLOper(&oper, allocator);
126 
127         auto args = new LPXLOPER12[opers.length + 1];
128         args[0] = &dllName;
129         foreach(i; 0 .. opers.length)
130             args[i + 1] = &opers[i];
131 
132         Excel12f(xlfRegister, cast(LPXLOPER12)null, args);
133     }
134 }
135 
136 extern(Windows) int xlAutoClose() {
137     import core.runtime: rt_term;
138 
139     callRegisteredAutoCloseFuncs;
140 
141     rt_term;
142     return 1;
143 }
144 
145 extern(Windows) int xlAutoFree12(LPXLOPER12 arg) nothrow {
146     import xlld.memorymanager: autoFree;
147     import xlld.xlcall: xlbitDLLFree;
148 
149     if(!(arg.xltype & xlbitDLLFree)) {
150         log("[ERROR]: Trying to free XLOPER12 without xlbitDLLFree, ignoring");
151         return 0;
152     }
153 
154     autoFree(arg);
155     return 1;
156 }
157 
158 extern(Windows) LPXLOPER12 xlAddInManagerInfo12(LPXLOPER12 xAction) {
159     import xlld.xlcall: XLOPER12, XlType, xltypeInt, xlCoerce, xlerrValue;
160     import xlld.wrap: toAutoFreeOper;
161     import xlld.framework: Excel12f;
162 
163     static XLOPER12 xInfo, xIntAction;
164 
165     //
166     // This code coerces the passed-in value to an integer. This is how the
167     // code determines what is being requested. If it receives a 1,
168     // it returns a string representing the long name. If it receives
169     // anything else, it returns a #VALUE! error.
170     //
171 
172     //we need an XLOPER12 with a _value_ of xltypeInt
173     XLOPER12 arg;
174     arg.xltype = XlType.xltypeInt;
175     arg.val.w = xltypeInt;
176 
177     Excel12f(xlCoerce, &xIntAction, [xAction, &arg]);
178 
179     if (xIntAction.val.w == 1) {
180         xInfo = "My XLL".toAutoFreeOper;
181     } else {
182         xInfo.xltype = XlType.xltypeErr;
183         xInfo.val.err = xlerrValue;
184     }
185 
186     return &xInfo;
187 }
188 
189 version(Windows) {
190     extern(Windows) void OutputDebugStringW(const wchar* fmt) nothrow;
191 
192     const(wchar)* toWStringz(in wstring str) nothrow {
193         return (str ~ '\0').ptr;
194     }
195 
196     void log(T...)(T args) {
197         import std.conv: text, to;
198         try
199             OutputDebugStringW(text(args).to!wstring.toWStringz);
200         catch(Exception)
201             OutputDebugStringW("[DataServer] outputDebug itself failed"w.toWStringz);
202     }
203 } else version(unittest) {
204     void log(T...)(T args) {
205     }
206   } else {
207     void log(T...)(T args) {
208         import std.experimental.logger: trace;
209         try
210             trace(args);
211         catch(Exception ex) {}
212     }
213 }
214 
215 version(unittest) {
216     // to link
217     extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow {
218         return [];
219     }
220 
221     int gAutoCloseCounter;
222 
223     @DontTest
224         void testAutoCloseFunc() nothrow {
225         ++gAutoCloseCounter;
226     }
227 }