Константы закреплены в C#?
Я работаю на C# с API Borland C, который использует много байтовых указателей для строк. Я столкнулся с необходимостью передать некоторые строки C# как (недолговечный) байт*.
было бы моим естественным предположением, что объект const не будет выделен в куче, но будет храниться непосредственно в памяти программы, но я не смог проверить это в любой документации.
вот пример того, что я сделал, чтобы сгенерировать указатель на постоянную строку. Этот работает ли, как предполагалось в тестировании, я просто не уверен, что это действительно безопасно, или это работает только благодаря удаче.
private const string pinnedStringGetWeight = "getWeight";
unsafe public static byte* ExampleReturnWeightPtr(int serial)
{
fixed (byte* pGetWeight = ASCIIEncoding.ASCII.GetBytes(pinnedStringGetWeight))
return pGetWeight;
}
этот const действительно закреплен, или есть шанс, что его можно переместить?
@Kragen:
вот импорт:
[DllImport("sidekick.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int getValueByFunctionFromObject(int serial, int function, byte* debugCallString);
это фактическая функция. Да, на самом деле требуется указатель статической функции:
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);
}
Ниже приведен еще один метод, который я использовал при издевательстве над моим API, используя статическую структуру, которая Я также надеялся, что был прижат. Я надеялся найти способ упростить это.
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;
}
}
}
2 ответов
как выделяются константы, в этом случае не имеет значения, потому что ASCIIEncoding.ASCII.GetBytes()
возвращает новый массив байтов (он не может вернуть внутренний массив константы, так как он кодируется по-разному (edit: there is hopefully нет способ получить указатель на string
внутренний массив, так как строки неизменяемы)). Однако гарантия того, что GC не коснется массива, длится только до тех пор, пока fixed
scope does-другими словами, когда функция возвращается, память больше не закрепленный.
основываясь на комментарии Kragans, я посмотрел на правильный способ Маршалла моей строки в байтовый указатель и теперь использую следующее Для первого примера, который я использовал в своем вопросе:
[DllImport("sidekick.dll", CallingConvention = CallingConvention.Winapi)]
public static extern int getValueByFunctionFromObject(int serial, int function, [MarshalAs(UnmanagedType.LPStr)]string debugCallString);