1 /** 2 Utility functions for writing XLLs 3 */ 4 module xlld.framework; 5 6 7 import xlld.xlcall; 8 9 10 /** 11 Will free any malloc'd memory associated with the given 12 LPXLOPER, assuming it has any memory associated with it 13 14 Parameters: 15 16 LPXLOPER pxloper Pointer to the XLOPER whose associated 17 */ 18 void freeXLOper(T, A)(T pxloper, ref A allocator) 19 if(is(T == LPXLOPER) || is(T == LPXLOPER12)) 20 { 21 import std.experimental.allocator: dispose; 22 23 switch (pxloper.xltype & ~XlType.xlbitDLLFree) with(XlType) { 24 case xltypeStr: 25 if (pxloper.val.str !is null) { 26 void* bytesPtr = pxloper.val.str; 27 const numBytes = (pxloper.val.str[0] + 1) * wchar.sizeof; 28 allocator.dispose(bytesPtr[0 .. numBytes]); 29 } 30 break; 31 case xltypeRef: 32 if (pxloper.val.mref.lpmref !is null) 33 allocator.dispose(pxloper.val.mref.lpmref); 34 break; 35 case xltypeMulti: 36 auto cxloper = pxloper.val.array.rows * pxloper.val.array.columns; 37 const numOpers = cxloper; 38 if (pxloper.val.array.lparray !is null) 39 { 40 auto pxloperFree = pxloper.val.array.lparray; 41 while (cxloper > 0) 42 { 43 freeXLOper(pxloperFree, allocator); 44 pxloperFree++; 45 cxloper--; 46 } 47 allocator.dispose(pxloper.val.array.lparray[0 .. numOpers]); 48 } 49 break; 50 case xltypeBigData: 51 if (pxloper.val.bigdata.h.lpbData !is null) 52 allocator.dispose(pxloper.val.bigdata.h.lpbData); 53 break; 54 default: // todo: add error handling 55 break; 56 } 57 } 58 59 @("Free regular XLOPER") 60 unittest { 61 import xlld.memorymanager: allocator; 62 XLOPER oper; 63 freeXLOper(&oper, allocator); 64 } 65 66 @("Free XLOPER12") 67 unittest { 68 import xlld.memorymanager: allocator; 69 XLOPER12 oper; 70 freeXLOper(&oper, allocator); 71 } 72 73 /** 74 Wrapper for the Excel12 function that allows passing D arrays 75 76 Purpose: 77 A fancy wrapper for the Excel12() function. It also 78 does the following: 79 80 (1) Checks that none of the LPXLOPER12 arguments are 0, 81 which would indicate that creating a temporary XLOPER12 82 has failed. In this case, it doesn't call Excel12 83 but it does print a debug message. 84 (2) If an error occurs while calling Excel12, 85 print a useful debug message. 86 (3) When done, free all temporary memory. 87 88 #1 and #2 require _DEBUG to be defined. 89 90 Parameters: 91 92 int xlfn Function number (xl...) to call 93 LPXLOPER12 pxResult Pointer to a place to stuff the result, 94 or 0 if you don't care about the result. 95 int count Number of arguments 96 ... (all LPXLOPER12s) - the arguments. 97 98 Returns: 99 100 A return code (Some of the xlret... values, as defined 101 in XLCALL.H, OR'ed together). 102 103 Comments: 104 */ 105 106 int Excel12f(int xlfn, LPXLOPER12 pxResult, LPXLOPER12[] args...) nothrow @nogc 107 { 108 import xlld.xlcallcpp: Excel12v; 109 import std.algorithm: all; 110 111 assert(args.all!(a => a !is null)); 112 return Excel12v(xlfn, pxResult, cast(int)args.length, cast(LPXLOPER12*)args.ptr); 113 } 114 115 int Excel12f(int xlfn, LPXLOPER12 result, XLOPER12[] args...) nothrow { 116 117 auto ptrArgs = new LPXLOPER12[args.length]; 118 119 foreach(i, ref arg; args) 120 ptrArgs[i] = () @trusted { return &arg; }(); 121 122 return Excel12f(xlfn, result, ptrArgs); 123 } 124 125 int Excel12f(int xlfn, LPXLOPER12 pxResult) nothrow @nogc 126 { 127 return Excel12f(xlfn, pxResult, LPXLOPER12[].init); 128 } 129 130 __gshared immutable excel12Exception = new Exception("Error calling Excel12f"); 131 132 /** 133 D version of Excel12f. "D version" in the sense that the types 134 are all D types. Avoids having to manually convert to XLOPER12. 135 e.g. excel12(xlfFoo, 1.0, 2.0); 136 137 Returns a value of type T to specified at compile time which 138 must be freed with xlld.memorymanager: autoFreeAllocator 139 */ 140 T excel12(T, A...)(int xlfn, auto ref A args) @trusted { 141 import xlld.memorymanager: gTempAllocator, autoFreeAllocator; 142 import xlld.wrap: toXlOper, fromXlOper; 143 import xlld.xlcall: xlretSuccess; 144 145 XLOPER12[A.length] operArgs; 146 LPXLOPER12[A.length] operArgPtrs; 147 148 scope(exit) gTempAllocator.deallocateAll; 149 150 foreach(i, _; A) { 151 operArgs[i] = args[i].toXlOper(gTempAllocator); 152 operArgPtrs[i] = &operArgs[i]; 153 } 154 155 XLOPER12 result; 156 if(Excel12f(xlfn, &result, operArgPtrs) != xlretSuccess) 157 throw excel12Exception; 158 159 return result.fromXlOper!T(autoFreeAllocator); 160 } 161 162 /** 163 Version of excel12 that avoids automatic memory management 164 by asking the caller to supply a compile-time function 165 to call on the result. 166 */ 167 auto excel12Then(alias F, A...)(int xlfn, auto ref A args) { 168 import std.traits: Parameters, Unqual, isArray, ReturnType; 169 import std.experimental.allocator: dispose; 170 import xlld.memorymanager: autoFreeAllocator; 171 172 static assert(Parameters!F.length == 1, "Must pass function of one argument"); 173 alias T = Parameters!F[0]; 174 auto excelRet = excel12!T(xlfn, args); 175 static if(is(ReturnType!F == void)) 176 F(excelRet); 177 else 178 auto ret = F(excelRet); 179 180 static if(isArray!T) { 181 import std.range: ElementType; 182 alias RealType = Unqual!(ElementType!T)[]; 183 } else 184 alias RealType = Unqual!T; 185 186 void freeRet(U)() @trusted { 187 autoFreeAllocator.dispose(cast()excelRet); 188 } 189 190 static if(__traits(compiles, freeRet!RealType())) 191 freeRet!RealType(); 192 193 static if(!is(ReturnType!F == void)) 194 return ret; 195 }