Accesso ai dati FILESTREAM con OpenSqlFilestreamAccess FILESTREAM Data with OpenSqlFilestream

L'API OpenSqlFilestream ottiene un handle di file compatibile con Win32 per un oggetto binario di grandi dimensioni (BLOB) FILESTREAM archiviato nel file system.The OpenSqlFilestream API obtains a Win32 compatible file handle for a FILESTREAM binary large object (BLOB) stored in the file system. L'handle può essere passato a una qualsiasi delle API Win32 seguenti: ReadFile, WriteFile, TransmitFile, SetFilePointer, SetEndOfFileo FlushFileBuffers.The handle can be passed to any of the following Win32 APIs: ReadFile, WriteFile, TransmitFile, SetFilePointer, SetEndOfFile, or FlushFileBuffers. Se si passa questo handle a qualsiasi altra API Win32, viene restituito l'errore ERROR_ACCESS_DENIED.If you pass this handle to any other Win32 API, the error ERROR_ACCESS_DENIED is returned. L'handle deve essere chiuso passandolo all'API CloseHandle Win32 prima che venga eseguito il commit o il rollback della transazione.The handle must be closed by passing it to the Win32 CloseHandle API before the transaction is committed or rolled back. La mancata chiusura dell'handle provoca perdite di risorse sul lato server.Failing to close the handle will cause server-side resource leaks.

È necessario eseguire l'accesso al contenitore di tutti i dati FILESTREAM in una transazione di SQL ServerSQL Server .You must perform All FILESTREAM data container access in a SQL ServerSQL Server transaction. Transact-SQLTransact-SQL Anche le istruzioni possono essere eseguite nella stessa transazione. statements can also be executed in the same transaction. In questo modo viene mantenuta la coerenza tra i dati SQL e dati BLOB FILESTREAM.This maintains consistency between the SQL data and FILESTREAM BLOB data.

Per accedere al BLOB FILESTREAM usando Win32, è necessario abilitare l' autorizzazione Windows .To access the FILESTREAM BLOB by using Win32, Windows Authorization must be enabled.

Importante

Quando il file è aperto per l'accesso in scrittura, la transazione è di proprietà dell'agente FILESTREAM.When the file is opened for write access, the transaction is owned by the FILESTREAM agent. Finché non viene rilasciata la transazione è consentito solo l'I/O dei file Win32.Only Win32 file I/O is allowed until the transaction is released. Per rilasciare la transazione, è necessario chiudere l'handle di scrittura.To release the transaction, the write handle must be closed.

SintassiSyntax


HANDLE OpenSqlFilestream (  
    LPCWSTR FilestreamPath,  
    SQL_FILESTREAM_DESIRED_ACCESS DesiredAccess,  
    ULONG OpenOptions,  
    LPBYTE FilestreamTransactionContext,  
    SIZE_T FilestreamTransactionContextLength,  
    PLARGE_INTEGER AllocationSize);  

ParametriParameters

FilestreamPathFilestreamPath
[in] Percorso nvarchar(max) restituito dalla funzione PathName .[in] Is the nvarchar(max) path that is returned by the PathName function. La funzione PathName deve essere chiamata dal contesto di un account che dispone dell'autorizzazione SELECT o UPDATE di SQL ServerSQL Server per la tabella e la colonna FILESTREAM.PathName must be called from the context of an account that has SQL ServerSQL Server SELECT or UPDATE permissions on the FILESTREAM table and column.

DesiredAccessDesiredAccess
[in] Imposta la modalità utilizzata per accedere ai dati BLOB FILESTREAM.[in] Sets the mode used to access FILESTREAM BLOB data. Questo valore viene passato alla funzione DeviceIoControl.This value is passed to the DeviceIoControl Function.

NomeName ValoreValue SignificatoMeaning
SQL_FILESTREAM_READSQL_FILESTREAM_READ 00 I dati possono essere letti dal file.Data can be read from the file.
SQL_FILESTREAM_WRITESQL_FILESTREAM_WRITE 11 I dati possono essere scritti nel file.Data can be written to the file.
SQL_FILESTREAM_READWRITESQL_FILESTREAM_READWRITE 22 I dati possono essere scritti e letti dal file.Data can be read and written from the file.

