博文

SQL数据库数据处理类应用举例(2012-5-2 20:54:00)

SQL Data Provider VB.NET Class

Example Usage

SQLDataProvider Class Documentation

This class provides a fast and universal method for accessing SQL Server database.

Create Instance

At first you create an instance of SqlDatabase class.

Dim sqldb As New SqlDatabase("Data Source=(local); Initial Catalog= ; UId = ; Pwd = ;")

For more information about connection strings, visit ConnectionStrings.com.

ExecuteNonQuery Method

Executes a Transact-SQL statement against the connection and returns the number of rows affected.

Dim params(0 To 1) As SqlParameter
params(0) = New SqlParameter("@Firstname", SqlDbType.NVarChar, 120)
params(0).Value = "Stefan"
params(1) = New SqlParameter("@Lastname", SqlDbType.NVarChar, 120)
params(1).Value = "Cameron"
sqldb.ExecuteNonQuery("Insert Into dbo.Users(Firstname, LastName) Values(@FirstName, @LastName)", CommandType.Text, params)

If you are using stored procedure,you can execute that without declaring parameters such as following code:

sqldb.ExecuteNonQuery("dbo.CreateUser", Nothing, "Stefan", "Cameron")

ExecuteScalar Method

Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.

Dim count As Integer = sqldb.ExecuteScalar("Select Count(*) From dbo.Users", CommandType.Text) 
MsgBox("Number of row(s): " & count)

ExecuteReader Method

Sends the CommandText to the Connection and builds a SqlDataReader.

Dim FirstName As String = String.Empty 
Dim LastName As String = String.Empty 

Dim params(0) As SqlParameter 
params(0) = New SqlParameter("@Id", SqlDbType.Int) 
params(0).Value = 1 

Dim dr As IDataReader = sqldb.ExecuteReader("Select * From dbo.Users Where (Id = @Id)", CommandType.Text, params) 
While dr.Read() 
    FirstName = dr("Firstname") 
    LastName = dr("Lastname") 
End While 
dr.Close() 

MsgBox(FirstName & " " & LastName, MsgBoxStyle.Information)

There is a sample for using stored procedure:

Create Procedure [dbo].[GetUserInfo] 
    ( 
        @Id int 
    ) 
As 
Begin 
    Select * From dbo.Users Where (Id = @Id) 
End

Dim FirstName As String = String.Empty 
Dim LastName As String = String.Empty 

Dim dr As IDataReader = sqldb.ExecuteReader("dbo.GetUserInfo", Nothing, 1) 
While dr.Read() 
    FirstName = dr("Firstname") 
    LastName = dr("Lastname") 
End While 
dr.Close() 

MsgBox(FirstName & " " & LastName, MsgBoxStyle.Information)

Using Return Value Parameter

If you are using stored procedure,you can get the value of 'return value parameter'.

Create Procedure dbo.UserExists
    (
        @Firstname nvarchar(120),
        @Lastname nvarchar(120)
    )
As
Begin
    If Exists(Select * From dbo.Users Where (Firstname = @Firstname) And (Lastname = @Lastname))
        Return 1
End

Dim retval As Integer
sqldb.ExecuteNonQuery("dbo.UserExists", retval, "Stefan", "Cameron")
MsgBox("User Exists: " & IIf(retval = 1, "Yes", "No"))

FillDataset Method

Adds or refreshes rows in the System.Data.DataSet to match those in the data source using the System.Data.DataSet name, and creates a System.Data.DataTable named "Table."

Binding a DataGridView with FillDataset method.

DataGridView1.DataSource = sqldb.FillDataset("Select * From dbo.Users", CommandType.Text).Tables(0)

ExecuteDataset Method

Calls the respective INSERT, UPDATE, or DELETE statements for each inserted, updated, or deleted row in the System.Data.DataSet with the specified System.Data.DataTable name.

' Getting the System.Data.DataSet.
Dim ds As DataSet = CType(DataGridView1.DataSource, DataTable).DataSet

' Declaring insert command object
Dim inscmd As New SqlCommand("Insert Into dbo.Users(Firstname, Lastname) Values(@Firstname, @Lastname)")
With inscmd
    .CommandType = CommandType.Text
    .Parameters.Add(New SqlParameter("@Firstname", SqlDbType.NVarChar, 120)).SourceColumn = "Firstname"
    .Parameters.Add(New SqlParameter("@Lastname", SqlDbType.NVarChar, 120)).SourceColumn = "Lastname"
End With

' Declaring update command object
Dim updcmd As New SqlCommand("Update dbo.Users Set Firstname = @Firstname, Lastname = @Lastname Where (Id = @Id)")
With updcmd
    .CommandType = CommandType.Text
    .Parameters.Add(New SqlParameter("@Id", SqlDbType.Int)).SourceColumn = "Id"
    .Parameters.Add(New SqlParameter("@Firstname", SqlDbType.NVarChar, 120)).SourceColumn = "Firstname"
    .Parameters.Add(New SqlParameter("@Lastname", SqlDbType.NVarChar, 120)).SourceColumn = "Lastname"
End With

' Declaring delete command object
Dim delcmd As New SqlCommand("Delete From dbo.Users Where (Id = @Id)")
With delcmd
    .CommandType = CommandType.Text
    .Parameters.Add(New SqlParameter("@Id", SqlDbType.Int)).SourceColumn = "Id"
End With

' Updating data source
sqldb.ExecuteDataset(inscmd, updcmd, delcmd, ds, ds.Tables(0).TableName)

阅读全文(124) | 评论:1 | 复制链接

VB.NET用于SQL数据库处理的类(2012-5-2 20:53:00)

SQL Data Provider VB.NET Class

The Class

This class is for simplifying and accelerating working with SQL, using this class is very simple; there is a sample below for this class, which I hope is useful. You can report bugs, opinions and suggestions to me.

Imports System
Imports System.IO
Imports System.Text
Imports System.Data
Imports System.Data.SqlClient

