2013-04-18 4 views
2

네이티브 dll에 C# 콜백 함수를 전달하려고합니다. 그것은 잘 작동하지만 콜백 메서드의 부모 개체에 액세스 할 수있는 방법을 찾을 수 없습니다.네이티브 dll에 전달 된 C# 콜백에서 관리 객체에 액세스

class MyForm: Form { 
    public delegate void CallbackDelegate(IntPtr thisPtr); 

    [DllImport("mylib.dll", CallingConvention = CallingConvention.StdCall)] 
    public static extern void Test(CallbackDelegate callback); 

    int Field; 

    static void Callback(IntPtr thisPtr) 
    { 
     // I need to reference class field Field here. 
     MyForm thisObject = ?MagicMethod?(thisPtr); 

     thisObject.Field = 10; 
    } 

    void CallExternalMethod() 
    { 
     Test(Callback); 
    } 
} 

내가 this의 포인터를 얻는 시도하지만 다음과 같은 예외를 가지고 : 여기에 내가하고 싶은 것을 보여줍니다하는 코드입니다. "개체가 아닌 원시 또는 비 blittable 데이터를 포함합니다." 아마도 부모 개체가 WindowsForms 폼이라는 것을 언급해야합니다.

UPDATE DLL이 델파이 및 테스트 기능의 서명에 기록 된

는 다음 나는 포인터를 얻기 위해 시도 할 때

type 
    TCallback = procedure of object; stdcall; 

procedure Test(Callback: TCallback); stdcall; 

나는 위의 오류 메시지를 수신 다음 코드를 사용하여 클래스 :

var ptr = GCHandle.Alloc(this, GCHandleType.Pinned).AddrOfPinnedObject(); 
+0

어떻게 MYLIB.DLL에 기본 시험 방법 선언이야? 나는 정의를 요구하지 않고, 메소드의 서명만을 요구한다. – Mzn

+0

더 많은 코드를 제공 할 수 있습니까? –

+0

@David Heffernan 관련성이없는 세부 사항을 피하기 위해 원본을 복사하는 대신 실제로 코드를 작성하고 있습니다. 나는 모든 관련 코드를 썼다고 생각한다. 없어진 물건 있어요? – Max

답변

3

당신은 이것을 생각하고 있습니다. C# 마셜 링은 대리인 주위에 썽크를 만듭니다. 그렇게하지 마십시오. 델파이 측면에서

이처럼 쓰기 :
type 
    TCallback = procedure; stdcall; 

는 단순히 of object를 제거합니다. 는 C# 측면에서

은 대리인은 다음과 같습니다

public delegate void CallbackDelegate(); 

당신이 당신의 위임에 사용하는 방법은 다음과 같습니다

void Callback() 
{ 
    // an instance method, so *this* is available 
} 

를 그리고이 같은 네이티브 코드를 호출 :

void CallExternalMethod() 
{ 
    Test(Callback); 
} 

그럼 모두합시다. 델파이에 당신은 쓸 수 있습니다 :

library MyLib; 

type 
    TCallback = procedure; stdcall; 

procedure Test(Callback: TCallback); stdcall; 
begin 
    Callback; 
end; 

exports 
    Test; 

begin 
end. 

그리고 C#을 측면에

가 :

class MyForm: Form { 
    public delegate void CallbackDelegate(); 

    [DllImport("mylib.dll")] 
    public static extern void Test(CallbackDelegate callback); 

    int Field; 

    static void Callback() 
    { 
     Field = 10; 
    } 

    void CallExternalMethod() 
    { 
     Field = 0; 
     Test(Callback); 
     Debug.Assert(Field == 10); 
    } 
} 
+0

와우! 이것은 정말로 기괴합니다. 그래서 thisPtr은 여기에 더미 매개 변수와 같습니다. 고마워요! 그것은 실제로 작동합니다. – Max

+0

양쪽에서 thisPtr을 제거해야합니다. 마샬 러가 당신을 위해 그것을 덤벼 들게하십시오. –

+0

다음은 주제에 대한 또 다른 질문입니다. http://stackoverflow.com/questions/15666173/how-do-non-static-callbacks-work-from-native-code –

0

당신은 System.Runtime.InteropServices.GCHandle 클래스를 사용하기로하고, its MSDN documentation has examples. 여기에는 정적 메서드를 사용하여 C# 참조를 관리되지 않는 값과 변환하는 방법이 포함됩니다.

내가 확인 할 빠른 방법이 없다, 그러나이 같은 작업을해야 다음 IntPtr는 다음과 같이 얻어 져야한다

static void Callback(IntPtr thisPtr) 
{ 
    MyForm thisObject = (MyForm)GCHandle.FromIntPtr(thisPtr).Target; 

    thisObject.Field = 10; 
} 

:

IntPtr thisPtr = GCHandle.ToIntPtr(GCHandle.Alloc(thisObject))); 

PS : 주 몇 가지에 피 호출자 또는 발신자에게 핸들을 해제해야합니다.

+0

내 질문을 업데이트했습니다. 이 코드를 제공 할 수 있습니까? 이 수업을 사용하려고했지만 실패했습니다. – Max

+0

@Max이 방법이 작동하는지 테스트 할 수있는 빠른 방법은 없지만 일반적인 생각입니다. – Medinoc

+0

그게'GCHandleType.Pinned' 매개 변수를 사용한다는 것을 제외하고는 내가하고 있었던 일입니다. 고정 해제 포인터를 원시 코드에 전달할 수 없다고 생각했습니다. – Max

관련 문제