Nota

Questi valori vengono definiti nell'enumerazione SQL_FILESTREAM_DESIRED_ACCESS in sqlncli.h.These values are defined in the SQL_FILESTREAM_DESIRED_ACCESS enumeration in sqlncli.h.

OpenOptionsOpenOptions
[in] Attributi e flag del file.[in] The file attributes and flags. Questo parametro può includere inoltre qualsiasi combinazione dei flag seguenti.This parameter can also include any combination of the following flags.

FlagFlag ValoreValue SignificatoMeaning
SQL_FILESTREAM_OPEN_NONESQL_FILESTREAM_OPEN_NONE 0x00000000:0x00000000: Il file è aperto o creato senza opzioni speciali.The file is being opened or created with no special options.
SQL_FILESTREAM_OPEN_FLAG_ASYNCSQL_FILESTREAM_OPEN_FLAG_ASYNC 0x00000001L0x00000001L Il file è aperto o creato per gli I/O asincroni.The file is being opened or created for asynchronous I/O.
SQL_FILESTREAM_OPEN_FLAG_NO_BUFFERINGSQL_FILESTREAM_OPEN_FLAG_NO_BUFFERING 0x00000002L0x00000002L Il sistema apre il file senza utilizzare la memorizzazione nella cache di sistema.The system opens the file by using no system caching.
SQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGHSQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGH 0x00000004L0x00000004L Il sistema scrive senza utilizzare alcuna cache intermedia,The system does not write through an intermediate cache. ma accede direttamente al disco.Writes go directly to disk.
SQL_FILESTREAM_OPEN_FLAG_SEQUENTIAL_SCANSQL_FILESTREAM_OPEN_FLAG_SEQUENTIAL_SCAN 0x00000008L0x00000008L L'accesso a un file viene eseguito in sequenza, partendo dall'inizio e fino alla fine.A file is accessed sequentially from beginning to end. Il sistema può interpretare questa situazione come hint per l'ottimizzazione della memorizzazione del file nella cache.The system can use this as a hint to optimize file caching. Se un'applicazione sposta il puntatore del file per l'accesso casuale, potrebbe non essere possibile ottimizzare la memorizzazione nella cache.If an application moves the file pointer for random access, optimal caching may not occur.
SQL_FILESTREAM_OPEN_FLAG_RANDOM_ACCESSSQL_FILESTREAM_OPEN_FLAG_RANDOM_ACCESS 0x00000010L0x00000010L L'accesso al un file viene eseguito in modo casuale.A file is accessed randomly. Il sistema può interpretare questa situazione come hint per l'ottimizzazione della memorizzazione del file nella cache.The system can use this as a hint to optimize file caching.

FilestreamTransactionContextFilestreamTransactionContext
[in] Valore restituito dalla funzione GET_FILESTREAM_TRANSACTION_CONTEXT .[in] The value that is returned by the GET_FILESTREAM_TRANSACTION_CONTEXT function.

FilestreamTransactionContextLengthFilestreamTransactionContextLength
[in] Numero di byte nei dati varbinary(max) restituiti dalla funzione GET_FILESTREAM_TRANSACTION_CONTEXT.[in] Number of bytes in the varbinary(max) data that is returned by the GET_FILESTREAM_TRANSACTION_CONTEXT function. La funzione restituisce una matrice di N byte.The function returns an array of N bytes. Il valore N è determinato dalla funzione e costituisce una proprietà della matrice di byte restituita.N is determined by the function and is a property of the byte array that is returned.

AllocationSizeAllocationSize
[in] Specifica le dimensioni dell'allocazione iniziale del file di dati in byte.[in] Specifies the initial allocation size of the data file in bytes. In modalità di lettura viene ignorato.It is ignored in read mode. Questo parametro può essere Null nel caso in cui venga utilizzato il comportamento del file system predefinito.This parameter can be NULL, in which case the default file system behavior is used.

Valore restituitoReturn Value

