随笔 - 89, 评论 - 163, 引用 - 33

导航

关于

标签

每月存档

最新留言

广告

【第1页/共5页,89条】
首页
前页
1
2009年02月20日

[原文作者]:Jared Parsons

[原文链接]:Script Blocks and Arguments: Figuring it out for myself … again

    脚本块是一个为了存储一个表达式或者一个声明的分组的powershell构架。它和C#/F#/VB中的Lambda表达式其实是一样的。最近,我需要去用一个脚本块,但我却发现我忘了如何去读取一个在这个脚本块中已经传递过的参数。

    忘记如何使用一项功能其实算不上一篇好帖子。但是,这个问题我至少已经遇到了四回,所以,想写个帖子来给自己长点记性,或者至少能从google上得到一个满意的搜索结果。

    通常当我在PowerShell方面遇到问题时我会走以下几个步骤:

    1. 使用内置的帮助命令;

    2. 在我的脚本库中搜索之前的关于这个样式的用法;

    3. Google一下,找个解决办法;

    4. 自己动手做些小测试来确定正确的用法

    问题只有在第四步亲自去做时才会出现,脚本块也是如此。

    其实,想弄明白说明文档通常是件很杂的活。脚本块相关文档在about_script_block下面,这与这个内置的类型【scriptblock】有些矛盾。这导致我要试好几次才能得到想要的。而内部网上的资料用处也不大,那只有些基本的样本。

    我的内部库脚本中使用了大量的脚本块,但是由于所谓的精简的句法规则而使得搜索通过没什么成果可言。毕竟他们仅仅需要一副在PowerShell脚本中很普通的支架。

    所以我不得不做第三步:Google一下了。当然,我绝不是一位Google忍者。事实上我经常会被没什么技术含量的妻子羞辱,说我只会去Google资料。偶尔有几回,我甚至会去咨询她我应该使用什么搜索关键词,天哪!真是太丢脸了!

    当使用“script”,“block”和“argument”关键词时,我能得到好的结果的机会相当的小。最近随着关于PowerShell的帖子的不断增多,事情有了些转机,但是仍然很难得到一篇好的关于脚本块的文章。

    现在我们来自己动手做个实验了。大部分脚本在PowerShell中的入口点都有权限去使用内置的$args变量,如这两个方法:Scripts和filters。

     PS> function test() { $args.Count } 
     PS> test 42
     1
     PS> test 42 "astring"
     2

    可能与脚本块相同。

     PS> $a = { $args.Count }
     PS> & $a 42
     1
     PS> & $a 42 "astring"
     2
     PS>

    成功了!下回我应该能记住了。

posted on 2009-02-20 14:31:47 by vbcti  评论(1) 阅读(3507)

 

[原文作者]:Jared Parsons

[原文链接]:PowerShell LINQ: Skip-While

     在PowerShell LINQ这个系列中,接下来要介绍的就是SkipWhile。这个LINQ函数带有一个枚举类型的实例和一个判断条件。这个函数中的判断条件以当前元素的值来作为参数进行判断,只要判断条件为true就跳过该元素,直到判断条件为false时,将剩余的元素全部返回。

     LINQ版本以Func<T,TResult>的形式传入判断条件。在PowerShell中等价于delegate的是一个脚本块,但是它和.Net delegate不同,它没有什么途径可以使得Skip-While函数接受特殊的数字或者特殊的参数类型,不过在调用该函数时,将会获得相关契约的暗示。

    这个函数会自动匹配SkipWhile 的LINQ版本的相关契约,而不需要给它绝对的输入。

    #============================================================================

    # Skip while the condition is true

    #============================================================================

    function Skip-While() {

    param ( [scriptblock]$pred = $(throw "Need a predicate") )

    begin {

    $skip = $true

    }

    process {

    if ( $skip ) {

    $skip = & $pred $_

    }

    if ( -not $skip ) {

    $_

    }

    }

    end {}

    }

    用例如下:

    PS) 1..10 | Skip-While { $args[0] -lt 6 }

    6

    7

    8

    9

    10

    PS)

posted on 2009-02-20 14:29:35 by vbcti  评论(0) 阅读(3421)

 

[原文作者]:Jared Parsons

[原文链接]:Simulating Closures in PowerShell

 

    在之前的博文中我曾经提到过Power Shell缺少在脚本块中对闭包的支持.这对我正在研究的开发针对PowerShell的LINQ, 比如DSL, 是个很大的障碍。想象下下面的语句:

     $a = from it in $source where {$it -gt 5 }

    它大致相当于以下的C#代码

     var a = from it in source where it > 5;

    在C#中这段代码是能正常运行的,因为追根究底其中的WHERE语句“it > 5”被转化成了Lambda表达式。它要获取的变量在Lambda表达式中是通过闭包来实现的。为了在PowerShell中实现相似的功能,值$it在“where”句块执行的时候必须被转换。

    所幸PowerShell的灵活性出乎意料的好。当一个脚本块执行的时候,它会通过遍历各个不同的域并尝试转换所有的变量。第一被搜索的域是脚本块,然后是脚本块执行所在的本地代码块。使用new-variable,我们能生成和脚本块所寻求的变量名相同的变量。

PS) $sb = { write-host $it }
PS) & $sb
 
PS) new-variable "it" 42 -scope local
PS) & $sb
42

    成功了!现在我们唯一要做的就是通过生成一个函数Run-Scriptblock来概括这种行为。这个函数涉及到以下两点:

    1.需要执行的脚本块;

     2.一系列名字/值对。每一对都代表执行脚本块所必需的一个变量。

     代码:

#============================================================================
# Runs a script block.  The $list parameter must be a list of string, value
# combinations.  The script block will be executed with variables of the 
# specified name and value in scope
#============================================================================
function Run-Scriptblock() {
    param ( [scriptblock] $sb = $(throw "Need a script block"), 
            [object[]]$list= $(throw "Please specify the list of names and values") )
 
    for ( $i = 0; $i -lt $list.Length; $i = $i+2 ) {
        $name = [string]($list[$i])
        $value = $list[$i+1]
        new-variable -name $name -value $value -scope "local"
    }
 
    & $sb
}

    Example Usage:

PS) $sb = { write-host $it }
PS) run-scriptblock $sb "it",42
42
PS) $it
 

     现在我们已经有执行一个“where”语句的方法了。下次我们将讨论如何在PowerShell中定一个LINQ DSL的实际操作。

posted on 2009-02-20 14:26:04 by vbcti  评论(0) 阅读(3275)

 

[原文作者]:Jaredpar
[原文链接]:BclExtras Library

    今天我在Code Gallery上发布了一个名叫BclExtras的.NET实用类库。这是一些类的集合,标准.NET基类库的扩展。BclExtras类库专注于功能编程、多线程编程、LINQ扩展、单元测试和类型判断。

    该项目由我自己的许多项目演变而来。在过去一年里,我一直将其作为单独的测试类库。开始的时候,我写了大量关于多线程的代码,但是最近又加入了许多功能性API与集合。

    我发布的这个类库即有源代码也有二进制文件,可用于.NET 2.0和.NET 3.5。该类库的.NET 2.0版本中包含了一些2.0SP1 CLR没有实现的特性,例如:Func<T>、 Action<T>、扩展属性和LINQ枚举类的一部分功能。这使得基于.NET 2.0的应用程序可以使用大部分LINQ表达式。为了避免冲突,我在类库的.NET 3.0版本中移除了这些类型。

    以前我也发布过该类库,叫做RantPack。起初作为我个人的类库,名称让人难以理解。但是对我来说,RantPack也没有什么特别的意义。所以我给它取了一个对大多数人有意义的名称。

标签:DotNet, C#, RantPack, BclExtras

posted on 2009-02-20 14:22:47 by vbcti  评论(0) 阅读(3355)

 

[原文作者]:Lucian Wischik

