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 }