Запуск «GC.Collect» исправляет мой сбой, но я не понимаю, почему
Вопрос
У меня есть этот фрагмент кода (из примера кода Nokia PC Connection 3.2 на C#):
DAContentAccessDefinitions.CA_FOLDER_INFO folderInfo =
new DAContentAccessDefinitions.CA_FOLDER_INFO();
folderInfo.iSize = Marshal.SizeOf(folderInfo); //(32)
IntPtr bufItem = Marshal.AllocHGlobal(folderInfo.iSize);
//I often get a AccessViolationException on the following line
Marshal.StructureToPtr(folderInfo, bufItem, true);
Если я побегу GC.Collect()
в начале этого, то я не получаю AccessViolationException
.Но я не хочу замедлять эту функцию без необходимости.Я попробовал поставить GC.Keepalive
в разных местах, но безуспешно.
CA_FOLDER_INFO
определяется как:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CA_FOLDER_INFO
{
public int iSize;
public int iFolderId;
public int iOptions;
public string pstrName;
public string pstrPath;
public int iSubFolderCount;
public IntPtr pSubFolders;
public IntPtr pParent;
}
В данном случае мне не нужны ни одна из строк, и я меняю их определения на IntPtr
кажется, исключение исчезает.
Что здесь происходит и как правильно предотвратить исключение?
Решение
Ваша проблема в том, что вы передаете true методу Marshal.StructureToPtr, поэтому он пытается освободить два указателя строк (которые иногда недействительны).В этом случае вам нужно передать false, поскольку вы только что выделили эту память в куче.(т.е.там нечего освобождать).
Другие советы
Вы уверены, что Marshal.Sizeof(bufItem) и Marshal.Sizeof(folderInfo) одинаковы?
И, может быть, тот факт, что вы не инициализируете строки?Поскольку вы говорите, что не получаете ошибку, когда они имеют IntPtr (по умолчанию IntPtr.Zero), я бы попробовал установить для них обоих пустые строки, прежде чем вы попытаетесь маршалировать элемент буфера.
[Редактировать]
Возможно, вам следует попробовать закрепить дескриптор буфера и маршалировать его со структурой, а не наоборот.Что-то вроде этого:
DAContentAccessDefinitions.CA_FOLDER_INFO folderInfo;
GCHandle pinnedHandle = GCHandle.Alloc(buffItem, GCHandleType.Pinned);
folderInfo = (DAContentAccessDefinitions.CA_FOLDER_INFO)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(DAContentAccessDefinitions.CA_FOLDER_INFO));
pin.Free();
//folderInfo should contain the data from buffItem
Используйте фиксированное ключевое слово, чтобы получить указатель на исходный файл. folderInfo
.
Возможно, неуправляемые ресурсы чем-то не освобождаются.Проверьте, не реализовано ли что-нибудь из того, что вы используете. IDодноразовый и если да, то заверните его в using { }
блокировать.