Skip to content

Commit 1a8ad0f

Browse files
authoredOct 7, 2024··
Merge pull request #11 from Excel-DNA/ExtendedRegistration
Documented extended registration usage
2 parents 203cb13 + c9c9e45 commit 1a8ad0f

File tree

1 file changed

+452
-0
lines changed

1 file changed

+452
-0
lines changed
 
+452
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,452 @@
1+
---
2+
title: "Extended Registration"
3+
---
4+
5+
## Nullable parameter
6+
7+
```csharp
8+
[ExcelFunction]
9+
public static string NullableDouble(double? d)
10+
{
11+
return "Nullable VAL: " + (d.HasValue ? d : "NULL");
12+
}
13+
```
14+
15+
| Cell | Formula | Result
16+
| ----- | -------------------- | ------
17+
| A1 | =NullableDouble(1.2) | Nullable VAL: 1.2
18+
| A2 | =NullableDouble() | Nullable VAL: NULL
19+
20+
## Optional parameter
21+
22+
```csharp
23+
[ExcelFunction]
24+
public static string OptionalDouble(double d = 1.23)
25+
{
26+
return "Optional VAL: " + d.ToString();
27+
}
28+
```
29+
30+
| Cell | Formula | Result
31+
| ----- | -------------------- | ------
32+
| A1 | =OptionalDouble(2.3) | Optional VAL: 2.3
33+
| A2 | =OptionalDouble() | Optional VAL: 1.23
34+
35+
## Range parameter
36+
37+
```csharp
38+
[ExcelFunction]
39+
public static string Range(Microsoft.Office.Interop.Excel.Range r)
40+
{
41+
return r.Address;
42+
}
43+
```
44+
45+
| Cell | Formula | Result
46+
| ----- | ------------------ | ------
47+
| A1 | =Range(B2) | $B$2
48+
| A2 | =Range(B2:C4) | $B$2:$C$4
49+
| A3 | =Range((B2,D5:E6)) | $B$2,$D$5:$E$6
50+
51+
## Enums parameter and return value
52+
53+
```csharp
54+
[ExcelFunction]
55+
public static string Enum(DateTimeKind e)
56+
{
57+
return "Enum VAL: " + e.ToString();
58+
}
59+
60+
[ExcelFunction]
61+
public static DateTimeKind EnumReturn(string s)
62+
{
63+
return Enum.Parse<DateTimeKind>(s);
64+
}
65+
```
66+
67+
| Cell | Formula | Result
68+
| ----- | -------------------------- | ------
69+
| A1 | =Enum("Unspecified") | Enum VAL: Unspecified
70+
| A2 | =Enum("Local") | Enum VAL: Local
71+
| A3 | =Enum(1) | Enum VAL: Utc
72+
| A4 | =EnumReturn("Unspecified") | Unspecified
73+
| A5 | =EnumReturn("Local") | Local
74+
75+
## String array parameter
76+
77+
```csharp
78+
[ExcelFunction]
79+
public static string StringArray(string[] s)
80+
{
81+
return "StringArray VALS: " + string.Concat(s);
82+
}
83+
```
84+
85+
| Cell | Formula | Result
86+
| ----- | ------------------- | ------
87+
| A1 | 01 |
88+
| A2 | 2.30 |
89+
| A3 | World |
90+
| B1 | =StringArray(A1:A3) | StringArray VALS: 12.3World
91+
92+
## String array 2D parameter
93+
94+
```csharp
95+
[ExcelFunction]
96+
public static string StringArray2D(string[,] s)
97+
{
98+
string result = "";
99+
for (int i = 0; i < s.GetLength(0); i++)
100+
{
101+
for (int j = 0; j < s.GetLength(1); j++)
102+
{
103+
result += s[i, j];
104+
}
105+
106+
result += " ";
107+
}
108+
109+
return $"StringArray2D VALS: {result}";
110+
}
111+
```
112+
113+
| Cell | Formula | Result
114+
| ----- | --------------------- | ------
115+
| A1 | 01 |
116+
| A2 | 2.30 |
117+
| A3 | Hello |
118+
| B1 | 5 |
119+
| B2 | 6.7 |
120+
| B3 | World |
121+
| C1 | =StringArray2D(A1:B3) | StringArray2D VALS: 15 2.36.7 HelloWorld
122+
123+
## params parameter
124+
125+
```csharp
126+
[ExcelFunction]
127+
public static string ParamsFunc1(
128+
[ExcelArgument(Name = "first.Input", Description = "is a useful start")]
129+
object input,
130+
[ExcelArgument(Description = "is another param start")]
131+
string QtherInpEt,
132+
[ExcelArgument(Name = "Value", Description = "gives the Rest")]
133+
params object[] args)
134+
{
135+
return input + "," + QtherInpEt + ", : " + args.Length;
136+
}
137+
138+
[ExcelFunction]
139+
public static string ParamsFunc2(
140+
[ExcelArgument(Name = "first.Input", Description = "is a useful start")]
141+
object input,
142+
[ExcelArgument(Name = "second.Input", Description = "is some more stuff")]
143+
string input2,
144+
[ExcelArgument(Description = "is another param ")]
145+
string QtherInpEt,
146+
[ExcelArgument(Name = "Value", Description = "gives the Rest")]
147+
params object[] args)
148+
{
149+
var content = string.Join(",", args.Select(ValueType => ValueType.ToString()));
150+
return input + "," + input2 + "," + QtherInpEt + ", " + $"[{args.Length}: {content}]";
151+
}
152+
153+
[ExcelFunction]
154+
public static string ParamsJoinString(string separator, params string[] values)
155+
{
156+
return String.Join(separator, values);
157+
}
158+
```
159+
160+
| Cell | Formula | Result
161+
| ----- | ------------------------------------------- | ------
162+
| A1 | =ParamsFunc1(1,\"2\",4,5) | 1,2, : 2
163+
| A2 | =ParamsFunc2(\"a\",,\"c\",\"d\",,\"f\") | a,,c, [3: d,ExcelDna.Integration.ExcelMissing,f]
164+
| A3 | =ParamsJoinString(\"//\",\"5\",\"4\",\"3\") | 5//4//3
165+
166+
## Async functions and tasks
167+
168+
```csharp
169+
[ExcelAsyncFunction]
170+
public static string AsyncHello(string name, int msToSleep)
171+
{
172+
return $"Hello async {name}";
173+
}
174+
175+
[ExcelAsyncFunction]
176+
public static async Task<string> AsyncTaskHello(string name, int msDelay)
177+
{
178+
await Task.Delay(msDelay);
179+
return $"Hello async task {name}";
180+
}
181+
182+
[ExcelFunction]
183+
public static Task<string> TaskHello(string name)
184+
{
185+
return Task.FromResult($"Hello task {name}");
186+
}
187+
```
188+
189+
## Object handles
190+
191+
Create and reuse .NET objects:
192+
193+
```csharp
194+
public class Calc
195+
{
196+
private double d1, d2;
197+
198+
public Calc(double d1, double d2)
199+
{
200+
this.d1 = d1;
201+
this.d2 = d2;
202+
}
203+
204+
public double Sum()
205+
{
206+
return d1 + d2;
207+
}
208+
}
209+
210+
[ExcelFunction]
211+
public static Calc CreateCalc(double d1, double d2)
212+
{
213+
return new Calc(d1, d2);
214+
}
215+
216+
[ExcelFunction]
217+
public static double CalcSum(Calc c)
218+
{
219+
return c.Sum();
220+
}
221+
```
222+
223+
| Cell | Formula | Result |
224+
| ----- | --------------------- | ------ |
225+
| A1 | =CreateCalc(1.2, 3.4) | |
226+
| A2 | =CalcSum(A1) | 4.6 |
227+
228+
229+
Thread safe creation and use is supported:
230+
231+
```csharp
232+
[ExcelFunction(IsThreadSafe = true)]
233+
public static Calc CreateCalcTS(double d1, double d2)
234+
{
235+
return new Calc(d1, d2);
236+
}
237+
238+
[ExcelFunction(IsThreadSafe = true)]
239+
public static double CalcSumTS(Calc c)
240+
{
241+
return c.Sum();
242+
}
243+
```
244+
245+
Object resources are automatically disposed when no longer used:
246+
247+
```csharp
248+
public class DisposableObject : IDisposable
249+
{
250+
public static int ObjectsCount { get; private set; } = 0;
251+
private bool disposedValue;
252+
253+
public DisposableObject()
254+
{
255+
++ObjectsCount;
256+
}
257+
258+
protected virtual void Dispose(bool disposing)
259+
{
260+
if (!disposedValue)
261+
{
262+
if (disposing)
263+
{
264+
--ObjectsCount;
265+
}
266+
267+
disposedValue = true;
268+
}
269+
}
270+
271+
public void Dispose()
272+
{
273+
Dispose(disposing: true);
274+
GC.SuppressFinalize(this);
275+
}
276+
}
277+
278+
[ExcelFunction]
279+
public static DisposableObject CreateDisposableObject(int x)
280+
{
281+
return new DisposableObject();
282+
}
283+
```
284+
285+
## User defined parameter conversions
286+
287+
```csharp
288+
public class TestType1
289+
{
290+
public string Value;
291+
292+
public TestType1(string value)
293+
{
294+
Value = value;
295+
}
296+
}
297+
298+
public class TestType2
299+
{
300+
public string Value;
301+
302+
public TestType2(string value)
303+
{
304+
Value = value;
305+
}
306+
}
307+
308+
[ExcelParameterConversion]
309+
public static TestType2 Order1ToTestType2FromTestType1(TestType1 value)
310+
{
311+
return new TestType2("From TestType1 " + value.Value);
312+
}
313+
314+
[ExcelParameterConversion]
315+
public static TestType1 Order2ToTestType1(string value)
316+
{
317+
return new TestType1(value);
318+
}
319+
320+
[ExcelParameterConversion]
321+
public static TestType1 Order3ToTestType1Also(string value)
322+
{
323+
return new TestType1("Also " + value);
324+
}
325+
326+
[ExcelParameterConversion]
327+
public static Version ToVersion(string s)
328+
{
329+
return new Version(s);
330+
}
331+
332+
[ExcelFunction]
333+
public static string TestType1(TestType1 tt)
334+
{
335+
return "The TestType1 value is " + tt.Value;
336+
}
337+
338+
[ExcelFunction]
339+
public static string TestType2(TestType2 tt)
340+
{
341+
return "The TestType2 value is " + tt.Value;
342+
}
343+
344+
[ExcelFunction]
345+
public static string Version2(Version v)
346+
{
347+
return "The Version value with field count 2 is " + v.ToString(2);
348+
}
349+
```
350+
351+
| Cell | Formula | Result
352+
| ----- | -------------------- | ------
353+
| A1 | =Version2("4.3.2.1") | The Version value with field count 2 is 4.3
354+
| A2 | =TestType1("world") | The TestType1 value is world
355+
| A3 | =TestType2("world2") | The TestType2 value is From TestType1 world2
356+
357+
User defined parameter conversions are sorted alphabetically by function name.
358+
359+
More complex type conversions (like `TestType2 Order1ToTestType2FromTestType1(TestType1 value)`) should be ordered before simpler type conversions they dependent on (like `TestType1 Order2ToTestType1(string value)`).
360+
361+
Subsequent multiple conversions for the same type (like `TestType1 Order3ToTestType1Also(string value)`) are ignored and the first one (like `TestType1 Order2ToTestType1(string value)`) is used.
362+
363+
## Function execution handler
364+
365+
Monitor Excel functions execution with a custom handler, marked with `ExcelFunctionExecutionHandlerSelector` attribute:
366+
367+
```csharp
368+
internal class FunctionLoggingHandler : FunctionExecutionHandler
369+
{
370+
public int? ID { get; set; }
371+
372+
public override void OnEntry(FunctionExecutionArgs args)
373+
{
374+
// FunctionExecutionArgs gives access to the function name and parameters,
375+
// and gives some options for flow redirection.
376+
377+
// Tag will flow through the whole handler
378+
if (ID.HasValue)
379+
args.Tag = $"ID={ID.Value} ";
380+
else
381+
args.Tag = "";
382+
args.Tag += args.FunctionName;
383+
384+
Logger.Log($"{args.Tag} - OnEntry - Args: {args.Arguments.Select(arg => arg.ToString())}");
385+
}
386+
387+
public override void OnSuccess(FunctionExecutionArgs args)
388+
{
389+
Logger.Log($"{args.Tag} - OnSuccess - Result: {args.ReturnValue}");
390+
}
391+
392+
public override void OnException(FunctionExecutionArgs args)
393+
{
394+
Logger.Log($"{args.Tag} - OnException - Message: {args.Exception}");
395+
}
396+
397+
public override void OnExit(FunctionExecutionArgs args)
398+
{
399+
Logger.Log($"{args.Tag} - OnExit");
400+
}
401+
402+
[ExcelFunctionExecutionHandlerSelector]
403+
public static IFunctionExecutionHandler LoggingHandlerSelector(IExcelFunctionInfo functionInfo)
404+
{
405+
if (functionInfo.CustomAttributes.OfType<LoggingAttribute>().Any())
406+
{
407+
var loggingAtt = functionInfo.CustomAttributes.OfType<LoggingAttribute>().First();
408+
return new FunctionLoggingHandler { ID = loggingAtt.ID };
409+
}
410+
411+
return new FunctionLoggingHandler();
412+
}
413+
}
414+
```
415+
416+
The default return value for async functions that are in process is #N/A. You can, for example, return the newer #GETTING_DATA error code creating the following function execution handler:
417+
418+
```csharp
419+
internal class AsyncReturnHandler : FunctionExecutionHandler
420+
{
421+
public override void OnSuccess(FunctionExecutionArgs args)
422+
{
423+
if (args.ReturnValue.Equals(ExcelError.ExcelErrorNA))
424+
args.ReturnValue = ExcelError.ExcelErrorGettingData;
425+
}
426+
427+
[ExcelFunctionExecutionHandlerSelector]
428+
public static IFunctionExecutionHandler AsyncReturnHandlerSelector(IExcelFunctionInfo functionInfo)
429+
{
430+
return new AsyncReturnHandler();
431+
}
432+
}
433+
```
434+
435+
## Function registration processing
436+
437+
You can implement custom function wrappers during registration using `ExcelFunctionProcessor` attribute:
438+
439+
```csharp
440+
public interface IExcelFunctionInfo
441+
{
442+
ExcelFunctionAttribute FunctionAttribute { get; }
443+
List<IExcelFunctionParameter> Parameters { get; }
444+
IExcelFunctionReturn Return { get; }
445+
List<object> CustomAttributes { get; }
446+
447+
LambdaExpression FunctionLambda { get; set; }
448+
}
449+
450+
[ExcelFunctionProcessor]
451+
public static IEnumerable<IExcelFunctionInfo> ProcessFunctions(IEnumerable<IExcelFunctionInfo> registrations, IExcelFunctionRegistrationConfiguration config)
452+
```

0 commit comments

Comments
 (0)
Please sign in to comment.