OpenSqlFilestream을 사용하여 FILESTREAM 데이터 액세스Access FILESTREAM Data with OpenSqlFilestream

OpenSqlFilestream API는 파일 시스템에 저장된 FILESTREAM BLOB(Binary Large Object)에 대한 Win32 호환 파일 핸들을 가져옵니다.The OpenSqlFilestream API obtains a Win32 compatible file handle for a FILESTREAM binary large object (BLOB) stored in the file system. 핸들이 ReadFile, WriteFile, TransmitFile, SetFilePointer, SetEndOfFile또는 FlushFileBuffers의 Win32 API 중 하나로 전달될 수 있습니다.The handle can be passed to any of the following Win32 APIs: ReadFile, WriteFile, TransmitFile, SetFilePointer, SetEndOfFile, or FlushFileBuffers. 이 핸들을 다른 Win32 API에 전달하면 ERROR_ACCESS_DENIED 오류가 반환됩니다.If you pass this handle to any other Win32 API, the error ERROR_ACCESS_DENIED is returned. 핸들은 트랜잭션이 커밋 또는 롤백되기 전에 Win32 CloseHandle API에 전달하는 방식으로 닫아야 합니다.The handle must be closed by passing it to the Win32 CloseHandle API before the transaction is committed or rolled back. 핸들을 닫지 못하면 서버 쪽 리소스 노출이 발생합니다.Failing to close the handle will cause server-side resource leaks.

모든 FILESTREAM 데이터 컨테이너 액세스는 SQL ServerSQL Server 트랜잭션에서 수행해야 합니다.You must perform All FILESTREAM data container access in a SQL ServerSQL Server transaction. Transact-SQLTransact-SQL 문도 동일한 트랜잭션에서 실행할 수 있습니다. statements can also be executed in the same transaction. 이를 통해 SQL 데이터와 FILESTREAM BLOB 데이터 간의 일관성을 유지 관리할 수 있습니다.This maintains consistency between the SQL data and FILESTREAM BLOB data.

Win32를 사용하여 FILESTREAM BLOB에 액세스하려면 Windows 인증 을 설정해야 합니다.To access the FILESTREAM BLOB by using Win32, Windows Authorization must be enabled.

중요

파일이 쓰기 액세스 권한으로 열린 경우 트랜잭션은 FILESTREAM 에이전트의 소유입니다.When the file is opened for write access, the transaction is owned by the FILESTREAM agent. 트랜잭션이 해제되기 전까지는 Win32 파일 I/O만 허용됩니다.Only Win32 file I/O is allowed until the transaction is released. 트랜잭션을 해제하려면 쓰기 핸들을 닫아야 합니다.To release the transaction, the write handle must be closed.

구문Syntax


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

매개 변수Parameters

FilestreamPathFilestreamPath
[in] PathName 함수에서 반환하는 nvarchar(max) 경로입니다.[in] Is the nvarchar(max) path that is returned by the PathName function. PathName은 FILESTREAM 테이블 및 열에 대한 SQL ServerSQL Server SELECT 또는 UPDATE 권한이 있는 계정의 컨텍스트에서 호출해야 합니다.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] FILESTREAM BLOB 데이터에 액세스하는 데 사용되는 모드를 설정합니다.[in] Sets the mode used to access FILESTREAM BLOB data. 이 값은 DeviceIoControl Function(DeviceIoControl 함수)에 전달됩니다.This value is passed to the DeviceIoControl Function.

이름Name Value 의미Meaning
SQL_FILESTREAM_READSQL_FILESTREAM_READ 00 데이터를 파일에서 읽을 수 있습니다.Data can be read from the file.
SQL_FILESTREAM_WRITESQL_FILESTREAM_WRITE 11 데이터를 파일에 쓸 수 있습니다.Data can be written to the file.
SQL_FILESTREAM_READWRITESQL_FILESTREAM_READWRITE 22 데이터를 파일에서 읽고 쓸 수 있습니다.Data can be read and written from the file.
참고

이러한 값은 sqlncli.h의 SQL_FILESTREAM_DESIRED_ACCESS 열거에서 정의합니다.These values are defined in the SQL_FILESTREAM_DESIRED_ACCESS enumeration in sqlncli.h.

OpenOptionsOpenOptions
[in] 파일 특성 및 플래그입니다.[in] The file attributes and flags. 이 매개 변수는 다음 플래그의 조합을 포함할 수도 있습니다.This parameter can also include any combination of the following flags.