Se la funzione viene eseguita correttamente, il valore restituito è un handle aperto per il file specificato.If the function succeeds, the return value is an open handle to a specified file. Se la funzione ha esito negativo, il valore restituito è INVALID_HANDLE_VALUE.If the function fails, the return value is INVALID_HANDLE_VALUE. Per informazioni dettagliate sull'errore, chiamare GetLastError().For extended error information, call GetLastError().

EsempiExamples

Negli esempi seguenti viene illustrato come utilizzare l'API OpenSqlFilestream per ottenere un handle Win32.The following examples show you how to use the OpenSqlFilestream API to obtain a Win32 handle.

using System.IO;
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;

namespace FILESTREAM
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlConnection sqlConnection = new SqlConnection(
                "Integrated Security=true;server=(local)");

            SqlCommand sqlCommand = new SqlCommand();
            sqlCommand.Connection = sqlConnection;

            try
            {
                sqlConnection.Open();

                //The first task is to retrieve the file path
                //of the SQL FILESTREAM BLOB that we want to
                //access in the application.

                sqlCommand.CommandText =
                      "SELECT Chart.PathName()"
                    + " FROM Archive.dbo.Records"
                    + " WHERE SerialNumber = 3";

                String filePath = null;

                Object pathObj = sqlCommand.ExecuteScalar();
                if (DBNull.Value != pathObj)
                    filePath = (string)pathObj;
                else
                {
                    throw new System.Exception(
                        "Chart.PathName() failed"
                      + " to read the path name "
                      + " for the Chart column.");
                }

                //The next task is to obtain a transaction
                //context. All FILESTREAM BLOB operations
                //occur within a transaction context to
                //maintain data consistency.

                //All SQL FILESTREAM BLOB access must occur in 
                //a transaction. MARS-enabled connections
                //have specific rules for batch scoped transactions,
                //which the Transact-SQL BEGIN TRANSACTION statement
                //violates. To avoid this issue, client applications 
                //should use appropriate API facilities for transaction management, 
                //management, such as the SqlTransaction class.

                SqlTransaction transaction = sqlConnection.BeginTransaction("mainTranaction");
                sqlCommand.Transaction = transaction;

                sqlCommand.CommandText =
                    "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()";

                Object obj = sqlCommand.ExecuteScalar();
                byte[] txContext = (byte[])obj;

                //The next step is to obtain a handle that
                //can be passed to the Win32 FILE APIs.

                SqlFileStream sqlFileStream = new SqlFileStream(filePath, txContext, FileAccess.ReadWrite);

                byte[] buffer = new byte[512];

                int numBytes = 0;

                //Write the string, "EKG data." to the FILESTREAM BLOB.
                //In your application this string would be replaced with
                //the binary data that you want to write.

                string someData = "EKG data.";
                Encoding unicode = Encoding.GetEncoding(0);

                sqlFileStream.Write(unicode.GetBytes(someData.ToCharArray()),
                    0,
                    someData.Length);

                //Read the data from the FILESTREAM
                //BLOB.

                sqlFileStream.Seek(0L, SeekOrigin.Begin);

                numBytes = sqlFileStream.Read(buffer, 0, buffer.Length);

                string readData = unicode.GetString(buffer);

                if (numBytes != 0)
                    Console.WriteLine(readData);

                //Because reading and writing are finished, FILESTREAM 
                //must be closed. This closes the c# FileStream class, 
                //but does not necessarily close the the underlying 
                //FILESTREAM handle. 
                sqlFileStream.Close();

                //The final step is to commit or roll back the read and write
                //operations that were performed on the FILESTREAM BLOB.

                sqlCommand.Transaction.Commit();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
            finally
            {
                sqlConnection.Close();
            }
            return;
        }
    }
}
Imports System.IO
Imports System 
Imports System.Collections.Generic 
Imports System.Text 
Imports System.Data 
Imports System.Data.SqlClient 
Imports System.Data.SqlTypes 

