2017-12-11 18 views
1

CLR에서 특정 API 함수를 사용할 수 있도록 C# 어셈블리를 통해 네이티브 코드 API (C++ .dll)를 실행하려고합니다. SQL Server의 저장 프로 시저. C++ dll에서 사용하려고 시도하는 함수는 데이터 기록자의 원시 데이터에 액세스하고 관리되지 않는 형식의 데이터를 반환합니다. 그런 다음 결과를 SQL Server에 마샬링하고 파이프하기 위해 C# 어셈블리에 남아 있습니다.SQL Server 2016 CLR 저장 프로 시저 오류 : "시스템 어설 션 검사가 실패했습니다"

저는 C++ dll에 대한 소스 코드가 없으므로, 실제적으로 후드 (써드 파티)가 정확히 무엇인지 모릅니다. 그러나 C# 콘솔 앱에서이 API 함수에 문제없이 액세스 할 수 있습니다 (.NET에서 C++ dll을 래핑하기 위해 https://lennilobel.wordpress.com/2014/01/29/calling-c-from-sql-clr-c-code/에 의존했습니다). C# 콘솔 응용 프로그램을 개발 한 다음 클래스 라이브러리로 변환하고 "[Microsoft.SqlServer.Server.SqlProcedure]"클래스를 래핑하고 UNSAFE 모드에서 원하는 SQL 데이터베이스에 어셈블리를 추가했습니다. 또한 clr이 SQL 서버에서 활성화되어 있고 TRUSTWORTHY가 사용중인 데이터베이스에서 해제되어 있는지 확인했습니다.

그러나 C# 어셈블리를 사용하는 저장 프로 시저를 호출하려고하면 다음과 같은 문제가 발생합니다.

Location: AppDomain.cpp:2705 
Expression: hr != E_POINTER 
SPID:  66 
Process ID: 3584 
Msg 3624, Level 20, State 1, Procedure sp_direct_proficy_api, Line 0 [Batch Start Line 2] 
A system assertion check has failed. Check the SQL Server error log for details. Typically, an assertion failure is caused by a software bug or data corruption. To check for database corruption, consider running DBCC CHECKDB. If you agreed to send dumps to Microsoft during setup, a mini dump will be sent to Microsoft. An update might be available from Microsoft in the latest Service Pack or in a Hotfix from Technical Support. 
Msg 596, Level 21, State 1, Line 2 
Cannot continue the execution because the session is in the kill state. 
Msg 0, Level 20, State 0, Line 2 
A severe error occurred on the current command. The results, if any, should be discarded. 

시스템 어썰트 점검에 대해 몇 가지 Google 검색을 수행했으며 일반적으로 데이터베이스 손상으로 인해 발생하는 것으로 나타났습니다. DBCC CHECKDB를 실행 했으므로 문제가되지 않습니다. 필자는 Leonard의 예제 (위의 링크에서 본)를 본질적으로 훨씬 단순한 C++ dll로 착수 한 것과 동일한 프로세스로 복제했습니다. 이 예제에서는 오류가 발생하지 않았으므로 SQL Server와 C++ API간에 appdomain에 대한 경쟁이 존재한다고 생각합니다.

내 질문

이 내가 할 시도하고있는 무슨에 대한 예상 문제인가? CLR 저장 프로 시저를 사용할 때 SQL Server가 컴퓨터 메모리에 액세스하고 appdomain을 요청하는 방법에 대해 많이 알지 못하지만 SQL Server와 C++ API간에 몇 가지 유해한 리소스 경쟁이있는 것처럼 보입니다.

다음은 C# 어셈블리의 두 부분 (C# harness에서 C++ dll을 호출하고 저장 프로 시저에서 액세스 할 클래스)입니다. 액세스 C에 저장 프로 시저에 사용되는 C++

public class IHUAPI 
{ 
const string DLLNAME = "IHUAPI.dll"; 

static class IHU64 
{ 

    [DllImport(DLLNAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "[email protected]")] 
    public static extern ihuErrorCode ihuConnect(string server, string username, string password, out int serverhandle); 

    [DllImport(DLLNAME, CallingConvention = CallingConvention.StdCall, EntryPoint = "ihuReadRawDataByTime")] 
    public static extern ihuErrorCode ihuReadRawDataByTime(int serverhandle, string tagname, ref IHU_TIMESTAMP start, ref IHU_TIMESTAMP end, out int numberOfSamples, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4, ArraySubType = UnmanagedType.LPStruct)] out IHU_DATA_SAMPLE[] samples); 
} 

public static ihuErrorCode ihuConnect(string server, string username, string password, out int serverhandle) 
{ 

     return IHU64.ihuConnect(server, username, password, out serverhandle); 
} 

public static ihuErrorCode ihuReadRawDataByTime(int serverhandle, string tagname, IHU_TIMESTAMP start, IHU_TIMESTAMP end, out IHU_DATA_SAMPLE[] samples) 
{ 
     int numberOfSamples; 
     return IHU64.ihuReadRawDataByTime(serverhandle, tagname, ref start, ref end, out numberOfSamples, out samples); 
} 
} 

C# 국회에서

C# DLL 가져 오기 ++ API

