2011-12-16 2 views
1

Dll 메소드를 호출 할 때 가끔 예외가 발생하고 때로는 예외가 발생합니다.Delphi DLL 액세스 예외적 예외 발생

내가 이런 식으로 전화 해요 :

public class DllTest 
{ 

    [DllImport(@"MyDll.dll")] 
    public extern static string MyMethod(string someStringParam); 
} 


class Program 
{  

    static void Main(string[] args) 
    { 
     DllTest.MyMethod("SomeString"); 
    } 
} 

그리고 예외를 나는 가끔이있다 얻을 :

AccessViolationException

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

난 단지이 예외가 왜 사람이 어떤 생각을 가지고 있습니까 가끔? 때로는 원활하게 작동하지 않는 이유는 무엇입니까?

+0

DLL의 함수 호출 규칙은 무엇입니까? – ain

+2

델파이 메소드의 코드를 보여주세요 –

+0

델파이에서 함수의 큰 확률 선언은 C#의 카운터와 다르다 –

답변

14

p/invoke 코드와 Delphi 코드가 일치하지 않습니다. Delphi 코드는 표시하지 않았지만 C# 코드는 Delphi 코드의 모양을 알기에 충분합니다.

DllImport 속성은 호출 규칙 및 문자 집합에 기본값을 사용합니다. 즉, 호출 규칙은 stdcall이고 문자 집합은 ANSI입니다. 마샬링 특성을 지정하지 않았으므로 기본 마샬링을 사용해야합니다.

function MyMethod(someStringParam: PChar): PChar; stdcall; 
begin 
    Result := ??; 
end; 

을 그리고 지금 여기에 문제는 다음과 같습니다

따라서 델파이 코드는 다음과 같이해야합니다. p/invoke marshaller는 매우 특별한 방법으로 string 반환 값을 처리합니다. 반환 값의 메모리를 할당 해제하는 것은 p/invoke marshaller의 책임이라고 가정합니다. 그리고 네이티브 코드와 동일한 할당자를 사용해야합니다. 마샬 러가 가정하는 것은 공유 COM 할당자가 사용된다는 것입니다.

그래서 기본 코드는 CoTaskMemAlloc을 호출하여 COM 할당자를 통해 메모리를 할당해야한다는 규칙이 있습니다. 내 코드는 당신의 코드가 그렇게하지 않는다는 것이고 그것은 확실히 오류를 가져올 것입니다.

다음은 코드에서 C# 서명과 함께 작동하는 원시 델파이 함수를 만드는 방법의 예입니다. 이 방법을 채택 할 수있는 동안

function MyMethod(someStringParam: PChar): PChar; stdcall; 
var 
    Size: Integer; 
begin 
    Size := SizeOf(Char)*(StrLen(someStringParam)+1);//+1 for zero-terminator 
    Result := CoTaskMemAlloc(Size); 
    Move(someStringParam^, Result^, Size); 
end; 

나는 대안을 권장합니다. 모든 문자열을 C# 측의 BSTR 및 델파이 측의 WideString으로 마샬링하십시오. COM 할당 자에 의해 할당되는 일치하는 형식입니다. 양 당사자는 이러한 유형으로 무엇을해야하는지 정확히 알고 있으며 인생을 더 쉽게 만듭니다.

델파이는 함수 반환 값으로 다른 ABI를 사용하기 때문에 안타깝게도 interop 경계를 가로 질러 델파이 함수에서 WideString을 반환 할 수 없습니다. 이 문제에 대한 자세한 내용은 내 질문에 있습니다. Why can a WideString not be used as a function return value for interop?

따라서이 문제를 해결하기 위해 델파이 코드의 반환 형식을 TBStr으로 선언 할 수 있습니다.귀하의 코드는 다음과 같을 것이다 : 나를 위해

C#을

[DllImport(@"MyDll.dll")] 
[return: MarshalAs(UnmanagedType.BStr)] 
private static extern string MyMethod(
    [MarshalAs(UnmanagedType.BStr)] 
    string someStringParam 
); 

델파이

function MyMethod(someStringParam: WideString): TBStr; stdcall; 
begin 
    Result := SysAllocString(POleStr(someStringParam)); 
end; 
+0

당신의 대답은 굉장했고 많은 것들을 해결했습니다. 지금 막 extern 메서드를 문자열로 게시 한 것을 알게되었지만 무효화되었습니다. 그러나 그것은 문제가되지 않습니다. 왜냐하면 그것은 당신이 그것을 고려하고 그것을 대답에 추가하게 만들었 기 때문입니다. 그래도 또 다른 예를 들어 주시겠습니까? ref (out)로 전달 된 문자열 매개 변수를 사용하여 Delphi 함수를 어떻게 소비해야합니까? 그리고 오랜 명확하고 놀라운 답변에 많은 감사 드리며, 당신은 정말로 나를 도왔습니다. – Smur

+1

'BSTR' /'WideString'으로 처리하십시오. 이것은 델파이 측의'var' 또는'out' 파라미터와 C# 측의'ref' 또는'out' 파라미터입니다. 실제로,'BSTR' /'WideString'은 이것을 쉽게하는 방법입니다. –

+1

굉장합니다. 다시 한번 고마워. :) – Smur

2

을 UnmanagedType.BStr를 사용하여 닷넷 문자열로 델파이 WideString으로의 마샬링은 아주 잘 작동 In 및 Out 매개 변수의 경우. 그러나 문자열을 반환하는 함수의 경우에는 실패합니다. WS는() 예외가 발생하면서,

[DllImport(@"my.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] 
[return: MarshalAs(UnmanagedType.BStr)] 
static extern string WS(
    [MarshalAs(UnmanagedType.BStr)] 
    string val 
); 

[DllImport("my.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)] 
static extern void WS1(
    [MarshalAs(UnmanagedType.BStr)] 
    out string res); 

WS1에 전화() 잘 작동 -

function WS(val: WideString): WideString; stdcall; 
begin 
    result := val; 
end; 

procedure WS1(out result: widestring); stdcall; 
begin 
    result := 'ABCDE'; 
end; 

과 기자 닷넷 선언 - 나는 델파이 기능을 가지고 있습니다. 예외는 델파이 프로젝트에 포함 된 단위에 따라 다릅니다. "SysUtils"또는 "Classes"가 포함 된 경우 .NET 응용 프로그램에서 SEHException가 발생합니다. "외부 구성 요소에서 예외가 발생했습니다."두 장치가 모두 제외되면 응용 프로그램에 "009C43B4에서 런타임 오류 203"오류 대화 상자가 표시되고 실행이 종료됩니다. . BTW, "ShareMem"단위의 사용은 아무 것도 변경하지 않습니다.

+0

이것은 답변이 아닙니다. 이 질문에 대한 재조명입니다 : http://stackoverflow.com/questions/9349530/why-can-a-widestring-not-be-used-as-a-function-return-value-for-interop –