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 core.sys.windows.dll;
58         switch (dwReason)
59         {
60         case DLL_PROCESS_ATTACH:
61             Runtime.initialize();
62             dll_process_attach( hDLL, true );
63             break;
64         case DLL_PROCESS_DETACH:
65             Runtime.terminate();
66             dll_process_detach( hDLL, true );
67             break;
68         case DLL_THREAD_ATTACH:
69             dll_thread_attach( true, true );
70             break;
71         case DLL_THREAD_DETACH:
72             dll_thread_detach( true, true );
73             break;
74         default:
75             break;
76         }
77         return true;
78     }
79 }
80 
81 // this function must be defined in a module compiled with
82 // the current module
83 // It's extern(C) so that it can be defined in any module
84 extern(C) WorksheetFunction[] getWorksheetFunctions() @safe pure nothrow;
85 
86 extern(Windows) int xlAutoOpen() {
87     import core.runtime: rt_init;
88 
89     rt_init(); // move to DllOpen?
90     registerAllWorkSheetFunctions;
91 
92     return 1;
93 }
94 
95 private void registerAllWorkSheetFunctions() {
96     import xlld.memorymanager: allocator;
97     import xlld.sdk.framework: Excel12f, freeXLOper;
98     import xlld.sdk.xlcall: xlGetName, xlfRegister, XLOPER12;
99     import xlld.conv: toXlOper;
100     import std.algorithm: map;
101     import std.array: array;
102 
103     // get name of this XLL, needed to pass to xlfRegister
104     static XLOPER12 dllName;
105     Excel12f(xlGetName, &dllName);
106 
107     foreach(strings; getWorksheetFunctions.map!(a => a.toStringArray)) {
108         auto opers = strings.map!(a => a.toXlOper(allocator)).array;
109         scope(exit) foreach(ref oper; opers) freeXLOper(&oper, allocator);
110 
111         auto args = new LPXLOPER12[opers.length + 1];
112         args[0] = &dllName;
113         foreach(i; 0 .. opers.length)
114             args[i + 1] = &opers[i];
115 
116         Excel12f(xlfRegister, cast(LPXLOPER12)null, args);
117     }
118 }
119 
120 extern(Windows) int xlAutoClose() {
121     import core.runtime: rt_term;
122 
123     callRegisteredAutoCloseFuncs;
124 
125     rt_term;
126     return 1;
127 }
128 
129 extern(Windows) int xlAutoFree12(LPXLOPER12 arg) nothrow {
130     import xlld.memorymanager: autoFree;
131     import xlld.sdk.xlcall: xlbitDLLFree;
132 
133     if(!(arg.xltype & xlbitDLLFree)) {
134         log("[ERROR]: Trying to free XLOPER12 without xlbitDLLFree, ignoring");
135         return 0;
136     }
137 
138     autoFree(arg);
139     return 1;
140 }
141 
142 extern(Windows) LPXLOPER12 xlAddInManagerInfo12(LPXLOPER12 xAction) {
143     import xlld.sdk.xlcall: XLOPER12, XlType, xltypeInt, xlCoerce, xlerrValue;
144     import xlld.sdk.framework: Excel12f;
145     import xlld.conv: toAutoFreeOper;
146 
147     static XLOPER12 xInfo, xIntAction;
148 
149     //
150     // This code coerces the passed-in value to an integer. This is how the
151     // code determines what is being requested. If it receives a 1,
152     // it returns a string representing the long name. If it receives
153     // anything else, it returns a #VALUE! error.
154     //
155 
156     //we need an XLOPER12 with a _value_ of xltypeInt
157     XLOPER12 arg;
158     arg.xltype = XlType.xltypeInt;
159     arg.val.w = xltypeInt;
160 
161     Excel12f(xlCoerce, &xIntAction, [xAction, &arg]);
162 
163     if (xIntAction.val.w == 1) {
164         xInfo = "My XLL".toAutoFreeOper;
165     } else {
166         xInfo.xltype = XlType.xltypeErr;
167         xInfo.val.err = xlerrValue;
168     }
169 
170     return &xInfo;
171 }
172 
173 
174 version(Windows) {
175     extern(Windows) void OutputDebugStringW(const wchar* fmt) @nogc nothrow;
176 }
177 
178 
179 /**
180    Polymorphic logging function.
181    Prints to the console when unit testing and on Linux,
182    otherwise uses the system logger on Windows.
183  */
184 void log(A...)(auto ref A args) @trusted {
185     try {
186         version(unittest) {
187             version(Have_unit_threaded) {
188                 import unit_threaded: writelnUt;
189                 writelnUt(args);
190             } else {
191                 import std.stdio: writeln;
192                 writeln(args);
193             }
194         } else version(Windows) {
195             import nogc.conv: text, toWStringz;
196             scope txt = text(args);
197             scope wtxt = txt[].toWStringz;
198             OutputDebugStringW(wtxt.ptr);
199         } else {
200             import std.experimental.logger: trace;
201             trace(args);
202         }
203     } catch(Exception e) {
204         import core.stdc.stdio: printf;
205         printf("Error - could not log\n");
206     }
207 }