2013-04-20 4 views
7

현재 매우 이상한 일이 있습니다. C++에서 Delphi DLL로 구조체를 매개 변수로 전달할 때 모든 것이 정상적으로 작동합니다. 그러나 결과적으로 레코드를 받고 싶으면 잘못된 값이나 예외가 발생합니다. 레코드 정렬을 비활성화하여 전달하는 것이 효과가 있습니다! 코드가 깔끔합니다!Delphi DLL에서 C++ 로의 함수 결과 전달

델파이 DLL :

TSimpleRecord = packed record 
    Nr1 : Integer; 
    Nr2 : Integer; 
end; 

//... 

function TTest() : TSimpleRecord; cdecl; 
begin 
    Result.Nr1 := 1; 
    Result.Nr2 := 201; 
    ShowMessage(IntToStr(SizeOf(Result))); 
end; 

C++ 호출 :

#pragma pack(1) 
struct TSimpleRecord 
{ 
    int Nr1; 
    int Nr2; 
}; 

//... 

    typedef TSimpleRecord (__cdecl TestFunc)(void); 
    TestFunc* Function; 
    HINSTANCE hInstLibrary = LoadLibrary("Reactions.dll"); 
    if (hInstLibrary) 
    { 
     Function = (TestFunc*)GetProcAddress(hInstLibrary, "TTest"); 
     if (Function) 
     { 
      TSimpleRecord Result = {0}; 
      Result = Function(); 
      printf("%d - %d - %d", sizeof(Result), Result.Nr1, Result.Nr2); 
      cin.get(); 
     } 
    } 

내가 아니라 함수의 결과로 매개 변수 작품으로이 기록을 통과 왜 아무 생각 없어했다!?

아무도 나를 도와 줄 수`

PS

감사 : 내가 말했듯이, C++ 및 레코드가 8 바이트 크다는 것을 델파이 쇼 모두를.

+0

실제로 델파이 함수에서 반환 값을 설정하고 있습니까? – Angew

+0

Result.Nr1 : = 1; Result.Nr2 : = 201; 그 일을해야합니다 – Henry

+0

죄송합니다, 내가 마지막으로 델파이와 상호 작용할 때, '결과'는 여전히 함수 식별자 인 IIRR을 사용했습니다. – Angew

답변

5

일부 컴파일러는 레지스터에 struct 유형 (경우에 따라 크기에 따라 다름)을 반환하고 나머지는 결과를 저장해야하는 숨겨진 추가 매개 변수를 추가합니다. 불행히도, 이들을 반환하는 방법에 동의하지 않는 두 개의 컴파일러를 다루는 것처럼 보입니다.

대신 명시 적으로 out 매개 변수를 사용하여 문제를 피할 수 있어야합니다.

procedure TTest(out Result: TSimpleRecord); cdecl; 
begin 
    Result.Nr1 := 1; 
    Result.Nr2 := 201; 
end; 

그에 따라 C++ 코드를 업데이트해야합니다.

Rudy Velthuis has written about this

는 : EAX (상위 32 비트와 EDX, 그리고 낮은 사람과 EAX) :

이것은 ABCVar 구조체가 레지스터 EDX에 반환 된 것을 나에게 보여 주었다. 이것은 델파이가 레코드로하는 일이 아니며,이 크기의 레코드조차도 아닙니다. Delphi는 이러한 반환 유형을 추가 var 매개 변수로 취급하고 아무 것도 반환하지 않습니다 (따라서 함수는 실제로 프로 시저 임).

[...]

델파이 EDX로 리턴하는 유일한 유형 : EAX 조합 INT64이다. 문제를 방지 할 수있는 또 다른 방법은 델파이 ++도 행동이 C에서 정의되지 않은 것 같은 상황에서 같은 유형의 말장난을 허용

function TTest() : Int64; cdecl; 
begin 
    TSimpleRecord(Result).Nr1 := 1; 
    TSimpleRecord(Result).Nr2 := 201; 
end; 

참고 것을 제안

.

+0

고마워, 정확히 내가 무엇을 찾고 있었는지! Int64를 사용하는 예제가 나에게 적합하지 않더라도 – Henry

+0

@Henry 어떻게 작동합니까? 오류 메시지가 나타나거나 여전히 잘못된 동작을합니까? 독립 실행 형 응용 프로그램에서 결과가 레지스터로 반환된다는 것을 확인했기 때문에 묻습니다. 다행히도 문제를 피하는 다른 방법은 작동하고 있습니다. :) – hvd

+0

Int64 트릭이 작동해야한다는 것에 동의하지만. 그것이 모든 문서를 읽는 방법입니다. –

1

델파이는 반환 값으로 플랫폼 표준 ABI를 따르지 않습니다. 표준 ABI는 값에 따라 호출자에게 반환 값을 전달합니다. 델파이는 반환 값을 암시적인 추가 var 매개 변수로 취급하고 다른 모든 매개 변수 뒤에 전달합니다. documentation에는 규칙이 설명되어 있습니다.

전화 코드를 변경하여 일치시킬 수 있습니다. C++ 함수에서 struct 매개 변수에 대한 추가 참조를 전달하십시오.

typedef void (__cdecl TestFunc)(TSimpleRecord&);  

C++ 측에서이 작업을 수행하려는 경우 명확성을 위해 델파이 측에서 동일한 변경을하는 것이 가장 좋습니다.

Delphi는 반환 값에 대한 플랫폼 표준을 따르지 않으므로 다른 도구와 호환되는 유형으로 제한하는 것이 좋습니다. 이는 최대 32 비트의 정수 값, 포인터 및 부동 소수점 값을 의미합니다.

일반적으로 엄지 손가락으로 레코드를 묶지 마십시오. 그렇게하면 성능에 영향을 미치는 잘못된 정렬이 생깁니다. 질문에서 기록을 위해, 양쪽 필드가 같은 크기이기 때문에 어쨌든 패딩이 없을 것입니다.

관련 문제