2016-09-25 6 views
11

Win32 플랫폼의 Delphi 응용 프로그램에서 FPU Control Word에서 무슨 일이 일어나는지 이해해 주시겠습니까?FPU로 시작하는 응용 프로그램이 왜 Default8087CW와 다른 제어 워드입니까?

새 VCL 응용 프로그램을 만들 때 제어 단어는 1372h로 설정됩니다. 이것이 내가 이해할 수없는 첫 번째 이유입니다. 이유는 1332h가 1330h가 아니고 Default8087CWSystem 단위로 정의 된 것입니다.

두 차이 :

1001101110010 //1372h 
1001100110010 //1332h 

이 문서에 따라 예약되거나 사용되지 6 비트이다.

두 번째 질문은 CreateOleObject입니다.

function CreateOleObject(const ClassName: string): IDispatch; 
var 
    ClassID: TCLSID; 
begin 
    try 
    ClassID := ProgIDToClassID(ClassName); 
{$IFDEF CPUX86} 
    try 
     Set8087CW(Default8087CW or $08); 
{$ENDIF CPUX86} 
     OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or 
     CLSCTX_LOCAL_SERVER, IDispatch, Result)); 
{$IFDEF CPUX86} 
    finally 
     Reset8087CW; 
    end; 
{$ENDIF CPUX86} 
    except 
    on E: EOleSysError do 
     raise EOleSysError.Create(Format('%s, ProgID: "%s"',[E.Message, ClassName]),E.ErrorCode,0) { Do not localize } 
    end;  
end; 

상기 기능 137Ah에 제어 워드를 변경하고, 그래서 제 3 비트 (오버플 마스크)를 온한다. 함수에 들어가기 전에 있던 단어의 상태를 복원하는 대신 왜 Reset8087CW을 호출하는지 이해할 수 없습니까?

답변

5

제 6 비트는 예약되어 무시됩니다. 이 두 제어 단어는 실제로 FPU가 동일하게 동작한다는 의미에서 동일합니다. 시스템은 단지 예약 된 비트를 설정합니다. 값을 $1332으로 설정하려고해도 시스템에서이 값을 $1372으로 설정합니다. 6 번째 비트에 어떤 값을 지정하든 관계없이 항상 설정됩니다. 따라서이 값들을 비교할 때 그 비트를 무시해야합니다. 여기에 대해 걱정할 사항이 없습니다.

CreateOleObject에 대해 작성자는 해당 함수를 사용하려고하면 COM 객체를 사용할 때 실제로 오버 플로우를 마스크하려고한다고 결정했습니다. 그들이 왜 그렇게했으며, 32 비트 코드에 대해서만 누가 알 수 있습니까? 아마도 그들은 일상적으로 오버플로되는 COM 객체들을 발견했을 것입니다. 그래서이 고집적 석고를 추가했습니다. 생성시 오버플로를 가려주는 것만으로는 충분하지 않았으므로 객체를 사용할 때 RTL 설계자가 오버플로를 언 마스킹하기로 선택했을 때 수행해야합니다.

또는 아마도 버그 였을 것입니다. 그들은 사람들이 행동에 의존했기 때문에 32 비트 코드를 고치지 않기로 결정했으나 64 비트 코드를 수정했습니다.

어쨌든이 기능은 특별히 특별한 일을하지 않습니다. 당신은 그것을 사용할 필요가 없습니다. 당신이 원하는 것을하는 당신 자신의 것을 쓸 수 있습니다.

interop로 작업 할 때 부동 소수점 컨트롤이 문제가됩니다. Delphi 코드는 마스크되지 않은 예외를 예상합니다. 일반적으로 다른 도구로 작성된 코드는이를 마스크합니다. 이상적으로는 Delphi 코드를 호출하고 반환 할 때 마스크를 해제 할 때 예외를 마스크하는 것이 좋습니다. 다른 라이브러리가 임의로 제어 단어를 변경하도록 기대하십시오. 또한 Set8087CW은 스레드 안전성이 없으므로 Embarcadero가 수년 동안 언급하지 못한 큰 문제입니다.

