COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 26.04.09 16:02
Оценка:
Есть сторонняя COM библиотека, в API которой используется тип DATE. Есть Interop Assembly для этой библиотеки, где DATE маршалится как тип DateTime.
Некоторые COM объекты имеют DATE свойство — это некий временной token, идентифицирующий объект. Эти DATE значения можно передавать другим методам для извлечения объектов из базы (и других манипуляций).

Проблема следующая: из-за различного внутреннего представления дат (DATE — double, DateTime — long), при маршалинге теряется точность передаваемых значений. Поскольку в COM библиотеке сравнение дат выполняется на точное совпадение (по сути сравниваются double), то необходимый объект найти не удается.

Пример:

COMDBConnectionClass db = new COMDBConnectionClass();
COMClass expected = db.CustomObjects[0];
DateTime moment = expected.MomentDate;
COMClass actual = db.GetObjectByMoment(moment); // error


То, что точность теряется, демонстрирует и этот пример.
DateTime now = DateTime.UtcNow;    // .NET date/time
double toOle = now.ToOADate();    // .NET -> OLE (as it seems what happens on interop)
DateTime fromOleDate = DateTime.FromOADate(toOle); // OLE -> .NET (as it seems what happens on interop)
TimeSpan diff = now - fromOleDate; // The difference is less than 1 millisecond.


Есть ли какие-либо workaround-ы, решающие эту проблему?
com intterop datetime date
Re: COM Interop: DateTime & OLE DATE
От: Аноним  
Дата: 26.04.09 16:43
Оценка:
Здравствуйте, Nikolkos, Вы писали:

N>Есть сторонняя COM библиотека, в API которой используется тип DATE. Есть Interop Assembly для этой библиотеки,

N>...
N>Есть ли какие-либо workaround-ы, решающие эту проблему?

Если честно, то
double toOle = now.ToOADate(); // .NET -> OLE (as it seems what happens on interop)

это делать бесчеловечно...
double как тип имеет длину 64 бита, а сопроцессор использует
80 бит, что будет в оставшихся битах одному богу известно.
По большей части я сомневаюсь что маршалинг идет через
приведение типа, но тем не менее,
попробуй форматировать дату как строку и конвертировать
ее обратно в дату.... хотя это прыжки через голову.

PS
Будя честным лично я всегда так и делал.
Re[2]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 26.04.09 18:12
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Если честно, то

А>double toOle = now.ToOADate(); // .NET -> OLE (as it seems what happens on interop)

А>это делать бесчеловечно...

А>double как тип имеет длину 64 бита, а сопроцессор использует
А>80 бит, что будет в оставшихся битах одному богу известно.
А>По большей части я сомневаюсь что маршалинг идет через
А>приведение типа, но тем не менее,
А>попробуй форматировать дату как строку и конвертировать
А>ее обратно в дату.... хотя это прыжки через голову.

Дело в том, что API COM библиотеки не изменить — в TLB указан тип DATE (как ты предлагаешь подсунуть туда строку?). В том, что примерно так выполняется маршалинг я убедился в отладчике (имея исходный код COM библиотеки и сравнивая значения передаваемых/получаемых параметров).

Можно убедится в этом также, переопределив один из COM интерфейсов и заменив DateTime на double.

[ComImport, Guid("...")]
public interface ICOMClass_FIX
{
    double MomentDate {get;}
}


Т.к. double является blittable типом, то проблем не возникает: double параметр COM библиотека нормально обрабатывает, идентифицируя объект. Как решение, переопределение каждого интерфейса таким образом не подходит. Во-первых, COM API _очень_ большой; во-вторых, объекты типа DATE могут передаваться в вариантных массивах; ну а в-третьих, double в качестве DateTime — это не .NET юзабильно (т.н. unfriendly .NET API).

PS
Небольшое пояснение: решается задача создания .NET оберток для имеющегося COM API.
Re: COM Interop: DateTime & OLE DATE
От: _FRED_ Черногория
Дата: 27.04.09 05:56
Оценка:
Здравствуйте, Nikolkos, Вы писали:

N>Есть сторонняя COM библиотека, в API которой используется тип DATE. Есть Interop Assembly для этой библиотеки, где DATE маршалится как тип DateTime.


А как был сделан этот самый "Interop Assembly для этой библиотеки"?
Help will always be given at Hogwarts to those who ask for it.
Re[2]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 27.04.09 06:36
Оценка:
_FR>А как был сделан этот самый "Interop Assembly для этой библиотеки"?