Namespace SqlDataProvider

    ''' <summary>
    ''' This class provides a fast and universal method for accessing SQL Server database.This class cannot be inherited.
    ''' </summary>
    Public NotInheritable Class SqlDatabase

#Region " Local Property Declarations "

        Dim _connectionString As String

#End Region

#Region " Constructor "

        ''' <summary>
        ''' Initializes a new instance of the ADO.SqlDatabase class.
        ''' </summary>
        ''' <param name="connectionString">The connection used to open the SQL Server database.</param>
        Public Sub New(ByVal connectionString As String)
            _connectionString = connectionString
        End Sub

#End Region

#Region " Public Properties "

        ''' <summary>
        ''' Gets or sets the string used to open a SQL Server database.
        ''' </summary>
        ''' <returns>The connection string that includes the source database name, and other parameters needed to establish the initial connection.</returns>
        Public Property ConnectionString() As String
            Get
                Return _connectionString
            End Get
            Set(ByVal value As String)
                _connectionString = value
            End Set
        End Property

#End Region

#Region " Private Methods "

        Private Sub AssignParameters(ByVal cmd As SqlCommand, ByVal cmdParameters() As SqlParameter)
            If (cmdParameters Is Nothing) Then Exit Sub
            For Each p As SqlParameter In cmdParameters
                cmd.Parameters.Add(p)
            Next
        End Sub

        Private Sub AssignParameters(ByVal cmd As SqlCommand, ByVal parameterValues() As Object)
            If Not (cmd.Parameters.Count - 1 = parameterValues.Length) Then Throw New ApplicationException("Stored procedure's parameters and parameter values does not match.")
            Dim i As Integer
            For Each param As SqlParameter In cmd.Parameters
                If Not (param.Direction = ParameterDirection.Output) AndAlso Not (param.Direction = ParameterDirection.ReturnValue) Then
                    param.Value = parameterValues(i)
                    i += 1
                End If
            Next
        End Sub

#End Region

#Region " ExecuteNonQuery "

        ''' <summary>
        ''' Executes a Transact-SQL statement against the connection and returns the number of rows affected.
        ''' </summary>
        ''' <param name="cmd">The Transact-SQL statement or stored procedure to execute at the data source.</param>
        ''' <param name="cmdType">A value indicating how the System.Data.SqlClient.SqlCommand.CommandText property is to be interpreted.</param>
        ''' <param name="parameters">The parameters of the Transact-SQL statement or stored procedure.</param>
        ''' <returns>The number of rows affected.</returns>
        Public Function ExecuteNonQuery(ByVal cmd As String, ByVal cmdType As CommandType, Optional ByVal parameters() As SqlParameter = Nothing) As Integer
            Dim connection As SqlConnection = Nothing
            Dim transaction As SqlTransaction = Nothing
            Dim command As SqlCommand = Nothing
            Dim res As Integer = -1
            Try
                connection = New SqlConnection(_connectionString)
                command = New SqlCommand(cmd, connection)
                command.CommandType = cmdType
                Me.AssignParameters(command, parameters)
                connection.Open()
                transaction = connection.BeginTransaction()
                command.Transaction = transaction
                res = command.ExecuteNonQuery()
                transaction.Commit()
            Catch ex As Exception
                If Not (transaction Is Nothing) Then
                    transaction.Rollback()
                End If
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If Not (connection Is Nothing) AndAlso (connection.State = ConnectionState.Open) Then connection.Close()
                If Not (command Is Nothing) Then command.Dispose()
                If Not (transaction Is Nothing) Then transaction.Dispose()
            End Try
            Return res
        End Function

        ''' <summary>
        ''' Executes a Transact-SQL statement against the connection and returns the number of rows affected.
        ''' </summary>
        ''' <param name="spname">The stored procedure to execute at the data source.</param>
        ''' <param name="returnValue">The returned value from stored procedure.</param>
        ''' <param name="parameterValues">The parameter values of the stored procedure.</param>
        ''' <returns>The number of rows affected.</returns>
        Public Function ExecuteNonQuery(ByVal spname As String, ByRef returnValue As Integer, ByVal ParamArray parameterValues() As Object) As Integer
            Dim connection As SqlConnection = Nothing
            Dim transaction As SqlTransaction = Nothing
            Dim command As SqlCommand = Nothing
            Dim res As Integer = -1
            Try
                connection = New SqlConnection(_connectionString)
                command = New SqlCommand(spname, connection)
                command.CommandType = CommandType.StoredProcedure
                connection.Open()
                SqlCommandBuilder.DeriveParameters(command)
                Me.AssignParameters(command, parameterValues)
                transaction = connection.BeginTransaction()
                command.Transaction = transaction
                res = command.ExecuteNonQuery()
                returnValue = command.Parameters(0).Value
                transaction.Commit()
            Catch ex As Exception
                If Not (transaction Is Nothing) Then
                    transaction.Rollback()
                End If
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If Not (connection Is Nothing) AndAlso (connection.State = ConnectionState.Open) Then connection.Close()
                If Not (command Is Nothing) Then command.Dispose()
                If Not (transaction Is Nothing) Then transaction.Dispose()
            End Try
            Return res
        End Function

#End Region

#Region " ExecuteScalar "

        ''' <summary>
        ''' Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
        ''' </summary>
        ''' <param name="cmd">The Transact-SQL statement or stored procedure to execute at the data source.</param>
        ''' <param name="cmdType">A value indicating how the System.Data.SqlClient.SqlCommand.CommandText property is to be interpreted.</param>
        ''' <param name="parameters">The parameters of the Transact-SQL statement or stored procedure.</param>
        ''' <returns>The first column of the first row in the result set, or a null reference if the result set is empty.</returns>
        Public Function ExecuteScalar(ByVal cmd As String, ByVal cmdType As CommandType, Optional ByVal parameters() As SqlParameter = Nothing) As Object
            Dim connection As SqlConnection = Nothing
            Dim transaction As SqlTransaction = Nothing
            Dim command As SqlCommand = Nothing
            Dim res As Object = Nothing
            Try
                connection = New SqlConnection(_connectionString)
                command = New SqlCommand(cmd, connection)
                command.CommandType = cmdType
                Me.AssignParameters(command, parameters)
                connection.Open()
                transaction = connection.BeginTransaction()
                command.Transaction = transaction
                res = command.ExecuteScalar()
                transaction.Commit()
            Catch ex As Exception
                If Not (transaction Is Nothing) Then
                    transaction.Rollback()
                End If
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If Not (connection Is Nothing) AndAlso (connection.State = ConnectionState.Open) Then connection.Close()
                If Not (command Is Nothing) Then command.Dispose()
                If Not (transaction Is Nothing) Then transaction.Dispose()
            End Try
            Return res
        End Function

        ''' <summary>
        ''' Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
        ''' </summary>
        ''' <param name="spname">The stored procedure to execute at the data source.</param>
        ''' <param name="returnValue">The returned value from stored procedure.</param>
        ''' <param name="parameterValues">The parameter values of the stored procedure.</param>
        ''' <returns>The first column of the first row in the result set, or a null reference if the result set is empty.</returns>
        Public Function ExecuteScalar(ByVal spname As String, ByRef returnValue As Integer, ByVal ParamArray parameterValues() As Object) As Object
            Dim connection As SqlConnection = Nothing
            Dim transaction As SqlTransaction = Nothing
            Dim command As SqlCommand = Nothing
            Dim res As Object = Nothing
            Try
                connection = New SqlConnection(_connectionString)
                command = New SqlCommand(spname, connection)
                command.CommandType = CommandType.StoredProcedure
                connection.Open()
                SqlCommandBuilder.DeriveParameters(command)
                Me.AssignParameters(command, parameterValues)
                transaction = connection.BeginTransaction()
                command.Transaction = transaction
                res = command.ExecuteScalar()
                returnValue = command.Parameters(0).Value
                transaction.Commit()
            Catch ex As Exception
                If Not (transaction Is Nothing) Then
                    transaction.Rollback()
                End If
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If Not (connection Is Nothing) AndAlso (connection.State = ConnectionState.Open) Then connection.Close()
                If Not (command Is Nothing) Then command.Dispose()
                If Not (transaction Is Nothing) Then transaction.Dispose()
            End Try
            Return res
        End Function

#End Region

#Region " ExecuteReader "

        ''' <summary>
        ''' Sends the System.Data.SqlClient.SqlCommand.CommandText to the System.Data.SqlClient.SqlCommand.Connection, and builds a System.Data.SqlClient.SqlDataReader using one of the System.Data.CommandBehavior values.
        ''' </summary>
        ''' <param name="cmd">The Transact-SQL statement or stored procedure to execute at the data source.</param>
        ''' <param name="cmdType">A value indicating how the System.Data.SqlClient.SqlCommand.CommandText property is to be interpreted.</param>
        ''' <param name="parameters">The parameters of the Transact-SQL statement or stored procedure.</param>
        ''' <returns>A System.Data.SqlClient.SqlDataReader object.</returns>
        Public Function ExecuteReader(ByVal cmd As String, ByVal cmdType As CommandType, Optional ByVal parameters() As SqlParameter = Nothing) As IDataReader
            Dim connection As SqlConnection = Nothing
            Dim command As SqlCommand = Nothing
            Dim res As SqlDataReader = Nothing
            Try
                connection = New SqlConnection(_connectionString)
                command = New SqlCommand(cmd, connection)
                command.CommandType = cmdType
                Me.AssignParameters(command, parameters)
                connection.Open()
                res = command.ExecuteReader(CommandBehavior.CloseConnection)
            Catch ex As Exception
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            End Try
            Return CType(res, IDataReader)
        End Function

        ''' <summary>
        ''' Sends the System.Data.SqlClient.SqlCommand.CommandText to the System.Data.SqlClient.SqlCommand.Connection, and builds a System.Data.SqlClient.SqlDataReader using one of the System.Data.CommandBehavior values.
        ''' </summary>
        ''' <param name="spname">The stored procedure to execute at the data source.</param>
        ''' <param name="returnValue">The returned value from stored procedure.</param>
        ''' <param name="parameterValues">The parameter values of the stored procedure.</param>
        ''' <returns>A System.Data.SqlClient.SqlDataReader object.</returns>
        Public Function ExecuteReader(ByVal spname As String, ByRef returnValue As Integer, ByVal ParamArray parameterValues() As Object) As IDataReader
            Dim connection As SqlConnection = Nothing
            Dim command As SqlCommand = Nothing
            Dim res As SqlDataReader = Nothing
            Try
                connection = New SqlConnection(ConnectionString)
                command = New SqlCommand(spname, connection)
                command.CommandType = CommandType.StoredProcedure
                connection.Open()
                SqlCommandBuilder.DeriveParameters(command)
                Me.AssignParameters(command, parameterValues)
                res = command.ExecuteReader(CommandBehavior.CloseConnection)
                returnValue = command.Parameters(0).Value
            Catch ex As Exception
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            End Try
            Return CType(res, IDataReader)
        End Function

#End Region

#Region " FillDataset "

        ''' <summary>
        ''' Adds or refreshes rows in the System.Data.DataSet to match those in the data source using the System.Data.DataSet name, and creates a System.Data.DataTable named "Table."
        ''' </summary>
        ''' <param name="cmd">The Transact-SQL statement or stored procedure to execute at the data source.</param>
        ''' <param name="cmdType">A value indicating how the System.Data.SqlClient.SqlCommand.CommandText property is to be interpreted.</param>
        ''' <param name="parameters">The parameters of the Transact-SQL statement or stored procedure.</param>
        ''' <returns>A System.Data.Dataset object.</returns>
        Public Function FillDataset(ByVal cmd As String, ByVal cmdType As CommandType, Optional ByVal parameters() As SqlParameter = Nothing) As DataSet
            Dim connection As SqlConnection = Nothing
            Dim command As SqlCommand = Nothing
            Dim sqlda As SqlDataAdapter = Nothing
            Dim res As New DataSet
            Try
                connection = New SqlConnection(_connectionString)
                command = New SqlCommand(cmd, connection)
                command.CommandType = cmdType
                AssignParameters(command, parameters)
                sqlda = New SqlDataAdapter(command)
                sqlda.Fill(res)
            Catch ex As Exception
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If Not (connection Is Nothing) Then connection.Dispose()
                If Not (command Is Nothing) Then command.Dispose()
                If Not (sqlda Is Nothing) Then sqlda.Dispose()
            End Try
            Return res
        End Function