Module Module1
    Public Sub Main(ByVal args As String())
        '        Dim sqlConnection As New SqlConnection("Integrated Security=true;server=(local)")
        Dim sqlConnection As New SqlConnection("Integrated Security=true;server=kellyreyue\MSSQL1")

        Dim sqlCommand As New SqlCommand()
        sqlCommand.Connection = sqlConnection

        Try
            sqlConnection.Open()

            'The first task is to retrieve the file path 
            'of the SQL FILESTREAM BLOB that we want to 
            'access in the application. 

            sqlCommand.CommandText = "SELECT Chart.PathName()" + " FROM Archive.dbo.Records" + " WHERE SerialNumber = 3"

            Dim filePath As String = Nothing

            Dim pathObj As Object = sqlCommand.ExecuteScalar()
            If Not pathObj.Equals(DBNull.Value) Then
                filePath = DirectCast(pathObj, String)
            Else
                Throw New System.Exception("Chart.PathName() failed" + " to read the path name " + " for the Chart column.")
            End If

            'The next task is to obtain a transaction 
            'context. All FILESTREAM BLOB operations 
            'occur within a transaction context to 
            'maintain data consistency. 

            'All SQL FILESTREAM BLOB access must occur in 
            'a transaction. MARS-enabled connections 
            'have specific rules for batch scoped transactions, 
            'which the Transact-SQL BEGIN TRANSACTION statement 
            'violates. To avoid this issue, client applications 
            'should use appropriate API facilities for transaction management, 
            'management, such as the SqlTransaction class. 

            Dim transaction As SqlTransaction = sqlConnection.BeginTransaction("mainTranaction")
            sqlCommand.Transaction = transaction

            sqlCommand.CommandText = "SELECT GET_FILESTREAM_TRANSACTION_CONTEXT()"

            Dim obj As Object = sqlCommand.ExecuteScalar()
            Dim txContext As Byte() = Nothing

            Dim contextLength As UInteger

            If Not obj.Equals(DBNull.Value) Then
                txContext = DirectCast(obj, Byte())
                contextLength = txContext.Length()
            Else
                Dim message As String = "GET_FILESTREAM_TRANSACTION_CONTEXT() failed"
                Throw New System.Exception(message)
            End If

            'The next step is to obtain a handle that 
            'can be passed to the Win32 FILE APIs. 

            Dim sqlFileStream As New SqlFileStream(filePath, txContext, FileAccess.ReadWrite)

            Dim buffer As Byte() = New Byte(511) {}

            Dim numBytes As Integer = 0

            'Write the string, "EKG data." to the FILESTREAM BLOB. 
            'In your application this string would be replaced with 
            'the binary data that you want to write. 

            Dim someData As String = "EKG data."
            Dim unicode As Encoding = Encoding.GetEncoding(0)

            sqlFileStream.Write(unicode.GetBytes(someData.ToCharArray()), 0, someData.Length)

            'Read the data from the FILESTREAM 
            'BLOB. 

            sqlFileStream.Seek(0, SeekOrigin.Begin)

            numBytes = sqlFileStream.Read(buffer, 0, buffer.Length)

            Dim readData As String = unicode.GetString(buffer)

            If numBytes <> 0 Then
                Console.WriteLine(readData)
            End If

            'Because reading and writing are finished, FILESTREAM 
            'must be closed. This closes the c# FileStream class, 
            'but does not necessarily close the the underlying 
            'FILESTREAM handle. 
            sqlFileStream.Close()

            'The final step is to commit or roll back the read and write 
            'operations that were performed on the FILESTREAM BLOB. 

            sqlCommand.Transaction.Commit()
        Catch ex As System.Exception
            Console.WriteLine(ex.ToString())
        Finally
            sqlConnection.Close()
        End Try
        Return
    End Sub
End Module
#include <windows.h>
#include <sql.h>
#include<sqltypes.h>
#include<sqlext.h>
#include <stdio.h>
#include <sqlncli.h>

#define COPYBUFFERSIZE 4096

/// <summary>
///This class iterates though the ODBC error queue and prints all of the
///accumulated error messages to the console.
/// </summary>

class ODBCErrors
{
private:
    int         m_iLine;    //Source code line on which the error occurred
    SQLSMALLINT m_type;     //Type of handle on which the error occurred
    SQLHANDLE   m_handle;   //ODBC handle on which the error occurred

public:
    /// <summary>
    ///Default constructor for the ODBCErrors class
    ///</summary>

