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.sdk.xll;
11 
12 import xlld: WorksheetFunction, LPXLOPER12;
13 version(testingExcelD) 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 void callRegisteredAutoCloseFuncs() nothrow {
35     foreach(func; gAutoCloseFuncs) func();
36 }
37 
38 
39 version(Windows) {
40     version(exceldDef)
41         enum dllMain = false;
42     else version(unittest)
43         enum dllMain = false;
44     else
45         enum dllMain = true;
46 } else
47       enum dllMain = false;
48 
49 
50 static if(dllMain) {
51 
52     import core.sys.windows.windows;
53 
54     extern(Windows) BOOL DllMain( HANDLE hDLL, DWORD dwReason, LPVOID lpReserved )
55     {
56         import core.runtime;
57         import std.c.windows.windows;
58         import core.sys.windows.dll;
59         switch (dwReason)
60         {
61         case DLL_PROCESS_ATTACH:
62             Runtime.initialize();
63             dll_process_attach( hDLL, true );
64             break;
65         case DLL_PROCESS_DETACH:
66             Runtime.terminate();
67             dll_process_detach( hDLL, true );
68             break;
69         case DLL_THREAD_ATTACH:
70             dll_thread_attach( true, true );
71             break;
72         case DLL_THREAD_DETACH:
73             dll_thread_detach( true, true );
74             break;
75         default:
76             break;
77         }
78         return true;
79     }
80 }
81 
82 // this function must be defined in a module compiled with
83 // the current module
84 // It's extern(C) so that it can be defined in any module
85 extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow;
86 
87 extern(Windows) int xlAutoOpen() {
88     import core.runtime:rt_init;
89 
90     rt_init(); // move to DllOpen?
91     registerAllWorkSheetFunctions;
92 
93     return 1;
94 }
95 
96 private void registerAllWorkSheetFunctions() {
97     import xlld.memorymanager: allocator;
98     import xlld.sdk.framework: Excel12f, freeXLOper;
99     import xlld.sdk.xlcall: xlGetName, xlfRegister, XLOPER12;
100     import xlld.conv: toXlOper;
101     import std.algorithm: map;
102     import std.array: array;
103 
104     // get name of this XLL, needed to pass to xlfRegister
105     static XLOPER12 dllName;
106     Excel12f(xlGetName, &dllName);
107 
108     foreach(strings; getWorksheetFunctions.map!(a => a.toStringArray)) {
109         auto opers = strings.map!(a => a.toXlOper(allocator)).array;
110         scope(exit) foreach(ref oper; opers) freeXLOper(&oper, allocator);
111 
112         auto args = new LPXLOPER12[opers.length + 1];
113         args[0] = &dllName;
114         foreach(i; 0 .. opers.length)
115             args[i + 1] = &opers[i];
116 
117         Excel12f(xlfRegister, cast(LPXLOPER12)null, args);
118     }
119 }
120 
121 extern(Windows) int xlAutoClose() {
122     import core.runtime: rt_term;
123 
124     callRegisteredAutoCloseFuncs;
125 
126     rt_term;
127     return 1;
128 }
129 
130 extern(Windows) int xlAutoFree12(LPXLOPER12 arg) nothrow {
131     import xlld.memorymanager: autoFree;
132     import xlld.sdk.xlcall: xlbitDLLFree;
133 
134     if(!(arg.xltype & xlbitDLLFree)) {
135         log("[ERROR]: Trying to free XLOPER12 without xlbitDLLFree, ignoring");
136         return 0;
137     }
138 
139     autoFree(arg);
140     return 1;
141 }
142 
143 extern(Windows) LPXLOPER12 xlAddInManagerInfo12(LPXLOPER12 xAction) {
144     import xlld.sdk.xlcall: XLOPER12, XlType, xltypeInt, xlCoerce, xlerrValue;
145     import xlld.sdk.framework: Excel12f;
146     import xlld.conv: toAutoFreeOper;
147 
148     static XLOPER12 xInfo, xIntAction;
149 
150     //
151     // This code coerces the passed-in value to an integer. This is how the
152     // code determines what is being requested. If it receives a 1,
153     // it returns a string representing the long name. If it receives
154     // anything else, it returns a #VALUE! error.
155     //
156 
157     //we need an XLOPER12 with a _value_ of xltypeInt
158     XLOPER12 arg;
159     arg.xltype = XlType.xltypeInt;
160     arg.val.w = xltypeInt;
161 
162     Excel12f(xlCoerce, &xIntAction, [xAction, &arg]);
163 
164     if (xIntAction.val.w == 1) {
165         xInfo = "My XLL".toAutoFreeOper;
166     } else {
167         xInfo.xltype = XlType.xltypeErr;
168         xInfo.val.err = xlerrValue;
169     }
170 
171     return &xInfo;
172 }
173 
174 version(Windows) {
175     extern(Windows) void OutputDebugStringW(const wchar* fmt) nothrow @nogc;
176 
177     const(wchar)* toWStringz(in wstring str) @safe nothrow {
178         return &(str ~ '\0')[0];
179     }
180 
181     void log(T...)(T args) {
182         import std.conv: text, to;
183         try
184             OutputDebugStringW(text(args).to!wstring.toWStringz);
185         catch(Exception)
186             OutputDebugStringW("[DataServer] outputDebug itself failed"w.toWStringz);
187     }
188 } else version(unittest) {
189     void log(T...)(T args) {
190     }
191   } else {
192     void log(T...)(T args) {
193         import std.experimental.logger: trace;
194         try
195             trace(args);
196         catch(Exception ex) {}
197     }
198 }