2011-06-03 31 views
1

假设我们有一个像这样的数据进行原生库:值类型到IntPtr的应用程序生命周期中使用

double *pointer = &globalValue; 
SetAddress(pointer); 

//Then we can change value and write it to disk 
globalValue = 5.0; 

FlushValues(); // this function writes all values 
       // registered by SetAddress(..) functions 

... //Then we change our value (or values) 
FlushValues(); //and do write any time we need 

现在我必须把它互操作到C#。我想避免使用不安全的...但是...我不知道=)

所以在C#端我们将有一些我们将写的类。我们能做的事情写,如:

public class Data 
{ 
    [Serializable] <-- somehow we mark what we are going to write 
    double myValue; 

    ... 

    [Serializable] 
    double myValueN; 
} 

public class LibraryInterop 
{ 
     IntPtr[] pointers; //Pointers that will go to SetAddressMethod 
     ... 

     public void RegisterObject(Data data) 
     { 
      ... //With reflection we look for [Serializable] values 
       //create a pointer for each and some kind of BindingInfo 
       //that knows connection of pointers[i] to data.myValueN 

      //Then add this pointers to C++ library 
      foreach(IntPtr pointer in pointers) { SetAddress(pointer);} 
     } 

     public void Flush() 
     { 
      //Loop through all added pointers 
      for(int i=0; i<pointers.Length; i++) 
      { 
       double value = ... //With reflections and BindingInfo we find data.myValueN 
           // that corresponds to pointers[i] 

       // then we write this value to memory of IntPtr 
       // we have to brake value to bytes and write it one by one to memory 
       // we could use BitConverter.DoubleToInt64Bits() but double - is just 
       // an example, so in general case we will use GetBytes 
       int offset = 0; 
       foreach(byte b in BitConverter.GetBytes(value)) 
       { 
        Marshal.WriteByte(pointer[i],offset,byte); 
        offset++; 
       } 
      } 

      //Save values of IntPtr to disk 
      FlushValues(); 
     } 
    } 

然后用户代码如下所示然后

var library = new LibraryInterop(); 
    var data1 = new Data(); 
    var data2 = new AnotherData(); 
    library.RegisterObject(data1); 
    library.RegisterObject(data2); 

    ...//change data 
    library.Flush(); 
    ...//change data 
    library.Flush(); 
    ...//change data 
    library.Flush(); 

所以在C++中,我们有一个非常整齐的结构。我们有指针,我们填充这些指针后面的数据,并在FlushValues上记录所有这些值。

C#部分不能有SetAddress(ref double值)。由于地址可能会发生变化,我们不得不针对指针 - 使用unsafe + fixed或IntPtr,并且有很多令人头疼的问题。

另一方面,通过将data.myValue装箱为Object,我们可以拥有“托管指针”。因此,如果可以以某种方式将此ValueType data.myValue绑定到IntPtr而没有这种应对和反思 - 它将会更加简洁。

是否有可能做到这一点互操作,并有更少的丑陋和缓慢的C#部分,那么我列在这里?

回答

2

(有一些重大的缺点,以这个...)

您可以使用GCHandleAlloc(data1,GCHandleType.Pinned)“pin”一个对象,然后从GCHandle.AddrOfPinnedObject得到IntPtr。如果你这样做,你将能够将IntPtr传递给你的本地代码,它应该按预期工作。

但是,这会在破坏垃圾收集器的效率方面造成很多问题。如果你决定做这样的事情,我建议在你的程序的很早之前分配所有的对象(可能在调用GC.Collect()之后立即进行,这是我通常不推荐的),并且让它们独立于你的节目的一生。这会稍微缓解GC问题,因为它会尽早将它们分配到“最佳”可能位置,并将它们留在只会在Gen2集合中触及的地方。

+0

非常有趣的解决方案。我应该看看它。谢谢! – MajesticRa 2011-06-04 00:01:34

+0

它要求直接呼叫者完全信任。所以它不能被部分信任或透明的代码使用。它不是和普通的美国安全一样吗? – MajesticRa 2011-06-04 00:04:46

+0

@MajesticRa:即使是不安全的代码,实际上也很困难 - 您仍然需要“永久”固定对象,这需要这种技术。 'fixed'只对一段代码有效。 – 2011-06-04 00:10:59

相关问题