    ODBCErrors()
    {
        m_iLine  = -1;
        m_type   = 0;
        m_handle = SQL_NULL_HANDLE;
    }

    /// <summary>
    ///Constructor for the ODBCErrors class
    /// </summary>
    /// <param name="iLine">
    /// This parameter is the source code line
    /// at which the error occurred.
    ///</param>
    /// <param name="type">
    /// This parameter is the type of ODBC handle passed in
    /// the next parameter.
    ///</param>
    /// <param name="handle">
    /// This parameter is the handle on which the error occurred.
    ///</param>

    ODBCErrors(int iLine, SQLSMALLINT type, SQLHANDLE handle)
    {
        m_iLine  = iLine;
        m_type   = type;
        m_handle = handle;
    }

    ///<summary>
    /// This method iterates though the error stack for the handle passed
    /// into the constructor and displays those errors on the console.
    ///</summary>

    void Print()
    {
        SQLSMALLINT i = 0, len = 0;
        SQLINTEGER  native;
        SQLTCHAR    state[9], text[256];
        SQLRETURN   sqlReturn = SQL_SUCCESS;

        if ( m_handle == SQL_NULL_HANDLE )
        {
            wprintf_s(TEXT("The error handle is not a valid handle.\n"), m_iLine);
            return;
        }

        wprintf_s(TEXT("Error Line(%d)\n"), m_iLine);

        while( sqlReturn == SQL_SUCCESS )
        {
            len = 0;

            sqlReturn = SQLGetDiagRec(
                m_type,
                m_handle,
                ++i,
                state,
                &native,
                text,
                sizeof(text)/sizeof(SQLTCHAR),
                &len);

            if ( SQL_SUCCEEDED(sqlReturn) )
                wprintf_s(TEXT("Error(%d, %ld, %s) : %s\n"), i, native, state, text);
        }
    }
};


BOOL CopyFileToSQL(LPTSTR srcFilePath, LPTSTR dstFilePath, LPBYTE transactionToken, SQLINTEGER cbTransactionToken)
{
	BOOL bRetCode = FALSE;

	HANDLE srcHandle = INVALID_HANDLE_VALUE;
	HANDLE dstHandle = INVALID_HANDLE_VALUE;
    BYTE   buffer[COPYBUFFERSIZE] = { 0 };

    TCHAR *szErrMsgSrc   = TEXT("Error opening source file.");
    TCHAR *szErrMsgDst   = TEXT("Error opening destFile file.");
    TCHAR *szErrMsgRead  = TEXT("Error reading source file.");
    TCHAR *szErrMsgWrite = TEXT("Error writing SQL file.");

    try
    {
	    if ( (srcHandle = CreateFile(
            srcFilePath,
            GENERIC_READ,
            FILE_SHARE_READ,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_SEQUENTIAL_SCAN,
            NULL)) == INVALID_HANDLE_VALUE )
            throw szErrMsgSrc;

    	if ( (dstHandle =  OpenSqlFilestream(
            dstFilePath,
            Write,
            0,
            transactionToken,
            cbTransactionToken,
            0)) == INVALID_HANDLE_VALUE)
            throw szErrMsgDst;

        DWORD bytesRead = 0;
        DWORD bytesWritten = 0;

    	do
        {
            if ( ReadFile(srcHandle, buffer, COPYBUFFERSIZE, &bytesRead, NULL) == 0 )
                throw szErrMsgRead;

    		if (bytesRead > 0)
            {
        		if ( WriteFile(dstHandle, buffer, bytesRead, &bytesWritten, NULL) == 0 )
                    throw szErrMsgWrite;
            }
		} while (bytesRead > 0);

        bRetCode = TRUE;
	}
    catch( TCHAR *szErrMsg )
    {
        wprintf_s(szErrMsg);
        bRetCode = FALSE;
    }

    if ( srcHandle != INVALID_HANDLE_VALUE )
        CloseHandle(srcHandle);

    if ( dstHandle != INVALID_HANDLE_VALUE )
    	CloseHandle(dstHandle);

    return bRetCode;
}

