2011-07-18 13 views
11

Estoy trabajando en C# con una API de Borland C que usa muchos punteros de bytes para cadenas. Me he enfrentado a la necesidad de pasar algunas cadenas de C# como (de corta duración) byte *.¿Las constantes están ancladas en C#?

Sería mi suposición natural que un objeto const no se asignaría en el montón, pero se almacenaría directamente en la memoria del programa, pero no he podido verificar esto en ninguna documentación.

Aquí hay un ejemplo de lo que he hecho para generar un puntero a una cadena constante. Esto funciona como estaba previsto en las pruebas, no estoy seguro si es realmente seguro, o solo funciona por suerte.

private const string pinnedStringGetWeight = "getWeight"; 

unsafe public static byte* ExampleReturnWeightPtr(int serial) 
{ 
    fixed (byte* pGetWeight = ASCIIEncoding.ASCII.GetBytes(pinnedStringGetWeight)) 
     return pGetWeight; 
} 

¿Esto está realmente const, o existe la posibilidad de que se pueda mover?


@Kragen:

Aquí está la importación:

[DllImport("sidekick.dll", CallingConvention = CallingConvention.Winapi)] 
public static extern int getValueByFunctionFromObject(int serial, int function, byte* debugCallString); 

Ésta es la función real. Sí, en realidad se requiere un puntero de función estática:

private const int FUNC_GetWeight = 0x004243D0; 

private const string pinnedStringGetWeight = "getWeight"; 

unsafe public static int getWeight(int serial) 
{ 
    fixed (byte* pGetWeight = ASCIIEncoding.ASCII.GetBytes(pinnedStringGetWeight)) 
     return Core.getValueByFunctionFromObject(serial, FUNC_GetWeight, pGetWeight); 
} 

siguiente es otro método que utilicé cuando burlarse de mi API, utilizando una estructura estática, que también esperaba que fuera clavado. Esperaba encontrar una forma de simplificar esto.

public byte* getObjVarString(int serial, byte* varName) 
{ 
    string varname = StringPointerUtils.GetAsciiString(varName); 
    string value = MockObjVarAttachments.GetString(serial, varname); 
    if (value == null) 
     return null; 
    return bytePtrFactory.MakePointerToTempString(value); 
} 

static UnsafeBytePointerFactoryStruct bytePtrFactory = new UnsafeBytePointerFactoryStruct(); 
private unsafe struct UnsafeBytePointerFactoryStruct 
{ 
    fixed byte _InvalidScriptClass[255]; 
    fixed byte _ItemNotFound[255]; 
    fixed byte _MiscBuffer[255]; 

    public byte* InvalidScriptClass 
    { 
     get 
     { 
      fixed (byte* p = _InvalidScriptClass) 
      { 
       CopyNullString(p, "Failed to get script class"); 
       return p; 
      } 
     } 
    } 

    public byte* ItemNotFound 
    { 
     get 
     { 
      fixed (byte* p = _ItemNotFound) 
      { 
       CopyNullString(p, "Item not found"); 
       return p; 
      } 
     } 
    } 

    public byte* MakePointerToTempString(string text) 
    { 
     fixed (byte* p = _ItemNotFound) 
     { 
      CopyNullString(p, text); 
      return p; 
     } 
    } 

    private static void CopyNullString(byte* ptrDest, string text) 
    { 
     byte[] textBytes = ASCIIEncoding.ASCII.GetBytes(text); 
     fixed (byte* p = textBytes) 
     { 
      int i = 0; 
      while (*(p + i) != 0 && i < 254 && i < textBytes.Length) 
      { 
       *(ptrDest + i) = *(p + i); 
       i++; 
      } 
      *(ptrDest + i) = 0; 
     } 
    } 
} 
+1

Este no es el forma de ordenar las cadenas de caracteres para el código nativo - ¿puedes publicar un ejemplo de la llamada (presumiblemente P/Invocar) a tu API de C? – Justin

+1

Sospecho que las cadenas constantes se "fijan" de manera efectiva, ya que no creo que estén almacenadas en el montón recogido de basura. Una evidencia para esto es que las cadenas constantes se cargan usando el código de operación IL de "Ldstr", con la cadena constante proporcionada como parte del cuerpo del método. –

+1

Debo aclarar mi declaración anterior. "La instrucción fija evita que el recolector de basura reubique una variable movible. La declaración fija solo se permite en un contexto inseguro. La solución fija también se puede usar para crear almacenamientos intermedios de tamaño fijo". - http://msdn.microsoft.com/en-us/library/f58wzh21(v=VS.100).aspx - RunUO es tan 2010 :-) –

Respuesta

13

¿Cómo se asignan los constantes no debería importar en este caso, porque ASCIIEncoding.ASCII.GetBytes() devuelve una nueva matriz de bytes (no puede volver matriz interna de la constante, ya que se codifica de forma diferente (edit: no es de esperar que sin camino para obtener un puntero a una matriz interna string, ya que las cadenas son inmutables)). Sin embargo, la garantía de que el GC no tocará el arreglo solo dura tanto como el alcance fixed; en otras palabras, cuando la función retorna, la memoria ya no está fijada.

+0

Esto definitivamente responde mi pregunta. Totalmente extrañado esto. Muchas gracias. Mi byte devuelto * no es confiable una vez que regresé, he tenido suerte. – Derrick

+0

Si responde su pregunta, debe marcar esto como la respuesta. –

+0

@Judah Himango: No estoy seguro de si se responde la pregunta del tema literal de si las constelaciones son fijadas, a pesar de que esta respuesta es completamente suficiente para satisfacer la pregunta sobre la (in) corrección de mi propio código. – Derrick

1

Basado en comentarios Kragans, miré en la forma correcta de ordenar de mi cadena a un puntero de bytes y ahora estoy usando lo siguiente para el primer ejemplo que utilicé en mi pregunta:

 [DllImport("sidekick.dll", CallingConvention = CallingConvention.Winapi)] 
     public static extern int getValueByFunctionFromObject(int serial, int function, [MarshalAs(UnmanagedType.LPStr)]string debugCallString); 
Cuestiones relacionadas