기술 문서

Delphi E2010 tagLOGBRUSH 및 tagLOGBRUSH32 호환되지 않는 유형 수정

· Delphi 프로그래밍

Delphi XE2 이후 버전은 Windows API 선언의 상당 부분을 범위 지정된 Winapi.Windows 유닛으로 옮기고 여러 레코드 선언을 더 엄격하게 만들었습니다. 따라서 이전 Delphi 버전에서 컴파일되던 코드도 Enhanced Metafile 레코드가 tagLOGBRUSH32 값을 노출하지만 GDI API가 tagLOGBRUSH 또는 TLogBrush를 기대하는 경우 실패할 수 있습니다.

이 증상은 보통 EMF 레코드를 재생하거나 PEMRCreateBrushIndirect에서 GDI 브러시 개체를 다시 만들 때 나타납니다. 다음과 같은 직접 호출은 합리적으로 보이지만, 최신 Delphi 컴파일러에서는 두 레코드 유형이 대입 호환되지 않습니다.

1
2
3
4
5
procedure VEMRCREATEBRUSHINDIRECT(Data: PEMRCreateBrushIndirect);
begin
  GDIObjects[Data^.ihBrush] := CreateBrushIndirect(Data^.lb);
//GDIObjects and CreateBrushIndirect are defined in the winapi.windows unit.
end;

컴파일러는 E2010 Incompatible types: 'tagLOGBRUSH' and 'tagLOGBRUSH32'를 보고합니다. 안전하지 않은 캐스트로 레코드를 강제로 넘기지 마십시오. 더 안전한 수정은 로컬 TLogBrush를 만들고 호환되는 필드를 명시적으로 복사한 뒤 그 구조체를 CreateBrushIndirect에 전달하는 것입니다.

1
2
3
4
5
6
7
8
9
procedure VEMRCREATEBRUSHINDIRECT(Data: PEMRCreateBrushIndirect);
var
  LogBrush: TLogBrush;
begin
  LogBrush.lbStyle := Data^.lb.lbStyle;
  LogBrush.lbColor := Data^.lb.lbColor;
  LogBrush.lbHatch := Data^.lb.lbHatch;
  GDIObjects[Data^.ihBrush] := CreateBrushIndirect(LogBrush);
end;

명시적 복사가 더 안전한 이유

tagLOGBRUSH32 는 EMF 레코드 형식에서 사용되어 직렬화된 메타파일 데이터가 안정적인 32비트 필드 레이아웃을 갖도록 합니다. CreateBrushIndirect는 현재 API 선언에서 사용하는 일반 Windows 브러시 구조체를 받습니다. lbStylelbColorlbHatch 를 복사하면 변환이 문서화되고 컴파일러별 레코드 호환성에 의존하지 않게 됩니다.

이 패턴은 레거시 그래픽 코드를 Unicode Delphi 또는 64비트 대상으로 이식할 때도 검토하기 쉽습니다. 나중에 Windows SDK 선언에서 형식 별칭이 다시 바뀌더라도, 필드별 변환은 의도한 데이터 경계를 명확하게 보여 줍니다.

이식 체크리스트

  • 최신 Delphi 프로젝트의 uses 목록에는 Winapi.Windows 를 유지합니다.
  • EMF 레코드 필드를 GDI 호출이 기대하는 API 구조체로 복사합니다.
  • 바이너리 레이아웃과 수명을 확인하지 않았다면 이를 무조건적인 포인터 캐스트로 바꾸지 마십시오.
  • 브러시를 만든 후에는 기존 GDIObjects 테이블의 개체 소유 규칙을 유지합니다.

Delphi 업그레이드 중 이 문제가 나타나는 이유

레거시 EMF 재생 코드는 Windows 레코드를 API 구조체와 직렬화된 메타파일 구조체가 서로 바꿔 쓸 수 있는 것처럼 취급하는 경우가 많습니다. 이전 유닛과 느슨한 별칭은 때때로 이런 가정이 컴파일되게 했습니다. 최신 Delphi 선언은 더 엄격하므로 Windows API에 이미 존재하던 경계를 컴파일러가 드러냅니다.

따라서 이 오류는 단순한 구문 문제가 아닙니다. 코드가 파일 형식 구조체에서 실제 GDI 호출로 넘어가고 있다는 유용한 경고입니다. 변환을 명시적으로 유지하면 나중에 64비트 빌드, Unicode 마이그레이션 또는 업데이트된 Windows SDK 선언을 검토할 때 코드가 더 안전합니다.

하지 말아야 할 일

포인터 캐스트는 컴파일러 오류를 없애는 것처럼 보일 수 있지만 실제 변환을 숨기고 이후 유지보수를 어렵게 만듭니다. 또한 어떤 필드를 의도적으로 사용하는지 검토자가 알 수 없습니다. 필드 복사는 짧고 결정적이며 스스로 설명되므로 애플리케이션 코드와 컴포넌트 샘플에 더 나은 기본 선택입니다.

EMF 파서가 비슷한 레코드를 많이 처리한다면 이 변환을 작은 도우미 함수로 감싸고 여러 브러시 스타일로 테스트하십시오. 그러면 재생 루프를 읽기 쉽게 유지하면서도 Windows API 경계를 명확하게 둘 수 있습니다.