В моем коде на C # я пытаюсь получить массив структур из устаревшей C ++ DLL (код, который я не могу изменить).
В этом коде C ++ структура определяется следующим образом:
struct MyStruct
{
char* id;
char* description;
};
Метод, который я вызываю (get_my_structures), возвращает указатель на массив структур MyStruct:
MyStruct* get_my_structures()
{
...
}
Существует еще один метод, который возвращает количество структур, поэтому я знаю, сколько структур возвращено.
В моем коде на C # я определил MyStruct следующим образом:
[StructLayout(LayoutKind.Sequential)]
public class MyStruct
{
[MarshalAsAttribute(UnmanagedType.LPStr)] // <-- also tried without this
private string _id;
[MarshalAsAttribute(UnmanagedType.LPStr)]
private string _description;
}
Подпись взаимодействия выглядит так:
[DllImport("legacy.dll", EntryPoint="get_my_structures")]
public static extern IntPtr GetMyStructures();
Наконец, код, который выбирает массив структур MyStruct, выглядит следующим образом:
int structuresCount = ...;
IntPtr myStructs = GetMyStructures();
int structSize = Marshal.SizeOf(typeof(MyStruct)); // <- returns 8 in my case
for (int i = 0; i < structuresCount; i++)
{
IntPtr data = new IntPtr(myStructs.ToInt64() + structSize * i);
MyStruct ms = (MyStruct) Marshal.PtrToStructure(data, typeof(MyStruct));
...
}
Беда в том, что только самая первая структура (одна с нулевым смещением) будет правильно распределена. Последующие имеют фиктивные значения в членах _id и _description. Значения не полностью уничтожены, или кажется, что это строки из других областей памяти. Сам код не вылетает.
Я убедился, что код C ++ в get_my_structures () действительно возвращает правильные данные. Данные не были случайно удалены или изменены во время или после разговора.
При просмотре в отладчике структура памяти возвращаемых данных в C ++ выглядит следующим образом:
0: id (char*) <---- [MyStruct 1]
4: description (char*)
8: id (char*) <---- [MyStruct 2]
12: description (char*)
16: id (char*) <---- [MyStruct 3]
...
[Обновление 18/11/2009]
Вот как код на C ++ подготавливает эти структуры (фактический код гораздо уродливее, но это достаточно близкое приближение):
static char buffer[12345] = {0};
MyStruct* myStructs = (MyStruct*) &buffer;
for (int i = 0; i < structuresCount; i++)
{
MyStruct* ms = <some other permanent address where the struct is>;
myStructs[i].id = (char*) ms->id;
myStructs[i].description = (char*) ms->description;
}
return myStructs;
По общему признанию, приведенный выше код выполняет некрасивое преобразование и копирует необработанные указатели, но, похоже, все же делает это правильно. По крайней мере, это то, что я вижу в отладчике: вышеупомянутый (статический) буфер содержит все эти голые указатели char *, хранящиеся один за другим, и они указывают на допустимые (нелокальные) расположения в памяти.
Пример Павла показывает, что это действительно единственное место, где все может пойти не так. Я попытаюсь проанализировать, что происходит с теми «конечными» местами, где действительно находятся строки, а не местами, где хранятся указатели.