使用Visual Studio 2010与2012工作于相同项目

过去,当开发人员将一个 Visual Studio 项目迁移至新版本时,该项目将没法在任何一个早期版本中使用。Visual Studio 2012 RC 推出了一个叫做“项目往返切换(project round-tripping)“的特性,现在团队可以让项目在 Visual Studio 2010 和 2012 之间往返工作了。

Visual Studio 2010 SP1 包含了支持项目往返切换的框架。据 Visual Studio 博客上说,

在 Visual Studio 2010 中,我们启用了完整的多目标支持,用于服务 Visual Studio 与 .NET 运行时之间的一对一支持。它可以让 Visual Studio 2010 中创建的项目定向到老版本的 .NET Framework,这推进了我们启用 Visual Studio 项目在不同版本之间的往返切换的脚步。在 Visual Studio 11 中,我们自然而然地迈出了另一步,正确处理不同环境下的项目。

在使用往返切换功能之前,需要了解的重要事项是此类项目仅向后兼容 Visual Studio 2010 SP1,并且应用程序必须目标定向为 .NET Framework 2-4。兼容性同样根据项目类型的不同而有所不同,有一些项目是完全不可行的。(微软已经提供了一份包含兼容性细节的清单。)

项目行为

微软声明说,大部分项目在 Visual Studio 2010 SP1 与 2012 中的行为一致,从而往返切换没有问题。对于在构建路径上使用硬编码版本号的项目,它们中的版本号会自动更新为变量进行替代,所有的改动就仅限于此。应当可以无缝进行往返切换的项目类型有:Windows Forms、Sharepoint、WPF 以及大部分 SQL Server 项目。

其他项目也可以自动升级用于支持往返切换。Visual Studio 2012 在项目打开时,会显示一个”检查项目及解决方案改动“的对话框并显示影响的条目。用户可以在升级发生前检查提到的改动,之后过程会创建一个备份。一旦 Visual Studio 2012 执行修改后,项目就可以同时在 2012 与 2010 SP1 中使用了。Silverlight 4 与5、Blend 和 Visual Studio 2010 Web 项目也属于此类。

特定类型的项目在升级后的行为与此前的 Visual Studio 一样:一旦它们被转换后,将不能在任何其他版本的 Visual Studio 中使用。这些项目类型包括F#、LightSwitch 以及 SQL Server LocalDB。

不在此列的项目

Windows Phone、Windows Mobile、Windows Azure 云工具、BizTalk 2010 R2 以及 Visual Studio 安装程序(*.msi)项目没法在 Visual Studio 2012 RC 中使用。微软对是否在最终版本中为上述类型的项目提供往返切换支持尚未说明。对于安装程序项目,微软鼓励开发人员使用 InstallShield Limited Edition for Visual Studio 进行替代。

ASP.NET Web API现使用Json.NET

ASP.NET Web API发布候选版获得了若干增强,如将Json.NET作为默认的序列化器,可测试性更好的Http消息处理器,以及IAPIExplorer API等等。

特别一提的是,Json.NET目前成为了Web API默认的序列化器,这意味着我们不再需要切换默认实现——例如,为了能够使用日期(Json.NET 4.5默认使用ISO 8601日期格式)。结果,多余的System.Json.dll已经被移除。正如微软所说:

Json.NET为一个现代Web框架提供了灵活性与性能。

HttpClient现在派生于一个新的HttpMessageInvoker类。我们可以使用后者对Http消息处理器进行单元测试,而不再需要直接使用更为重量级的前者。此外,IAPIExplorer API现已加入到发行版本中。完整的改动可以查看发行说明

ASP.NET Web API是微软用于构建RESTful Web服务的WCF替代方案。RC版还发布了其他ASP.NET Web栈组件,如ASP.NET web pages 2ASP.NET MVC 4

VB6何以长盛不衰

微软刚刚将其 Windows 系统对 Visual Basic 6 程序的完全兼容支持延长到了 Windows 8 的整个产品周期. Visual Basic 6 最早发售于 1998 年, 所以现在来看 VB6 程序将至少被支持 24 年. 相比之下 .NET Framework 1.1 (2002 年发行) 则仅仅被支持了 7 年, 到了 Windows 7 (2009) 便被微软抛弃了.

