2009-12-28 14 views
14

Aşağıdaki ayrıntılı tanıtım için özür dilerim. P/Invoke içsellerini bildiğimden daha iyi bir içgörüye ihtiyacım var.C# P/Invoke: Fonksiyon göstergelerini içeren yapıları işaretleme

C'den C# 'a kadar işlev işaretçileri içeren yapılarımı örneklendiriyorum. Bunu yapmanın en temiz ve/veya en verimli yolu olup olmadığını bilmek isterim. döndürür

enum INTERFACES 
{ 
    FOO, 
    BAR 
}; 

: Aşağıdaki numaralandırma değerleri getInterface(int) birini geçmek zorunda

void* getInterface(int id); 

: Ben aşağıdaki giriş noktası sağlar C kodlu bir yerli DLL ile arabirim ediyorum

Aşağıdaki gibi işlev işaretçileri içeren bir yapı için bir işaretçi bir işaretçi:

typedef struct IFOO 
{ 
    void (*method1)(void* self, int a, float b); 
    void (*method2)(void* self, int a, float b, int c); 
} IFoo; 

A nd burada C kullanmak nasıl: C# '

IFoo* interface = (IFoo*)getInterface(FOO); 
interface->method1(obj, 0, 1.0f); // where obj is an instance of an object 
            // implementing the IFoo interface. 

Ben P/çağır kullanılarak getInterface(int) giriş noktası eşleştiren bir Library sınıf var.

class Library 
{ 
    [DllImport("MyDLL"), EntryPoint="getInterface", CallingConvention=CallingConvention.Cdecl)] 
    public static extern IntPtr GetInterface(int id); 
}; 

Sonra tanımlanmış:

struct IFoo 
{ 
    public M1 method1; 
    public M2 method2; 


    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void M1(IntPtr self, int a, float b); 

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    public delegate void M2(IntPtr self, int a, float b, int c); 
} 

Ve bu şekilde kullanıyorum:

  1. haritalama mı: Ben şu soru var

    IntPtr address = Library.GetInterface((int)Interfaces.FOO); 
    IFoo i = (IFoo)Marshal.PtrToStructure(address, typeof(IFoo)); 
    
    i.method1(obj, 0, 1.0f): // where obj is an instance of an object 
             // implementing the IFoo interface. 
    

    tek bir po haritalamadan daha az verimli tüm yapı Marshal.GetDelegateForFunctionPointer() kullanarak yapının içine inter? Çoğunlukla bir arabirim açık tüm yöntemleri gerekmez yana

    , ben (test edilmiş ve eserleri) yapabilirsiniz:

    unsafe 
    { 
        IntPtr address = Library.GetInterface(id); 
        IntPtr m2address = new IntPtr(((void**)address.toPointer())[1]); 
    
        M2 method2 = (M2)Marshal.GetDelegateForFunctionPointer(m2address, typeof(M2)); 
    
        method2(obj, 0, 1.0f, 1); 
    } 
    
  2. kerede Marshal.PtrToStructure() kullanarak tüm yapıyı haritalama, orada azdır tarif ettiğimden daha ayrıntılı bir şekilde mi? Her yöntem vb için delege türlerini tanımlamaktan daha az anlam ifade etmek isterim?


DÜZENLEME: yukarıdaki kod parçacıkları açıklık ve bütünlük açısından, için, objvoid* createObject(int type) giriş noktası ile elde edilen bir örneğidir.


EDIT2: Yöntemin bir avantajı, 1) Marshal.GetDelegateForFunctionPointer() Framework 2.0 itibaren kullanılabilir olmasıdır. Bununla birlikte, Marshal.PrtToStructure() her zaman mevcut olmuştur. Bu, bugünlerde 1.0 uyumluluğunu sağlamaya değer olduğundan emin değilim.


EDIT3: Ben Reflector kullanılarak oluşturulan kodu incelemek için çalıştı ama tüm ilginç ayrıntılar PtrToStructureHelper gibi yardımcı fonksiyonları yapılır ve maruz kalmadığı beri çok bilgi vermez. Öyleyse, eğer çerçeve içersinde ne yapıldığını görebilseydim, o zaman çalışma zamanı şeyleri uzaklaştırma fırsatına sahip ve tam olarak ne, neden ve ne zaman olduğunu bilmiyorum :)

Bununla birlikte, açıklanan iki yaklaşımı karşılaştırdım. benim soruma Marshal.PtrToStructure() yaklaşımı, Marshal.GetDelegateForFunctionPointer() yaklaşımına kıyasla% 10 civarında bir faktör tarafından daha yavaştı; Bu, ilgi çekici olmayan tüm işlevler için IntPtr s içeren bir yapıya sahiptir.

ayrıca kendi haddelenmiş Marshaller ile Marshal.GetDelegateForFunctionPointer() karşılaştırılmıştır: I, çağrı yığınını temsil eden bir struct hizalanmasını bellekte pin I asm kodlanmış trambolini kullanımı doğal tarafına adresini geçecek şekilde arama fonksiyonu kullanımları bellek alanı parametre yığını olarak (bu, cdecl x86 çağrı sözleşmesi yığındaki tüm işlev parametrelerini geçtiği için mümkündür). Zamanlamalar eşdeğerdi.

cevap

2

Sorunuzun cevabını bilmiyorum 1. Marshal.PtrToStructure()'un diğer Mareşal ilkelleri açısından uygulandığını tahmin ediyorum, bu nedenle yalnızca tek Marshal.GetDelegateForFunctionPointer'u kullanmak daha verimli olacaktır. Ama bu sadece bir tahmin - bunun için ödediğinize değer.