#End Region

#Region " ExecuteDataset "

        ''' <summary>
        ''' Calls the respective INSERT, UPDATE, or DELETE statements for each inserted, updated, or deleted row in the System.Data.DataSet with the specified System.Data.DataTable name.
        ''' </summary>
        ''' <param name="insertCmd">A command used to insert new records into the data source.</param>
        ''' <param name="updateCmd">A command used to update records in the data source.</param>
        ''' <param name="deleteCmd">A command for deleting records from the data set.</param>
        ''' <param name="ds">The System.Data.DataSet to use to update the data source. </param>
        ''' <param name="srcTable">The name of the source table to use for table mapping.</param>
        ''' <returns>The number of rows successfully updated from the System.Data.DataSet.</returns>
        Public Function ExecuteDataset(ByVal insertCmd As SqlCommand, ByVal updateCmd As SqlCommand, ByVal deleteCmd As SqlCommand, ByVal ds As DataSet, ByVal srcTable As String) As Integer
            Dim connection As SqlConnection = Nothing
            Dim sqlda As SqlDataAdapter = Nothing
            Dim res As Integer = 0
            Try
                connection = New SqlConnection(_connectionString)
                sqlda = New SqlDataAdapter
                If Not (insertCmd Is Nothing) Then insertCmd.Connection = connection : sqlda.InsertCommand = insertCmd
                If Not (updateCmd Is Nothing) Then updateCmd.Connection = connection : sqlda.UpdateCommand = updateCmd
                If Not (deleteCmd Is Nothing) Then deleteCmd.Connection = connection : sqlda.DeleteCommand = deleteCmd
                res = sqlda.Update(ds, srcTable)
            Catch ex As Exception
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If Not (connection Is Nothing) Then connection.Dispose()
                If Not (insertCmd Is Nothing) Then insertCmd.Dispose()
                If Not (updateCmd Is Nothing) Then updateCmd.Dispose()
                If Not (deleteCmd Is Nothing) Then deleteCmd.Dispose()
                If Not (sqlda Is Nothing) Then sqlda.Dispose()
            End Try
            Return res
        End Function

#End Region

#Region " ExecuteScript "

        ''' <summary>
        ''' Executes a SQL query file against the connection.
        ''' </summary>
        ''' <param name="filename">SQL query file name.</param>
        ''' <param name="parameters">The parameters of the SQL query file.</param>
        Public Sub ExecuteScript(ByVal filename As String, Optional ByVal parameters() As SqlParameter = Nothing)
            Dim fStream As FileStream = Nothing
            Dim sReader As StreamReader = Nothing
            Dim connection As SqlConnection = Nothing
            Dim command As SqlCommand = Nothing
            Try
                fStream = New FileStream(filename, FileMode.Open, FileAccess.Read)
                sReader = New StreamReader(fStream)
                connection = New SqlConnection(ConnectionString)
                command = connection.CreateCommand()
                connection.Open()
                While (Not sReader.EndOfStream)
                    Dim sb As New StringBuilder
                    While (Not sReader.EndOfStream)
                        Dim s As String = sReader.ReadLine
                        If (Not String.IsNullOrEmpty(s)) AndAlso (s.ToUpper.Trim = "GO") Then
                            Exit While
                        End If
                        sb.AppendLine(s)
                    End While
                    command.CommandText = sb.ToString
                    command.CommandType = CommandType.Text
                    AssignParameters(command, parameters)
                    command.ExecuteNonQuery()
                End While
            Catch ex As Exception
                Throw New SqlDatabaseException(ex.Message, ex.InnerException)
            Finally
                If (Not IsNothing(connection)) AndAlso (connection.State = ConnectionState.Open) Then connection.Close()
                If (Not IsNothing(command)) Then command.Dispose()
                If (Not IsNothing(sReader)) Then sReader.Close()
                If (Not IsNothing(fStream)) Then fStream.Close()
            End Try
        End Sub

#End Region

    End Class

End Namespace

阅读全文(77) | 评论:0 | 复制链接

以另一个用户的名义运行某一进程(2012-5-2 20:50:00)

Start a Process as a Different User

To use this code please create a new module or add it to your existing project.

Function ConvertToSecureString(ByVal str As String)
        Dim password As New SecureString
        For Each c As Char In str.ToCharArray
            password.AppendChar(c)
        Next
        Return password
End Function
Sub Main()
       dim username as string = "Administrator"
       dim password as SecureString = ConvertToSecureString("my password")
       dim domain as string = Nothing
       dim filename as string = "notepad.exe" ' %SYSTEMROOT%\system32
        Try
            System.Diagnostics.Process.Start(filename,username, password, domain)
        Catch ex As Win32Exception
            MessageBox.Show("Wrong username or password.", _
		"Error logging in as administrator", MessageBoxButtons.OK, _
		 MessageBoxIcon.Error)
        End Try
End Sub

阅读全文(46) | 评论:0 | 复制链接

如何让软件在Vista系统上有管理员权限(2012-5-2 20:39:00)


Making a .NET app run on Vista with Administrator priviledges

By James Crowley, published on 06 Aug 2008 | Filed in
If you're targeting Windows Vista and your application requires administrator priviledges (such as accessing the program files directory), then it will fail unless you include a manifest so Windows knows. Fortunately, it's very simple.