플래그Flag Value 의미Meaning
SQL_FILESTREAM_OPEN_NONESQL_FILESTREAM_OPEN_NONE 0x00000000:0x00000000: 특별한 옵션 없이 파일을 열거나 만듭니다.The file is being opened or created with no special options.
SQL_FILESTREAM_OPEN_FLAG_ASYNCSQL_FILESTREAM_OPEN_FLAG_ASYNC 0x00000001L0x00000001L 비동기 I/O를 위해 파일을 열거나 만듭니다.The file is being opened or created for asynchronous I/O.
SQL_FILESTREAM_OPEN_FLAG_NO_BUFFERINGSQL_FILESTREAM_OPEN_FLAG_NO_BUFFERING 0x00000002L0x00000002L 시스템에서 시스템 캐싱을 사용하지 않고 파일을 엽니다.The system opens the file by using no system caching.
SQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGHSQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGH 0x00000004L0x00000004L 시스템에서 중간 캐시를 통해 쓰지 않습니다.The system does not write through an intermediate cache. 쓰기는 디스크에 바로 적용됩니다.Writes go directly to disk.
SQL_FILESTREAM_OPEN_FLAG_SEQUENTIAL_SCANSQL_FILESTREAM_OPEN_FLAG_SEQUENTIAL_SCAN 0x00000008L0x00000008L 처음부터 끝까지 순차적으로 파일에 액세스합니다.A file is accessed sequentially from beginning to end. 시스템에서는 이 필드를 힌트로 사용하여 파일 캐싱을 최적화할 수 있습니다.The system can use this as a hint to optimize file caching. 응용 프로그램에서 임의 액세스를 위해 파일 포인터를 이동하는 경우 최적 캐싱이 수행되지 않을 수도 있습니다.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 파일에 임의로 액세스합니다.A file is accessed randomly. 시스템에서는 이 필드를 힌트로 사용하여 파일 캐싱을 최적화할 수 있습니다.The system can use this as a hint to optimize file caching.

FilestreamTransactionContextFilestreamTransactionContext
[in] GET_FILESTREAM_TRANSACTION_CONTEXT 함수에서 반환하는 값입니다.[in] The value that is returned by the GET_FILESTREAM_TRANSACTION_CONTEXT function.

FilestreamTransactionContextLengthFilestreamTransactionContextLength
[in] GET_FILESTREAM_TRANSACTION_CONTEXT 함수에서 반환하는 varbinary(max) 데이터의 바이트 수입니다.[in] Number of bytes in the varbinary(max) data that is returned by the GET_FILESTREAM_TRANSACTION_CONTEXT function. 이 함수는 N바이트의 배열을 반환합니다.The function returns an array of N bytes. N은 함수에 의해 결정되며, 반환되는 바이트 배열의 속성입니다.N is determined by the function and is a property of the byte array that is returned.

AllocationSizeAllocationSize
[in] 데이터 파일의 처음 할당 크기(바이트)를 지정합니다.[in] Specifies the initial allocation size of the data file in bytes. 읽기 모드에서는 무시됩니다.It is ignored in read mode. 이 매개 변수는 NULL일 수 있으며, 이 경우 기본 파일 시스템 동작이 사용됩니다.This parameter can be NULL, in which case the default file system behavior is used.

반환 값Return Value

이 함수가 성공적으로 실행되면 지정된 파일에 대해 열린 핸들이 반환되고If the function succeeds, the return value is an open handle to a specified file. 함수가 실패하면 INVALID_HANDLE_VALUE가 반환됩니다.If the function fails, the return value is INVALID_HANDLE_VALUE. 확장 오류 정보를 보려면 GetLastError()를 호출합니다.For extended error information, call GetLastError().

Examples

다음 예에서는 OpenSqlFilestream API를 사용하여 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);
}

주의Remarks

이 API를 사용하려면 SQL ServerSQL Server Native Client를 설치해야 합니다.The SQL ServerSQL Server Native Client must be installed to use this API. SQL ServerSQL Server Native Client는 SQL ServerSQL Server 또는 SQL ServerSQL Server 클라이언트 도구와 함께 설치됩니다.The SQL ServerSQL Server Native Client is installed with SQL ServerSQL Server or SQL ServerSQL Server client tools. 자세한 내용은 Installing SQL Server Native Client(SQL Server Native Client 설치)를 참조하세요.For more information, see Installing SQL Server Native Client.

참고 항목See Also

Binary Large Object (Blob) 데이터 (SQL Server) Binary Large Object (Blob) Data (SQL Server)
FILESTREAM 데이터 부분 업데이트 Make Partial Updates to FILESTREAM Data
FILESTREAM 응용 프로그램에서 데이터베이스 작업과의 충돌 방지 Avoid Conflicts with Database Operations in FILESTREAM Applications