Mar 21

ObjectReader, v2.0

呃。。。这次支持了数组和内嵌其它类型,另外发现Field和Property混用的话顺序会出错,所以加上了限制。

      
Imports System.Reflection
Imports System.IO
Imports System.Runtime.Serialization

Namespace SAPStudio.Utils.IO

    <AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False)> _
    Public Class ObjectReaderWriterIncludeAttribute
        Inherits Attribute
    End Class
    <AttributeUsage(AttributeTargets.Field Or AttributeTargets.Property, AllowMultiple:=False)> _
    Public Class ArraySizeAttribute
        Inherits Attribute
        Private _size As Integer
        Public ReadOnly Property Size() As Integer
            Get
                Return _size
            End Get
        End Property
        Public Sub New(ByVal size As Integer)
            _size = size
        End Sub
    End Class
    <AttributeUsage(AttributeTargets.Class Or AttributeTargets.Struct, AllowMultiple:=False)> _
    Public Class ObjectReaderWriterOptionsAttribute
        Inherits Attribute
        Private _dataMember As DataMemberTypes
        Public ReadOnly Property DataMember() As DataMemberTypes
            Get
                Return _dataMember
            End Get
        End Property
        Sub New(ByVal dataMember As DataMemberTypes)
            _dataMember = dataMember
        End Sub
    End Class
    Public Enum DataMemberTypes
        Field
        [Property]
    End Enum
    Friend Structure Getter
        Public Method As MethodInfo
        Public InvokeWithAdditionalData As Boolean
        Public AdditionalData As Object()
        Public Function Invoke(ByVal reader As BinaryReader) As Object
            Return Method.Invoke(reader, If(InvokeWithAdditionalData, New Object() {reader, AdditionalData}, Nothing))
        End Function
    End Structure
    ''' <summary>
    ''' Construct a object using data from a <see cref="System.IO.BinaryReader" /> or <see cref="System.IO.Stream"  />.
    ''' </summary>
    ''' <typeparam name="T">Type of the object. Can be a ValueType</typeparam>
    ''' <remarks>Because Type.GetMembers only preserves declaration order of members with same type, You can only use either property or field to store data. Tested in .Net Framework 3.0, applies to .Net 2.0 too(I think). Apply ObjectReaderWriterOptions attribute to the type definition to choose which to use.</remarks>
    Public NotInheritable Class ObjectReader(Of T As New)
        Shared _gettersAndSetters As New List(Of KeyValuePair(Of Getter, MemberInfo))
        Shared Sub New()
            Dim options = CType(Attribute.GetCustomAttribute(GetType(T), GetType(ObjectReaderWriterOptionsAttribute)), ObjectReaderWriterOptionsAttribute)
            Dim dataMemberType As MemberTypes = If(options Is Nothing OrElse options.DataMember = DataMemberTypes.Field, MemberTypes.Field, MemberTypes.Property)
            For Each m In GetType(T).GetMembers(BindingFlags.Public Or BindingFlags.Instance)
                If m.MemberType <> dataMemberType Then Continue For
                Dim fieldType As Type
                If m.MemberType = MemberTypes.Field Then
                    Dim fi = CType(m, FieldInfo)
                    If fi.IsNotSerialized OrElse fi.IsInitOnly OrElse fi.IsLiteral Then Continue For
                    fieldType = fi.FieldType
                Else
                    Dim pi = CType(m, PropertyInfo)
                    If Not (pi.CanWrite AndAlso pi.CanRead) Then Continue For
                    fieldType = pi.PropertyType
                End If
                Dim getter As Getter
                If fieldType.IsPrimitive Then
                    getter = GetGetterPrimitive(fieldType)
                ElseIf fieldType.IsArray AndAlso Attribute.IsDefined(m, GetType(ArraySizeAttribute)) Then
                    Dim elementType = fieldType.GetElementType()
                    If elementType.IsPrimitive OrElse Attribute.IsDefined(m, GetType(ObjectReaderWriterIncludeAttribute)) Then
                        getter = GetGetterArray(elementType, CType(Attribute.GetCustomAttribute(m, GetType(ArraySizeAttribute)), ArraySizeAttribute).Size)
                    End If
                ElseIf Attribute.IsDefined(m, GetType(ObjectReaderWriterIncludeAttribute)) Then
                    getter = GetGetterNested(fieldType)
                Else
                    Continue For
                End If
                If getter.Method Is Nothing Then Continue For
                _gettersAndSetters.Add(New KeyValuePair(Of Getter, MemberInfo)(getter, m))
            Next
        End Sub

        Public Shared Function Read(ByVal br As BinaryReader) As T
            If _gettersAndSetters.Count = 0 Then Throw New NotSupportedException("No member in specified type is supported")
            If GetType(T).IsValueType Then Return ReadValueType(br) 'VB will preserve value of ValueType when calling a method if the type of the variable is Object
            Dim ret As New T
            For Each pair In _gettersAndSetters
                Dim value = pair.Key.Invoke(br)
                If pair.Value.MemberType = MemberTypes.Field Then
                    CType(pair.Value, FieldInfo).SetValue(ret, value)
                Else
                    CType(pair.Value, PropertyInfo).SetValue(ret, value, Nothing)
                End If
            Next
            Return ret
        End Function

        Public Shared Function Read(ByVal s As Stream, ByVal isLittleEndian As Boolean) As T
            Using wrapper As New StreamWrapper(s), br = If(isLittleEndian, New BinaryReader(wrapper), New BigEndianBinaryReader(wrapper))
                Return Read(br)
            End Using
        End Function

        Public Shared Function Read(ByVal s As Stream) As T
            Return Read(s, True)
        End Function

        Private Shared Function ReadValueType(ByVal br As BinaryReader) As T
            Dim ret As ValueType = CType(Activator.CreateInstance(GetType(T)), ValueType)
            For Each pair In _gettersAndSetters
                Dim value = pair.Key.Invoke(br)
                If pair.Value.MemberType = MemberTypes.Field Then
                    CType(pair.Value, FieldInfo).SetValue(ret, value)
                Else
                    CType(pair.Value, PropertyInfo).SetValue(ret, value, Nothing)
                End If
            Next
            Return CType(DirectCast(ret, Object), T)
        End Function

        Private Shared Function GetGetterPrimitive(ByVal type As Type) As Getter
            Dim brType = GetType(BinaryReader)
            Dim ret = New Getter With {.Method = brType.GetMethod("Read" & type.Name, BindingFlags.Instance Or BindingFlags.Public, Nothing, CallingConventions.Any, New Type() {}, Nothing), .InvokeWithAdditionalData = False}
            Return ret
        End Function

        Private Shared Function GetGetterNested(ByVal type As Type) As Getter
            Dim orType = GetType(ObjectReader(Of )).MakeGenericType(type)
            Try
                Return New Getter With { _
                                .Method = orType.GetMethod("ReadNested", BindingFlags.Static Or BindingFlags.NonPublic, Nothing, CallingConventions.Any, New Type() {GetType(BinaryReader), GetType(Object())}, Nothing), _
                                .InvokeWithAdditionalData = True}
            Catch ex As Exception
                Return Nothing
            End Try
        End Function

        Private Shared Function ReadNested(ByVal reader As BinaryReader, ByVal dummy As Object()) As T
            Return Read(reader)
        End Function

        Private Shared Function GetGetterArray(ByVal type As Type, ByVal count As Integer) As Getter
            Dim elementGetter As Getter
            If type.IsPrimitive Then
                elementGetter = GetGetterPrimitive(type)
            Else
                elementGetter = GetGetterNested(type)
            End If

            Try
                Return New Getter With { _
                                        .Method = GetType(ObjectReader(Of T)).GetMethod("ReadArray", BindingFlags.Static Or BindingFlags.NonPublic, Nothing, CallingConventions.Any, New Type() {GetType(BinaryReader), GetType(Object())}, Nothing).MakeGenericMethod(type), _
                                        .InvokeWithAdditionalData = True, _
                                        .AdditionalData = New Object() {elementGetter, count}}
            Catch ex As Exception
                Return Nothing
            End Try
        End Function

        Private Shared Function ReadArray(Of U As New)(ByVal reader As BinaryReader, ByVal data() As Object) As U() 'data: element getter, array size
            Dim arraySize As Integer = CType(data(1), Integer)
            If GetType(U) Is GetType(Byte) Then 'Optimized method
                Dim ret = reader.ReadBytes(arraySize)
                If ret.Length < arraySize Then
                    Throw New EndOfStreamException()
                End If
                Return CType(CType(ret, Object), U())
            Else
                Dim elementGetter As Getter = CType(data(0), Getter)
                Dim ret As New List(Of U)
                For i = 0 To arraySize - 1
                    ret.Add(CType(elementGetter.Invoke(reader), U))
                Next
                Return ret.ToArray()
            End If
        End Function

        Private Sub New()

        End Sub
    End Class

End Namespace

        

Mar 13

在.Net Framework 2.0中使用扩展方法

因为扩展方法需要.net 3.5的缘故,装好VS2008之后一直没用过。刚才偶然在google发现用一个小技巧就能在.net 2.0中使用。

很简单,新建一个类,内容如下:

      
Namespace System.Runtime.CompilerServices
    <AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method Or AttributeTargets.Assembly)> _
    Public Class ExtensionAttribute
        Inherits Attribute
    End Class
End Namespace

        

注意要在项目属性里把根命名空间设置为空。然后就照常定义扩展方法就可以了(C#的this关键字没测试过,不知道有没有问题)。VS2008会把这些方法正确识别为扩展方法。

Feb 26

ObjectReader

功能:使用BinaryReader读取数据构造一个类,支持除IntPtr以外的基本类型

      
Public NotInheritable Class ObjectReader(Of T As New)
    Shared _gettersAndSetters As New List(Of KeyValuePair(Of MethodInfo, MemberInfo))
    Shared Sub New()
        For Each m In GetType(T).GetMembers(BindingFlags.Public Or BindingFlags.Instance)
            If m.MemberType <> MemberTypes.Field AndAlso m.MemberType <> MemberTypes.Property Then Continue For
            If m.MemberType = MemberTypes.Field Then
                Dim fi = CType(m, FieldInfo)
                If Not fi.FieldType.IsPrimitive Then Continue For
                Dim getter = GetGetter(fi.FieldType)
                If getter Is Nothing Then Continue For
                _gettersAndSetters.Add(New KeyValuePair(Of MethodInfo, MemberInfo)(getter, fi))
            Else
                Dim pi = CType(m, PropertyInfo)
                If Not pi.PropertyType.IsPrimitive Then Continue For
                If Not pi.CanWrite Then Continue For
                Dim getter = GetGetter(pi.PropertyType)
                If getter Is Nothing Then Continue For
                _gettersAndSetters.Add(New KeyValuePair(Of MethodInfo, MemberInfo)(getter, pi))
            End If
        Next
    End Sub

    Public Shared Function Read(ByVal br As BinaryReader) As T
        If _gettersAndSetters.Count = 0 Then Throw New NotSupportedException("No member in specified type is supported and writable")
        If GetType(T).IsValueType Then Return ReadValueType(br) 'VB对结构使用反射时不能声明为Object。。。
        Dim ret As New T
        For Each pair In _gettersAndSetters
            Dim value = pair.Key.Invoke(br, Nothing)
            If pair.Value.MemberType = MemberTypes.Field Then
                CType(pair.Value, FieldInfo).SetValue(ret, value)
            Else
                CType(pair.Value, PropertyInfo).SetValue(ret, value, Nothing)
            End If
        Next
        Return ret
    End Function

    Private Shared Function ReadValueType(ByVal br As BinaryReader) As T
        Dim ret As ValueType = CType(Activator.CreateInstance(GetType(T)), ValueType)
        For Each pair In _gettersAndSetters
            Dim value = pair.Key.Invoke(br, Nothing)
            If pair.Value.MemberType = MemberTypes.Field Then
                CType(pair.Value, FieldInfo).SetValue(ret, value)
            Else
                CType(pair.Value, PropertyInfo).SetValue(ret, value, Nothing)
            End If
        Next
        Return CType(DirectCast(ret, Object), T)
    End Function

    Private Shared Function GetGetter(ByVal type As Type) As MethodInfo
        Dim brType = GetType(BinaryReader)
        Dim ret = brType.GetMethod("Read" & type.Name, BindingFlags.Instance Or BindingFlags.Public, Nothing, CallingConventions.Any, New Type() {}, Nothing)
        Return ret
    End Function

    Private Sub New()

    End Sub
End Class
        

Feb 20

火星可能。。。使reflector反编译.Net系统程序集时显示局部变量名

刚刚才发现reflector能够解析pdb的内容。。。于是出现了此文。

方法很简单。。。先用NetMassDownloader下载好所有符号文件,然后复制到对应目录就可以了(2.0的复制到%SystemRoot%\Microsoft.net\Framework\v2.0.50727,3.0/3.5的复制到%ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0(or v3.5)。

Feb 19

发现内嵌精简字体的一点问题

这几天发现了一个问题,Haali分离器加载内嵌字体后如果不正常退出会导致字体残留在内存,后续的进程所有加载同一种字体的操作都会被系统忽略,直接使用之前的字体。这就导致了如果后面加载的字体内含前面的字体没有包含的字符的话,这些字符就无法显示了。

目前的解决方法只有在Haali分离器不正常退出后注销一下,就能够恢复正常。等我有空了再研究下怎么彻底解决这个问题。。。

Feb 15

郁闷死了。。。

花了一个星期把MS放出的所有.net framework源码搞了下来,发现vs2008死活不认本地的源代码,还是跑到服务器上下载。之前的功夫全白费了。。。囧

 

Update: 在IIS里建了个假源码服务器,然后在hosts里加上地址,终于弄好了。。。

Feb 05

VS2008中文版安装手记

花了大半天时间终于把VS2008装上了,现在把安装时出现的问题记录下:

  1. 安装程序加载时提示deffactory.dat无法加载,用记事本打开发现里面空空如也。。。(BS微软啊。。。发布之前都不检查的么。。。)立刻去找google大神,找到的答案全部是英文版的,还好改了一下就能用:
  2. [Version]
    Signature="$Windows NT$"
    Provider="Microsoft Visual Studio Team System 2008 Team Suite – CHS"
    Version=900.100.00
    NullString=Null String
    Lang=2052

    [Scenario List]
    vsscenario.dll

    [Scenario Factory Information]
    Default Scenario=DECDD26F-5491-11d2-BEE7-00C04F797FB8

  3. 装的时候Web创作组件死活装不上,又要去google。。。找到的解决方法是先把安装光盘上WCU\WebDesignerCore\WebDesignerCore.exe解压出来(用winrar),运行里面的setup.exe安装。但是这次还是装不上,一直提示找不到文件。后来灵机一动,把路径指向Office 2007光盘,搞定。
  4. 装好之后立刻去搞.net源代码的配置(之前看过关于.net开源的文章,早就流口水了),发现设置指南里提到的补丁(KB944899)装不上,不过这个问题早就预料到了。祭出杀手锏Universal Extractor,把安装包里面的.msp文件解开。里面有2个文件,把大的那个改名为vsdebug.dll,丢到C:\Program Files\Common Files\microsoft shared\VSA\9.0\VsaEnv\Packages\Debugger里面就OK了。
Feb 03

TFM处理后仍残余拉丝的偷懒解决方法

话说TIVTC真是个好东西,全自动VFR太方便了,效果还非常好。最近处理某混合DVD片源时发现残余大量拉丝,因为实在不想手动处理(太麻烦了。。。),想出了这个办法。

以下脚本内容大部分A自Dgwxx大神的主页

function CombingInfo(clip c, int "thresh")
{
file = "ovr.txt"
global clip = c
global sep=" +"

global combedthreshold=default(thresh,70)
c=SeparateFields(c)
c=c.Blur(0.0,1.58).Blur(0.0,1.58).Blur(0.0,1.58).Blur(0.0,1.58)
c=c.Blur(0.0,1.58).Blur(0.0,1.58).Blur(0.0,1.58)
c=c.Blur(0.0,1.58).Blur(0.0,1.58)
c=c.Blur(0.0,1.58)
c=weave(c)
c = FrameEvaluate(c, "global a = IsCombed(clip, combedthreshold)")
c = WriteFileIf(c, file, "a==true", "current_frame", "sep")
return c
}

dgdecode_mpeg2source("d2v.d2v")
tfm(slow=2,d2v="d2v.d2v")
combinginfo
crop(0,0,32,32)

(后面的crop是为了加快处理速度)

然后把脚本从头到尾播一次(推荐avs2avi,速度很快),生成ovr.txt,最后直接在tfm的参数里指定ovr="ovr.txt"就可以了。