쉬운 길은 없습니다. 프로그램에서 부동 소수점을 사용하지 않는다면 예외를 그냥 가려서 잘 처리 할 수 ​​있습니다. 그렇지 않으면 제어 워드가 모든 스레드의 모든 지점에서 적절하게 설정되어 있는지 확인해야합니다. 일반적으로 표준 Delphi RTL을 사용하는 것은 불가능합니다. 개인적으로 RTL의 핵심 부분을 threadsafe 버전으로 대체하여이 문제를 해결합니다. 이 QC 보고서에서 이렇게하는 방법을 문서화했습니다 : QC#107411.

+0

파일을 QC 보고서에 다운로드했습니다. 파일 이름은 잘리고 확장되지 않습니다. '.zip'을 추가하면 그 문제가 해결되었습니다. @AllenBauer가 탑재 된 상태에서 오랜 RTL 버그를 고칠 수 없었습니다. –

+2

@LURD 그가 원했던 것 같지만 경영진은 그것을 허용하지 않을 것이라고 생각합니다. –

+0

고마워, 그게 내가 생각한거야. CW를 $ 1332 tho로 설정할 가능성이 없다는 것은 내 마음에 오지 않았다. – Wodzu

1

면책 조항 : Delphi XE에서 질문을 디버깅했습니다.

먼저 두 번째 질문입니다. 당신이 Set8087CW의 코드를 보면

당신은 Default8087CW 변수에 새로운 FPU CW 값을 저장하는 것을 볼 수 있습니다, 그리고 Reset8087CWDefault8087CW에서 FPU CW를 복원; 그래서 Set8087CWReset8087CW 호출은 분명히

Memo1.Lines.Clear; 
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 1372 
Set8087CW(Default8087CW or $08); 
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 137A 
Reset8087CW; 
Memo1.Lines.Add(IntToHex(Get8087CW, 4)); // 137A 

버그에 의해 입증되는, 전혀 아무것도하지 않는다.

이제 첫 번째 질문 - 흥미로운 디버깅 연습이었습니다.

델파이 VCL 애플리케이션의 Default8087CWControls.pas 유닛의 초기화 부에서 호출 TApplication.Create에서 호출 Classes.AllocateHWnd에서 호출 Windows.CreateWindowEx 함수 1,372 1,332 진수로 변경된다.

CreateWindowEx 코드를 살펴보십시오. 어떤 일이 발생하는지 설명합니다. 필자는 더 이상 논의하고 싶지 않습니다. Delphi의 FPU 지원은 너무 복잡하고 버그가 있습니다.

+0

'Reset8087CW'는 실제로 뭔가를합니다. 'Default8087CW'에서 현재 발견 할 수있는 것으로 제어 단어를 설정합니다. 'CoCreateInstance'가 제어 단어를 수정하면 종종 그렇게 될 수 있습니다. 그리고 다른 분석은 잘못되었습니다. 'CreateWindowEx'는'Set8087CW'를 호출하지만'Get8087CW' 호출에 의해 반환 된 값을 전달합니다. FP 유닛이'$ 1332 '를 갖게되어 행운을 비네. 그것은 단지 당신이 그렇게하도록 내버려 두지 않을 것입니다. –

+0

이렇게 좀 더 자세히 디버깅하면 FPU가 'System'의 초기화 섹션에서 만들어진'_FpuInit' 호출에서 초기화된다는 것을 알 수 있습니다. 'Default8087CW', 보통'$ 1332'에있는 내용을 전달하지만 시스템은 6 번째 비트를 강제 설정하므로 값 설정은 $ 1372입니다. 'CreateWindowEx'에 대한 어떤 호출도 할 필요가 없습니다. –

+0

@DavidHeffernan - 분석이 정확합니다. 디버거에서'Default8087CW'가 어떻게 변경되는지 추적했습니다. FPU CW는 결코'$ 1332'로 설정되지 않고'CreateWindowEx'는 FPU CW를 읽는 것에서'$ 1372'를 가져 와서'Default8087CW'를'$ 1372'로 설정합니다. – kludg