First, add a manifest file to the root of your .NET executable project - you can do this by selecting "Add New Item" and picking "Application Manifest File". Alternatively just create a blank file called "app.manifest". The standard template that Visual Studio gives you looks like this:
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" 
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
    <security>
      <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
        <!-- UAC Manifest Options
            If you want to change the Windows User Account Control level replace the 
            requestedExecutionLevel node with one of the following.

        <requestedExecutionLevel  level="asInvoker" uiAccess="false" />
        <requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />
        <requestedExecutionLevel  level="highestAvailable" uiAccess="false" />

            If you want to utilize File and Registry Virtualization for backward 
            compatibility then delete the requestedExecutionLevel node.
        -->  <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges>
    </security>
  </trustInfo>
</asmv1:assembly>

The key part is the requestedExecutionLevel mode - which by default is set to "asInvoker". This means that the application will run under the priviledges of whoever started the application - remembering that under UAC even administrators on windows Vista are running as Users until they elevate their permissions.

If your application always requires administrator priviledges, then you can change this value to "requireAdministrator". Now, whenever your application starts it will always trigger UAC and ask the user to allow administrator access for your program.

Note that it's worth seriously considering whether you actually need to do this, and why - especially given the intrusive experience of UAC. For instance, you shouldn't need to write settings to Program Files, as Windows provides the user profile area and registry for just that purpose. In general UAC should hopefully force us all to think a bit more carefully about where we're storing data, and what permissions the application *really* needs.


阅读全文(50) | 评论:0 | 复制链接

如何让编写的软件适应win 7之类的系统换代(2012-5-2 20:14:00)

当新的操作系统应用时,苦逼的程序员首先想到的,可能是自己的软件能否兼容新系统。如果不想花时间修改软件,那么最好花时间来改善软件。

Enhancing your applications for Windows 7

As developers, we often shriek a little whenever a new operating system is released. Our most common question is “will my application run?” Enhancements in compatibility have made this question a mute point. Now that we don’t have to spend as much time fixing our applications, we can concentrate on improving them.

As .NET developers, we have often been left behind on major operating system integrations, unless we were willing to dive into unmanaged code. Windows 7 brings new salvation for .NET developers, and it comes in the form of the Windows 7 API Code Pack for the.NET Framework.

You can download the Windows API Code Pack for Microsoft .NET Framework athttp://code.msdn.microsoft.com/WindowsAPICodePack.

The Code Pack provides .NET developers a library exposing some of the new functionality in Windows 7, as well as features available in previous versions of Windows.

In this article, we’ll explore some of the functionality provided to you in the Code Pack, as well as offer suggestions as to how these features can help you enhance your Windows 7 applications. Every feature discussed is easily programmable using the Code Pack. This article will not provide any source code about any particular feature, but the Code Pack comes with fantastic documentation and several full source examples for each feature.

Taskbar

One of the many immediately recognizable new features of Windows 7 is the enhanced Taskbar. This is a major step since we haven’t seen a radical change in the taskbar since its introduction in Windows 95. The redesigned taskbar gives us several new features that are easily programmable through the Windows API Code pack.

Jump Lists

Jump Lists give you the ability to provide your users with a list of common tasks and commonly accessed files. For example, Internet Explorer will provide you with a list of frequently accessed web pages. Additionally, it’ll allow you to quickly open a new tab or start an InPrivate browsing session.

jump lists

Taskbar Thumbnail Tabbed Preview

Thumbnail Tabbed Previews expand the existing functionality you get by running your application on Windows 7. By default, the operating system will display a thumbnail of your application running whenever you mouse over it on the taskbar. This provides the user with instant feedback on what your application currently looks like. It might be nice to provide additional previews to your customers. For example, in Internet Explorer, you are provided a preview for each tab you currently have open within the browser. This allows users to quickly navigate to the tab of choice instead of restoring Internet Explorer and selecting a tab.

IE open with multiple tabs in taskbar

Taskbar Thumbnail Toolbar

In addition to displaying a preview for your application, it might be beneficial to give the user tasks that can be performed from the preview itself. This functionality is separate from jump lists, since thumbnail previews are available only when the application is running. For example, Windows Media Player gives Windows 7 users the ability to pause and skip tracks from within the thumbnail.

WMP taskbar thumbnail image

Progress Bars

The new progress bar functionality for the Taskbar gives you the ability to show progress from within your application’s icon on the taskbar. Easily sync any progress bar within your application to the progress bar on the taskbar, and provide your users instant feedback. This functionality gives your users the information they care about without having to bring your application into focus.

IE Progress Bar

Icon Overlays

Windows 7 provides you with one additional taskbar feature in order to give you feedback without having to open the application. Icon Overlays allow you to add an icon to the bottom right hand corner of your application’s taskbar icon.

A great example of this feature is Windows Live Messenger. When you log into Messenger, it’ll provide you with an icon reflecting your present status. If you are online and available, Messenger will display a green “online” icon.

Messenger Icon Overlays

Libraries and Known Folders

Libraries are a new feature to Windows 7 that allows you to have virtual folders containing certain file types. These libraries allow you to browse common file types, such as documents, music, videos, and pictures in one folder instead of having to browse to several folders. These file types are presented in a virtual view (or a Library).

Known folders are not a new concept to Windows. I’ve heard the term “special folders” used in previous versions of Windows. Consider a case where you want to write to the user’s Start menu, but are not sure where the folder for the Start menu is located. For standard users, the path can be inferred. However, we never want to trust Windows to create all user paths equal. A best practice is to ask the operating system where the path resides. The Code Pack gives you easy access to all these types of folders, including Libraries.

Search API

Search has become a large part of our operating system experience. From our Start menu, we can quickly search our programs, documents, music, photos, and emails. Why shouldn’t we be able to take that power and implement it in our applications? The Search API gives us the ability to: search any folder on the hard drive; add conditions such as date range and keywords; get deeper into the files and search meta tags, such as author, title, and comments.

Common File Dialogs

The idea of common file dialogs is nothing new. If you’ve used an OpenFileDialog before, you’ve used the older version of a common file dialog. In Windows 7, we can take advantage of its new common file dialogs. The code pack can help you take advantage of these dialogs with little hassle.

The new common file dialogs will allow you to easily set the dialog to known folders, libraries, and various saved searches. You can even direct the user to search connectors, such as Microsoft Outlook. A search connector is a component that allows you to search applications and services within Windows. Examples of search connectors include Outlook, Amazon, PriceGrabber, and YouTube. Applications can use these connectors to search the services. Take advantage of the new power, while keeping the common look and feel of Windows 7.

Common file dialog

Task Dialogs

Message boxes are a wonderful tool that many of us use all time. The main problem with message boxes is that they are difficult to customize for various situations.

Task dialogs were introduced in Vista, and they perform similarly to message boxes, except that they provide additional parameters that are controlled by the developer. These parameters can include progress bars, radio buttons, custom buttons, expandable sections, footers, and even options such as “Don’t show this dialog again” prompts. Use task dialogs to provide additional message box functionality without having to build your own.

Task dialogs

DirectX

DirectX has been around for a long time. Debuting with Windows 95, DirectX took care of multimedia tasks that normally had to be custom written. The downside, especially for .NET developers, was that DirectX libraries were entirely unmanaged. The Windows 7 API Code Pack provides managed support for DirectX. As of this writing, the code pack supports DirectX 10.0, 10.1, 11.0, Direct2D, DirectWrite, DirectX Graphics Infrastructure (DXGI), and Windows Imaging Components (WIC). If you’ve ever been interested in looking at DirectX, but the lack of managed APIs have stopped you, then you should give the code pack a look.

Sensors Platform API

Sensors are an amazing addition to Windows 7. They give you the ability to change your application depending on your current environment. This means that your application can react to changes in light or acceleration. Imagine building an application with two color schemes, one for low light and another for higher ambient light. Using a light sensor, you can automatically switch between the two, thus making your application usable in any situation. If you’ve used a GPS, you’ve seen this type of feature in action.

Location sensors can detect your location via GPS. This is an opt-in technology, meaning that your users have to turn the feature on before your application can support it. After it’s turned on, your application can easily identify your location.

Extended Linguistic Services API

In a perfect world, our applications would only have to work in one language. However, in the expanding technological world, we need to be able take our applications into other cultures.

