|
web
newsgroups
|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
.NET Marshalling Optimization - FILETIME conversionI think this ok and probably should not worry about it and look at it as some future point, but I am wondering if the following should be done another way. Background: In our SDK, it makes extensive use of the 8 byte FILETIME Win32 structure which is defined (in C/C++) typedef struct _FILETIME { DWORD dwLowDateTime; DWORD dwHighDateTime; } FILETIME, *PFILETIME; A DWORD (Double Word) is an unsigned int32. In VB.NET, it would be: Public Structure FILETIME Public dwLowDateTime As UInteger Public dwHighDateTime As UInteger End Structure Side Note: I understand there is already a FILETIME in System.Runtime.InteropServices.ComTypes.FILETIME But I don't wish to bind to any more assemblies if I don't have to or require developers have to import it for each source code plus, when you use COMTYPE.FILETIME, you get tons of silly IDE underscores and compiler warnings about compatibility. So I am sticking with my own FILETIME. If someone feels there a good reason to use COMTYPE.FILETIME, please comment. Now, this is only one step in creating the SDK. The usefulness is how its employed and used easily by developers close to the way they are using it now. So in the spirits and elegance of VB.NET OOPs and type conversion automation, I added a bunch of operators: Public Structure FILETIME Public dwLowDateTime As UInteger Public dwHighDateTime As UInteger Public Sub new(hi as Uinteger, lo as Uinteger) dwHighDateTime = hi dwLowDateTime = lo End Sub ' convert Operator for converting self to long Public Shared Widening Operator CType(ft As FILETIME) As Long Return MakeLong64(ft) End Operator ' convert Operator for assigning a long Public Shared Widening Operator CType(l As long) As FILETIME Dim bytes() As Byte = BitConverter.GetBytes(l) dim lo as UInteger = BitConverter.ToUInt32(bytes, 0) dim hi as UInteger = BitConverter.ToUint32(bytes, 4) Return new FILETIME(hi,lo) End Operator ' convert operator for assigning a Datetime Public Shared Widening Operator CType(dt As DateTime) As FILETIME Dim bytes() As Byte = BitConverter.GetBytes(dt.ToBinary()) dim lo as UInteger = BitConverter.ToUInt32(bytes, 0) dim hi as UInteger = BitConverter.ToUint32(bytes, 4) Return new FILETIME(hi,lo) End Operator ' convert operator for assiging a FILETIME and returning Datetime Public Shared Widening Operator CType(ft As FILETIME) As DateTime Return DateTime.FromBinary(MakeLong64(ft)) End Operator ' operator: return diff in seconds Public Shared Operator -(lt As FILETIME, rt as FILETIME) As long Return (MakeLong64(lt)-MakeLong64(rt))/10000000 End Operator ' operator: compare less than Public Shared Operator < (lt As FILETIME, rt as FILETIME) _ As boolean Return MakeLong64(lt) < MakeLong64(rt) End Operator ' operator: compare greater than Public Shared Operator > (lt As FILETIME, rt as FILETIME) _ As boolean Return MakeLong64(lt) > MakeLong64(rt) End Operator ' operator: compare greater/equal than Public Shared Operator >= (lt As FILETIME, rt as FILETIME) _ As boolean Return MakeLong64(lt) >= MakeLong64(rt) End Operator ' operator: compare less/equal than Public Shared Operator <= (lt As FILETIME, rt as FILETIME) _ As boolean Return MakeLong64(lt) <= MakeLong64(rt) End Operator ' operator: compare equal Public Shared Operator = (lt As FILETIME, rt as FILETIME) _ As boolean Return MakeLong64(lt) = MakeLong64(rt) End Operator ' operator: compare not equal Public Shared Operator <> (lt As FILETIME, rt as FILETIME) _ As boolean Return MakeLong64(lt) <> MakeLong64(rt) End Operator Public Overrides Function ToString() as string return DateTime.FromBinary( _ MakeLong64(dwHighDateTime, dwLowDateTime)) end function End Structure These two functions are part of my DATELIB.VB class Public Function MakeLong64(hi As object, lo As object) As Long Return CLng(CLng(hi) << 32) + CLng(lo) End Function Public Function MakeLong64(ft As FILETIME) As Long Return MakeLong64(ft.dwHighDateTime, ft.dwLowDateTime) End Function All those provides various ways to convert between FILETIME and DATETIME: sub DoDateTest1() dim [now] as datetime = DateTime.Now() dim ft1 as FILETIME = [now].ToBinary() dim ft2 as FILETIME = [now] dim ft3 as New FILETIME(ft2.dwHighDateTime, ft2.dwLowDateTime) dim dt4 as DateTime = ft3 dim dt5 as DateTime = dt4.AddSeconds(30) dim ft6 as FILETIME = dt5 dim diff7 as long = ft6 - ft2 dim ft8 as FileTime = dt4.AddYears(1) Console.WriteLine("now: {0,25} ", [now]) Console.WriteLine("ft1: {0,25} ", ft1) Console.WriteLine("ft2: {0,25} ", ft2) Console.WriteLine("ft3: {0,25} ", ft3) Console.WriteLine("dt4: {0,25} ", dt4) Console.WriteLine("dt4: {0,25} ", dt5) Console.WriteLine("ft6: {0,25} ", ft6) Console.WriteLine("diff7: {0,25} ", diff7) Console.WriteLine("ft6 > ft2: {0}", ft6 > ft2) 'expect true Console.WriteLine("ft1 = ft2: {0}", ft1 = ft2) 'expect true Console.WriteLine("ft1 <>ft2: {0}", ft1 <> ft2) 'expect false Console.WriteLine("ft8: {0,25} ", ft8) end sub My questions to the VB.NET experts: 1) Is using this structure FILETIME with all these operator going to be a performance hit? 2) Related to #1, should I break this up? A Structure and a Class? There were two things that made think about it: - Desire to separate SDK library with wrapper classes; a base SDK library with structures and API function imports, and a wrapper library for usability. - The SDK has many structures that use the FILETIME. Thinking there might be RTTI and serialization overhead during the marshalling. Even if negligible, is there a preferred alternative to reduce redundancy? Any tips, suggestions, comments, "Got chas?" thanks -- Mike wrote:
Show quoteHide quote > Folks, Some things first:> > I think this ok and probably should not worry about it and look at it > as some future point, but I am wondering if the following should be > done another way. > > Background: > > In our SDK, it makes extensive use of the 8 byte FILETIME Win32 > structure which is defined (in C/C++) > > typedef struct _FILETIME { > DWORD dwLowDateTime; > DWORD dwHighDateTime; > } FILETIME, *PFILETIME; > > A DWORD (Double Word) is an unsigned int32. > > In VB.NET, it would be: > > Public Structure FILETIME > Public dwLowDateTime As UInteger > Public dwHighDateTime As UInteger > End Structure > > Side Note: I understand there is already a FILETIME in > > System.Runtime.InteropServices.ComTypes.FILETIME > > But I don't wish to bind to any more assemblies if I don't > have to or require developers have to import it for each source > code plus, when you use COMTYPE.FILETIME, you get tons of silly > IDE underscores and compiler warnings about compatibility. So > I am sticking with my own FILETIME. If someone feels there > a good reason to use COMTYPE.FILETIME, please comment. > > Now, this is only one step in creating the SDK. The usefulness is how > its employed and used easily by developers close to the way they are > using it now. So in the spirits and elegance of VB.NET OOPs and type > conversion automation, I added a bunch of operators: > > Public Structure FILETIME > Public dwLowDateTime As UInteger > Public dwHighDateTime As UInteger > > Public Sub new(hi as Uinteger, lo as Uinteger) > dwHighDateTime = hi > dwLowDateTime = lo > End Sub > > ' convert Operator for converting self to long > Public Shared Widening Operator CType(ft As FILETIME) As Long > Return MakeLong64(ft) > End Operator > > ' convert Operator for assigning a long > Public Shared Widening Operator CType(l As long) As FILETIME > Dim bytes() As Byte = BitConverter.GetBytes(l) > dim lo as UInteger = BitConverter.ToUInt32(bytes, 0) > dim hi as UInteger = BitConverter.ToUint32(bytes, 4) > Return new FILETIME(hi,lo) > End Operator > > ' convert operator for assigning a Datetime > Public Shared Widening Operator CType(dt As DateTime) As FILETIME > Dim bytes() As Byte = BitConverter.GetBytes(dt.ToBinary()) > dim lo as UInteger = BitConverter.ToUInt32(bytes, 0) > dim hi as UInteger = BitConverter.ToUint32(bytes, 4) > Return new FILETIME(hi,lo) > End Operator > > ' convert operator for assiging a FILETIME and returning Datetime > Public Shared Widening Operator CType(ft As FILETIME) As DateTime > Return DateTime.FromBinary(MakeLong64(ft)) > End Operator > > ' operator: return diff in seconds > Public Shared Operator -(lt As FILETIME, rt as FILETIME) As long > Return (MakeLong64(lt)-MakeLong64(rt))/10000000 > End Operator > > ' operator: compare less than > Public Shared Operator < (lt As FILETIME, rt as FILETIME) _ > As boolean > Return MakeLong64(lt) < MakeLong64(rt) > End Operator > > ' operator: compare greater than > Public Shared Operator > (lt As FILETIME, rt as FILETIME) _ > As boolean > Return MakeLong64(lt) > MakeLong64(rt) > End Operator > > ' operator: compare greater/equal than > Public Shared Operator >= (lt As FILETIME, rt as FILETIME) _ > As boolean > Return MakeLong64(lt) >= MakeLong64(rt) > End Operator > > ' operator: compare less/equal than > Public Shared Operator <= (lt As FILETIME, rt as FILETIME) _ > As boolean > Return MakeLong64(lt) <= MakeLong64(rt) > End Operator > > ' operator: compare equal > Public Shared Operator = (lt As FILETIME, rt as FILETIME) _ > As boolean > Return MakeLong64(lt) = MakeLong64(rt) > End Operator > > ' operator: compare not equal > Public Shared Operator <> (lt As FILETIME, rt as FILETIME) _ > As boolean > Return MakeLong64(lt) <> MakeLong64(rt) > End Operator > > Public Overrides Function ToString() as string > return DateTime.FromBinary( _ > MakeLong64(dwHighDateTime, dwLowDateTime)) > end function > End Structure > > These two functions are part of my DATELIB.VB class > > Public Function MakeLong64(hi As object, lo As object) As Long > Return CLng(CLng(hi) << 32) + CLng(lo) > End Function > > Public Function MakeLong64(ft As FILETIME) As Long > Return MakeLong64(ft.dwHighDateTime, ft.dwLowDateTime) > End Function > > All those provides various ways to convert between FILETIME > and DATETIME: > > sub DoDateTest1() > dim [now] as datetime = DateTime.Now() > dim ft1 as FILETIME = [now].ToBinary() > dim ft2 as FILETIME = [now] > dim ft3 as New FILETIME(ft2.dwHighDateTime, ft2.dwLowDateTime) > > dim dt4 as DateTime = ft3 > dim dt5 as DateTime = dt4.AddSeconds(30) > dim ft6 as FILETIME = dt5 > dim diff7 as long = ft6 - ft2 > > dim ft8 as FileTime = dt4.AddYears(1) > > Console.WriteLine("now: {0,25} ", [now]) > Console.WriteLine("ft1: {0,25} ", ft1) > Console.WriteLine("ft2: {0,25} ", ft2) > Console.WriteLine("ft3: {0,25} ", ft3) > Console.WriteLine("dt4: {0,25} ", dt4) > Console.WriteLine("dt4: {0,25} ", dt5) > Console.WriteLine("ft6: {0,25} ", ft6) > Console.WriteLine("diff7: {0,25} ", diff7) > Console.WriteLine("ft6 > ft2: {0}", ft6 > ft2) 'expect true > Console.WriteLine("ft1 = ft2: {0}", ft1 = ft2) 'expect true > Console.WriteLine("ft1 <>ft2: {0}", ft1 <> ft2) 'expect false > Console.WriteLine("ft8: {0,25} ", ft8) > end sub - Why not ULong? - Datetime.tobinary() can not be "bitblitted" to FILETIME. - There's DateTime.ToFiletime, DateTime.FromFiletime. (looking at them, it also answers why you might have used Long instead of ULong) - I'd like the "-" operator to return a TimeSpan (because you will never remember the unit of the returned Long value otherwise. Seconds? Ticks?) > My questions to the VB.NET experts: For performance reasons, I'd skip the MakeLong64 call, for example:> > 1) Is using this structure FILETIME with all these operator going to > be a performance hit? Public Shared Operator = (lt As FILETIME, rt as FILETIME) _ As boolean Return lt.dwlowdatetime = rt.dwlowdatetime andalso _ lt.dwhidatetime=rt.dwhidatetime End Operator Or even faster: <structlayout(explicit)> Public Structure FILETIME <fieldoffset(0)> private DateTime As ULong <fieldoffset(0)> Public dwLowDateTime As UInteger <fieldoffset(4)> Public dwHighDateTime As UInteger End Structure Public Shared Operator = (lt As FILETIME, rt as FILETIME) _ As boolean Return lt.DateTime = rt.DateTime End Operator This would simplify it a lot because conversion can be omitted in most places. In general, I would prefer _not_ to work with a FILETIME object. I'd use it only if necessary with API calls. Then immediatelly convert to DateTime or DateTimeOffset and work with them. Then you can also drop some operators (or even everything but the two fields). I'm not sure what you mean with #2. Armin Armin Zingler wrote:
> Some things first: Am I using it wrong? See a problem? I saw a MSDN note saying TO/From > - Why not ULong? > - Datetime.tobinary() can not be "bitblitted" to FILETIME. Binary would be the way to do proper type casting and conversion. > - There's DateTime.ToFiletime, DateTime.FromFiletime. (looking at them, it Right. Being trying to stay as strict as required.> also answers why you might have used Long instead of ULong) > - I'd like the "-" operator to return a TimeSpan (because you will never Yes, definitely crossed my mind to use timespace or DateTimeDiff, etc. > remember the unit of the returned Long value otherwise. Seconds? Ticks?) But the reason why because we make extensive use of FILETIME calculations and differences and off the top of my head I didn't want to introduce yet another class for this part. One quick usage, Get Files updated in the last 30 seconds: dim nowUTC as FILETIME = SystemFileTime() Dim FileList As New ServerFiles(Of TFileRecord) for each File as TFileRecord in FileList if (nowUTC - file.PostTime) < 30 then .... end if next I did a timer profile and there was real difference in performance. Impressed in fact. I might get it in when all said in done. For now, need to duplicate functionally, then replace. Show quoteHide quote >> My questions to the VB.NET experts: Good catch!>> >> 1) Is using this structure FILETIME with all these operator going to >> be a performance hit? > > > For performance reasons, I'd skip the MakeLong64 call, for example: > > Public Shared Operator = (lt As FILETIME, rt as FILETIME) _ > As boolean > > Return lt.dwlowdatetime = rt.dwlowdatetime andalso _ > lt.dwhidatetime=rt.dwhidatetime > > End Operator > Or even faster: OMG! Get out of town! I've been looking to see how to do union idea in > > <structlayout(explicit)> > Public Structure FILETIME > <fieldoffset(0)> private DateTime As ULong > <fieldoffset(0)> Public dwLowDateTime As UInteger > <fieldoffset(4)> Public dwHighDateTime As UInteger > End Structure VB.NET. THANKS!!! Thats going to help in other areas! > This would simplify it a lot because conversion can be omitted in most No doubt. Thanks for sharing this!> places. > In general, I would prefer _not_ to work with a FILETIME object. I'd use Right, considered - making it easier for developer to use what they > it only if necessary with API calls. Then immediatelly convert to DateTime > or DateTimeOffset and work with them. Then you can also drop some > operators (or even everything but the two fields). know (and its documented via MSDN <g>). > I'm not sure what you mean with #2. I think Partial Classes answered this. I just pulled all the FILETIME and SYSTEMTIME structure and consolidatedw with other WIN32 stuff and put into its own WIN32.vb file but I used Partial CLass WCSERVERAPI so its part of the main SDK class library. But at first the class was caled WIN32 and that required a reference to WIN32. Either via import line or directly in the code. I still need to one day get a better organization of my new classes, modules and libraries. Right now I am porting many of my C/C++ libraries and putting them all into one DLL - separate files though. Thanks a million for your great input. -- |
|||||||||||||||||||||||