我的一个学生曾经开玩笑说过 Visual Basic 6 就像 Windows 环境中“杀不死的小强”. 实际上这个比喻不无道理 - 蟑螂生存的秘诀在于它的简单, 它们所做的所有事情就是在自己占据的角落里坚持活下去, 除此之外什么都没有. 同样, Visual Basic 6 也满足了开发者为它预设的市场的所有需求——允许经验不多的程序员快速开发简单的程序. Visual Basic 从未被当作一种提供给专业程序员开发复杂程序的语言.
Visual Basic 6 实现其价值的方式是将 Windows 的各种复杂之处简化, 因此简单的工作对于 VB 来说非常容易完成. 不过另一方面, 相对复杂的工作比如使用线程则基本是不可能的. 对我来说有一个经验规则很有用:VB 中, 用了十分钟实现不了的功能对我来说肯定是没法实现的. 此外 VB 成功的另一个关键之处是由于其提供的功能较少, 所以所需的学习时间和学习难度也较低. 举个例子说, 就是学习开巴士需要的时间显然比学习怎么开战斗机少, 而成为一个优秀的 Visual Basic 程序员需要的时间也显然比成为一个优秀的 C++ 程序员少; 而当时 Visual Basic 6 面对的主要同类对手就是 C++.
不过到了 Visual Basic .NET 时代微软显然没有意识到这些. 当 VB .NET 成为一个所谓“全功能语言”的时候, 微软的开发者为它加上了 C# 所拥有的所有高级和复杂的功能 —— 线程, 多态继承, 等等. 也因此 VB .NET 变成了一个和 C# 要求相同的技能, 相同的学习过程和给人完全相同体验的编程语言. 当然这并不完全是微软的判断失误: 微软员工其实做了他们认为 Visual Basic 开发者们要求他们做的事情, 满足了 VB6 用户的要求.
但对于 Visual Basic 来说, 用“沉默的大多数”来描述大部分开发者非常合适. 几乎所有的 Visual Basic 6 用户都对现在的产品非常满意——沿用我们前面所使用的比喻, 他们很愿意只做公交司机: 每天五点下班回家, 不用加班到半夜; 周末在家和家人在一起而不用回到办公室; 晚上和配偶在一起, 而不是坐在办公室里带着充满血丝的眼睛连夜编程, 早上吃着昨天剩下的冷饭. 他们并不在意 Visual Basic 6 既没有提供运算符重载也不提供完整的面向对象模型, 所以他们没有抱怨什么.
而微软听到的声音则来自 3% 愿意成为战斗机驾驶员的 Visual Basic 6 公交司机——他们在闲暇时间参加业内会议, 在网上论坛中提问, 给各种关于 VB 的文章写自己的回复. 而也是这些人不能满足只在幻想中发射导弹击毁刚刚别了他们巴士的轿车——他们真的去向微软要求新的巴士必须带着和战斗机一样的加力燃烧室, 两边挂着格斗导弹, 车尾带上干扰弹发射器, 驾驶座还要有平视显示器. 因为他们是 Visual Basic 开发者中喊话声音最大的一群, 所以微软照做了.
于是到最后, 将 Visual Basic .NET 交给熟悉了 Visual Basic 6 的大部分程序员就如同人施放魔法将一只宠物狗仅仅在生理上变成了丛林狼, 然后便对着它大喊“去林子里抓只羊回来!”适应了宠物狗生活的狼显然会这么想“哈? 您没事儿吧? 我哪儿也不去就在这儿带着等你给我开罐头.”于是 Visual Basic 6 程序员依然如故.
当然 Visual Basic 6 也并非完美无缺. 最好的例子可能就是 On Error Resume Next 了——在工作的时候某一步执行出问题了, 所以我们继续按原计划该干什么干什么然后看看结果什么样? 想想都知道这明显不合理. 然而我们仍然应该记住由技能较低 (所以便宜) 的开发人员对功能有限 (便宜) 的程序进行快速 (便宜) 开发能解决很大一部分实际生产中遇到的问题, 而 Visual Basic 6 是这种情景中一种优秀的工具.
Visual Basic 6 解决的问题并不会凭空消失, 所以到微软提供一种可以替代 VB6 的工具前, Windows 系统中的小强还是会继续顽强的生存下去. 我愿意赌五块钱, 就算到了 Windows 9 还是 Windows 10, 微软也还是必须提供 Visual Basic 兼容.

