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 }