最初の部分を読んでいない人- 読む
構造の作成作業の最初の部分を完了した後、タスクは私にとって複雑でした。 作業に必要なライブラリ-起動するまでサイズがわからない配列を割り当てました。 それは動的でした、つまり データが多いほど、配列は大きくなります。 ここでタスクがより面白くなったのは、 このサイズが指定された構造を使用するのに十分だった古い方法は、もはや適切ではありませんでした。
それから私はさらにマーシャリングを勉強し始め、問題を解決するのに役立った元sクラスからのいくつかのメソッドを見つけました。
そして、実際にはタスク自体:
-入力のライブラリは、ポインターの配列へのポインターを取ります(void *)o_O
-配列の内容-他の配列へのこのポインター、たとえば、ファイルアドレスが示されている文字の配列、何らかのデータを含むint配列へのポインター
なぜなら 私はしばしばいくつかのコードを呼び出さなければなりません、私は特別な静的UnMemoryクラスを作成しました、そのコードは以下です
/// <summary>
///
/// </summary>
public static class UnMemory
{
/// <summary>
///
/// </summary>
private static Queue< IntPtr > queue = new Queue< IntPtr >();
public static void Enqueue( IntPtr ptr)
{
queue.Enqueue(ptr);
}
private static void FreeIntPtr( IntPtr ptr)
{
if (ptr != IntPtr .Zero)
Marshal.FreeCoTaskMem(ptr);
}
/// <summary>
///
/// </summary>
public static void FreeMemory()
{
while (queue.Count > 0)
{
IntPtr temp = queue.Dequeue();
// ,
Marshal.FreeCoTaskMem(temp);
}
}
}
* This source code was highlighted with Source Code Highlighter .
このクラスでは、生成されたアンマネージメモリへのすべてのポインターを追加するキューを定義したため、最終的に静的メソッドFreeMomory()を呼び出して、割り当てられたメモリを完全にクリアします。 キューにポインタを追加するには、UnMemory.Enqueue(ptr)を呼び出す必要があります。
ポインターを参照して各ポインターをクリアするよりも便利なように思えました。 結局、何かをスキップしてメモリリークが発生する可能性があります。
また、UnMemoryと呼ばれる別のクラスが必要でした。このクラスは、管理されていないメモリにスペースを割り当て、データを格納し、もちろんそこから読み取ります。
彼のコードは以下です
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
public static class UnMemory<T>
where T : struct
{
/// <summary>
///
/// </summary>
/// <param name="memory_object"> </param>
/// <param name="ptr"></param>
/// <typeparam name="T"> </typeparam>
public static void SaveInMem(T memory_object, ref IntPtr ptr)
{
if ( default (T).Equals(memory_object))
{
//
ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf( typeof (T)));
UnMemory.Enqueue(ptr);
return ;
}
if (ptr == IntPtr .Zero)
{
//
ptr = Marshal.AllocCoTaskMem(Marshal.SizeOf( typeof (T)));
//
Marshal.StructureToPtr(memory_object, ptr, false );
}
else
{
//
Marshal.StructureToPtr(memory_object, ptr, true );
}
UnMemory.Enqueue(ptr);
}
/// <typeparam name="T">IntPtr, int, float</typeparam>
/// <exception cref="System.ArgumentException"> #1 IntPtr, int, float</exception>
public static void SaveInMem2(T[] managedArray, ref IntPtr pnt)
{
Debug.Assert(managedArray != null , " Null" );
Debug.Assert(managedArray.Length != 0, " 0" );
if (pnt == IntPtr .Zero)
{
// . = *
//int size = Marshal.SizeOf(typeof(T)) * managedArray.Length;
int size = Marshal.SizeOf(managedArray[0]) * managedArray.Length;
pnt = Marshal.AllocCoTaskMem(size);
}
// , Marshal.Copy
if ( typeof (T) == typeof ( int ))
{
int [] i = managedArray as int [];
Marshal.Copy(i, 0, pnt, i.Length);
}
else if ( typeof (T) == typeof ( byte ))
{
byte [] b = managedArray as byte [];
Marshal.Copy(b, 0, pnt, b.Length);
}
else if ( typeof (T) == typeof ( float ))
{
float [] f = managedArray as float [];
Marshal.Copy(f, 0, pnt, f.Length);
}
else if ( typeof (T) == typeof ( char ))
{
//
byte [] b = Encoding .Default.GetBytes(managedArray as char []);
Marshal.Copy(b, 0, pnt, b.Length);
}
else if ( typeof (T) == typeof ( IntPtr ))
{
IntPtr [] p = managedArray as IntPtr [];
Marshal.Copy(p, 0, pnt, p.Length);
}
else
throw new ArgumentException( " #1 IntPtr, int, float char" );
// ,
UnMemory.Enqueue(pnt);
}
/// <summary>
///
/// </summary>
/// <param name="ptr"></param>
/// <param name="type"> </param>
/// <returns> </returns>
public static T ReadInMem( IntPtr ptr)
{
return (T)Marshal.PtrToStructure(ptr, typeof (T));
}
public static T[] ReadInMem2( IntPtr ptr, int size)
{
if ( typeof (T) == typeof ( int ))
{
int [] memInt = new int [size];
Marshal.Copy(ptr, memInt, 0, size);
return memInt as T[];
}
else if ( typeof (T) == typeof ( byte ))
{
byte [] memByte = new byte [size];
Marshal.Copy(ptr, memByte, 0, size);
return memByte as T[];
}
else if ( typeof (T) == typeof ( float ))
{
float [] memFloat = new float [size];
Marshal.Copy(ptr, memFloat, 0, size);
return memFloat as T[];
}
else if ( typeof (T) == typeof ( IntPtr ))
{
IntPtr [] memIntPtr = new IntPtr [size];
Marshal.Copy(ptr, memIntPtr, 0, size);
return memIntPtr as T[];
}
else
throw new ArgumentException( " #1 int, float char" );
}
/// <summary>
///
/// </summary>
public static class UnArray
{
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="array"> </param>
/// <returns> </returns>
public static T[,] Rank1_Rank2(T[] array, int x, int y)
{
T[,] res = new T[x, y];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="array"> </param>
/// <returns> </returns>
public static T[] ToRank1(T[,] array, int x, int y)
{
T[] res = new T[x * y];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="array"> </param>
/// <returns> </returns>
public static T[, ,] Rank1_Rank3(T[] array, int x, int y, int z)
{
T[, ,] res = new T[x, y, z];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="array"> </param>
/// <returns> </returns>
public static T[] ToRank1(T[, ,] array, int x, int y, int z)
{
T[] res = new T[x * y * z];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="array"> </param>
/// <returns> </returns>
public static T[, , ,] Rank1_Rank4(T[] array, int x, int y, int z, int w)
{
T[, , ,] res = new T[x, y, z, w];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
/// <summary>
///
/// </summary>
/// <typeparam name="T"> </typeparam>
/// <param name="array"> </param>
/// <returns> </returns>
public static T[] ToRank1(T[, , ,] array, int x, int y, int z, int w)
{
T[] res = new T[x * y * z * w];
int size = Buffer.ByteLength(array);
Buffer.BlockCopy(array, 0, res, 0, size);
return res;
}
}
}
* This source code was highlighted with Source Code Highlighter .
このクラスは一般化されているため、各タイプに対して多数のメソッドを個別に作成することはできません。 このクラスの意味は、管理されたメモリから管理されていないメモリに配列を変換します。 SaveInMemメソッドは、渡す構造体を保存し、SaveInMem2メソッドは、intPtr、Int、floatなどの配列を保存します。制限は、Marshal.Copyメソッド自体です。このメソッドは、intとバイトなどに別々にコピーを実装します。 したがって、各タイプについて、if(typeof(T)== typeof(int))などの独自の呼び出しを実装する必要がありました。
SaveMemおよびSaveMem2と同様に、構造および配列を読み取るためのReadInMemおよびReadInMem2メソッド。
記事の最初の部分で述べたように、私は多次元配列を操作しなければなりませんでしたが、マーシャリングではこれが許可されず、1次元配列のみを読み書きできます。 したがって、UnArrayのサブクラスを作成します。これも一般化されており、1次元から2次元、3次元、4次元、およびその逆の配列を作成するいくつかのメソッドがあります。 確かに、最初の部分でこれらのすべてのメソッドがさらに必要でした。2番目の部分では、配列を操作するとき、それらはほとんど役に立ちませんでしたが、いくつかのメソッドを追加できれば便利になります。
そして、今、私たちは挑戦そのものに直接行きます。
Cからの呼び出しは次のようになります。
extern "C" int __import TkzIvc( void *mpGS[]);
* This source code was highlighted with Source Code Highlighter .
C#の場合、この呼び出しは次のようになります。
[DllImport( @"DllTkzIvc.dll" )]
private static extern int _TkzIvc([In] IntPtr mpGS);
* This source code was highlighted with Source Code Highlighter .
最もシンプルなままです。 使用するラッパーを作成する
[StructLayout(LayoutKind.Sequential)]
public class mpSh_Struct : IDisposable
{
private IntPtr [] mpSh = new IntPtr [5];
private int size_vetv; //
/// <summary>
///
/// </summary>
/// <value> 255 </value>
/// <exception cref="System.ArgumentOutOfRangeException">, , 255 </exception>
public string PathSh
{
get
{
return Marshal.PtrToStringAnsi( this .mpSh[0]);
}
set
{
if ( String .IsNullOrEmpty( value ) || value .Length > 255)
throw new ArgumentOutOfRangeException( " 255 " );
this .mpSh[0] = Marshal.StringToHGlobalAnsi( value );
}
}
///<summary>
/// int[]
///</summary>
public int [] TypeV
{
get { return UnMemory< int >.ReadInMem2( this .mpSh[1], this .size_vetv); }
set { UnMemory< int >.SaveInMem2( value , ref this .mpSh[1]); }
}
/// <summary>
/// char u1[][6]
/// </summary>
/// <value> 5 </value>
public string [] u1
{
get
{
//
byte [] mem = UnMemory< byte >.ReadInMem2( this .mpSh[2], this .size_vetv * 6);
// , string[]
int length = this .size_vetv;
//
string [] res = new string [length];
for ( int i = 0; i < length; i++)
{
// , String
res[i] = Encoding .Default.GetString(mem, i * 6, 6).TrimEnd( '\0' );
}
return res;
}
set
{
// char[]
char [] res = new char [ value .Length * 6];
for ( int i = 0; i < value .Length; i++)
{
if ( value [i] != null )
value [i].CopyTo(0, res, i * 6, value [i].Length);
}
//
UnMemory< char >.SaveInMem2(res, ref this .mpSh[2]);
}
}
/// <summary>
/// float[]
/// </summary>
public float [] EK1B1
{
get { return UnMemory< float >.ReadInMem2( this .mpSh[4], this .size_vetv); }
set { UnMemory< float >.SaveInMem2( value , ref this .mpSh[4]); }
}
public int [] ParamSh
{
get { return UnMemory< int >.ReadInMem2( this .mpSh[3], 6); }
set
{
this .size_vetv = value [0]; // (ParamSh[0])
UnMemory< int >.SaveInMem2( value , ref this .mpSh[3]);
}
}
/// <summary>
///
/// </summary>
public bool Read( out string errorText)
{
try
{
IntPtr t = new IntPtr ();
UnMemory< IntPtr >.SaveInMem2( this .mpSh, ref t);
_TkzIvc(t);
mpSh = UnMemory< IntPtr >.ReadInMem2(t, this .mpSh.Length);
int [] paramSh = this .ParamSh; //
this .size_vetv = paramSh[0]; //
errorText = String .Empty;
return true ;
}
catch (DllNotFoundException)
{
errorText = " . " ;
return false ;
}
catch (Exception exp)
{
errorText = exp.Message;
return false ;
}
}
public void Dispose()
{
//
UnMemory.FreeMemory();
// ,
for ( int i = 1; i < 5; i++)
{
IntPtr ptr = mpSh[i];
//
UnMemory.FreeIntPtr(ptr);
}
}
}
* This source code was highlighted with Source Code Highlighter .
プレーンテキスト、文字列には、Marshal.StringToHGlobalAnsi(テキスト)を使用したことに注意してください。 なぜなら これは、文字「\ 0」で終わる通常のテキスト文字列を読み書きするためのマーシャルクラスの特別なメソッドです。
ポインターの配列はmpShに格納され、すべてのフィールドに対してgetおよびsetメソッドが実装されます。これらのメソッドは、アンマネージメモリをクラス自体からほぼ透過的に読み書きします。 Readメソッドは、ポインターの配列をアンマネージメモリに転送して、後でそこからデータを読み取ります(ライブラリが呼び出されると、このデータが書き込まれます)。 Readを呼び出した後、目的のフィールドにアクセスするだけで、必要な変数を読み取ることができます。
確かに、1つの制限があります...配列への参照を取得し(int [] paramSh = this.ParamSh)、フィールドに常時アクセスする(this.size_vetv = this.ParamSh [0])よりも、それを操作する方が適切です。 アクセス時に、データは管理されていないメモリから再読み込みされるためです。 これはすぐに発生するという事実にもかかわらず、多くの呼び出しがある場合、十分な時間がかかります。
PS:CustomMarshalingはこの記事の一部とは見なされませんでした。 その本質は、特定の構造の保存方法を完全にカスタマイズできることです。これにより、ライブラリのさまざまなバージョンをサポートできます。 たとえば、DateTimeを特定の形式の文字列として保存するなど。このようなケースはあまり一般的ではないため、別の記事で強調表示できます。
残念ながら、ソースコードを記事に添付することはできません。 これはマニュアルを承認しません:)しかし、ソースなしでマーシャリングの操作方法が明確になるように、最大のコードを提供しようとしました。 不明な点がある場合は、ご記入ください。ご質問にお答えします。