델파이의 두 가지 BUG 분석 및 수리
3티어 데이터베이스 개발을 위해 델파이 7을 사용하다가 두 가지 작은 문제에 부딪혔는데, 반복적인 시도를 통해 마침내 델파이 7에서 두 개의 작은 버그를 발견하고 이를 수정했습니다(델파이 6에서도 같은 버그가 있는 것 같습니다). 이 기사에서는 성공의 기쁨을 모든 사람과 공유합니다. 저도 델파이를 처음 접해서 글에 잘못된 부분이 많을 것 같아 수정해 달라고 부탁하고 싶습니다.
BUG1. 매개변수를 전달할 때 한자가 잘립니다.
BUG를 재현하는 방법:
백그라운드에서는 SQL Server 2000이 사용되며, 테스트용 XsHeTong 테이블이 있어 실제 상황에 맞게 조정할 수 있습니다.
먼저 데이터 서버를 만듭니다. 새 프로젝트를 만들고 원격 데이터 모듈을 만들고 여기에 ADOConnection, ADODataSet 및 DataSetPRovider를 배치하고 해당 설정을 지정합니다. ADODataSet의 ComamndText를 비워 두고 해당 옵션에서 poAllowCommandText를 True로 설정합니다. 컴파일하고 실행합니다.
클라이언트 프로그램을 다시 생성합니다. 새 프로젝트를 생성하고, 양식에 DCOMConnection을 배치하고, 이전에 생성된 데이터 서버에 연결하고, ClientDataSet을 배치하고, 여기에 DCOMConnection에 대한 연결을 설정하고, 해당 ProviderName을 위의 서버로 설정합니다. DataSetProvider. 마지막으로 DataSource와 DBGrid를 배치하고 해당 설정을 하여 결과를 확인한 후 테스트용 Button을 배치합니다.
Button의 OnClick에서 다음과 유사한 코드를 작성합니다(여기서는 XsHeTong 테이블과 해당 테이블의 두 필드 HTH(문자 15), GCMC(varchar 100)를 사용했으며 실제 테스트 상황에 따라 조정할 수 있습니다).
ClientDataSet1을 사용하면
시작하다
닫다;
CommandText := 'XsHeTong(HTH, GCMC) 값에 삽입(:HTH,:GCMC)';
Params[0].AsString := '12345';
Params[1].AsString := '잘릴 중국어 문자';
실행하다;
닫다;
CommandText := 'XsHeTong에서 * 선택';
열려 있는;
끝;
프로그램을 실행하고 버튼을 누르면 레코드가 삽입된 것을 확인할 수 있는데, 아쉽게도 "잘라낼 한자"가 "잘리겠습니다"로 되어 있는데, 한자가 없는 "12345"가 제대로 삽입되어 있습니다. .
버그 분석 및 복구:
비교를 위해 ADOConnection, ADOCommand, ADOTable을 직접 사용해 C/S 아키텍처를 테스트해보았는데 결과가 정확했고 한자가 잘리지 않았습니다. 이는 이 버그가 3계층 아키텍처에만 나타나는 것을 보여줍니다.
SQL Server 프로파일러를 사용하여 실행을 위해 SQL Server에 제출된 문을 조사하고 2계층 아키텍처와 3계층 아키텍처 간의 다음 차이점을 찾아보세요.
2계층 아키텍처:
exec sp_executesql N'Insert into XsHeTong(HTH, GCMC) 값(@P1,@P2)', N'@P1 varchar(15),@P2 varchar(100)', '12345', '잘릴 한자 '
3계층 아키텍처:
exec sp_executesql N'XsHeTong(HTH, GCMC) 값(@P1,@P2)에 삽입', N'@P1 varchar(5),@P2 varchar(7)', '12345', '잘립니다.
당연히 2계층 구조에서는 실제 라이브러리 구조에 따라 매개변수의 길이가 전달되며, 3계층 구조에서는 실제 매개변수의 문자열 길이에 따라 매개변수의 길이가 전달된다. 문자열 길이가 잘못 계산된 것 같습니다. 한자는 길이가 두 글자로 처리됩니다.
델파이의 VCL 라이브러리를 디버깅하려면 프로젝트 옵션의 "컴파일러 옵션"에서 "디버그 DCU 사용"을 선택해야 합니다.
먼저 클라이언트 프로그램을 추적한 다음 ClientDataSet1.Execute를 추적한 다음 AppServer.AS_Execute(ProviderName, CommandText, Params, OwnerData)가 나올 때까지 TCustomClientDataSet.Exectue, TCustomeClientDataSet.PackageParams, TCustomClientDataSet.DoExecute 등과 같은 일련의 함수를 수행합니다. 서버에 요청을 제출합니다. 서버 측에 문제가 있는 것 같습니다.
서버를 추적하고 반복적으로 시도한 후에 TCustomADODataSet.PSSetCommandText 함수에 집중했습니다. 반복적이고 세부적인 추적 후에 대상은 TCustomADODataSet.PSSetParams, TParameter.Assign, TParameter.SetValue, VarDataSize와 같이 더욱 정확해졌습니다. 마침내 BUG의 소스인 VarDataSize 함수를 찾았습니다. 해당 코드는 다음과 같습니다.
function VarDataSize(const Value: OleVariant): 정수;
시작하다
VarIsNull(값)이면
결과 := -1
그렇지 않은 경우 VarIsArray(값)이면
결과 := VarArrayHighBound(값, 1) + 1
그렇지 않은 경우 TVarData(Value).VType = varOleStr이면
시작하다
Result := Length(PWideString(@TVarData(Value).VOleStr)^); //문제가 있는 라인
결과 = 0이면
결과 := -1;
끝
또 다른
결과 := SizeOf(OleVariant);
끝;
실제 매개변수의 길이가 계산되는 함수입니다. Value에 있는 값의 주소를 가져와서 이를 WideString 포인터로 사용하여 문자열의 길이를 찾습니다. be truncated"는 길이가 14가 아닌 7이 됩니다.
문제가 발견되면 해결하는 것은 어렵지 않습니다.
Result := Length(PWideString(@TVarData(Value).VOleStr)^); //문제가 있는 라인
다음으로 변경
결과 := Length(PAnsiString(@TVarData(Value).VOleStr)^); //문제 없음
그게 다야.
그러나 이렇게 하면 영어 문자열의 길이를 찾을 때 길이가 두 배가 되므로 이 줄을 다음과 같이 변경할 수도 있습니다.
결과 := 길이(값);
이렇게 하면 중국어, 영어, 한-영 혼합 문자열이든 정확한 길이를 얻을 수 있습니다. 이것은 아직도 저를 곤혹스럽게 만드는 질문입니다. 왜 볼랜드는 포인터를 통해 매개변수 값의 길이를 찾기 위해 원을 그리며 돌아다니나요? 혹시 아시는 분 계시면 설명 부탁드립니다!
일부 친구는 3계층 아키텍처를 통해 수행되지 않으면 왜 문자열 잘림 문제가 발생하지 않는지 질문할 수 있습니다. 대답은 복잡하지 않습니다. ADOCommand를 통해 SQL Server에 직접 명령을 보낼 때 테이블 구조에 따라 매개 변수 길이를 결정합니다. 먼저 SQL Server에 메시지를 보냅니다.
SET FMTONLY ON XsHeTong에서 HTH,GCMC 선택 SET FMTONLY OFF
테이블 구조를 얻으려면. 3계층 아키텍처에서는 TCustomADODataSet도 내부적으로 TADOCommand 개체를 사용하여 명령을 실행하지만 테이블 구조를 얻은 후 이 값을 매개변수 길이로 사용하지 않고 실제 매개변수를 기반으로 길이를 다시 계산하는 결과가 나왔습니다. 오류.
BUG2. ClientDataSet의 조회 필드에 문제가 있습니다.
BUG를 재현하는 방법:
새 프로젝트를 생성하고 여기에 두 개의 ClientDataSet(cds1 및 cds2)를 배치합니다. 해당 데이터 소스는 임의적일 수 있습니다. 그 중 cds1이 기본 데이터 세트입니다. 이 조회 필드는 문자 필드를 기반으로 합니다. cds1.value에서 cds2에서 해당 값을 찾습니다.
일반적으로 프로그램을 실행하는 것은 정상적인 일이지만 cds1의 Lookup 필드 값에 작은따옴표 "'"가 나타나면(레코드 수정 또는 추가 가능, 작은따옴표 입력 시도) 즉시 오류가 발생합니다. : 종료되지 않은 문자열 상수입니다.
버그 분석 및 복구:
이 BUG의 원인은 이전 버그보다 훨씬 더 분명합니다. 작은따옴표의 부작용을 제대로 처리하지 못해서 발생했을 것입니다.
마찬가지로 VCL의 소스 코드를 추적해 보겠습니다.
프로그램을 실행하고 오류가 발생하면 호출 스택 창(View->Debug Windows) 메뉴를 열어서 이전 호출 중 일부가 명확하고 관련된 부분부터 시작해 보겠습니다. 원인을 확인하기 위해 Lookup으로 첫 번째로 Lookup과 관련된 함수 호출은 TField.CalcLookupValue입니다. 이 함수에 중단점을 설정하고 프로그램을 다시 실행한 후 중단 후 단일 단계 디버깅을 수행합니다.
TCustomClientDataSet.Lookup->TCustomClientDataSet.LocateRecord
위의 여러 함수 호출 후 LocateRecord 프로세스에서 대상을 빠르게 설정합니다. 이 프로세스에서는 Lookup 필드의 설정을 기반으로 해당 필터 조건을 생성한 다음 해당 필터 조건을 대상 데이터 세트에 추가합니다. 발견되었으며 결함은 필터 조건 생성에 있습니다. 예를 들어, cds1의 Cust 필드(001로 가정) 값을 기반으로 cds2로 이동하고 CustID 필드 값을 기반으로 해당 CustName 필드 값을 찾아야 합니다. 생성된 조건은 [CustID] = '001'이어야 하지만 Cust의 값이 aa'bb인 경우 생성된 조건은 [CustID] = 'aa'bb'가 되며 이는 분명히 완료되지 않은 문자열 상수로 이어집니다.
일반적으로 작은따옴표 안에 작은따옴표가 나타나는 문제를 해결할 때 생성된 조건이 [CustID] = 'aa''bb'인 한 여기서도 마찬가지입니다. 오류가 발생하지 않습니다. 따라서 소스 코드를 다음과 같이 수정할 수 있습니다.
LocateRecord 프로시저에서 다음 코드를 찾으세요.
ftString, ftFixedChar, ftWideString, ftGUID
if (i = Fields.Count - 1) 및 (옵션의 loPartialKey) then
ValStr := Format('''%s*''',[VarToStr(값)]) else
ValStr := Format('''%s''',[VarToStr(값)]);
다음으로 변경:
ftString, ftFixedChar, ftWideString, ftGUID:
if (i = Fields.Count - 1) 및 (옵션의 loPartialKey) then
ValStr := Format('''%s*''',[ StringReplace(VarToStr(값),'''','''''',[rfReplaceAll])])
또 다른
ValStr := Format('''%s''',[ StringReplace(VarToStr(Value),'''','''''',[rfReplaceAll])]);
즉, 필터 조건 문자열을 생성할 때 조건의 필터 값에 있는 모든 작은따옴표를 1에서 2로 변경합니다.
이 수정 사항의 정확성을 확인하기 위해 TCustomADODataSet에서 해당 LocateRecord 프로세스를 확인했습니다(TADODataSet에서 Lookup 필드를 사용할 때 작은따옴표로 인한 오류는 없으며 TCustomClientDataSet을 사용할 때만). 처리 방법은 다음과 같습니다. TCustomClientDataSet은 약간 다릅니다. GetFilterStr 함수를 통해 필터 조건을 구성하지만 GetFilterStr에서는 작은따옴표 문제를 올바르게 처리합니다. 따라서 이런 식으로 보면 TCustomClientDataSet의 LocateRecord에서 작은따옴표를 올바르게 처리하지 못하는 문제는 실제로 Borland에서 사소한 누락입니다.