Вопрос

I'm using .NET library exposed to COM from VBA. The library interface uses the TimeSpan class for settable property. Is there any way I can construct the TimeSpan via COM, so that I can assign a value to the property?

I found I can instantiate some System assembly types, if I register the assembly for COM:
https://learn.microsoft.com/en-us/previous-versions/ms364069(v=vs.80)#calling-into-the-framework

But it does not seem to be useful at all, as the TimeSpan is an immutable object whose value can be set only via a constructor with parameter or static factory method. None of these can be called via COM, afaik.

I'm hoping there's some trick. For instance one can create the TimeSpan indirectly using the following C# code that could theoretically be convertible to COM, was the DateTime class exposed to COM (what it is not, contrary to the TimeSpan):

new DateTime().AddMilliseconds(1000).TimeOfDay;

Obviously I can implement my own .NET assembly exposed to COM with some factory method. But I'm hoping for a solution using "standard" .NET libraries or a plain VBA code only.

Это было полезно?

Решение

Lots of the basic .NET types have the [ComVisible(true)] attribute so they can be used in COM interop. So does TimeSpan, the CLR will automatically marshal it for you.

Trouble is, there's no standard COM Automation type that unambiguously represents a time span. Like there is for DateTime, it is marshaled as a scaled double, 0 means 12/30/1899 and it increments by 1.0 for every day. TimeOfDay is the fraction. DateTime.ToOADate() returns it. No such standard was ever picked for a time span. Compare to the VBScript DateDiff() function, it returns an integer whose value depends on the unit you ask for.

So the CLR has few ways to keep everybody happy, it punts and marshals the TimeSpan.Ticks property value. A 64-bit integer, unit is 100-nanoseconds. The corresponding Automation type is VT_I8.

Trouble is, lots of COM hosts peter out at 32-bit integers. VBA does. So they tend to fall over, the common error message is "Automation type not supported".

Nothing you can't work around, you can expose the value with a different type that VBA does support. A double for example. You just need to document how the COM client should interpret it. A value of 0 is entirely unambiguous, only the scaling is a design choice. Since TimeSpan already has a property that returns the time span as a double, you'd probably favor milliseconds. So you can simply return TimeSpan.TotalMilliseconds and round-trip with TimeSpan.FromMilliseconds(). Using a unit of days, like DateTime does, is attractive as well. Use TimeSpan.TotalDays and TimeSpan.FromDays(). Exposing it as int is an option as well. But watch out for overflow, if you pick msec then time spans larger than 27 days cannot be represented. Up to you to make the choice.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top