Windows 7 introduces Extended Linguistic Services APIs to assist you in this task. You can adapt your application based on the user’s location and culture preferences. Give users all over the world a natural user experience without a lot of additional overhead.

Power Management API

Tracking the power source of the host machine might be important in the application you’re developing, and the Code Pack gives you a framework for managing and polling several aspects of the machine’s power supply. The Power Management APIs provide you information about the power source being used. If you are using a laptop or tablet, you can get feedback such as battery health, current charge, and charge rate.

The API also allows you to track power events easily. Subscribe to a variety of events, such as: system busy, battery life change, power source change, and monitor on or off.

Application Restart and Recovery API

Our applications aren’t perfect, and our users are prone to finding even the smallest bugs in our code. How many times have you been working in Word only to have it crash right before you were about to save? If you restart Word, it automatically detected that there was a crash, and offers to open a cached copy of the document for you. The Application Restart and Recovery API gives your application the same functionality. You can save the user’s state on a regular period, and gracefully recover from application crashes.

Network List Manager API

It might be necessary in your application to have knowledge about the machine’s internet connection. Basic questions, such as “Is my machine connected to the internet” and “What connection is my machine using to connect to the network?” can easily be answered with the Network List Manager API.

Conclusion

I hope this overview has given you enough insight into the power and flexibly available to you in the Windows 7 API Code Pack. I encourage every developer to go and download it today. Start taking advantage of the features, and enhance your application!


阅读全文(67) | 评论:0 | 复制链接

清空垃圾箱的代码(2012-5-2 20:05:00)

http://www.developerfusion.com/code/5673/how-to-empty-a-recycle-bin/

貌似能够清空回收站,清空时不会提示用户确认,而且没有提示音和进度条。

Usage: 
Dim Action As New ShellActions 
Action.EmptyRecycleBin() 

Public Class ShellActions 

Shared Function _ 
SHEmptyRecycleBin(ByVal hWnd As Integer, ByVal pszRootPath As String, _ 
ByVal dwFlags As Integer) As Integer 
End Function 

Sub New() 
EmptyRecycleBin() 
End Sub 

Sub EmptyRecycleBin(Optional ByVal rootPath As String = "", _ 
Optional ByVal noConfirmation As Boolean = True, Optional ByVal NoProgress _ 
As Boolean = True, Optional ByVal NoSound As Boolean = True) 

Const SHERB_NOCONFIRMATION = &H1 
Const SHERB_NOPROGRESSUI = &H2 
Const SHERB_NOSOUND = &H4 

If rootPath.Length > 0 AndAlso rootPath.Substring(1, 2) <> ":\" Then 
rootPath = rootPath.Substring(0, 1) & ":\" 
End If 

Dim flags As Integer = (noConfirmation And SHERB_NOCONFIRMATION) Or _ 
(NoProgress And SHERB_NOPROGRESSUI) Or (NoSound And SHERB_NOSOUND) 
SHEmptyRecycleBin(0, rootPath, flags) 

End Sub


阅读全文(60) | 评论:0 | 复制链接

早上半个多小时学习F#(2012-1-15 9:50:00)

简单搜集了这方面的资料

保存下来

希望对大家有用


阅读全文(271) | 评论:0 | 复制链接

F#介绍及与VB.NET等语言的区别与联系(英文)(2012-1-15 9:44:00)

http://www.developerfusion.com/article/122079/intro-to-f-programming/

Intro to F# Programming

Microsoft took something of a risk with the Visual Studio 2010 release, adding the first brand-new “out of the box” language to the .NET ecosystem since its introduction of C# back in 2001. I speak, of course, of VisualF#, an object/functional hybrid language based on work done by Microsoft Research. Providing both leading-edge functional programming and familiar object-oriented capabilities, F# gives the developer more power to solve problems -- all in fewer lines of code, thanks to succinct syntax.

For developers who have never looked at F# before (a vast majority of the .NET developer population), this article takes a quick glance at the F# syntax. I want to give the F# neophyte enough familiarity with the language to understand code samples in other, more in-depth, articles. Full disclosure dictates, however, that F# is a rich and powerful language, and cannot be explained in all nuance in a single article. The concepts and reasoning of functional programming can only be hinted at in this space; more articles and demonstrations of the power of the “functional” side of F#’s “object/functional hybrid” nature will appear on this website in the weeks and months to come. Welcome to the tip of the iceberg.

Basics

Consider the following F# program, which I created using the Visual Studio “F# Application” project template that ships with Visual Studio 2010:

let msg = "Hello, F#!" System.Console.WriteLine(msg)

Now ponder the following truths about F#:

.NET Accessibility. Observe that F# is capable of calling into the .NET Base Class Library (BCL), as evidenced by the above use of the Console class. F# comes with its own libraries, of course (including the “printfn” function, which serves much the same purpose as WriteLine), but has full access to the entirety of the BCL, including WPFWCF, Workflow, System.Xml, System.Web, and any other assembly accessible to C# or VB.NET. Referencing them uses the same “Add Reference” mechanism in Visual Studio as any other .NET project.

.NET Assembly. Careful inspection of the compiled artifact, using a tool such as Reflector or ILDasm, reveals that the F# compiler produces an executable assembly similar to those produced by C# or VB.NET. Although the structure of the IL generated from the F# compiler looks somewhat different from that produced by the C# or VB.NET compilers, it is still IL, and therefore any library produced by F# is fully accessible from C# or VB.NET. There is even an F# Library project template in Visual Studio. In formal .NET terminology, this and the previous paragraph mean that F# is a “CLI Consumer” (able to consume other CLI assemblies) and a “CLI Producer” (able to produce assemblies for use by other CLI languages).

Strongly-typed, type inferenced. F# is strongly-typed, like C#, meaning that attempts to use the “msg” value defined above as an integer will yield compiler errors—the compiler knows that “msg” must be a string (a System.String, to be precise) because it infers that from the right-hand side of the assignment. However, unlike C# which can only do this for “var”-declared local variables, F# can do this type inference throughout the entire program (with some exceptions), which has the nice effect of reducing the amount of code that must be typed and read without losing the benefits of strong type-checking at compile-time.

Top-level declarations. Contrary to the C# equivalent of this program, F# does not require the programmer to define a class and a static Main() method to serve as the entry point, although it is perfectly happy to allow the developer to do exactly that. F# by default assumes that the first line in the file is to be executed, and unless told otherwise will do precisely that. More importantly, F# permits top-level declarations of values without requiring a class surrounding it, which can simplify certain code scenarios immensely.