[原文链接]:System.Diagnostics.Process: redirect StandardInput, StandardOutput, StandardError

    有时我们运行一个外部适用程序,向里面输入数据然后获取它的输出。这往往很容易发生这样的死锁:

     ' BAD CODE

     Using p As New System.Diagnostics.Process

     p.StartInfo.FileName = "cat"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardInput = True

     p.Start()

     p.StandardInput.Write("world" & vbCrLf & "hello")

    ' 这里将发生死锁,如果P写到输出接口的量达到12k

     p.StandardInput.Close()

     Dim op = p.StandardOutput.ReadToEnd()

     p.WaitForExit()

     p.Close()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     End Using

    这段程序将发生死锁,因为“Cat”首先从标准输入接口中读取,然后写到标准输出接口,最后再读取它,这样一直循环下去直到没有任何东西可读取。但是如果它的标准输出接口已经填满了,并且没有对象去读取,这样就不能继续往里面写东西,从而出现阻塞。

    这里的12k仅是一个随机数,我不会关注于它...

     ' BAD CODE

     Using p As New System.Diagnostics.Process

     p.StartInfo.FileName = "findstr"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardError = True

     p.Start()

    ' deadlock here if p needs to write more than 12k to StandardError

     Dim op = p.StandardOutput.ReadToEnd()

     Dim err = p.StandardError.ReadToEnd()

     p.WaitForExit()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     Console.WriteLine("ERROR:") : Console.WriteLine(err)

     End Using

     MSDN文档中说:你可以通过异步读取操作来避免这些依赖和潜在的死锁;或者你可以通过创建两个线程,让其中一个独立线程来读取输出流来避免死锁。因此我们将这样来做:

     使用线程来重定向就不会死锁

     '  GOOD CODE: 这里不会发生死锁.

     Using p As New Diagnostics.Process

     p.StartInfo.FileName = "sort"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardInput = True

     p.Start()

     Dim op = ""

     ' do NOT WaitForExit yet since that would introduce deadlocks.

      p.InputAndOutputToEnd("world" & vbCrLf & "hello", op, Nothing)

     p.WaitForExit()

     p.Close()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     End Using

     ''' <summary>

     ''' InputAndOutputToEnd: a handy way to use redirected input/output/error on a p.

     ''' </summary>

     ''' <param name="p">The p to redirect. Must have UseShellExecute set to false.</param>

     ''' <param name="StandardInput">This string will be sent as input to the p. (must be Nothing if not StartInfo.RedirectStandardInput)</param>

     ''' <param name="StandardOutput">The p's output will be collected in this ByRef string. (must be Nothing if not StartInfo.RedirectStandardOutput)</param>

     ''' <param name="StandardError">The p's error will be collected in this ByRef string. (must be Nothing if not StartInfo.RedirectStandardError)</param>

     ''' <remarks>This function solves the deadlock problem mentioned at http://msdn.microsoft.com/en-us/library/system.diagnostics.p.standardoutput.aspx</remarks>  

     <Runtime.CompilerServices.Extension()> Sub InputAndOutputToEnd(ByVal p As Diagnostics.Process, ByVal StandardInput As String, ByRef StandardOutput As String, ByRef StandardError As String)

     If p Is Nothing Then Throw New ArgumentException("p must be non-null")

     ' Assume p has started. Alas there's no way to check.

      If p.StartInfo.UseShellExecute Then Throw New ArgumentException("Set StartInfo.UseShellExecute to false")

      If (p.StartInfo.RedirectStandardInput <> (StandardInput IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Input only when StartInfo.RedirectStandardInput")

      If (p.StartInfo.RedirectStandardOutput <> (StandardOutput IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Output only when StartInfo.RedirectStandardOutput")

     If (p.StartInfo.RedirectStandardError <> (StandardError IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Error only when StartInfo.RedirectStandardError")

 

     Dim outputData As New InputAndOutputToEndData

     Dim errorData As New InputAndOutputToEndData

 

     If p.StartInfo.RedirectStandardOutput Then

     outputData.Stream = p.StandardOutput

     outputData.Thread = New Threading.Thread(AddressOf InputAndOutputToEndProc)

     outputData.Thread.Start(outputData)

     End If

     If p.StartInfo.RedirectStandardError Then

     errorData.Stream = p.StandardError

     errorData.Thread = New Threading.Thread(AddressOf InputAndOutputToEndProc)

     errorData.Thread.Start(errorData)

     End If

 

     If p.StartInfo.RedirectStandardInput Then

     p.StandardInput.Write(StandardInput)

     p.StandardInput.Close()

    End If

'

    If p.StartInfo.RedirectStandardOutput Then outputData.Thread.Join() : StandardOutput = outputData.Output

    If p.StartInfo.RedirectStandardError Then errorData.Thread.Join() : StandardError = errorData.Output

    If outputData.Exception IsNot Nothing Then Throw outputData.Exception

    If errorData.Exception IsNot Nothing Then Throw errorData.Exception

    End Sub

    Private Class InputAndOutputToEndData

    Public Thread As Threading.Thread

    Public Stream As IO.StreamReader

    Public Output As String

    Public Exception As Exception

    End Class

    Private Sub InputAndOutputToEndProc(ByVal data_ As Object)

    Dim data = DirectCast(data_, InputAndOutputToEndData)

    Try : data.Output = data.Stream.ReadToEnd : Catch e As Exception : data.Exception = e : End Try

    End Sub

posted on 2009-02-20 14:19:00 by vbcti  评论(0) 阅读(3632)

 

[原文作者]:Jared Parsons

[原文链接]:If you implement IEquatable<T> you still must override Object’s Equals and GetHashCode

 

    CLR2.0(公共语言运行时2.0)包含了一个接口IEquatable<T>,用来进行类型安全的比较操作。之前可用的最好方法是用虚方法Equals来比较,这个方法类型较松散,因为它将对象作为参数传入,当然,对于在类型适当要求不高的客户端来讲,这个方法足够了

    class Student {

    public override bool Equals(object obj) {

    var other = obj as Student;

    if (other == null) {

    return false;

    }

    // rest of comparison

    }

    }

    IEquatable<T>中一个非常显著的改进是它提供了强类型的比较方法,防止了调用者和被调用方传递无法比较的对象类型,当然也消除了值类型装箱的开销。 

    这些优点很吸引人,但是当你执行IEquatable<T>接口时,必须重载Equals(Object参数)和GetHashCode方法。否则你会付出些代价,在之前的文章中我简短的介绍了下这方面的内容,但是今天就几个例子我想深入讨论下这个话题。

    在谈论技术细节之前,我们来想象一个场景,执行IEquatable<T>相当于陈述了“该对象明白相等的含义”,即在你的类中声明了对象之间如何比较是否相等,你的对象应该实现这个功能,防止使其他不熟悉这个对象的程序员迷惑,这可是件麻烦事。

    问题1IEqualityComparer<T>依赖于GetHashCode()

    强类型的集合,例如Dictionary<TKey,TValue> 和HashSet<T>,必须比较对象是否相等来实现其功能,从BCL(Base class library)2.0开始,由接口IEqualityComparer<T>来比较对象之间语义是否相等。这个接口在集合之外的很多地方得到运用,但是集合相关的类最能够体现该接口的意义。

    下面是IEqualityComparer<T>的定义

     public interface IEqualityComparer<T> {

     bool Equals(T x, T y);

     int GetHashCode(T obj) ;

     }

    默认的定义来自BCL中的GenericEqualityComparer<T>类,IEqualityComparer<T>的默认实现依赖于IEquatable<T>的实现。

    但是要实现IEquatable<T>的话要怎么实现GetHashCode()呢? 很简单,使用Object.GetHashCode()。这意味着一个对象想正常运行的话必须在IEqualityComparer<T>调用的地方实现IEquatable<T> 和GetHashCode()。

    但是等一下,我没有明显的实现IEqualityComparer<T>所以我可以摆脱那些麻烦了吗?不是这样,很少有人真正实现IEqualityComparer<T>,而是用EqualityComparere<T>来替代,这个函数用来默认读取一个给定类型的IEqualityComparer<T>接口。

     public static class Example {

     public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source) {

     return Distinct(source, EqualityComparer<T>.Default);

     }

     public static IEnumerable<T> Distinct<T>(

     this IEnumerable<T> source,

     IEqualityComparer<T> comparer) {

     // implementation

     }

     }

    事实上,采用IEqualityComparer<T>接口的方法有个标准模式,就是EqualityComparer<T>采用重载而不是直接调用,这个模式默认如下

     public static class Example {

     public static IEnumerable<T> Distinct<T>(this IEnumerable<T> source) {

     return Distinct(source, EqualityComparer<T>.Default);

     }

     public static IEnumerable<T> Distinct<T>(

     this IEnumerable<T> source,

     IEqualityComparer<T> comparer) {

     // implementation

     }

     }

    如果你的对象实现了IEquatable<T>,那么会创建一个GenericEqualityComparer<T>的实例,对象也因此依赖于GetHashCode方法(比较时要求用到)。

    问题2:弱类型集合及框架不使用IEquatable<T>

    IEquatable<T>接口仅提供强类型的相等比较,弱类型访问接口时相等比较始终是棘手的问题。考虑实例化最初1.0版本的集合类:ArrayList, Hashtable,等等,这些基于对象的集合都无法使用IEquatable<T>接口。因此这些集合必须依赖于比较内部对象的方法。

    如果不实现Object.Equals和Object.GetHashCode方法,你的类型可能无法进行任何比较操作。这样会导致类中对象相等等操作的不正确结果。

     class Person : IEquatable<Person> {

     public readonly string Name;

     public Person(string name) {

     Name = name;

     }

     public bool Equals(Person other) {

      if (other == null) {

      return false;

     }

     return StringComparer.Ordinal.Equals(Name, other.Name);

     }

     }

      static void EqualityCheck() {

     var p = new Person("Bob");

     var list = new ArrayList();

     list.Add(p);

     Console.WriteLine(list.Contains(p)); // Prints: True

     Console.WriteLine(list.Contains(new Person("Bob"))); // Prints: False

     }

    这和我们想要的相去甚远。这个例子中,两个Person的实例定义相同,但是在Contains方法中会产生错误。实现Object.Equals 和Object.GetHashCode两个方法会移除这个问题。

    以下框架仍然是用松散类型集合:WinForms, WPF, WebForms,等等,因此你肯定会在工程中遇到需要松散类型的情况。

    问题3:判断对象是否相等与哈希码在BCL中是密不可分的

    无论如何,判断对象是否相等与哈希码在BCL中是密不可分的,如果一个对象可以用来比较那么它一定可以生成一个哈希码。这种隐秘的联系存在于框架内部的许多地方。

    如上文所述,实现IEquatable<T>接口之后填充Object.Equals()是铁板钉钉的事实,Object.GetHashCode可能有点麻烦因为GetHashCode有许多隐秘的内部联系。频繁变化的对象很难提供一个有效的哈希机制,这种情况下就返回1。这样所有关于GetHashCode()的内部联系都可以运行,且耗时很少。也许这种方法会让Dictionary更像一个链表,但是总比不能运行好吧。

posted on 2009-02-20 14:11:29 by vbcti  评论(0) 阅读(3451)

 

[原文作者]: Jared Parsons

[原文链接]: LINQ like functions for PowerShell: Skip-Count

     PowerShell 管线,和C#/VB的LINQ很像,都是通过一系列的转换过滤一组数据,然后生成一系列新的数据,详细的内容我会在将来的帖子里细述。

     当我使用PowerShell的时候,我一直把使用LINQ的经验用在使用管线上。不幸的是,很多LINQ的经验都不适用于PowerShell。大部分是简单易写的,但是一部分有一点麻烦。在任何情况下,我们不需要明白两种。所以我将开始一系列可以使用在PowerShell上的LINQ表现。

    同时,我以后的帖子会有些长。我们现在让帖子更短一点。

    今天说的相当于Enumerable.Skip。这个操作将调过枚举中的“count”元素。在PowerShell中, 相当于跳过管线中的“count”元素。

    #============================================================================

    # Skip the specified number of items

    #============================================================================

    function Skip-Count() {

    param ( $count = $(throw "Need a count") )

    begin {

    $i = 0

    }

    process {

    if ( $i -ge $count ) {

    $_

    }

    $i += 1

    }

   end {}

    }

    Example:

    PS:) 1..10 | skip-count 5

    6

    7

    8

    9

   10

posted on 2009-02-20 14:04:07 by vbcti  评论(1) 阅读(3059)

 
2009年01月23日

 

   我偶尔会觉得有必要把COM的互操作和PInvoke结合起来。在某些场景中,用PInvoke的声明和方法会更容易编码一些。在这些场景中包括进COM对象,并且在签名上加上合适的Marshal标记也是合法的。

    最简单的完成这些场景的方法是有本地的签名只暴露IUnkown实例。在托管代码这边,用一个对象声明并且标记上MarshalAs(UnmanagedType.IUnknown)。例如:

[DllImport("SomeDll.dll")]

[return: MarshalAs(UnmanagedType.IUnknown)]

public static extern object GetSomeComObject();

     有一条需要记住,在这种场景中怎么处理ref这个关键字。在任何情况下,如果一个COM对象被当作来自于PInvoke的签名,CLR会假定他会去调用IUKnown::Release()。 相对的本地代码必须考虑这种情况,适当的对这个对象AddRef()。

    这已经包括了任何的场景,像上面的代码,COM对象返回的值是function [1].

posted on 2009-01-23 14:25:09 by vbcti  评论(0) 阅读(3902)

 

 

[原文作者]:Jaredpar

[原文链接]:Disabling JIT optimizations while debugging

 

     如果你正在调试一个托管应用程序,却发现不能查看任何局部变量或者函数参数的值,这是因为

     托管代码在编译时已经被优化了。下面的内容将告诉你怎样解决这个问题。我会教大家一种非常简单的小技巧来利用.ini文件禁止代码优化。它不需要你重新编译你的应用程序并且只要几秒钟就能实现。

     创建一个.ini文件并添加如下内容:

     [.NET Framework Debugging Control]

     GenerateTrackingInfo=1

     AllowOptimize=0

     这个小技巧真的能帮助你节省调试的时间。虽然已经有其他人把它写进博客里,但是在过去几周我总是需要去搜索它,于是我就干脆也把它写进自己博客了以防以后需要。

posted on 2009-01-23 13:59:45 by vbcti  评论(0) 阅读(3404)

 

 

[原文作者]:Lisa Feigenbaum

[原文链接]:Walkthrough: Quick Search for Files and Symbols in Visual Studio 2010

 

    快速查询是2010CTP(Community Technical Preview,社区技术预览)中我喜欢的功能之一,基本上我每时每刻都在使用。我希望读者在开发中也感受到这项功能的魅力。由于开发者需要不时在代码中寻找某些信息的需求,快速查询已经成为当今开发环境最常用的功能,这篇文章深入介绍了VS2010的这项功能。

    此博文是October VS2010 CTP(VS2010社区技术预览十月版)功能预览系列的一部分,该系列旨在深入浅出地介绍VS2010及.NET 4.0中的新功能,即使读者没有下载社区技术预览,也可以在本文结尾处或以下论坛:

    http://social.msdn.microsoft.com/Forums/en-US/vs2010ctpvbcs/thread/1eb74a01-0d50-4a58-b9b3-cdfae5807ef8

    就本文内容及预览发表评论。

    谢谢!

    Lisa(非译者)

    功能预览:针对文件及标签的快速查询

     本文介绍的是VS2010中针对文件及标签的快速查询。快速查询指借助模糊查询技术协助开发者在代码中快速定位至某段代码。当打开项目工程中任意代码文件时,用户可以借助按下CTRL+,(CTRL键和逗号键)快速启动此功能。在快速查询窗口中,用户可以输入任意数量的查询条件,VS会根据输入值寻找项目中符合条件的标签,包括文件名,类型名,成员名。

     此功能预览使用VS中自带的PeopleTrax例子来进行示范,用户可以在任意工程中使用此功能。

    打开示例工程

  1. 示例文件位于:C:\Program Files\Visual Studio 10.0\Samples\1033\TeamDev Samples.zip.
  2. 解压缩示例文件压缩包到自定义路径,找到并打开PeopleTrax 文件夹。
  3. 双击 PeopleTrax.sln在 Visual Studio 2010中打开该工程.

     使用快速查询来寻找标签

     1. Open any file. Click inside the code editor and press CTRL+,. The following window appears. 打开任意文件,点击代码编辑器内部然后按下CTRL+,,将打开如下窗口

       

        

 

 

     2. 在快速查询窗口顶部文本输入框内输入get. 快速查询窗口将列出所有包含单词 "get" 的标签, 如下图所示,输入不分大小写.

   

          

 

      3. 在 "get"之后, 输入空格及单词 name.快速查询窗口会显示所有包含单词"get" 和 "name"的标签, 如下图所示.

 

          

 

       4.按下向下键选中结果列表中的 GetNames 项. 按回车, Visual Studio 将定位到GetNames 方法定义处.

posted on 2009-01-23 13:53:48 by vbcti  评论(0) 阅读(3626)

 

 

[原文作者]:Bill Horst

[原文链接]:Did you know? You can unwind the call stack from exceptions (Bill Horst)

    解退一个异常堆栈的能力是Visual Basic.NET 2005的一个新引进的特性。当调式器触发了一个异常,你可以解退这个堆栈以便于使用代码编辑器修复这个异常并继续调式 修改后的代码。这个异常辅助用户界面有一个“Enable Editing”选项,这个选项可以在当前Solution中展开调式器到代码堆栈的最顶端。

 

  

 

 

    当一个异常未被处理的时候,unwind将会自动发生,但是这个特性可以在Options dialog(在Tools下面)被开启或关闭。(见下图)

  

  

 

    如果你试图在一个异常被触法后去编辑代码,并在解退栈之前,你可以被允许去使用“解退栈并且编辑代码”,“终止代码调试的session”,或“取消编辑”这三个选项。(见下图)

 

    

 

      用户还可以从call stack窗口中进行解退栈,通过在需要的Call stack中单击右键并选择“Unwind To This Frame”。这个特性只是当异常已经被处发并且没有被解退栈时才有效,并且只有当在堆栈的足够靠前的的地方才有效。(见下图)

 

  

     我们希望这个特性可以给您提供更有效率的代码调试,并可以帮助您加强在Visual Basic中使用“Edit and Continue”能力。

posted on 2009-01-23 13:34:31 by vbcti  评论(1) 阅读(3062)

 

 

    [原文作者]:Jared Parsons

    [原文链接]:VB Catch ... When: Why so special?

    VB Catch 的语法有一个独特的特点:When。它允许用户通过表达式筛选一些情况,而不是仅仅是筛选它的类型。 任何的代码都可以添加When去决定是否要处理某个异常。

    Sub Sub1()

    Try

    DoSomeAction()

    Catch ex As Exception When Filter(ex)

    Stop

    End Try

    End Sub

    新闻组通常会问,“为什么这么特别呢?”我也可以通过C#做同样的处理,例如:

static void Sub1()
{
    try
    {
        DoSomeAction();
    }
    catch (Exception ex)
    {
        if (Filter(ex))
        {
            throw;
        }
        HandleException();
    }
}
 
在某种程度上来说,确实是这样的。两种情况下代码都通过调用筛选程序来做决定是否要处理这个异常。细微的不同是在调用筛选的时候。
 
在VB中,When语句是作为IL异常来执行的。当异常被抛出,异常的筛选程序在堆栈展开之前就在进行了。这表示如果筛选方法创建了包括当时的堆栈的错误报告,它就可以展示出异常在什么地方发生的。
 
例如,在上面的代码中,如果DoSomeAction()被抛出,堆栈在筛选程序的表达式里面被检查,下面的堆栈就会被展示出来。
 
 
                
 
 注意,怎么使得DosomeAction这个方法如此清晰可见的?这对错误报告和调查都是很有力的帮助。它也允许在真正出错的语句上设置断点,而不仅仅只是一个验尸报告。
 
 在C#里面执行的代码是发生在堆栈展开之后。只要你不是在执行最优化的代码,仍然可以通过堆栈得到异常的原代码。但是你就不能在错误出现时检查代码了。
 
 
 
                
 
 
 

posted on 2009-01-23 11:23:39 by vbcti  评论(0) 阅读(3069)

 

 

   [原文作者]:Jared Parsons

   [原文链接]:NotImplementedException vs. NotSupportedException

    在我最近发表的一篇博客中,有一位叫Jeremy Gray的读者指出我有一处应该使用NotSupportedException,而我却用了NotImplementedException。起初我并不赞同。我之所以认为NotImplementedException是合适的选择是因为在界面上有一个我的潜在对象无法处理的。方法

    然而我也并不熟悉NotSupportedException,所以决定对他再做些研究。毕竟,在大众面前出错也是博客的乐趣之一,当然这也是一个金子般的机会。既然那篇文章是关于介绍API设计的,那么还会有什么方法是比使用另一个API设计更好的呢?

    在做过一些研究以后,我同意Jeremy的说法。针对这2种异常,我也总结出了以下一些差异

    NotSupportedException:由于有对应的属性来标示他是否被支持而导致执行一个方法失败时,就会抛出这个异常,

    例如:

    IColletion<T>.Add -> IsReadOnly

    Stream.Seek -> CanSeek

    Stream.Write -> CanWrite

    NotImplementedException:有其他原因导致的执行一个方法失败时抛出的异常

    例如:ICollection.Count, ICloneable.Clone, 等 ... [1]

    在我之前发表的那篇博客中使用的方法就是ICollection<T>.Add()。我当时是使用了一个不可变集合来处理的,这样的话,就不可能使用Add方法。既然我们有IsReadOnly这个属性来作为标示来表示Add()是禁用的,那么NotSupportedException当然是更好的选择。

    [1] 不执行这些方法可能不是一个好主意。

posted on 2009-01-23 11:09:24 by vbcti  评论(0) 阅读(3019)

 
2008年12月31日
 
 
       Visual Basic 9.0 提供了一个新的功能XML Literals,它将使得对XML的编程更加简单方便,并且在很大程度上的减少了我们的代码量。实际上,XML Literals使得我们用Visual Basic去做XML变得如此简单,以至于很多C#开发者也开始趋向于在VB.NET中来进行XML相关工作!如果想要了解更多信息,请参看下面来自TechEd US的内容:

posted on 2008-12-31 13:47:32 by VBCTI  评论(2) 阅读(3306)

 
2008年12月17日
[原文作者]Jonathan Aneja
 
      LINQ的核心是要求可以对任何数据源进行可查询,意味着它必需实现IEnumerable接口。(实际上实现起来远比这个复杂,需要详细了解的请参阅Visual Basic 9.0 Language Specification)。现在当我们使用LINQ to Dataset时遇到了一个问题:DataTable没有实现IEnumerable,我们怎样去建立对它的查询呢?
之前我们了解到Visual Studio 2008中包含一个程序集System.Data.DataSetExtensions.dll,里面定义了一个扩展方法AsEnumerable()。形式如下:
 
                <Extension()> _
                Public Function AsEnumerable(source As DataTable) As EnumerableRowCollection(Of DataRow)
 
       这个函数接受一个DataTable的参数,返回一个实现IEnumerable的对象,从而可以使用LINQ的标准查询操作。你要做的就是导入程序集System.Data(工程模板默认已经做了),然后就可以调用AsEnumerable()来使用针对DatasetLINQ
 
                Dim customers = TestDS.Tables("Customers")
 
                Dim franceCustomers = From cust In customers.AsEnumerable() _
                          Where cust!Country = "France" _
                          Select cust
 
      现在有一个有趣的事是在VBLINQ的工作方法,不需要在代码中明确地调用AsEnumerable()来实现编译通过。其实是这样的,尽管Datatable没有实现IEnumerable接口,然而编译器通过搜寻特定的方法来帮助实现,该方法能够使Datatable转变为某种可查询的类型。当VB编译器遇到基于某个类型的LINQ没有实现IEnumerable, IEnumerable(Of T), IQueryable, 或者IQueryable(Of T),它将按顺序做下面一些事:
1. 看类型中是否有一个一致的查询方法可见。
2. 看类型中是否有一个名字为AsQueryable的实例方法,改方法返回一个可查询的类型。
3. 看类型中是否有一个名字包含AsQueryable的扩展方法,改方法返回一个可查询的类型。
4. 看类型中是否有一个名字为AsEnumerable的实例方法,改方法返回一个可查询的类型。
5. 看类型中是否有一个名字包含AsEnumerable的扩展方法,改方法返回一个可查询的类型。
      其中任何一步,如果编译器发现一个匹配的方法,将插入一个对它的调用。对于Datatable,当命名空间System.Data已经导入时,编译器在第五步为其发现了一个匹配方法,于是建立了一个对AsEnumerable的调用。结果你就能够写如101 LINQ SamplesLINQ to Dataset的代码了:
 
        Dim customers = TestDS.Tables("Customers")
 
        Dim franceCustomers = From cust In customers _
                              Where cust!Country = "France" _
                              Select cust
 
      实际上就是要你提供一个AsEnumerable的扩展方法,该方法返回一个可查询的类型,从而使LINQ起作用。
注意对于强类型的Datasets,你不需要调用AsEnumerable,因为它们继承自TypedTableBase(Of T),该类型实现了IEnumerable,它是VS2008中的一个新类型,在VS2005Dataset设计器将生成继承自DataTable的代码,并且显式地实现IEnumerable
 
 

posted on 2008-12-17 17:30:52 by VBCTI  评论(0) 阅读(2456)

 
[原文作者]Lucian
[原文链接]Reflection on COM objects
      
     我希望拥有一台这样的完全形态照样机, 当你拍摄一个物体(object)时,它不仅仅把二维图像存储在SD卡上,而是把物体记录在完全形态这个存储卡上,并且了解这个物体和其所构成的所有关系。这样会包括这个物体各个角度的三维图像,关于其历史意义的短文,对其文化和经济中所扮演的角色的描述,详尽的内部图表展现了物体是怎样工作,以及一系列的超链接指向与这个物体有关的主体并且所有的这些都会被保存在维基百科中。
     你准备如何创建这样一个相机呢?
     以上这些是为了引出反射这个主题
     .NET 对象上的反射通过 System.Type 完成,非常简单。比如"Dim type = GetType(System.string)",现在您可以查看所有成员和 System.String 类的继承层次结构。
     如果我们有一个(.Net) interop 程序集,反射COM类型也同样简单。比如,一个project 添加COM引用Microsoft Speech Library,然后可以进行反射做”GetType(SpeechLib.SpVoice)”。其实这样是对(.Net)interop程序集中Runtime Callable Wrapper的反射,Runtime Callable Wrapper”是从COM类型的类库得到的,包括了这个COM类库所有的信息。[译注:Runtime Callble Wrapper(RCW),我们可以生成一个RCW,通过RCW.Net用户就可以使用.Net对象而不是COM组件,为了实现传统的COM程序与.NET程序之间的相互调用,.NET提供了包装类RCW(Runtime Callable Wrapper)CCW(COM Callable Wrapper)。每当一个.NET客户程序调用一个COM对象的方法时就会创建一个RCW对象,每当一个COM客户程序调用一个.NET对象的方法时就会创建一个CCW对象。
     不过有时,你只有COM组件而没有.Net interop程序集。在我为Visual Studio写托管插件时就遇过这样的情况。对于此处反射必须使用ITypeInfo而不是 System.Type 以下是代码以获取该 ITypeInfo然后输出所有成员。我是在 COM 编程的初学者,欢迎所有的建议和改进。(注意:我特意不尝试创建包装 ITypeInfo / TYPEDESC API,虽然那样是比较成熟的) [译注:一般情况下,.Net调用COM组件,我们都会让Visual Studio生成Interop 程序集,这样依然可以用一般的反射,但是让vs.net自动生成一个包装过的.net类库。这种方法虽然方便,但是有很明显的缺点,最致命的就是开发的机器上安装的Com对象的版本比客户机器上安装的高,开发的程序无法正确的运行。
' REFLECTION ON COM OBJECTS. Lucian Wischik, October 2008.
' (with thanks to Eric Lippert and Sonja Keserovic for their help)
'
' CLR允许你通过GetType()进行反射类型
' 对于COM组件,有时你需要通过ITypeInfo/TYPEDESC来进行反射
' * 如果COM组件已经被转换成一个托管的RCW
'   这时可以用RCW进行反射
' * 如果没有RCW可用,还是需要通过ITypeInfo/TYPEDESC
'   ItypeInfo是指向COM组件的指针,可以和System.Type得到一样的信息,Visual Studio对于COM的智能化提示,正是使用这个来反射COM组件
' * 如果没有类库,我们对组件不能做反射
'
' ITypeInfo – class/interface/structure的引用
' TYPEDESC – 表示一些原型(比如,Integer,或者一些复合类型
' 下面显示了怎么使用ItypeInfo来进行反射
'
 
Option Strict On
Imports System.Runtime.InteropServices
 
 
Module Module1
 
    ''' <summary>
    ''' UnmanagedCreateCOM: this is an unmanaged function which calls CoCreateInstance
    ''' to create an instance of CLSID_WebBrowser.
    ''' </summary>
    ''' <returns>returns a new COM object. The caller is expected to AddRef on it.</returns>
    <DllImport("createcom.dll", SetLastError:=False)> _
    Function UnmanagedCreateCOM() As IntPtr
    End Function
 
 
    Sub Main()
        ' .net类型的反射最直接:
        Console.WriteLine("=== REFLECTION ON .NET TYPE VIA .NET REFLECTION ===")
        ReflectOnDotNetType(GetType(System.String))
 
        ' 如果将COM组件加到引用中,反射也是很简单的
        ' 我们将一个COM组件加到引用中,然后反射
        ' 和普通的.net类型一样使用反射:
        Console.WriteLine("=== REFLECTION ON RCW'D COM TYPE VIA .NET REFLECTION ===")
        ReflectOnDotNetType(GetType(SpeechLib.SpVoice))
 
        ' But .net reflection gives pointless results on COM objects which lack an interop assembly:
        ' GetObjectForIUnknown just creates a tiny stub RCW for them with a handful of common functions.
        Console.WriteLine("=== REFLECTION ON NON-RCW'D COM TYPE VIA ITYPEINFO REFLECTION ===")
        ReflectOnDotNetType(Marshal.GetObjectForIUnknown(UnmanagedCreateCOM()).GetType())
 
        ' 这样我们需要使用ITypeInfo来代替:
        Console.WriteLine("=== REFLECTION ON NON-RCW'D COM TYPE VIA COM REFLECTION ===")
        ReflectOnCOMObjectThroughITypeInfo(Marshal.GetObjectForIUnknown(UnmanagedCreateCOM()))
    End Sub
 
 
 
    ''' <summary>
    ''' ReflectOnDotNetType: 反射.net 类型
    ''' </summary>
    ''' <param name="tt">the type to reflect upon</param>
    Sub ReflectOnDotNetType(ByVal tt As System.Type)
        Dim qt As New Queue(Of System.Type)
        qt.Enqueue(tt)
        While qt.Count > 0
            Dim t = qt.Dequeue
            Console.WriteLine("TYPE {0}", t.ToString)
            For Each i In t.GetInterfaces
                Console.WriteLine(" inherits {0}", i.ToString)
                qt.Enqueue(i)
            Next
            For Each m In t.GetMembers
                Console.WriteLine(" member {0}", m.ToString)
            Next
        End While
    End Sub
 
    ''' <summary>
    ''' IDispatch: 托管Idispatch 接口
    ''' </summary>
    ''' <remarks>We don't use GetIDsOfNames or Invoke, and so haven't bothered with correct signatures for them.</remarks>
    <ComImport(), Guid("00020400-0000-0000-c000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
    Interface IDispatch
        Sub GetTypeInfoCount(ByRef pctinfo As UInteger)
        Sub GetTypeInfo(ByVal itinfo As UInteger, ByVal lcid As UInteger, ByRef pptinfo As IntPtr)
        Sub GetIDsOfNames_unused()
        Sub Invoke_unused()
    End Interface
 
 
    ''' <summary>
''' ReflectOnCOMObjectThroughITypeInfo: 一个支持Idispatch and attempts COM组件
''' 得到它的ItypeInfor 接口
    ''' 通过这个方法反射COM类型.
    ''' </summary>
    ''' <param name="com">the com object upon which to reflect</param>
    Sub ReflectOnCOMObjectThroughITypeInfo(ByVal com As Object)
        ' How do we get ITypeInfo for a COM object?
        ' It would be nice to use Marshal.GetITypeInfoForType. But that fails when the com object
        ' doesn't have an interop assembly (e.g. when the com object was created for us
        ' by native code). So instead we have to use IDispatch::GetTypeInfo.
        Dim idisp = CType(com, IDispatch)
        Dim count As UInteger = 0 : idisp.GetTypeInfoCount(count)
        If (count < 1) Then Throw New ArgumentException("No type info", "com")
        Dim _typeinfo As IntPtr : idisp.GetTypeInfo(0, 0, _typeinfo)
        If (_typeinfo = IntPtr.Zero) Then Throw New ArgumentException("No ITypeInfo", "com")
        Dim typeInfo = CType(Marshal.GetTypedObjectForIUnknown(_typeinfo, GetType(ComTypes.ITypeInfo)), ComTypes.ITypeInfo)
        Marshal.Release(_typeinfo) ' to release the AddRef that GetTypeInfo did for us.
 
        AddTypeInfoToDump(typeInfo)
        While typeInfosToDump.Count > 0
            DumpTypeInfo(typeInfosToDump.Dequeue())
        End While
    End Sub
 
 
    ''' <summary>
    ''' DumpType: prints information about an ITypeInfo type to the console -- name, inheritance, members
    ''' </summary>
    ''' <param name="typeInfo">the type to dump</param>
    Sub DumpTypeInfo(ByVal typeInfo As ComTypes.ITypeInfo)
 
        ' Name:
        Dim typeName = "" : typeInfo.GetDocumentation(-1, typeName, "", 0, "")
        Console.WriteLine("TYPE {0}", typeName)
 
 
        ' TypeAttr: contains general information about the type
        Dim pTypeAttr As IntPtr : typeInfo.GetTypeAttr(pTypeAttr)
        Dim typeAttr = CType(Marshal.PtrToStructure(pTypeAttr, GetType(ComTypes.TYPEATTR)), ComTypes.TYPEATTR)
 
 
        ' Inheritance:
        For iImplType = 0 To typeAttr.cImplTypes - 1
            Dim href As Integer : typeInfo.GetRefTypeOfImplType(iImplType, href)
            ' "href" is an index into the list of type descriptions within the type library.
            Dim implTypeInfo As ComTypes.ITypeInfo = Nothing : typeInfo.GetRefTypeInfo(href, implTypeInfo)
            ' And GetRefTypeInfo looks up the index to get an ITypeInfo for it.
            Dim implTypeName = "" : implTypeInfo.GetDocumentation(-1, implTypeName, "", 0, "")
            Console.WriteLine(" Implements {0}", implTypeName)
            AddTypeInfoToDump(implTypeInfo)
        Next
 
 
        ' Function/Sub/Property成员:
        ' Note that property accessors are flattened, e.g. for a property "Fred as Integer"
        ' it will be represented as two members "[Get] Function Fred() As Integer", and "[Put] Sub Fred(Integer)"
        ' Each member is uniquely identified by an integer "MEMID".
        ' This memid is what's used e.g. when invoking the member.
        For iFunc = 0 To typeAttr.cFuncs - 1
 
            ' FUNCDESC 是这里的主要结构:
            Dim pFuncDesc As IntPtr : typeInfo.GetFuncDesc(iFunc, pFuncDesc)
            Dim funcDesc = CType(Marshal.PtrToStructure(pFuncDesc, GetType(ComTypes.FUNCDESC)), ComTypes.FUNCDESC)
 
            ' Each function notionally has a list of names associated with it. I'll just pick the first.
            Dim names As String() = {""}
            typeInfo.GetNames(funcDesc.memid, names, 1, 0)
            Dim funcName = names(0)
 
            ' Function 参数:
            Dim cParams = funcDesc.cParams
            Dim s = ""
            For iParam = 0 To cParams - 1
                Dim elemDesc = CType(Marshal.PtrToStructure(New IntPtr(funcDesc.lprgelemdescParam.ToInt64 + Marshal.SizeOf(GetType(ComTypes.ELEMDESC)) * iParam), GetType(ComTypes.ELEMDESC)), ComTypes.ELEMDESC)
                If s.Length > 0 Then s &= ", "
                If (elemDesc.desc.paramdesc.wParamFlags And 2) <> 0 Then s &= "out "
                s &= DumpTypeDesc(elemDesc.tdesc, typeInfo)
            Next
 
            ' 输出函数的其他信息:
            Dim props = ""
            If (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYGET) <> 0 Then props &= "Get "
            If (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYPUT) <> 0 Then props &= "Set "
            If (funcDesc.invkind And ComTypes.INVOKEKIND.INVOKE_PROPERTYPUTREF) <> 0 Then props &= "Set "
            Dim isSub = (funcDesc.elemdescFunc.tdesc.vt = VarEnum.VT_VOID)
            s = props & If(isSub, "Sub ", "Function ") & funcName & "(" & s & ")"
            s &= If(isSub, "", " as " & DumpTypeDesc(funcDesc.elemdescFunc.tdesc, typeInfo))
            Console.WriteLine(" " & s)
            typeInfo.ReleaseFuncDesc(pFuncDesc)
        Next
 
 
        ' Field 成员:
        For iVar = 0 To typeAttr.cVars - 1
            Dim pVarDesc As IntPtr : typeInfo.GetVarDesc(iVar, pVarDesc)
            Dim varDesc = CType(Marshal.PtrToStructure(pVarDesc, GetType(ComTypes.VARDESC)), ComTypes.VARDESC)
            Dim names As String() = {""}
            typeInfo.GetNames(varDesc.memid, names, 1, 0)
            Dim varName = names(0)
            Console.WriteLine(" Dim {0} As {1}", varName, DumpTypeDesc(varDesc.elemdescVar.tdesc, typeInfo))
        Next
 
        Console.WriteLine()
    End Sub
 
 
 
    ''' <summary>
    ''' DumpTypeDesc: given a TYPEDESC, dumps it out into a string e.g. "Ref Int" or
    ''' "Array of MyTypeInfo". Also calls AddTypeInfoToDump for every ITypeInfo encountered.
    ''' </summary>
    ''' <param name="tdesc">the TYPEDESC to dump</param>
    ''' <param name="context">the ITypeInfo that contained this TYPEDESC, for context</param>
    ''' <returns>a string representation of the TYPEDESC</returns>
    Function DumpTypeDesc(ByVal tdesc As ComTypes.TYPEDESC, ByVal context As ComTypes.ITypeInfo) As String
        Dim vt = CType(tdesc.vt, VarEnum)
        Select Case vt
 
            Case VarEnum.VT_PTR
                Dim tdesc2 = CType(Marshal.PtrToStructure(tdesc.lpValue, GetType(ComTypes.TYPEDESC)), ComTypes.TYPEDESC)
                Return "Ref " & DumpTypeDesc(tdesc2, context)
 
            Case VarEnum.VT_USERDEFINED
                Dim href = tdesc.lpValue.ToInt32()
                Dim refTypeInfo As ComTypes.ITypeInfo = Nothing : context.GetRefTypeInfo(href, refTypeInfo)
                AddTypeInfoToDump(refTypeInfo)
                Dim refTypeName = "" : refTypeInfo.GetDocumentation(-1, refTypeName, "", 0, "")
                Return refTypeName
 
            Case VarEnum.VT_CARRAY
                Dim tdesc2 = CType(Marshal.PtrToStructure(tdesc.lpValue, GetType(ComTypes.TYPEDESC)), ComTypes.TYPEDESC)
                Return "Array of " & DumpTypeDesc(tdesc2, context)
                ' lpValue is actually an ARRAYDESC structure, which also has information on the array dimensions,
                ' but alas .Net doesn't predefine ARRAYDESC.
 
            Case VarEnum.VT_VOID ' e.g. IUnknown::QueryInterface(Ref GUID, out Ref Ref Void)
                Return "Void"
            Case VarEnum.VT_VARIANT
                Return "Object"
            Case VarEnum.VT_UNKNOWN
                Return "IUnknown*"
 
            Case VarEnum.VT_BSTR
                Return "String"
            Case VarEnum.VT_LPWSTR
                Return "wchar*"
            Case VarEnum.VT_LPSTR
               Return "char*"
 
            Case VarEnum.VT_HRESULT
                Return "HResult"
 
            Case VarEnum.VT_BOOL
                Return "Bool"
            Case VarEnum.VT_I1
                Return "SByte"
            Case VarEnum.VT_UI1
                Return "Byte"
            Case VarEnum.VT_I2
                Return "Short"
            Case VarEnum.VT_UI2
                Return "UShort"
            Case VarEnum.VT_I4, VarEnum.VT_INT                
                Return "Integer"
            Case VarEnum.VT_UI4, VarEnum.VT_UINT
                Return "UInteger"
            Case VarEnum.VT_I8
                Return "Long"
            Case VarEnum.VT_UI8
                Return "ULong"
 
            Case Else
                ' 这里还有其他类型,我没有在这里列出
                ' 大家可以根据需要将其他的列出来.
                Return vt.ToString()
        End Select
    End Function
 
 
    Dim typeInfosToDump As New Queue(Of ComTypes.ITypeInfo)
    Dim typeInfosDumped As New HashSet(Of String)
    '
    Sub AddTypeInfoToDump(ByVal typeInfo As ComTypes.ITypeInfo)
        Dim typeName = "" : typeInfo.GetDocumentation(-1, typeName, "", 0, "")
        If typeInfosDumped.Contains(typeName) Then Return
        typeInfosToDump.Enqueue(typeInfo)
        typeInfosDumped.Add(typeName)
    End Sub
 
EndModule

 

 

 

posted on 2008-12-17 17:27:49 by VBCTI  评论(0) 阅读(4899)

 
         
      [原文作者]Lucian
      [原文链接]LiveRun - a VS plugin to see the output of your program immediately
 
     假设你正在会议室里演示即时编译的程序。有什么最佳的方式来进行演示呢?
     你还在往代码窗口里输入代码?这样,你得依赖听众的想像力――他们在脑海中构造这个程序是怎样运行的。此外,你还得依赖他们相信你的代码真的像和你所说的一样运作。
     或者你要不停的运行你的代码,这样程序的输出窗口会弹出,然后听众可以看到代码的实际工作情况?这是有风险的,因为每次切换代码和输出窗口会中断流程。并且你得依赖听众会记得他们每次看到的输出信息所对应的代码。
     我想这是一个可以被技术所解决的问题!我为Visual Studio 2008 写了一个小插件。它关注的是当前文本缓冲中所包含的代码,并在后台编译它,然后在前端窗口中显示输出。这一系列动作大概每2秒钟重复一次。你甚至不需要保存再重新编译代码就能看到输出了。这个插件特意制做成独立的控制台程序因为它用不着输入。这是一幅截图:
                    
     
     源代码很小而且简单易懂,并且可以从上面的链接中获得。
 
     有两个关键的地方。第一是要使用多线程进行处理。我想让源代码在后台线程中编译因此它并不会影响Visual Studio UI. 但是要获取当前缓冲的文本你必须使用UI线程,而且要显示输出你也得使用它。我使用System.Timers.Timer在后台线程触发它的事件,同时为需要UI线程的任务调用form.Invoke(…)
     我还使用了“non-AutoReset”计时器。我让它获取源码并进行编译+运行+显示,暂停2秒后,再去获取源码并进行编译+运行+显示,如此循环。换句话说,计时器的时间间隔必须设为2 秒在处理完先前的定时事件之后。
 
''' <summary>
''' OnTimer 处理non-autoreset计时器信号。它运行在后台线程中。从当前的缓冲区中获取
''' 源代码并编译它,然后显示输出。
''' </summary>
''' <remarks></remarks>
Sub OnTimer() Handles t.Elapsed
    Try
        Dim oldsrc = src
                '我们在后台线程中。但只能从 UI 线程获取源...
        '此委托将获取源代码并将它存储在"src"字段中
        f.Invoke(New Action(AddressOf GetSource))
        If src <> oldsrc Then
            Dim oldoutput = output
            ' 在后台编译并运行
            output = CompileAndRun(src)
            If output <> "" OrElse oldoutput = "" Then
                ' 在屏幕上显示输出必须在UI线程中完成
                ' 此委托从"output"字段中得到要输出的内容并显示它
                f.Invoke(New Action(AddressOf ShowOutput))
            End If
        End If
    Finally
        t.Start()
    End Try
End Sub
 
 
     另一个关键时刻与如何运行代码并捕获它的输出有关。在“My”命名空间中,VB在这方面有很多有用的函数。我的主要问题是合理的处理异常不留任何遗漏。(注意:获取临时文件名的代码并不完全准确:      事实上你在某个状态之前得到一个未使用的临时文件名并不意味着这个文件名会一直不被使用;同样,它也并不意味着添加了“.vb”扩展名的临时文件名会一直不被使用。但是把这种情况处理的更准确看直来并不十分划算;不管怎样,在异常处理中我们将所有的问题恢复到正常状态。)
 
Function CompileAndRun(ByVal src As String) As String
    Dim fn_exe = ""
    Dim fn_src = ""
    Dim vbc As System.Diagnostics.Process = Nothing
    Dim exe As System.Diagnostics.Process = Nothing
    Try
        ' 准备编译
        fn_src = My.Computer.FileSystem.GetTempFileName() & ".vb"
        My.Computer.FileSystem.WriteAllText(fn_src, src, False)
        fn_exe = My.Computer.FileSystem.GetTempFileName() & ".exe"
        Dim framework = Environment.ExpandEnvironmentVariables("%windir%\Microsoft.Net\Framework")
        Dim latest_framework = (From d In My.Computer.FileSystem.GetDirectories(framework) Where d Like "*\v*" Select d).Last
 
        ' 编译
        vbc = System.Diagnostics.Process.Start(New ProcessStartInfo _
                            With {.CreateNoWindow = True, _
                                  .UseShellExecute = False, _
                                  .FileName = latest_framework & "\vbc.exe", _
                                  .Arguments = String.Format("/out:""{0}"" /target:exe ""{1}""", fn_exe, fn_src)})
        Dim vbc_done = vbc.WaitForExit(3000)
        If Not vbc_done Then Return ""
        If vbc.ExitCode <> 0 Then Return ""
 
        ' 运行
        Dim pinfo = New ProcessStartInfo With {.CreateNoWindow = True, _
                                               .UseShellExecute = False, _
                                               .FileName = fn_exe, _
                                               .RedirectStandardOutput = True}
        exe = New System.Diagnostics.Process With {.StartInfo = pinfo}
        exe.Start()
        Dim output = exe.StandardOutput.ReadToEnd
        Dim exe_done = exe.WaitForExit(3000)
        If Not exe_done Then Return ""
        Return output
    Finally
        ' 我们可以巧妙的关闭VBC进程
        If vbc IsNot Nothing Then
            If Not vbc.HasExited Then
                Try : vbc.Kill() : Catch ex As Exception : End Try
                Try : vbc.WaitForExit() : Catch ex As Exception : End Try
            End If
            Try : vbc.Close() : Catch ex As Exception : End Try
            vbc = Nothing
        End If
 
        ' 我们可以巧妙的关闭EXE
        If exe IsNot Nothing Then
            If Not exe.HasExited Then
                Try : exe.Kill() : Catch ex As Exception : End Try
                Try : exe.WaitForExit() : Catch ex As Exception : End Try
            End If
            Try : exe.Close() : Catch ex As Exception : End Try
            exe = Nothing
        End If
 
        ' 删除剩余的文件
        Try : My.Computer.FileSystem.DeleteFile(fn_exe) : Catch ex As Exception : End Try
        Try : My.Computer.FileSystem.DeleteFile(fn_src) : Catch ex As Exception : End Try
    End Try
End Function
 
 
     一直以来,我都很喜欢听到建议,bug 修复,代码改进以及评论!

posted on 2008-12-17 17:21:59 by VBCTI  评论(0) 阅读(4203)

 
[原文作者]Young Joo                                          
       
     很多人问我TableAdapter是否能够从存储过程里读取多组数据结果。最直接的回答是:不能。你不能通过TableAdapter.Fill()方法来得到一个Dataset。但是我们可以通过另一种简单的方法来实现。
 
DataAdapter.Fill()和多组数据结果
 
     TableAdapter.Fill()方法通过调用DataAdapter.Fill()从数据库中读取数据。DataSet.Fill() 方法可以从存储过程里读取多组数据结果。为了获得多组数据结果,可以应用DataAdapter.Fill()的一个重载方法,它将Dataset作为参数,这样就可以把存储过程的多组数据结果返回给包含有多个表的Dataset
    这里,我们通过一个简单的示例来演示一下这种方法是怎样实现的:
    假设在Northwind数据库里有一个存储过程dbo.spSelectCustomersOrders
    CREATE PROCEDURE spSelectCustomersOrders
    AS
    BEGIN 
       SET NOCOUNT ON
       SELECT * FROM Customers 
       SELECT * FROM Orders
    END
   GO
     下面的代码调用了这个存储过程,并且把2组数据结果存储在Dataset里。
    Dim myConn As New System.Data.SqlClient.SqlConnection
    Dim myAdapter As New System.Data.SqlClient.SqlDataAdapter
    Dim mySelectCommand As New System.Data.SqlClient.SqlCommand
    Dim myDataset As New System.Data.DataSet

    myConn.ConnectionString = "Data Source=.\SQLExpress;Initial Catalog=Northwind;Integrated       Security=True"
    mySelectCommand.Connection = myConn
    mySelectCommand.CommandText = "dbo.spSelectCustomersOrders"
    myAdapter.SelectCommand = mySelectCommand
    myAdapter.Fill(myDataset)

    For Each table As System.Data.DataTable In myDataset.Tables
       Console.WriteLine("Table Name:" & table.TableName)
    Next
    代码的输出形式如下:
     Table Name: Table
     Table Name: Table1
    
     我们可以看到,DataAdapter.Fill()方法执行了存储过程,并且把2组数据结果分别存储在2个数据表里。
 
TableAdapter的解决方案
 
     然而,为什么TableAdapter.Fill()方法不能够正确地处理多组数据结果?那是因为TableAdapter.Fill()调用的DataAdapter.Fill()方法是以DataTable作为参数,而不是Dataset。这种情况,我们只需要在TableAdapter里创建一个新的Fill方法,令其调用以Dataset为参数的DataAdapter.Fill()方法。
     假设这里有一个包含CustomersOrdersNorthwindDataset.xsd文件。让我们用上面的存储过程来实现新的Fill方法。把下面的代码加到partial class文件里。(在Dataset Designer上,可以通过双击或者右键选择"View Code"来进入partial class,当然也可以手动创建一个空的class文件。)
    Namespace NorthwindDataSetTableAdapters
        Partial Public Class CustomersTableAdapter
            Public Function FillCustomersOrders(ByVal dataSet As NorthwindDataSet) As Integer 
                 Dim
multiSelectCommand As New System.Data.SqlClient.SqlCommand 
                 Dim returnValue As Integer 

                 
multiSelectCommand.Connection = Me.Connection 
                 multiSelectCommand.CommandText = "dbo.spSelectCustomersOrders" 
                 Me
.Adapter.SelectCommand = multiSelectCommand 
                 '' Map auto-created Table1 that holds the second result-set (Orders rows) to 
                 '' Orders DataTable in our Dataset. 
                 Me.Adapter.TableMappings.Add("Table1", "Orders"
                returnValue = Me.Adapter.Fill(dataSet) 

                Return
returnValue 
            End Function 
         End
Class
     End
Namespace
      有两点需要特别注意:
      首先,新的FillCustomersOrders是以Dataset为参数,这样当我们调用DataAdapter.Fill()方法时,数据结果就会准确地存储到Dataset里。
      第二,注意我们是怎样应用TableMapping将自动生成的数据表映射到Dataset里的Orders表。当应用DataAdapter.Fill()来读取多组数据结果,每一组数据结果都被单独地存储在Dataset的数据表里。默认情况下,这些数据表被命名为Table, Table1, Table2…,为了将这些数据标与Dataset里定义的数据表相对应,我们应用TableMapping。如果你打开NorthwindDataset.xsd后面的代码,在TableAdapter classInitAdapter()方法,你就会看到类似的代码:
    tableMapping.SourceTable = "Table"
    tableMapping.DataSetTable = "Customers"
    '' Colum mapping code skipped
     ...
     Me._adapter.TableMappings.Add(tableMapping)
 
      这段代码是为了保证DataAdapter.Fill方法返回的数据表与Dataset里的数据表相对应。在我们FillCustomersOrders示例里,第二组结果包含的是Orders信息,所以我们在Table1Orders之间创建了映射关系,确保数据FillOrders表中。
     把以上代码添加到partial class后,你就可以调用FillCustomersOrders方法来fill CustomersOrders
     CustomersTableAdapter.FillCustomersOrders(Me.NorthwindDataSet)
 
性能的考虑
 
      有些情况下,这种方法的确很有效。但是这也要看情况,也许你会想到这个方法可以避免多次访问数据库,从而提高性能,但如果仅仅只需要获取一小部分数据,却应用这种方法一次读取了大量的数据,这同样也会降低性能,倒不如一次读取小部分数据,需要其它数据时,与数据库建立另一个连接,再读取。ADO.NET在处理多个数据库连接方面性能优化得还是不错的,很多情况下,都不至于导致性能瓶颈。总之我们只需要遵循最基本的原则:只在需要的时候,才去读取数据。
     但有些情况下,读取多组数据结果还是很有帮助的,所以,应用我在这里所介绍的方法吧,但时刻也不要忘记性能的问题。
 
相关资源

posted on 2008-12-17 17:13:00 by VBCTI  评论(0) 阅读(4044)

 
[原文作者]:Lucian
 
    这是我们如何在VB将来某一个版本中实现Co/Contra-Variance的一系列探索的第一项。 这并不是对下一代VB的承诺,而是作为一个提议放在这里,从而能够从我们潜在的用户那儿得到一些反馈。
 
Sub EatFruit(ByVal x As IEnumerable(Of Fruit))
...
 
Dim x As New List(Of Apple)
x.Add(New GrannySmith)
x.Add(New GoldenDelicious)
EatFruit(x)
' ERROR: cannot convert List(Of Apple) to IEnumerable(Of Fruit)
    观察一下以上的代码,或许你觉的它是没问题的。这是个很常见的情景:一个库函数处理一些data类型,但是你的自定义类型继承了这个data类型。如何能够将一个自定义类型的集合传给这个库函数呢?
    我们正在考虑为VB语言增加一个特征来支持这种转变,我们称之为“Co/Contra-Variance”,简称为“Variance”。实际上大概在2005年的时候,CLR已经支持“Variance”了,但并没有一种发布的语言用到它。但是一些其它的语言用到它,这里有一些链接,都是 关于这个主题的:
    我将谈一下VB中如何使用Variance:如何用它让你的代码更简单或清晰,如果我们实现了它能解决什么问题。Variance博大精深,apples转化为fruit只不过是它的寻常一功能而已,以上的文章更是让人觉得它复杂。但是我相信我们提议的语法和例子能够揭去这层神秘的面纱。
    昨天我用Variance解决了一个问题:
Function Call(instance As Expression, method As MethodInfo, arguments As IEnumerable(Of Expression)) As MethodCallExpression
...
 
' Create a new callsite that takes two arguments:
Dim args As New List(Of ConstantExpression)
args.Add(Expression.Constant("x"))
args.Add(Expression.Constant("y"))
'
Dim call1 = Expression.Call(instance, method, args)
' args inherits from IEnumerable(Of ConstantExpression), which
' variance-converts to IEnumerable(Of Expression)
 
    对应于第一段,我们转化为:
' some example classes to get us started
Class Food : End Class
Class Fruit : Inherits Food : End Class
Class Apple : Inherits Fruit : End Class
Class GrannySmith : Inherits Apple : End Class
Class GoldenDelicious : Inherits Apple : End Class
 
' GoldenDelicious < Apple < Fruit < Food
' using < in the mathematical sense of "is smaller than",
' and in the VB sense of "can be converted to"
 
Class AppleBasket
    Implements IReadOnly(Of Apple)
    Implements IWriteOnly(Of Apple)
End Class
 
out”参数
    我们想用关键字“out”和“in”来介绍Variance:
Interface IReadOnly(Of Out T)
    Function Read() As T
End Interface
' "Out" declares that T will only ever be used
' as return type of functions *
 
Dim x As IReadOnly(Of Apple) = New AppleBasket
Dim y As IReadOnly(Of Fruit) = x
 
Dim f As Fruit = y.Read()
' This is guaranteed not to throw InvalidCastException
 
    当接口的参数类型是“out”时,它表明这个类型只能用来返回(其他地方表明传出数据),如果试图调用“Sub(ByVal x As T)”,就会产生一个编译错误。(CLR如何使用Variance限制了很多设计,我们希望能和其他的.NET语言兼容。)
    正是这个“out”保证了CLR能够转化接口:
 
' GoldenDelicions < Apple < Fruit < Food < Object
 
Dim apples As IReadOnly(Of Apple) = New AppleBasket
 
' It is allowed to change to an IReadOnly of something bigger:
Dim fruits As IReadOnly(Of Fruit) = apples
Dim foods As IReadOnly(Of Food) = apples
Dim things As IReadOnly(Of Object) = fruits
 
' It is an ERROR to change to an IReadOnly that is smaller:
Dim golds As IReadOnly(Of GoldenDelicious) = apples
 
' Also an ERROR to change to something unrelated
Dim cars As IReadOnly(Of Car) = apples
 
    通常来说,如果你有一个泛型接口IreadOnly(Of Out T),然后你可以把“Of T”转换为它可以转化的其它类型。很显然,这是类型安全的:
    Variance转换是类型安全的和有效的,它只用一句中间语言指令来实现,不需要运行时Runtime检查。(这区别于数组:每次往数组里放东西,都得进行Runtime检查。)
    参数类型是“out”的接口被成为covariant。
In”参数
Interface IWriteOnly(Of In T)
    Sub Write(ByVal x As T)
End Interface
' "In" declares that T will only ever be used
' as ByVal arguments to functions.
 
Dim x As IWriteOnly(Of Apple) = New AppleBasket
Dim z As IWriteOnly(Of GoldenDelicious) = x
 
z.Write(New GoldenDelicious)
 
    “In”参数正好相反。当接口的参数类型是“In”时,它表明这个类型只能用于ByVal引用(其他地方表明传入数据),如果试图调用“Function f() as T”,就会产生一个编译错误。
    “In”参数保证了反向的类型转换:
' GoldenDelcious < Apple < Fruit < Food < Object
 
Dim apples As IWriteOnly(Of Apple) = New AppleBasket
 
' It is allowed to convert to an IWriteOnly of something smaller:
Dim golds As IWriteOnly(Of GoldenDelicious) = apples
 
' It is an ERROR to convert to something bigger, or unrelated:
Dim foods As IWriteOnly(Of Food) = apples
Dim cars As IWriteOnly(Of Car) = apples
 
 
 
    参数类型是“out”的接口被成为contravariant。
同时有“In”和“Out”
    直到20世纪90年代,人们仍然在为“In”或者“Out”是否是正确的而争论。现在我们知道了他们都是正确的!第一个在这方面有说服力的论据是1995年Giuseppe Castagna的研究论文"Conflict Without A Cause" [PDF]。
    这里有两个例子,说明他们为什么是正确的,以及将他们放在一起:
Class AppleBasket
 Implements IReadOnly(Of Apple)
 Implements IWriteOnly(Of Apple)
 
 Private m_value As Apple
 
 Public Function Read() As Apple Implements IReadOnly(Of Apple).Read
    Return m_value
 End Function
 
 Public Sub Write(ByVal x As Apple) Implements IWriteOnly(Of Apple).Write
    m_value = x
 End Sub
End Class
 
Pipes: 为内部和外部契约(contracts)用“In”和“Out”
' Here we implement a Pipe. Each element in the pipe is an ICollection.
'    IList < ICollection < IEnumerable
'
' When we give out reader ("Out") access to the public, we force it so
' readers can only ever assume that elements are IEnumerable.
' And when we give out writer ("In") access, we force it so
' that writers must always put in IList
'
' This future-proofs our code in TWO directions: it forces the
' implementation to provide IList in case in the future we want
' to expose more to the clients; but it does so without making
' a public commitment to the clients that future implementations
' would have to uphold.
 
Class MyPipe(Of T)
 Implements IWriteOnly(Of T)
 Implements IReadOnly(Of T)
 
 Private contents As New Stack(Of T)
 
 Public Sub Write(ByVal x As T) Implements IWriteOnly(Of T).Write
    contents.Push(x)
 End Sub
 
 Public Function Read() As T Implements IReadOnly(Of T).Read
    Return contents.Pop()
 End Function
End Class
 
 
    我们很希望能得到用户的反馈,从而帮助我们决定是否将这个功能加入VB语言,并且思考让他如何工作。请踊跃评论。
    在以后的几周里,我会新更多关于Variance的东西。
    另外:关于这边文正的标题,这是我们的设想:
Dim x As New List(Of Apple)
Dim y As List(Of Fruit) = x
'
' ERROR: List(Of Fruit) cannot be converted to List(Of Apple)
' Consider using IEnumerable(Of Fruit) instead.

posted on 2008-12-17 17:07:39 by VBCTI  评论(0) 阅读(3686)

 
[原文作者]:Lisa Feigenbaum
 
Code snippets是在Visual Basic 2005中引入的。它提供了一个简单的办法来处理特殊的编码任务或者将一段代码在应用程序中不同的部分重复使用。Code snippets能通过许多种不同的方法来插入。具体哪种方法根据不同的情况而定。 当我们需要浏览code snippet的时候,可以用Code Snippet inserter.
Code Snippet Inserter
      Code Snippet Insert 可以用两种方法触发:
·         在你的代码文件中输入?然后按Tab;
·         点右键,然后从出现的菜单中选择“Insert Snippet..”
      然后你就能根据snippet的目录结构,选择到你需要使用的Snippet。 可以注意到title旁边有tooltips显示对当前选择的snippet的描述和快捷键的。
       对那些你经常使用的或者需要快速获取的snippets,你可以使用snippets的快捷键。一旦你知道了snippet的快捷键,你就能在你的代码中通过键入快捷键+Tab来插入snippet. 要知道一个snippet的快捷键, 可以通过在code snippet inserter中选中一个snippet来查看或者直接在Code Snippets Manager中查看。
   Code Snippets Manager
  
  
     “快捷键+Tab”插入模式也有利于使用关键词来做为快捷键的情况。比如说,在你的代码中键入“Select+Tab”,你能看到如下的snippet被插入了。这种snippets能扩展。
An Expansion Snippet: Select Case
       如果你按了tab键并且完成了你不想要的一个snippet的输入,只要按“CTRL+Z”就能使你的代码回到之前的状态。
       对其他更多的面向任务的snippet,snippet快捷键以相应snippet路径的缩写开头。这样,因为snippet“Send an Email”在“connectivity”目录下,所以它的快捷键是“conEmail。注意:虽然大多数快捷键包含大写字母,为了能正确插入snippet,你输入的类型并不需要完全与相应的大小写类型匹配。 就是说,你输入“conemail”或者“CONEMAIL”都能使snippet正确插入。
       通常你只需要记住一部分的snippet的快捷键。那样的话,你一旦键入快捷键的开头部分,然后按?+Tab,一个完整的快捷键列表会显示出来。比如,为了插入一个Application目录下用“app”为前缀的snippet,你能键入“app+?+Tab”或者“a+?+Tab”来触发完整的快捷键列表。 快捷键列表呈现,当前选中的snippet的title显示在tooltip的最上部。然后你就能双击或者按回车键来将snippet插入你的代码。
     Snippet Shortcut List
       大多数我们讨论的snippet插入方法都涉及到某种键入,但是仍然有一种特殊情况。那就是:拖放。其实code snippet是以.snippet为扩展名的xml文件。一旦你安装了Visual Basic,这种文件就会被自动拷贝到你的电脑。Code Snippets Manager显示了那些snippet文件的路径,这样你就能在磁盘上找到它们。一旦你确定了路径比并且用windows Explorer将你的snippet文件放置于磁盘上,你就能简单地将Snippet拖拽到你的代码中。

posted on 2008-12-17 17:02:00 by VBCTI  评论(0) 阅读(3598)

 
[原文作者]:Milind Lele
 
08年11月19日12:39PM 上传
VS的2010版本(点击此处下载)比以前的一个改进的地方是WPF的拖拽式数据绑定,WinForms的拖拽式数据绑定在Visual Studio 2005就已经存在了。我们在VS2010把拖拽式数据绑定应用到了WPF上。如果你熟悉WinForms的数据绑定,你会发现WPF的拖拽式数据绑定和WinForms的很相似。
 
VS2008 SP1让我们可以在工程中添加一个EDM(实体数据模型),在VS2010中,添加了在数据源窗口中支持EDM这一功能,所以如果添加了一个EDM到工程中,这个EDM可以显示在数据源窗口中,你知道拖动这个EDM到WPF窗体中就可以完成绑定了。
具体信息我发布在Visual Studio Data blog上了。
你可以下载CTP体验一下,然后告诉我你的想法。

posted on 2008-12-17 16:48:30 by VBCTI  评论(0) 阅读(4986)

 
2008年11月28日
[原文作者]Mary Lee
假定你已经使用免费的Visual Studio Express Edition开发了一个程序,现在你想对公众部署这个程序。
我们的话题就此开始!
创建一个新的部署工程:
1.      在“File”菜单里,点击“Add”,并单击“New Project”;
2.      在弹出“Add New Project”对话框的“Project Type”面板中,展开“Other Project Types”结点,然后选择“Setup and Deployment Projects”;
3.      在“Templates”面板中,选择你想要创建的部署工程类型;
在这个例子中,我使用的版本是Visual Basic 2008 Express Edition,所以当我打开“New Project”对话框的时候,没有“Other Project Types”这个结点。
只有在标准版(Standard Edition)或者更高版本(比如Professional Edition 或 Team System Edition)中才有“Other Projects Types”这个结点。同样地,你也不能下载Setup Project模板并添加到Visual Studio Express Edition版本中。
说到这里,你发现Visual Studio Express Edition虽然是免费,但功能受限了吧!下面列出的所有版本的功能都受到同样限制:Visual Basic 2005 Express Edition,Visual C# 2005 Express Edition, Visual C++ 2005 Express Edition, Visual Basic 2008 Express Edition,Visual C# 2008 Express Edition, and Visual C++ 2008 Express Edition。
希望总是有的!使用ClickOnce就可以轻松地与大伙共享你的程序。它的发布向导能够轻松地创建一个安装程序来核查并安装所需的初始环境,比如.NET Framework 或 SQL Server Express Edition。发布的文件可以拷贝到CD光盘,本地Web服务器或者文件分享网络,以便用户们可以下载并安装你的程序。如果你总结客户的使用反馈后给程序增加了新的功能特性,ClickOnce还可以轻松地更新你的程序!
我制作了一个网络浏览器,现在我要教你使用Visual Basic 2008 Express Edition发布向导来部署这个程序到文件分享网络上去。
1.      展开Solution Explorer,右击解决方案名字并点击 “Publish”。
接着你就看到发布向导Publish Wizard 打开了。
2.      输入你想要Visual Studio拷贝文件的位置,并单击“next”。
3.      输入你的最终用户的起始安装位置,并单击“next”。
 
这个位置叫做安装位置。这两个位置是分开的,以防你可能没有对文件共享网络的写入权限。在那种情况下,你只好将文件拷贝到本地,管理员会拷贝这些文件到文件共享网络中去。
4.      选择程序开始运行的路径,并单击“Next”。
如果你选择的是“available online or offline”,该程序将要被安装到最终用户的计算机。如果你选择“only available online only”,该程序只能从安装位置运行。
5.      在发布向导的最后一页,单击“Finish”完成即可。
6.      打开网络文件夹,核查所有文件已经拷贝。最终用户可以打开Browser. application, publish.htm,或 setup.exe files 来开始安装。
好,我们现在对安装过程进行测试。
7.      打开程序安装文件所在的位置。
8.      在 publish.htm文件中,单击“ launch” 或“Install”。
稍后你就看到Launching Application对话框
9.      在安全警告窗口中, 单击 “Install”。 
 
10.      测试你的程序。
在我创建的浏览器里我输入了http://www.microsoft.com并单击Go!!!,结果我的程序运行良好。
上面讲的对 Visual Basic and Visual C# Express Editions都适用。  The Visual C++ Express Edition 开发环境并不具备ClickOnce 发布向导,但是你可以在 Visual Studio 控制台模式下完成。更多信息请参阅:ClickOnce Deployment for Visual C++ Applications
愿你享受开发程序的乐趣!

posted on 2008-11-28 10:08:17 by VBCTI  评论(0) 阅读(6706)

 
【第1页/共5页,89条】
首页
前页
1

Powered by: Joycode.MVC引擎 0.5.2.0