void main()
{
    TCHAR *sqlDBQuery =
       TEXT("INSERT INTO Archive.dbo.Records(Id, SerialNumber, Chart)")
       TEXT(" OUTPUT GET_FILESTREAM_TRANSACTION_CONTEXT(), inserted.Chart.PathName()")
       TEXT("VALUES (newid (), 5, CONVERT(VARBINARY, '**Temp**'))");

	SQLCHAR transactionToken[32];
    
    SQLHANDLE henv = SQL_NULL_HANDLE;
    SQLHANDLE hdbc              = SQL_NULL_HANDLE;
    SQLHANDLE hstmt             = SQL_NULL_HANDLE;

    try
    {
        //These statements Initialize ODBC for the client application and
        //connect to the database.

        if ( SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_ENV, henv);

        if ( SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,(void*)SQL_OV_ODBC3, NULL) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_ENV, henv);

        if ( SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_ENV, henv);

	    //This code assumes that the dataset name "Sql Server FILESTREAM"
	    //has been previously created on the client computer system. An
        //ODBC DSN is created with the ODBC Data Source item in
        //the Windows Control Panel.

	    if ( SQLConnect(hdbc, TEXT("Sql Server FILESTREAM"),
                SQL_NTS, NULL, 0, NULL, 0) <= 0 )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_DBC, hdbc);

        //FILESTREAM requires that all read and write operations occur
        //within a transaction.
        if ( SQLSetConnectAttr(hdbc,
            SQL_ATTR_AUTOCOMMIT,
            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
            SQL_IS_UINTEGER) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_DBC, hdbc);

        if ( SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_DBC, hdbc);

        if ( SQLExecDirect(hstmt, sqlDBQuery, SQL_NTS) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

        //Retrieve the transaction token.
        if ( SQLFetch(hstmt) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

        SQLINTEGER cbTransactionToken = sizeof(transactionToken);

    	if ( SQLGetData(hstmt, 1,
            SQL_C_BINARY,
            transactionToken,
            sizeof(transactionToken),
            &cbTransactionToken) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

        //Retrieve the file path for the inserted record.

        TCHAR dstFilePath[1024];
        SQLINTEGER cbDstFilePath;

        if ( SQLGetData(hstmt, 2, SQL_C_TCHAR, dstFilePath, sizeof(dstFilePath), &cbDstFilePath) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

    	if ( SQLCloseCursor(hstmt) != SQL_SUCCESS )
            throw new ODBCErrors(__LINE__, SQL_HANDLE_STMT, hstmt);

        SQLUSMALLINT mode = SQL_ROLLBACK;

        if ( CopyFileToSQL(
            TEXT("C:\\Users\\Data\\chart1.jpg"),
            dstFilePath,
            transactionToken,
            cbTransactionToken) == TRUE )
            mode = SQL_COMMIT;

        SQLTransact(henv, hdbc, mode);
    }
    catch(ODBCErrors *pErrors)
    {
        pErrors->Print();
        delete pErrors;
    }

    if ( hstmt != SQL_NULL_HANDLE )
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

    if ( hdbc != SQL_NULL_HANDLE )
        SQLDisconnect(hdbc);

    if ( hdbc != SQL_NULL_HANDLE )
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc); 

    if ( henv != SQL_NULL_HANDLE )
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
}

OsservazioniRemarks

Per utilizzare questa API, è necessario che SQL ServerSQL Server Native Client sia installato.The SQL ServerSQL Server Native Client must be installed to use this API. SQL ServerSQL Server Native Client viene installato con SQL ServerSQL Server o con gli strumenti client di SQL ServerSQL Server .The SQL ServerSQL Server Native Client is installed with SQL ServerSQL Server or SQL ServerSQL Server client tools. Per altre informazioni, vedere Installazione di SQL Server Native Client.For more information, see Installing SQL Server Native Client.

Vedere ancheSee Also

Dati BLOB (Binary Large Object) (SQL Server) Binary Large Object (Blob) Data (SQL Server)
Esecuzione di aggiornamenti parziali di dati FILESTREAM Make Partial Updates to FILESTREAM Data
Evitare conflitti con le operazioni del database nelle applicazioni di FILESTREAMAvoid Conflicts with Database Operations in FILESTREAM Applications