RPC 및 프로세스 출력을 사용하여 SQL Server Native Client 저장 프로시저 실행
적용 대상: SQL Server Azure SQL 데이터베이스 Azure SQL Managed Instance Azure Synapse Analytics Analytics Platform System(PDW)
SQL Server 저장 프로시저에는 정수 반환 코드 및 출력 매개 변수가 있을 수 있습니다. 반환 코드 및 출력 매개 변수는 서버에서 마지막 패킷으로 전송되므로 행 집합이 완전히 해제될 때까지 애플리케이션에서 사용할 수 없습니다. 명령이 여러 결과를 반환하는 경우 IMultipleResults::GetResult가 DB_S_NORESULT 반환하거나 IMultipleResults 인터페이스가 완전히 해제될 때 출력 매개 변수 데이터를 사용할 수 있습니다.
Important
가능하면 Windows 인증을 사용하세요. Windows 인증을 사용할 수 없으면 런타임에 사용자에게 자격 증명을 입력하라는 메시지를 표시합니다. 자격 증명은 파일에 저장하지 않는 것이 좋습니다. 자격 증명을 유지해야 하는 경우 Win32 Crypto API를 사용하여 암호화해야 합니다.
반환 코드 및 출력 매개 변수를 처리하려면
RPC 이스케이프 시퀀스를 사용하는 SQL 문을 생성합니다.
ICommandWithParameters::SetParameterInfo 메서드를 호출하여 공급자에 대한 매개 변수를 설명합니다. PARAMBINDINFO 구조의 배열에 매개 변수 정보를 입력합니다.
DBBINDING 구조의 배열을 사용하여 바인딩 집합(각 매개 변수 작성기마다 하나씩)을 만듭니다.
IAccessor::CreateAccessor 메서드를 사용하여 정의된 매개 변수에 대한 접근자를 만듭니다. CreateAccessor 는 바인딩 집합에서 접근자를 만듭니다.
DBPARAMS 구조를 채웁니다.
Execute 명령을 호출합니다(이 경우 저장 프로시저 호출).
행 집합을 처리하고 IRowset::Release 메서드를 사용하여 해제합니다.
저장 프로시저에서 받은 반환 코드 및 출력 매개 변수 값을 처리합니다.
예시
이 예제에서는 행 집합, 반환 코드 및 출력 매개 변수 처리를 보여 줍니다. 결과 집합은 처리되지 않습니다. 이 샘플은 IA64에서 지원되지 않습니다.
이 문서의 Transact-SQL 코드 샘플은 AdventureWorks2022
또는 AdventureWorksDW2022
샘플 데이터베이스를 사용하며, 이는 Microsoft SQL Server 예시 및 커뮤니티 프로젝트(Microsoft SQL Server Samples and Community Projects) 홈 페이지에서 다운로드할 수 있습니다.
첫 번째(Transact-SQL) 코드 목록을 실행하여 애플리케이션에서 사용하는 저장 프로시저를 만듭니다.
ole32.lib oleaut32.lib를 사용하여 컴파일하고 두 번째(C++) 코드 목록을 실행합니다. 이 애플리케이션은 컴퓨터의 기본 SQL Server 인스턴스에 연결됩니다. 일부 Windows 운영 체제에서는 (localhost) 또는 (local)을 해당 SQL Server 인스턴스의 이름으로 변경해야 합니다. 명명된 인스턴스에 연결하려면 연결 문자열을 L"(local)"에서 L"(local)\\name"으로 변경합니다. 여기서 name은 명명된 인스턴스입니다. 기본적으로 SQL Server Express는 명명된 인스턴스에 설치됩니다. INCLUDE 환경 변수에 sqlncli.h가 포함된 디렉터리가 포함되어 있는지 확인합니다.
세 번째(Transact-SQL) 코드 목록을 실행하여 애플리케이션에서 사용하는 저장 프로시저를 삭제합니다.
USE AdventureWorks2022;
if exists (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[myProc]'))
DROP PROCEDURE myProc
GO
CREATE PROCEDURE myProc
@inparam nvarchar(5),
@outparam int OUTPUT
AS
SELECT Color, ListPrice
FROM Production.Product WHERE Size > @inparam
SELECT @outparam = 100
IF (@outparam > 0)
RETURN 999
ELSE
RETURN 888
GO
// compile with: ole32.lib oleaut32.lib
void InitializeAndEstablishConnection();
#define UNICODE
#define DBINITCONSTANTS
#define INITGUID
#define OLEDBVER 0x0250 // to include correct interfaces
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
#include <iostream>
#include <oledb.h>
#include <oledberr.h>
#include <SQLNCLI.h>
using namespace std;
IDBInitialize* pIDBInitialize = NULL;
IDBCreateSession* pIDBCreateSession = NULL;
IDBCreateCommand* pIDBCreateCommand = NULL;
ICommandText* pICommandText = NULL;
IRowset* pIRowset = NULL;
ICommandWithParameters* pICommandWithParams = NULL;
IAccessor* pIAccessor = NULL;
IDBProperties* pIDBProperties = NULL;
WCHAR* pStringsBuffer;
DBBINDING* pBindings;
const ULONG nInitProps = 4;
DBPROP InitProperties[nInitProps];
const ULONG nPropSet = 1;
DBPROPSET rgInitPropSet[nPropSet];
HRESULT hr;
HACCESSOR hAccessor;
const ULONG nParams = 3; // Number of parameters in the command
DBPARAMBINDINFO ParamBindInfo[nParams];
ULONG i;
ULONG cbColOffset = 0;
DB_UPARAMS ParamOrdinals[nParams];
DBROWCOUNT cNumRows = 0;
DBPARAMS Params;
// Declare an array of DBBINDING structures, one for each parameter in the command.
DBBINDING acDBBinding[nParams];
DBBINDSTATUS acDBBindStatus[nParams];
// The following buffer is used to store parameter values.
typedef struct tagSPROCPARAMS {
long lReturnValue;
long outParam;
long inParam;
} SPROCPARAMS;
int main() {
// The command to execute.
// WCHAR* wCmdString = L"{? = call myProc(?,?)}";
WCHAR* wCmdString = L"{rpc myProc}";
SPROCPARAMS sprocparams = {0,0,14};
// All the initialization activities in a separate function.
InitializeAndEstablishConnection();
// Create a new activity from the data source object.
if (FAILED(pIDBInitialize->QueryInterface(IID_IDBCreateSession,
(void**) &pIDBCreateSession))) {
cout << "Failed to access IDBCreateSession interface.\n";
goto EXIT;
}
if ( FAILED(pIDBCreateSession->CreateSession( NULL, IID_IDBCreateCommand,
(IUnknown**) &pIDBCreateCommand))) {
cout << "pIDBCreateSession->CreateSession failed.\n";
goto EXIT;
}
// Create a Command object.
if (FAILED(pIDBCreateCommand->CreateCommand( NULL,
IID_ICommandText,
(IUnknown**) &pICommandText))) {
cout << "Failed to access ICommand interface.\n";
goto EXIT;
}
// Set the command text.
if (FAILED(pICommandText->SetCommandText(DBGUID_DBSQL, wCmdString))) {
cout << "Failed to set command text.\n";
goto EXIT;
}
// Describe the command parameters (parameter name, provider specific name of
// the parameter's data type, and so on.) in an array of DBPARAMBINDINFO
// structures. This information is then used by SetParameterInfo().
ParamBindInfo[0].pwszDataSourceType = L"DBTYPE_I4";
ParamBindInfo[0].pwszName = L"@ReturnVal"; // return value from sp
ParamBindInfo[0].ulParamSize = sizeof(long);
ParamBindInfo[0].dwFlags = DBPARAMFLAGS_ISOUTPUT;
ParamBindInfo[0].bPrecision = 11;
ParamBindInfo[0].bScale = 0;
ParamOrdinals[0] = 1;
ParamBindInfo[1].pwszDataSourceType = L"DBTYPE_I4";
ParamBindInfo[1].pwszName = L"@inparam";
ParamBindInfo[1].ulParamSize = sizeof(long);
ParamBindInfo[1].dwFlags = DBPARAMFLAGS_ISINPUT;
ParamBindInfo[1].bPrecision = 11;
ParamBindInfo[1].bScale = 0;
ParamOrdinals[1] = 2;
ParamBindInfo[2].pwszDataSourceType = L"DBTYPE_I4";
ParamBindInfo[2].pwszName = L"@outparam";
ParamBindInfo[2].ulParamSize = sizeof(long);
ParamBindInfo[2].dwFlags = DBPARAMFLAGS_ISOUTPUT;
ParamBindInfo[2].bPrecision = 11;
ParamBindInfo[2].bScale = 0;
ParamOrdinals[2] = 3;
//Set the parameters information.
if ( FAILED(pICommandText->QueryInterface( IID_ICommandWithParameters,
(void**)&pICommandWithParams))) {
cout << "Failed to obtain ICommandWithParameters.\n";
goto EXIT;
}
if ( FAILED(pICommandWithParams->SetParameterInfo(nParams,
ParamOrdinals,
ParamBindInfo))) {
cout << "Failed in setting parameter information.(SetParameterInfo)\n";
goto EXIT;
}
// Describe the consumer buffer by filling in the array of DBBINDING structures.
// Each binding associates a single parameter to the consumer's buffer.
for ( i = 0 ; i < nParams ; i++ ) {
acDBBinding[i].obLength = 0;
acDBBinding[i].obStatus = 0;
acDBBinding[i].pTypeInfo = NULL;
acDBBinding[i].pObject = NULL;
acDBBinding[i].pBindExt = NULL;
acDBBinding[i].dwPart = DBPART_VALUE;
acDBBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;
acDBBinding[i].dwFlags = 0;
acDBBinding[i].bScale = 0;
}
acDBBinding[0].iOrdinal = 1;
acDBBinding[0].obValue = offsetof(SPROCPARAMS, lReturnValue);
acDBBinding[0].eParamIO = DBPARAMIO_OUTPUT;
acDBBinding[0].cbMaxLen = sizeof(long);
acDBBinding[0].wType = DBTYPE_I4;
acDBBinding[0].bPrecision = 11;
acDBBinding[1].iOrdinal = 2;
acDBBinding[1].obValue = offsetof(SPROCPARAMS, inParam);
acDBBinding[1].eParamIO = DBPARAMIO_INPUT;
acDBBinding[1].cbMaxLen = sizeof(long);
acDBBinding[1].wType = DBTYPE_I4;
acDBBinding[1].bPrecision = 11;
acDBBinding[2].iOrdinal = 3;
acDBBinding[2].obValue = offsetof(SPROCPARAMS, outParam);
acDBBinding[2].eParamIO = DBPARAMIO_OUTPUT;
acDBBinding[2].cbMaxLen = sizeof(long);
acDBBinding[2].wType = DBTYPE_I4;
acDBBinding[2].bPrecision = 11;
// Create an accessor from the above set of bindings.
hr = pICommandWithParams->QueryInterface(IID_IAccessor, (void**)&pIAccessor);
if (FAILED(hr))
cout << "Failed to get IAccessor interface.\n";
hr = pIAccessor->CreateAccessor( DBACCESSOR_PARAMETERDATA,
nParams,
acDBBinding,
sizeof(SPROCPARAMS),
&hAccessor,
acDBBindStatus);
if (FAILED(hr))
cout << "Failed to create accessor for the defined parameters.\n";
// Fill in DBPARAMS structure for the command execution. This structure
// specifies the parameter values in the command and is then passed to Execute.
Params.pData = &sprocparams;
Params.cParamSets = 1;
Params.hAccessor = hAccessor;
// Execute the command.
if (FAILED(hr = pICommandText->Execute( NULL,
IID_IRowset,
&Params,
&cNumRows,
(IUnknown **) &pIRowset))) {
cout << "Failed to execute command.\n";
goto EXIT;
}
printf("After command execution but before rowset processing.\n\n");
printf(" Return value = %d\n", sprocparams.lReturnValue);
printf(" Output parameter value = %d\n", sprocparams.outParam);
printf(" These are the same default values set in the application.\n\n\n");
// This result set is not important, so release it without processing.
pIRowset->Release();
printf("After processing the result set...\n");
printf(" Return value = %d\n", sprocparams.lReturnValue);
printf(" Output parameter value = %d\n\n", sprocparams.outParam);
// Release memory.
pIAccessor->ReleaseAccessor(hAccessor, NULL);
pIAccessor->Release();
pICommandWithParams->Release();
pICommandText->Release();
pIDBCreateCommand->Release();
pIDBCreateSession->Release();
if (FAILED(pIDBInitialize->Uninitialize()))
// Uninitialize is not required, but it fails if an interface
// has not been released. This can be used for debugging.
cout << "Problem uninitializing.\n";
pIDBInitialize->Release();
CoUninitialize();
return 0;
EXIT:
if (pIAccessor != NULL)
pIAccessor->Release();
if (pICommandWithParams != NULL)
pICommandWithParams->Release();
if (pICommandText != NULL)
pICommandText->Release();
if (pIDBCreateCommand != NULL)
pIDBCreateCommand->Release();
if (pIDBCreateSession != NULL)
pIDBCreateSession->Release();
if (pIDBInitialize != NULL)
if (FAILED(pIDBInitialize->Uninitialize()))
// Uninitialize is not required, but it fails if an interface has not
// been released. This can be used for debugging.
cout << "Problem in uninitializing.\n";
pIDBInitialize->Release();
CoUninitialize();
}
void InitializeAndEstablishConnection() {
// Initialize the COM library.
CoInitialize(NULL);
// Obtain access to the SQL Server Native Client OLE DB provider.
hr = CoCreateInstance( CLSID_SQLNCLI11,
NULL,
CLSCTX_INPROC_SERVER,IID_IDBInitialize,
(void **) &pIDBInitialize);
if (FAILED(hr))
cout << "Failed in CoCreateInstance().\n";
// Initialize the property values needed to establish the connection.
for (i = 0 ; i < nInitProps ; i++ )
VariantInit(&InitProperties[i].vValue);
//Specify server name.
InitProperties[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
InitProperties[0].vValue.vt = VT_BSTR;
// Replace "MySqlServer" with proper value.
InitProperties[0].vValue.bstrVal = SysAllocString(L"(local)");
InitProperties[0].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[0].colid = DB_NULLID;
// Specify database name.
InitProperties[1].dwPropertyID = DBPROP_INIT_CATALOG;
InitProperties[1].vValue.vt = VT_BSTR;
InitProperties[1].vValue.bstrVal = SysAllocString(L"AdventureWorks");
InitProperties[1].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[1].colid = DB_NULLID;
InitProperties[2].dwPropertyID = DBPROP_AUTH_INTEGRATED;
InitProperties[2].vValue.vt = VT_BSTR;
InitProperties[2].vValue.bstrVal = SysAllocString(L"SSPI");
InitProperties[2].dwOptions = DBPROPOPTIONS_REQUIRED;
InitProperties[2].colid = DB_NULLID;
// Properties are set, construct the DBPROPSET structure (rgInitPropSet) used to pass
// an array of // DBPROP structures (InitProperties) to the SetProperties method.
rgInitPropSet[0].guidPropertySet = DBPROPSET_DBINIT;
rgInitPropSet[0].cProperties = 4;
rgInitPropSet[0].rgProperties = InitProperties;
// Set initialization properties.
hr = pIDBInitialize->QueryInterface( IID_IDBProperties, (void **)&pIDBProperties);
if ( FAILED(hr))
cout << "Failed to obtain IDBProperties interface.\n";
hr = pIDBProperties->SetProperties( nPropSet, rgInitPropSet);
if (FAILED(hr))
cout << "Failed to set initialization properties.\n";
pIDBProperties->Release();
// Now establish a connection to the data source.
if ( FAILED(pIDBInitialize->Initialize()) )
cout << "Problem in initializing.\n";
}
USE AdventureWorks2022;
DROP PROCEDURE myProc
GO