[Microsoft.SqlServer.Server.SqlProcedure] 
public static void API_Query(string tagname, DateTime start_date, DateTime end_date) 
{ 

    int handle; 
    ihuErrorCode result; 
    result = IHUAPI.ihuConnect("houmseosprf007", "", "", out handle); 
    IHU_DATA_SAMPLE[] values; 
    IHU_TIMESTAMP start = new IHU_TIMESTAMP(start_date); 
    IHU_TIMESTAMP end = new IHU_TIMESTAMP(end_date); 

    ihuErrorCode result_api = IHUAPI.ihuReadRawDataByTime(handle, tagname, start, end, out values); 

    SqlMetaData[] md = new SqlMetaData[3]; 
    md[0] = new SqlMetaData("tagname", SqlDbType.Text); 
    md[1] = new SqlMetaData("return_value", SqlDbType.NVarChar, 50); 
    md[2] = new SqlMetaData("timestamp", SqlDbType.DateTime); 
    SqlDataRecord row = new SqlDataRecord(md); 
    SqlContext.Pipe.SendResultsStart(row); 

    DateTime p; 
    string p2; 

    for (int i = 1; i < (values == null ? 0 : values.Length); i++) 
    { 

     using (IHU_DATA_SAMPLE sample = values[i]) 
     { 
      if (sample.ValueDataType != ihuDataType.Array) 
      { 
       p = sample.TimeStamp.ToDateTime(); 
       p2 = sample.ValueObject.ToString(); 
       row.SetValue(0, tagname); 
       row.SetValue(1, p2); 

       row.SetValue(2, p); 

      } 
      else 
      { 

       p = sample.TimeStamp.ToDateTime(); 
       ihuArrayValue aValue = (ihuArrayValue)Marshal.PtrToStructure(sample.Value.ArrayPtr, typeof(ihuArrayValue)); 
       p2 = aValue.GetArrayValue.ToString(); 
       row.SetValue(0, tagname); 
       row.SetValue(1, p2); 
       row.SetValue(2, p); 


      } 
     } 

     SqlContext.Pipe.SendResultsRow(row); 
    } 

    SqlContext.Pipe.SendResultsEnd(); 
} 

답변

1

내가 시도하고있는 것으로 예상되는 문제입니까?

나는 "하지 않은 기대"등 너무 많은 "기대"언급하지 않았다, 또는 "놀라게해서는 안됩니다." 제 3 자 라이브러리가 격리되어있을 때 명확하게 작동하지만 SQL Server의 CLR 호스트에서 시작될 때는 받아 들여지지 않는 무언가를하고 있습니다. SQL Server의 CLR 호스트가 그대로 제한 될 수있는 좋은 이유가 있습니다.

그래서 "IHU"에 연결되어있는 서비스를 호스팅하는 서버에서 실행되는 웹 서비스로이 타사 C++ 라이브러리와 원래의 (작동중인) C# 래퍼를 호스트해야합니다. 그런 다음 SQLCLR 코드의 경우 HttpWebRequestHttpWebResponse을 사용하여 해당 웹 서비스를 호출하십시오. SendResultsRow() 루프의 응답 XML/JSON을 구문 분석하십시오.

하면 UNSAFE :-) 필요하지 않습니다처럼 EXTERNAL_ACCESS 일 업데이트 된 SQLCLR 코드의 PERMISSION_SET를 설정해야합니다, 당신은 여전히 ​​명령을 호출하기 위해 밖으로 껍질 필요없이 쿼리 일괄 처리에서 매우 즉각적인 응답을 얻을 또는 xp_cmdshell을 통해 온라인으로 보거나 SSIS를 호출하거나 작업을 예약 할 수도 있습니다.

+0

좋은 조언. 저장 프로 시저가있는 SQL Server 인스턴스를 호스팅하는 상자에서 웹 서비스를 호스팅하는 것과 동일한 문제가 발생합니까? – bidout

+0

나는 믿지 않는다.하지만 시험해보아야 할 것이다. 같은 서버를 사용하는 것이 좋지만 SQL Server가 실제로하는 일은 어떤 일을 할 수 있는지에 대한 의견보다는 더 중요합니다. "IHU"서비스가 SQL Server와 동일한 서버에 이미있는 경우이 웹 서비스를 실행하여 원격 서비스에서 원래 서버로 다시 전화를 걸도록 네트워크를 호출하지 않아도됩니다. . 그러나 "IHU"가 별도의 서버에 있다면 SQL Server가 얻을 수있는만큼의 실제 RAM을 선호하므로 웹 서비스에서 낭비하는 이유는 무엇입니까? –

+0

비 관리 코드 부분에 대한 웹 API를 작성했습니다. 저장 프로시 저는 클라이언트를 호출하고 클라이언트는 C++ dll을 호출하는 웹 API를 호출합니다. 저장 프로 시저를 실행하는 동일한 컴퓨터에서 호스팅을 테스트했으며 지금까지 문제없이 작동했습니다. 위에서 언급 한 RAM상의 이유 때문에 API 서버를 다른 서버로 마이그레이션하게 될 것입니다. – bidout

1

SQL Server와 C++ API간에 몇 가지 유해한 리소스 경쟁이있는 것처럼 보입니다.

예. SQL CLR에서 관리되지 않는 DLL을 실제로 사용하면 안됩니다. CLR 관리 코드는 메모리 안전하며 SQLCLR은 사용자 지정 관리 코드에서 발생하는 모든 문제로부터 SQL Server를 보호하도록 설계되었습니다. 그러나 관리되지 않는 코드를 사용하는 경우에는 안전성이 보장되지 않으며 SQL Server를 중단시킬 수도 있습니다.

SQL Server 프로세스와 별도로 수명이 짧은 클라이언트 프로세스에서 관리되지 않는 DLL을로드하고 클라이언트 프로세스가 종료 될 때 타사 DLL의 메모리 문제를 정리할 수 있도록 단명하십시오. SSIS는 이런 식으로 호스트하고 실행할 수있는 간단한 방법입니다.