Я сделал с помощью TlbImp.exe из SDK. COM библиотека "сторонняя" в том смысле, что существующий API менять нельзя (а так эта COM библиотека вполне даже "своя" .
Re[3]: COM Interop: DateTime & OLE DATE
От: Dwarffy  
Дата: 27.04.09 07:39
Оценка:
N>PS
N>Небольшое пояснение: решается задача создания .NET оберток для имеющегося COM API.

тут надо решать задачу о неиспользовании баженого компонента,
который неверно работает с double а именно с его сравнением.
Операция
>> DateTime fromOleDate = DateTime.FromOADate(toOle); // OLE -> .NET (as it seems what happens on interop)
>> TimeSpan diff = now — fromOleDate; // The difference is less than 1 millisecond.
На мой взгляд абсолютно неверна.
Если вы возьмете и разберете оба значения

double toOle = now.ToOADate();
DateTime fromOleDate = DateTime.FromOADate(toOle);
double dVal = fromOleDate.ToOADate();


на состовляющие и сравните все поля, (ГГ.ММ.ДД ЧЧ.ММ.СС.ммм) то получите
два абсолютно идентичных времени.
Если не знаете как это сделать, попробуйте в Excel он разложит вам оба числа
если итересно знать самому, могу дать коды.
Убедитесь сами.

Се-ля-ви.
Надо думать дальше как решать проблему
Re[4]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 27.04.09 08:39
Оценка:
Здравствуйте, Dwarffy, Вы писали:

D>Операция

>>> DateTime fromOleDate = DateTime.FromOADate(toOle); // OLE -> .NET (as it seems what happens on interop)
>>> TimeSpan diff = now — fromOleDate; // The difference is less than 1 millisecond.
D>На мой взгляд абсолютно неверна.
На данный момент меня больше волнует не то, что операция верна или нет. А то, что идентичность значений не соблюдается, хотя по сути выполняются операции над одним и тем же значением DateTime.

D>Если вы возьмете и разберете оба значения


D>
D>double toOle = now.ToOADate();
D>DateTime fromOleDate = DateTime.FromOADate(toOle);
D>double dVal = fromOleDate.ToOADate();
D>

D>на состовляющие и сравните все поля, (ГГ.ММ.ДД ЧЧ.ММ.СС.ммм) то получите
D>два абсолютно идентичных времени.

Понятно, что double значения, полученные в .NET, будут одинаковыми (dVal == toOle). Проблема в том, что при передаче DATE значений COM -> .NET и обратно (без каких-то манипуляций со значениями) теряется точность/идентичность этих значений DATE. Они отличаются менее, чем на 1 милисекунду. Ессно, что в формате ГГ.ММ.ДД ЧЧ.ММ.СС.ммм они будут одинаковые, т.к. тут не учитыватся микросекунды.
Re[5]: COM Interop: DateTime & OLE DATE
От: Dwarffy  
Дата: 27.04.09 10:14
Оценка:
Здравствуйте, Nikolkos, Вы писали:

N>Здравствуйте, Dwarffy, Вы писали:


D>>Операция

>>>> DateTime fromOleDate = DateTime.FromOADate(toOle); // OLE -> .NET (as it seems what happens on interop)
>>>> TimeSpan diff = now — fromOleDate; // The difference is less than 1 millisecond.
D>>На мой взгляд абсолютно неверна.
N>На данный момент меня больше волнует не то, что операция верна или нет. А то, что идентичность значений не соблюдается, хотя по сути выполняются операции над одним и тем же значением DateTime.

D>>Если вы возьмете и разберете оба значения


D>>
D>>double toOle = now.ToOADate();
D>>DateTime fromOleDate = DateTime.FromOADate(toOle);
D>>double dVal = fromOleDate.ToOADate();
D>>

D>>на состовляющие и сравните все поля, (ГГ.ММ.ДД ЧЧ.ММ.СС.ммм) то получите
D>>два абсолютно идентичных времени.

N>... , чем на 1 милисекунду. Ессно, что в формате ГГ.ММ.ДД ЧЧ.ММ.СС.ммм они будут одинаковые, т.к. тут не учитыватся микросекунды.


ммм — стоит за милисекунды, микросекундную точность, я вообще не уверен
Вы уточните про микросекунды, вообще это реально?
Re[6]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 27.04.09 11:12
Оценка:
Здравствуйте, Dwarffy, Вы писали:

D>ммм — стоит за милисекунды, микросекундную точность, я вообще не уверен

D>Вы уточните про микросекунды, вообще это реально?

Почему нет? DateTime имеет представление как long, значение которого доступно через свойство Ticks:

A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001, which represents DateTime.MinValue.


FromOADate, ToOADate как раз оперируют с этим значением ticks (можно посмотреть в рефлекторе). Так что формально точность до 1 тика (100 наносекунд = 0,1 микросекунда) DateTime обеспечивать должен.
Re: COM Interop: DateTime & OLE DATE
От: GlebZ Россия  
Дата: 27.04.09 13:22
Оценка:
Здравствуйте, Nikolkos, Вы писали:

N>То, что точность теряется, демонстрирует и этот пример.

N>
N>DateTime now = DateTime.UtcNow;    // .NET date/time
N>double toOle = now.ToOADate();    // .NET -> OLE (as it seems what happens on interop)
N>DateTime fromOleDate = DateTime.FromOADate(toOle); // OLE -> .NET (as it seems what happens on interop)
N>TimeSpan diff = now - fromOleDate; // The difference is less than 1 millisecond.
N>


N>Есть ли какие-либо workaround-ы, решающие эту проблему?

Это недостаток арифметических операций с double, и вообще для чисел с плавающей точкой.
Например 2.8-0.8 != 2
Обычно лечится тем, что сравнение делается только с некоторым округлением.
Re[7]: COM Interop: DateTime & OLE DATE
От: Аноним  
Дата: 27.04.09 13:23
Оценка:
Здравствуйте, Nikolkos, Вы писали:

N>Здравствуйте, Dwarffy, Вы писали:


N>FromOADate, ToOADate как раз оперируют с этим значением ticks (можно посмотреть в рефлекторе). Так что формально точность до 1 тика (100 наносекунд = 0,1 микросекунда) DateTime обеспечивать должен.


Молодой человек, вы меня извините, что я вас так называю,
но вы пытались взглянуть на описание OLE Datetime и DateTime,
повнимательнее?
Или хотите что бы тут прямо вам сказали, что "точность fload & double"
не совместимы после преобразования.

http://msdn.microsoft.com/en-us/library/ms187819.aspx
Accuracy — Rounded to increments of .000, .003, or .007 seconds

Но где вы, скажите на милость, наблюдаете микро?
в Datetime а-ля .NET, но тогда скажите на милость
откуда .NET

Как говориться. Вот вам и ответ.А то что
в программе происходят округления, так это уже по стандарту.
Вы извините, хоть где-то должен быть порядок.
Если "порядок" с double можно назвать порядком.
Re[2]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 27.04.09 13:31
Оценка:
Здравствуйте, GlebZ, Вы писали:

GZ>Это недостаток арифметических операций с double, и вообще для чисел с плавающей точкой.

GZ>Например 2.8-0.8 != 2
GZ>Обычно лечится тем, что сравнение делается только с некоторым округлением.

Тут не поспоришь, COM библиотека по сути некорректно сравнивает double значения. Но в данном случае это считается допустимым, т.к. эти DATE значения не участвуют в вычислениях, а используются лишь как идентификаторы (что вполне корректно работает внутри COM). Но при выполнении COM Interop, эти временные метки "искажаются" маршаллером.

В общем, на данный момент мне видится такой выход: дополнение существующего COM API методами, ориентированными на .NET Interop.
Re[8]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 27.04.09 13:41
Оценка:
N>>FromOADate, ToOADate как раз оперируют с этим значением ticks (можно посмотреть в рефлекторе). Так что формально точность до 1 тика (100 наносекунд = 0,1 микросекунда) DateTime обеспечивать должен.

А>но вы пытались взглянуть на описание OLE Datetime и DateTime,

А>повнимательнее?
Что я пропустил?

А>Или хотите что бы тут прямо вам сказали, что "точность fload & double"

А>не совместимы после преобразования.

Спасибо, я в курсе про ошибки округления и неточность преобразований. Мой вопрос был в другом: как это можно обойти при COM Interop значений DATE.

А>http://msdn.microsoft.com/en-us/library/ms187819.aspx

А>Accuracy — Rounded to increments of .000, .003, or .007 seconds

А>Но где вы, скажите на милость, наблюдаете микро?

А>в Datetime а-ля .NET, но тогда скажите на милость
А>откуда .NET

Ссылка на T-SQL не в кассу (я где-то говорил про SQL?). Используется не SQL база, а некая "кастомная" экзотическая база (Hypertrieve, сомневаюсь, что знакомо название). Так вот там "микро" наблюдается по полной программе (by design как говорится).
Re[8]: COM Interop: DateTime & OLE DATE
От: Nikolkos  
Дата: 27.04.09 15:06
Оценка:
Здравствуйте, Аноним, Вы писали:

А>А то что в программе происходят округления, так это уже по стандарту.


Дайте, пожалуйста, ссылочку на этот самый стандарт, касающийся маршалинга DATE при COM Interop? Здесь или тут об этом "округлении"/"потери_точности" ни слова.

Возможное объяснение — DateTime не предназначен для оперирования микросекундами (там и такого свойства нет, хотя есть те же самые тики). Тогда, мое мнение, подобное поведение маршалера по умолчанию следовало специально где-то обговорить (как и методы ToOADate, FromOADate).

Поэтому утверждения в MSDN:

DateTime.ToOADate Method
Return Value
Type: System.Double
A double-precision floating-point number that contains an OLE Automation date equivalent to the value of this instance.

DateTime.FromOADate Method
Return Value
Type: System.DateTime
A DateTime that represents the same date and time as d.


мягко говоря, не соответствуют действительности.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.