Sorunuz olduğu gibi 2. Hayır, bunu yapmak için daha az ayrıntılı yol yoktur. Daha ayrıntılı bir yol var. Eski stil MIDL derleyicisini, dll'niz için bir tür kitaplığı oluşturmak ve bu tür kitaplığı yüklemek için kullanabilirsiniz. Ancak, MIDL için kullanılabilir marşlama seçenekleri, C# 'da tanımlayabileceğiniz şekilde biraz daha sınırlıdır. Ve MIDL compler ile çalışmak oldukça zor, muhtemelen yönetilen kod ve hedef dll arasında birlikte yapmak için başka bir yönetilmeyen bir DLL yazmak zorunda kalmazsınız.

+0

Hakkında 1) Gerçekten neyin gerçekten yapıldığını kontrol etmenin mümkün olup olmadığını bilmiyorum: Optimizatör, yapının sadece 1 delegesinin kullanıldığını anlayabilir. Neyse en azından yeterince temiz görünüyor. –

+0

@Gregory Bence bu olası değil. Ancak kod çözmek ve kesin olarak öğrenmek için .NET reflektörünü kullanabilirsiniz. –

3

Baştan başlayacağım.

Kullanımı:

IFoo foo = UnsafeNativeMethods.GetFooInterface(); 
foo.Method1(0, 1.0f); 

Uygulama: noktası 1 için

internal interface IFoo 
{ 
    void Method1(int a, float b); 
    void Method2(int a, float b, int c); 
} 

internal static class UnsafeNativeMethods 
{ 
    public static IFoo GetFooInterface() 
    { 
     IntPtr self = GetInterface(InterfaceType.Foo); 
     NativeFoo nativeFoo = (NativeFoo)Marshal.PtrToStructure(self, typeof(NativeFoo)); 
     return new NativeFooWrapper(self, nativeFoo.Method1, nativeFoo.Method2); 
    } 

    [DllImport("mydll.dll", EntryPoint = "getInterface", CallingConvention = CallingConvention.Cdecl)] 
    private static extern IntPtr GetInterface(InterfaceType id); 

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    private delegate void Method1Delegate(IntPtr self, int a, float b); 

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
    private delegate void Method2Delegate(IntPtr self, int a, float b, int c); 

    private enum InterfaceType 
    { 
     Foo, 
     Bar 
    } 

    private struct NativeFoo 
    { 
     public Method1Delegate Method1; 
     public Method2Delegate Method2; 
    } 

    private sealed class NativeFooWrapper : IFoo 
    { 
     private IntPtr _self; 
     private Method1Delegate _method1; 
     private Method2Delegate _method2; 

     public NativeFooWrapper(IntPtr self, Method1Delegate method1, Method2Delegate method2) 
     { 
      this._self = self; 
      this._method1 = method1; 
      this._method2 = method2; 
     } 

     public void Method1(int a, float b) 
     { 
      _method1(_self, a, b); 
     } 

     public void Method2(int a, float b, int c) 
     { 
      _method2(_self, a, b, c); 
     } 
    } 
} 
+0

Eh, ben böyle yapıyorum.Sadece bu tür "güzel sarma" sorusuyla alakasızdı. Sonra biliyorum ki detaylarda açıklamamıştım, ama 'self' arayüz yapısının işaretçisi değil. Bu gerçekten 'bu' işaretçisidir. –

+0

Yerel kütüphanede 'void * obj = createObject (int type)' giriş noktası vardır. Türleri örneklendirmek için kullanırsınız, sonra örneklendirdiğiniz tür IFoo arabirimini uygularsa ('bool uygular (void * obj, int iface)' 'obj' ve' FOO' için 'true' döndürür, ' ((IFoo *) getInterface (FOO)) -> method1 (obj, 0, 1.0f); ' –

+0

Bu en basit değişiklik,' GetFooInterface' için bir 'IntPtr self' parametresi eklemektir. Bunun gibi karmaşık bir sarıcı için, aslında "" ve "" gibi "cast" işlemlerini gerçekleştirecek yöntemler yaratacağım. Bir adım daha ileri gidersem, dinamik olarak uygulama sınıfları oluştururdum, böylece C# '' ve 'as' operatörleri sorunsuz bir şekilde çalışır. –

1

: Yapınızın işlev işaretçileri bir sürü içerir ve sadece birkaç kullanırsanız

Marshal.GetDelegateForFunctionPointer() daha basittir. A (büyük) dezavantajı, işaretçiyi el işaretiyle hesaplamak zorunda olmanızdır (işaretçinin boyutunun 32/64 bit platformunda farklı olduğunu unutmayın). Bir yapının kullanımı daha kolaydır ancak marşlar daha fazla veridir. nokta 2 için

:

Ben az ayrıntılı bir yaklaşım mümkün olduğunu düşünmüyorum. Yalnızca kullanmak istediğiniz işlevin temsilci tanımlayabilir ve kullanmak istemediğiniz işlev işaretçileri için bir sahte temsilci kullanabilirsiniz. Bu şekilde, marshaling işlemi tamam olacak, ancak kalışsız delegeler içeren bir yapıyla sona ereceksiniz.

+0

Tamam, orada bir konsensüs var gibi görünüyor: tüm yapıyı sıralarken, kullanamayacağınız şey için ödeme yapıyorsunuz. İşaretçi boyutu hakkında, 'yeni IntPtr (((void **) address.toPointer()) [1])' büyüklüğü ne olursa olsun onu alır. –

+0

Ve evet, tüm yapıyı sıraya koyduğumda kullanacağım sadece daha az ayrıntılı bir seçenek, yalnızca ihtiyacım olan delegeleri tanımlamak ve kalan giriş noktaları için ham 'IntPtr' kullanmaktır –