Visual Studio 2012 Express 将免费发布给桌面开发者

微软今天宣布用于Windows桌面应用开发的Visual Studio Express 2012版将完全免费发布。之前微软曾经将Windows桌面应用的开发工作交给Visual Studio 2012专业版来完成,可以实现Metro应用、Web应用、Azure云应用和Windows Phone应用,售价400美元,而现在看来Visual Studio Express的功能已经可以覆盖到桌面应用,而且不需要付费。

微软将Express产品定位给Windows程序开发爱好者、初学者和开源的开发者,可实现命令行和桌面应用程序的开发,让他们可以在更低的成本下开始工作。

Google发布可视化编程语言Blockly

Google发布了完全可视化的编程语言 Google Blockly,类似 MIT 的儿童编程语言 Scratch,你可以通过类似玩乐高玩具的方式用一块块图形对象构建出应用程序。

每个图形对象都是代码块,你可以将它们拼接起来,创造出简单功能,然后将一个个简单功能组合起来,构建出一个程序。整个过程只需要鼠标的拖曳,不需要键盘敲击。该项目类似 Google 最近放弃的 App Inventor,不清楚 Blockly 与 App Inventor 或 Scratch 有何联系。

VB.Net将DataGridView中的内容保存为CSV文件

相关代码:

        <Extension()>
        Public Function ExportToCsv(dgv As DataGridView) As Boolean
            Dim dlg As New SaveFileDialog()
            dlg.Filter = "CSV(逗号分隔)(*.csv)|*.csv"
            dlg.FilterIndex = 0
            dlg.RestoreDirectory = True
            dlg.CreatePrompt = True
            dlg.Title = "保存为CSV(逗号分隔)文件"
            If dlg.ShowDialog() = DialogResult.OK Then
                Dim myStream As Stream
                myStream = dlg.OpenFile()
                Using sw As New StreamWriter(myStream, Encoding.Default)
                    Dim columnTitle As String = ""
                    Try
                        '写入列标题  
                        For i As Integer = 0 To dgv.ColumnCount - 1
                            If i > 0 Then
                                columnTitle += ","
                            End If
                            columnTitle += dgv.Columns(i).HeaderText
                        Next
                        columnTitle.Remove(columnTitle.Length - 1)
                        sw.WriteLine(columnTitle)
                        '写入列内容     
                        For j As Integer = 0 To dgv.Rows.Count - 1
                            Dim columnValue As String = ""
                            For k As Integer = 0 To dgv.Columns.Count - 1
                                If k > 0 Then
                                    columnValue += ","
                                End If
                                If dgv.Rows(j).Cells(k).Value Is Nothing Then
                                    columnValue += ""
                                Else
                                    Dim m As String = dgv.Rows(j).Cells(k).Value.ToString().Trim()
                                    columnValue += m.Replace(",", ",")
                                End If
                            Next
                            columnValue.Remove(columnValue.Length - 1)
                            sw.WriteLine(columnValue)
                        Next
                        sw.Close()
                        myStream.Close()
                    Catch e As Exception
                        Return False
                    Finally
                        sw.Close()
                        myStream.Close()
                    End Try
                End Using
                Return True
            Else
                Return False
            End If

使用方法:

在按钮单击事件中加入以下语句

DataGridView1.ExportToCsv

VBA使用正则表达式

通过菜单 工具 -> 引用 引用了Microsoft VBScript Regular Expressions 5.5 后就可以声明正则相关对象。主要有三个对象:RegExp、MatchCollection、Match。

示例代码:

Sub Test()
    Dim re As RegExp
    Dim mh As Match
    Dim mhs As MatchCollection
    Dim inpStr As String
    
    inpStr = "我的E-mail: me@lishewen.com 。"
    Set re = New RegExp
    re.Pattern = "(w+)@(w+).(w+)"
    Set mhs = re.Execute(inpStr)
    Set mh = mhs(0)                                      '只有一个匹配
    
    MsgBox "电子邮件地址是: " & mh.Value          '这里是匹配的内容
    MsgBox "用户名是:       " & mh.SubMatches(0)  '第一个括号中的内容
    MsgBox "邮箱是:         " & mh.SubMatches(1)  '第二个括号中的内容
    MsgBox "域名是:       " & mh.SubMatches(2)  '第三个括号中的内容    