(To be strictly correct, F# then takes the code written above and enfolds it inside a class-like declaration that matches the name of the file, so technically this code is inside a class named “Program”, matching the default “Program.fs” filename. This is of interest to those looking to call F# from C#.)

More Basics

A “Hello world” program, regardless of how familiar, offers only limited analysis opportunities. A slightly more complex program, which provides dietary advice while illustrating arrays and looping:

let quantityOfCookies = [| 1; 2; 3; 4; 5; 0; |]
let zero = quantityOfCookies.[5]
quantityOfCookies.[5] <- 6for i in quantityOfCookies do if i = zero then
  printfn "Good job. Eat healthy food." else if i = 1 then
  printfn "One cookie won’t hurt you." else if i = 2 then
  printfn "Two cookies is a nice snack." else   printfn "%d cookies is too many!" i

This is clearly an equally trivial program, but demonstrates several things at once:

Arrays. Arrays are available in F# (though generally discouraged in favor of lists, which will be seen next), and behave much as they do in C# or VB.NET, although the declaration is different, as seen in “quantityOfCookies”. Accessing the array is also slightly different from C# or VB.NET, in that the “.” must precede the square brackets. In keeping with the CLR platform as a whole, arrays are 0-indexed, as they are in C#.

Decision-making. A single equals (“=”) is used to test for equality in if/then branching, not the double-equals (“==”) familiar to C# programmer. Assignment in F# is done using the leftward-arrow operator, as seen above where the sixth element of quantityOfCookies is set to the value 6. (This ability to in-place modify the contents of the array is part of the reason arrays are discouraged in F#, and in functional languages as a whole. It is considered bad form to change the value in a functional language, leading many to refer to this kind of assignment as a destructive update.)

Looping. F# supports while and do/while loops, just as it does the “for” loop demonstrated above. As can be seen, one form of the “for” loop is to iterate across the contents of an array or list or other collection, just as “foreach … in” does in C#. Other forms are available, including straight integer range steps, but this is one of the more common idioms using it.

Indentation-sensitivity. The F# language does not use any sort of paired parentheses or “begin”/”end” markers to denote blocks of code; instead, it is indentation-sensitive, in that the indentation of code offers declaration of what code is in which nested block, much like the scripting language Python. Visual Studio will assist with ensuring the indentation is correct, but in general F#ers feel that the indentation becomes intuitive and falls away from notice before long.

We can tighten up our dietary advice program using a powerful feature of F# known as pattern-matching. It is well beyond the scope of this article to describe pattern-matching in its complete depth, but the following example gives some idea of its use:

for i in [1 .. 6] do     let printMsg = 
        match i with
        | 0 -> "Good job. Eat healthy food."         | 1 -> "One cookie won’t hurt you."         | 2 -> "Two cookies are a nice snack."         | n when n > 2 -> n.ToString() + " cookies are way too many!"         | _ -> failwith("Number out of expected range!")
    printfn "%s" printMsg

The pattern-match construct, on the surface, looks vaguely similar to a switch/case from C#, in that we see a test condition on the left, with a body of code on the right, separated by an arrow. Multiple match clauses are separated by a vertical pipe (“|”). However, pattern-matching can also bind the value being matched (“i”, each time through the loop) into a local value for use in the body of the match clause, such as the use of “n.ToString()” in the second-to-last clause. In that same clause we also see a guard condition: because we don’t want to match negative values, an added “when” clause restricts the match to “n” only when “n” is greater than 3. When present, a guard condition must be met in order for the match to occur. Finally, matching against “_” is a wildcard match, much as “default” works in C#. Note also that a pattern-match is an expression, and thus yields a value (the string constants declared in the body of the match clauses), allowing it to be assigned to the local “printMsg” value for later use. In the last/default case, the “failwith” expression generates a standard .NET exception.

Pattern-matching is used far and wide within F# code, and any student of the language would be well-advised to be comfortable with its myriad capabilities.

Objects

From its inception, F# embraced the “object-oriented” in its nature as much as its functional abilities. This means it can define classes that are every bit as “O-O”-ish as their C# counterparts. Defining a class type in F# can, in many cases, be even shorter than C# equivalents, owing to the type-inference and indentation-sensitivity. A class representing an individual human and the derived subtype describing a student of some educational facility looks like the following:

type Person(first : string, last : string, age : int) =
    member p.FirstName = first
    member p.LastName = last
    member p.Age = age
    member p.SayHowdy() =
        System.Console.WriteLine("{0} says, 'Howdy, all'!", p.FirstName)
    override p.ToString() =
        System.String.Format("[Person: first={0}, last={1}, age={2}]",
            p.FirstName, p.LastName, age)
let jess = new Person("Jessica", "Kerr", 33)
jess.SayHowdy()
type Student(first : string, last : string, age : int, subj : string) =
    inherit Person(first, last, age)
    let mutable subject = subj
    member s.Subject
        with get() = subject
        and set(value : string) = subject <- value     override s.ToString() =
        System.String.Format("[Student: subject={0} {1}]", 
            s.Subject, base.ToString())
    member s.DrinkBeer(?amt : int, ?kind : string) =
        let beers = match amt with Some(beers) -> beers | None -> 12
        let brand = match kind with Some(name) -> name | None -> "Keystone"         System.Console.WriteLine("{0} drank {1} {2}s!",
            s.FirstName, beers, brand)
let brian = new Student("Brian", "Randell", 42, "VB.NET")
brian.DrinkBeer(6)
brian.DrinkBeer(12, "Coors")
brian.DrinkBeer()

The structure of the code is similar in many ways to C# or VB.NET: a type name encloses a series of declarations of members on the type. F# does this without explicit begin/end brackets, using its indentation-sensitive syntax to denote scope. Other differences:

Constructors. Unlike C# or VB.NET which require a constructor to be defined as an explicit member within the class, F# uses the type-declaration line to declare the primary constructor, the one intended to represent the principal means of construction for this class. Any other constructors can be declared inside the class, but will defer to the primary for the actual work, as is generally the case for C# and VB.NET classes, as well.

Immutability. As is common with languages that derive, even in part, from functional roots, F# believes strongly in the notion of “immutability”, that once a value has been set, it cannot be changed. This is true both in the local variables “jess” and “brian”, as well as in the default declarations within the class Student. In fact, to help signify this notion of immutability, “jess” and “brian” will often be referred to as local “values”, not “variables”, since their values cannot vary. (These are also sometimes called “bindings” or “value bindings”, as in “binding a name to a value”.) When working with mutable values, assigning a new value requires use of the “left-arrow” operator, written as “<-“. This is sometimes called the “destructive update” operator (mentioned earlier), both since it “destroys” the value stored there previously and because the negative connotations remind programmers that immutability is better than mutability.

Members. F# uses the “member” keyword to denote the declaration of a public member—method, field, property, event, and so on —as opposed to the “let” syntax to declare an internally-accessible field. All the properties declared in Person are read-only, in keeping with F#’s “immutable values by default” mantra. A read-write property is demonstrated in Student: declare the property using the extended “with get() = … and set() = …” syntax to define get and set method blocks. In addition, since writable properties generally require mutable places to which to write the new values, declaration of the mutable “subject” field is also required. Again we see the destructive update syntax “<-“ to do the assignment. Only fields declared with “let” can be mutable, and these are always private to the type. Members are public unless declared private.

Self-identifiers. Glance at the member declarations: each has a “prefix” in front of the member name (such as “p.FirstName” in Person). This is the “self-identifier” -- the F# developer to choose the name of the reference to object upon which the method is being invoked. For the C#-trained, it may be helpful to write the Person definition of ToString() slightly more C#-ish:

    override this.ToString() =
        System.String.Format("[Person: first={0}, last={1}, age={2}]",
            this.FirstName, this.LastName, age)

Note that the self-identifier has access to all the members of Person; access to private fields or constructor parameters such as “age” is direct. The choice of name for the self-identifier is entirely up to the F# developer; consistency here, however, will be vital to long-term maintenance.

Inheritance. The Student type inherits from the Person class in the same way as any other C# or VB.NET class, by declaring the primary constructor on the “type” line and then using the “inherit” keyword to specify the type from which it inherits (Person). Note that in keeping with traditional inheritance rules in .NET, some constructor on the base type must be invoked, which is done by passing the parameters to the base type declaration. Note as well that to override base-class members, the “override” keyword is required.

Optional parameters. One of the things F# introduces is the ability to supply defaults to method parameters, using the “?” prefix to the parameter name. This means the parameter type is actually an “option” type, which is either a Some() of some value, or None, both of which can be pattern-matched using the syntax above. We’ll look at this in a future article.

Functions

The parts of F# unfamiliar to most .NET developers center on its functional language nature, meaning that functions—executable code—are first-class values alongside data elements. This means that they can be defined inline, defined in terms of other functions (known in some circles as “partial application” of functions), and passed to other functions for execution inside a particular context (the latter of which is known in academic circles as “higher-order functions”). Developers familiar with LINQ will already be familiar with this concept to some degree—“lambdas” are frequently passed in to LINQ expressions, making LINQ itself a series of higher-order functions.

For an example of this style of programming, consider a collection of Person objects defined above:

let people = [ new Person("Ted", "Neward", 40);
               new Person("Charlotte", "Neward", 39);
               new Person("Jessica", "Kerr", 33);
               new Person("Ken", "Sipe", 42);
               new Person("Mark", "Richards", 45);
               new Person("Michael", "Neward", 17); ]

In itself, this list of Persons (formally declared in F# as a “Person list”, as hovering over the declaration of “people” in Visual Studio will reveal) is interesting, but often programmers will want to deduce things about this particular group of people. For example, the goal may be to determine what the total age of all these people, as perhaps as the first step towards determining the average age. While this can easily be done using a “for” loop, as in:

let mutable totalAge1 = 0for p in people do     totalAge1 <- totalAge1 + p.Age;

… it turns out that this is a common operation to do. Writing this “for” loop over and over again is a violation of the Don’t Repeat Yourself principle -- not so much the actual summation of everybody’s age, but the deeper operation: that of iterating across a list and performing some operation on each element in the list, obtaining a result, and combining that with prior results. Abstracting this is a good example of how functional programmers think. We can separate the loop from the details of the operation inside:

let addAges (currentTotal : int) (p : Person) = currentTotal + p.Age
let iterateAndSum (people : Person list) =
    let mutable totalAge = 0
    for p in people do         totalAge <- addAges totalAge p
    totalAge

Notice a concise syntax: the last expression in a method determines the return value. Next, the actual “work” done of obtaining the result will need to be varied across each particular usage. The addAges function becomes an argument to a function that can do more than just sum:

let addAges (currentTotal : int) (p : Person) = currentTotal + p.Age
let iterateAndOperate (people : Person list) (op: int -> Person -> int) =
    let mutable totalAge = 0
    for p in people do         totalAge <- op totalAge p
    totalAge
let totalAge2 = iterateAndOperate people addAges

When the parameter “op” is passed in, it provides the behavior to execute to add the ages together (in the above example). The declaration for “op” is a bit strange, owing to F#’s history, but should be read as “a function taking an int and a Person a yielding an int result”. We now have an example of a higher-order function, because it takes a function to execute as part of its execution—iterateAndOperate takes the “op” function (“addAges” in the above case) as the operation to perform as necessary during the execution of iterateAndOperate.

With this, we can perform any cumulative integer operation on a Person. But why should this operate only on Persons? and only output an integer? If the iterateAndOperate function is written to take any generic type, as well as any appropriate starting value, it looks like:

let iterateAndOperate (seed : 'b) (coll : 'a list) (op : ('b -> 'a -> 'b)) =
    let mutable total = seed
    for it in coll do         total <- op total it
    total
let totalAge2 = iterateAndOperate 0 people addAges

Several things are happening here. First, the explicit declarations of “int” and “Person” have been replaced bytype parameters “’a” and “’b”, which make this function a generic function, able to take a list of any type “a” and iterate-and-operate on it to produce a single value of type “b”. Then, because the starting value, which was 0 in the earlier form, can’t always be assumed to be 0 (particularly since the ‘b type may not even be numeric), that gets passed in as well.

In itself, this hardly seems remarkable. After all, most business programming doesn’t really involve adding up numbers like this, or if it does, it uses Excel to do it. Most business programming does things far more difficult, like transforming a collection of model objects into XML for transmission across the wire as part of a Web services call, for example.

…which, as it turns out, is exactly what iterateAndOperate is capable of doing, as well:

let personToXML (currentXML : string) (p : Person) =
    currentXML + "<person>" + p.FirstName + "</person>" let peopleXML = (iterateAndOperate "<people>" people personToXML) + "</people>" System.Console.WriteLine(peopleXML)

…or, as it is more commonly written, passing in an anonymous function at the point of call:

let peopleXML2 = 
    (iterateAndOperate "<people>" people 
        (fun (curr : string) (p : Person) -> 
            curr + "<person>" + p.FirstName + "</person>")) + "</people>" System.Console.WriteLine(peopleXML2)

Note the use of the “fun” keyword (leading many F# speakers to note how F# has put the “fun” back into programming) to define a function inline, which can be useful in a number of cases where the function being defined will only be used once. Naturally, if a particular snippet of code is in fact written more than once, it can (and should) be given a name and reused across several points of use, rather than defined multiple times.

This particular example is so common that it has its own built-in function. “fold” does exactly what the iterateAndOperate function defined above does:

let peopleXML3 = (List.fold (fun (curr) (p : Person) -> 
                        curr + "<person>" + p.FirstName + "</person>") 
                    "<people>" people) + "</people>" System.Console.WriteLine(peopleXML3)

There are many other things that can be done with functions besides passing them as arguments. If this sounds fun to you, look up partial application and currying.

Summary

In this article, F#’s class definitions, branching and decision-making, pattern-matching, and higher-order function capability have seen the stage, but many other features remain as yet unrevealed. It is the hope of this author that there has been enough here to entice the curious (and the unwary) into spending more time investigating those depths.

F# is a language that embraces multiple language design paradigms within its borders, and attempts to pigeonhole it as a solely-functional or solely-object language will blind potential users to much of its power. Anything that can be done in C# can be done in F#, and work done in F# can be called from C#. The concise syntax is appealing, and great power lies in the function-manipulation capabilities available nowhere else in .NET. While it is certainly possible to use F# to write object-only programs, and while that may be a good path by which to get started learning F#, the greatest benefit will arise from using it the way it was intended: as a mix of the best of both the object-oriented and functional programming worlds.

Jessica Kerr is a consultant with Daugherty Business Solutions in St. Louis, MO. She forges programming and editing skills into a blade which impales bugs and slices run-on sentences into small pieces.

Ted Neward is an Architectural Consultant with Neudesic LLC, the co-author of “Professional F# 2.0” (Wrox), and believes that “he who knows the most programming languages, wins”. Or something like that. Visit his website at http://www.tedneward.com for more details on his activities, or read his blog athttp://blogs.tedneward.com. He resides in Redmond, WA, with his wife, two sons, two cats, one dog, and eight laptops.


阅读全文(612) | 评论:0 | 复制链接

F#编译器和核心库开放源代码(2012-1-15 9:43:00)

F# compiler and core library now open source

Early this morning Microsoft made available a package of compiler and library source code for F#. F# is a functional programming language that runs on the .NET framework. While it is not as mature as C# or VB, the languages more often used to build for .NET, it has progressed quickly and is extremely popular in development environments where functional paradigms have been traditional, for example banking and finance.

The release has been made available through the F# Power Pack, an open-source CodePlex project that previously solely hosted a set of additional libraries for F#. These additional libraries provide features such as LINQ library support; an HTML documentation generator; lexer and parser helpers; and other features for migrating between F# versions. The additions to the project are the code from the F# compiler (for the latest version of F#, 2.0) and the code for the core libraries. Binaries for these are not included, but are still freely available from the official F# website. The main F# release cycle coincides with Visual Studio versions, and Don Syme of the F# team says that new releases of the code will be made available to coincide with future service packs and versions of Visual Studio.

The code itself is on the F# CodePlex project, or you can download binaries from the F# site; more on the release on the F# blog.


阅读全文(485) | 评论:0 | 复制链接

F# 程式設計入門 (2)(2012-1-15 9:41:00)

作者:蔡學鏞

由於 F# 是相當新的技術,目前的學習資源不多。如果你想好好學習 F#,就必須閱讀 APress 出版的《Expert F#》,這是一本好書。我的許多解說範例取自這本書。

第一個範例程式

讓我們先來看一個例子。

#light
// 分析字串內重複出現的字
let wordCount text =
    let words = String.split [' '] text
    let wordSet = Set.of_list words
    let nWords = words.Length
    let nDups = words.Length - wordSet.Count
    (nWords,nDups)

let showWordCount text =
    let nWords,nDups = wordCount text
    printfn "--> %d words in the text" nWords
    printfn "--> %d duplicate words" nDups

利用 F# 的互動環境(fsi.exe),輸入上面的程式,會得到下面的結果:

val wordCount : string -> int * int
val showWordCount : string -> unit

每個值都有型別

意思是,定義了兩個值(val),分別是 wordCount 與 showWordCount,它們的型別分別是「string -> int * int」與「string -> unit」。「string -> int * int」意思是:需要一個字串(string)當參數,傳出值是兩個 int 所組成的值組(tuple)。「string -> int * int」意思是:需要一個字串(string)當參數,沒有傳出值。unit 型別相當於許多編程語言的 void。

利 用 let 定義 wordCount 時,在等號前面有出現「text」,這就是字串參數;而 wordCount 定義的最後,有出現「(nWords,nDups)」,這就是傳出值,它是兩個整數(int)所組成的值組(值組的每個元素用英文逗號隔開)。定義函數的 時候,最後一個出現的值,就是傳出值。

F# 內的每個值都有型別(type),可能是 int、string 或其他,當型別中出現箭頭(->)的時候(例如上面的 wordCount),表示這個值是一個函數,箭頭左邊的是參數的型別,箭頭右邊的是傳出值的型別。如果型別中出現兩個以上的箭頭,則箭頭所隔開的每個部 分都是參數的型別,只有最右邊的是傳出值的型別,例如:「string -> int -> unit」,表示此函數的參數有兩個,分別是字串與整數,且沒有傳出值。

型別推論

你 可能會覺得奇怪,我們並未宣告 text 的型別為字串,也沒有宣告 nWords 與 nDups 的型別為整數,為何 F# 會知道它們的型別?這就是型別推論(Type Inference) -- 根據程式的內容推斷出型別。text 被當作 String.split 的第二個參數,而這個參數必須是字串,所以 F# 知道 text 是字串。而 nWords 是字串的 Length 傳回值,這個傳回值必須是 int,所以 nWords 的型別是 int。而 nDups 是兩個 int 相減的結果,自然也是 int。

透過 Type Inference,我們寫程式時不需要宣告型別,相當方便。但是某些地方,F# 如果無法順利進行 Type Inference,我們就必須標明型別,否則編譯器或互動環境會提出警告。

用 let 進行繫結

利 用 let 的語法,可以將一個名稱繫結(bind)到一個值。例如「let a = 1」表示把 a 繫結到 1。上面的例子中,我們做了七次的 let 繫結。其中只有 wordCount、showWordCount 是全域的(global),其他都是區域的(local)。因此你可以在外面使用 wordCount、showWordCount,卻不能在外面使用 words、wordSet。

要注意繫結的先後次序,例如 wordSet 有用到 words,所以要先繫結 words,再繫結 wordSet。showWordCount 有用到 wordCount,所以要先定義 wordCount,在定義 showWordCount。

特別值得注意「let nWords,nDups = wordCount text」,等號左邊是一個名稱值組,而不是一個名稱,這表示會進行模式比對(Pattern Matching),比對成功才繫結。例如「let a, b = 1, 2」,就會將 a 設定為 1,且將 b 設定為 2。

透過 #light 簡化語法

為 何 wordCount 是全域的,但 words、wordSet、nWords、nDups 卻是區域的,從上面的程式碼中不難看出,因為區域的定義都有內縮。事實上,因為程式一開始有加上 #light,表示內縮也是語法的一部份。如果沒寫 #light,那麼程式內縮也沒用,而是必須明確地在 let 敘述的最後加上「in」,才表示是區域的定義。以上面的例子來說,就是這樣:

// 沒有 #light,下面的例子內縮也沒有用,且一定要加上 in
let wordCount text =
let words = String.split [' '] text in
let wordSet = Set.of_list words in
let nWords = words.Length in
let nDups = words.Length - wordSet.Count in
(nWords,nDups)

當然我們會比較偏向於使用 #light 的方式。使用 #light,除了 in 之外,還有「;;」、done、begin、end 等關鍵字,都可以省略不寫,而是透過內縮表示它們之間的關係。

呼叫函數與 Property

這個範例中,我們呼叫了 String 模組(module)的 split 函數、Set 模組的 of_list 函數、還有 printfn 函數;我們也使用到 String 物件的 Length property 與 Set 物件的 Count property。不管是取用模組內的函數,或物件內的成員,都是利用「.」。

呼叫函數時,如果需要傳入參數,不需要寫括號,而是直接把參數寫在後面。例如「f a b」,表示呼叫 f,傳入「兩個」參數,a 當作第一個參數, b 當作第二個參數。如果你寫成「f (a, b)」,那麼就變成,呼叫 f,傳入「一個」參數,這是「由 a 與 b 構成的」值組。兩者的意義不同。

printfn 是格式化的輸出字串,並換行。C 語言的使用者應該相當熟悉它。

值組與清單

將元素用逗號隔開,組成的資料結構,就是值組(tuple),例如 (nWords,nDups),型別為 int * int。事實上,值組內部的元素可以是不同的型別,例如 (1, "F#") 是個值組,其型別為 int * string。

F# 也很常使用清單(list),清單內的元素型別必須一致,且使用分號隔開,前後使用方括號,例如 [ 2; 3; 5; 7; 11],型別是 int list 也可以寫成 list。本範例中,當作 String.split 第一個參數的 [' '] 其實正是一個 list,只不過它只有一個元素(空白字元' '),如果是空的清單則寫成 [ ]。

第二個範例程式

接下來看第二個範例程式:

open System.Windows.Forms

let form = new Form(Visible=true,TopMost=true,Text="Welcome to F#")
let textB = new RichTextBox(Dock=DockStyle.Fill, Text="Here is some initial text")
form.Controls.Add(textB)

open System.IO
open System.Net

// 取得網頁內容
let http(url: string) =
    let req = System.Net.WebRequest.Create(url)
    let resp = req.GetResponse()
    let stream = resp.GetResponseStream()
    let reader = new StreamReader(stream)
    let html = reader.ReadToEnd()
    resp.Close()
    html

textB.Text <- http("http://news.bbc.co.uk")

使用 .NET API

這個範例中,我們使用到 .NET 的 WinForms、I/O、Networking API。再用到這些 API 之前,我們先透過 open,表示要使用它,這樣可以讓我們不用寫全名(full-qualified)。

open System.Windows.Forms
open System.IO
open System.Net

產生物件

產生物件一樣是透過 new 關鍵字來達成。如下所示:

let form = new Form(Visible=true,TopMost=true,Text="Welcome to F#")

從上面的例子來看,你可能以為 Form 類別有定義一個建構子(constructor),此建構子具有三個參數,分別是 boolean 表示是否 Visible、boolean 表示是否 TopMost、以及標題文字。如果你去查一下 Form 的說明文件,會找不到這樣的建構子。既然如此,上面又怎麼能這麼建構出 Form 物件呢?

從上面的例子來看,你可能以為 Form 類別有定義一個建構子(constructor),此建構子具有三個參數,分別是 boolean 表示是否 Visible、boolean 表示是否 TopMost、以及標題文字。如果你去查一下 Form 的說明文件,會找不到這樣的建構子。既然如此,上面又怎麼能這麼建構出 Form 物件呢?

let form = new Form ()
form.Visible <- true
form.TopMost <- true
form.Text <- "Welcome to F#"

設定 Property

上面用到「<-」,整個程式的最後一行也出現「<-」,這是指定(assignment)的意思。繫結(binding)和指定是不一樣的,繫結只能進行一次,使用的語法是「let a = b」,但指定可以指定多次,使用的語法「a <- b」。可以被繫結者,顯然必須是「不可被改變的」(immutable);而可以被指定者顯然必須是「可被改變的」(mutable)。.NET  物件的 Property 是可以被改變的(除非唯讀者),所以用指定的方式。

註明型別

之前提到過「型別推論」讓我們可以不必標明型別,但有些時候你想要(或需要)主動註明型別,寫法是這樣「名稱 : 名稱」。

在上面的範例定義 http 函數的時候,就有註明其參數 url 必須是字串,所以寫成這樣:

let http(url: string) = …

最後…

這篇文章利用兩個範例,詳細解說其中出現的語法,讓你對於 F# 有大概的認識。下一次的文章,會開始從最基本的一切說起,包括 F# 的各種型別、各種運算子、以及一些基本的 FP 語法。

http://msdn.microsoft.com/zh-tw/library/dd252674.aspx


阅读全文(580) | 评论:0 | 复制链接