End Sub

Outlook批量发送带附件的邮件

需求:

顺序读取指定文件夹中文件名,文件名的格式为“邮箱地址”+“空格”+“数字”
比如邮箱地址为a@126.com  指定目录中有文件名为 a@126.com.mp3  及a@126.com 1.mp3 (邮箱地址+空格+1)的两个音乐文件 则自动以附件方式发送两文件到a@126.com。
其中也可能只是一个邮箱地址,不包括空格和数字,从文件名中分析出邮箱地址,把该文件发送到该邮箱地址

解决方案:

制作Outlook插件实现

Outlook插件运行截图:

如需定制此类插件请联系我

Outlook发送邮件的VBA宏

Sub SendFile()
    Dim olApp As Outlook.Application
    Dim objMail As Outlook.MailItem
    Set olApp = ThisOutlookSession.Application
    Set objMail = olApp.CreateItem(olMailItem)
    With objMail
        .BodyFormat = olFormatPlain
        .Subject = "您的文件"
        .Body = "您的文件,请查收"
       '收件人
        .To = "lishewen@gmail.com"
        '抄送
        .CC = "me@lishewen.com"
        '附件
        .Attachments.Add "c:\算神\黎摄文.txt
        .Send
    End With
End Sub

VBA获取文件夹中的文件名的函数

'说明:获取指定文件夹中的文件
'参数:
'   path:字符串,指定的文件夹路径
'   searchOption:布尔值,True 所有文件; False 当前文件夹中的文件
'返回值:数组
'注意:数组第1项(GetFiles(0))始终为空,应从第2项(GetFiles(1))开始计算
Public Function GetFiles(path As String, searchOption As Boolean) As String()
    Dim result() As String
    Dim arr() As String
    Dim i, j As Integer
    
    arr = getFiles_(path, searchOption)
    For i = 0 To UBound(arr)
        If arr(i) <> "" Then
            j = j + 1
        End If
    Next
    If j > 0 Then '防止下标越界
        ReDim result(j) As String
        j = 1
        For i = 0 To UBound(arr)
            If arr(i) <> "" Then
                result(j) = arr(i)
                j = j + 1
            End If
        Next
    End If
    GetFiles = result
End Function

'本函数为私有函数,获取指定文件夹中的文件
'因返回的数组可能包含空元素,需由GetFiles进行过滤排除
Private Function getFiles_(path As String, searchOption As Boolean) As String()
    Dim oFso As FileSystemObject
    Dim oFolder, oFolder2 As Folder
    Dim oFile As File
    Dim i, j As Integer
    Dim list() As String
    ReDim result(0) As String
    
    Set oFso = CreateObject("Scripting.FileSystemObject")
    Set oFolder = oFso.GetFolder(path)
    
    '检查文件夹存在
    If Not oFso.FolderExists(path) Then
        getFiles_ = result
        Set oFile = Nothing
        Set oFolder2 = Nothing
        Set oFolder = Nothing
        Set oFso = Nothing
        Exit Function
    End If
    
    '当前文件夹中的文件
    If oFolder.files.Count > 0 Then
        ReDim Preserve result(oFolder.files.Count - 1)
        For Each oFile In oFolder.files
            result(i) = oFile.path
            i = i + 1
        Next
    End If
    
    '子文件夹中的文件
    If searchOption And oFolder.SubFolders.Count > 0 Then
        For Each oFolder2 In oFolder.SubFolders
            list = getFiles_(oFolder2.path, searchOption)
            i = UBound(result)
            ReDim Preserve result(i + UBound(list) + 1)
            For j = 0 To UBound(list)
                result(i + j + 1) = list(j)
            Next
        Next
    End If
    
    getFiles_ = result
    
    Set oFile = Nothing
    Set oFolder2 = Nothing
    Set oFolder = Nothing
    Set oFso = Nothing
End Function