Я пытаюсь отправить строку в COM-объект Delphi и ожидаю ответа от объекта, но по какой-то причине он вызывает исключение AccessViolationException. Это исключение, которое он выдает, описание исключения, переведенное на английский язык: Попытка чтения или записи в защищенную память. Это часто указывает на то, что другая память повреждена. Вывод программы (с трассировкой вершины стека):
QManservice запущен.
Нажмите любую клавишу, чтобы остановить.
Запрос на получение заказов.
Полученная строка: S $ ПОЛУЧИТЬ ЗАКАЗЫ,По поводу использования: System.AccessViolationException: поиск полной информации о том, что такое Schrijven van beveiligd geheugen. Дуид эр ваак оп дат ан гехойген бешадигд.
bij Microsoft.Win32.Win32Native.SysStringByteLen(IntPtr bstr)
bij System.StubHelpers.BSTRMarshaler.ConvertToManaged(IntPtr bstr)
bij QMan_SafanDarley.IWLM_.Send(String Msg, String & Answer)
bij WorkLoadManagerServiceDefinitions.QManService.SendStringtoCON(String codToSend) в D:\Michael\С# Projects\QManServiceConsoleApp\OrderEditor_WCF\QManService.cs: regel 210
bij WorkLoadManagerServiceDefinitions.QManService.RequestGetOrders() в D:\Michael\CR Projects\QManServiceConsoleApp\OrderEditor_WCF\QManService.cs: regel 67...
Это код, который вызывает COM
private string SendStringToCOM(string cmdToSend)
{
try
{
Console.WriteLine($"String received: {cmdToSend}");
if (WLM == null)
{
WLM = new WLM_();
}
string answer = string.Empty;
WLM.Send(cmdToSend, out answer);
Console.WriteLine("Answer received");
return answer;
} catch(Exception e)
{
Console.WriteLine(e.Message);
Console.ReadKey();
return string.Empty;
}
}
Это код в Delphi, который принимает вызов, он отправляет его другому модулю, который выполняет работу с базой данных в соответствии с полученной командой.
function TWLM_.Send(const Msg: WideString; out Answer: WideString) : Integer;
begin
Result := fmProduction.AnalyzeData(Msg, 0);
end;
Я должен добавить, что это работает на моем компьютере и компьютере сотрудника, но не на третьем компьютере. Любые предложения о том, как я могу решить эту проблему?
function TWLM_.Send(const Msg: WideString; out Answer: WideString) : Integer;
begin
Result := fmProduction.AnalyzeData(Msg, 0);
end;
Здесь вы ничего не назначаете параметру Answer
. Это передается как out
параметр, означающий, что метод должен назначить что-то ему. Это ведет себя точно так же, как возвращаемое значение функции.
Если вы ничего не назначите этой переменной, она будет иметь любое (неназначенное) значение, которое существовало в стеке в тот момент, когда метод выделил для него пространство стека. Это не будет действительный указатель на WideString
но потребительский код попытается упорядочить его, как если бы он был. Иногда это происходит сразу же, иногда нет, иногда может просто повредить другие данные. В любом случае это ошибка.
В нативном Delphi-коде вы можете использовать параметры out
и var
ведущие себя совершенно одинаково - в обоих случаях для ссылочных типов указатель вызывающего кода доступен для чтения и записи методом, принимающим параметр. Если метод решает не изменять значение, это не требуется. Однако при управляемом взаимодействии ожидается, что метод out
всегда будет назначать выходной параметр. С# обеспечивает это, а Delphi - нет.
В этом случае пустая строка, которую вы передали на стороне С#, вообще не передается методу - вы можете ожидать, что она останется пустой строкой, не измененной кодом Delphi, но сделав параметр параметром out
вызывающий код ожидает получить возвращаемое значение в этом параметре и немедленно перезаписывает переданную переменную тем, что возвращается (в данном случае указателем на бессмысленность). Любое значение, которое переменная на стороне С# могла иметь до передачи этому методу, будет, по расширению, недоступно на стороне Delphi/COM.
out
параметров автоматического inializates управляемого типа, как WideString
, допустимого значения по умолчанию при входе в функцию. Таким образом, WideString
будет инициализирован в ноль / пустую строку в этом случае. См. В чем разница между параметрами "var" и "out"? ,
out
параметров и возвращаемых значений осуществляется абонентом, подумал я. В случае вызова этого метода из кода Delphi, да, компилятор вставит инициализацию на вызывающей стороне, но сам метод не делает этого ... или, по крайней мере, это было мое понимание. Поскольку это вызывается оболочкой C #, эта оболочка должна была решить, что делать с переменной при входе. Или я что-то здесь совершенно неправильно понимаю?
@J... комментарий о том, что параметр ответа не был назначен, был, в моем случае, проблемой. Я попросил коллегу изменить dll, чтобы присвоить значение параметру, и это, кажется, исправляет ошибку, и теперь она работает так, как положено.
WideString
- это оболочка для COMBSTR
. Является ли вашаstring
кода C # маршалингом s какBSTR
при PInvoking дляSend()
? Вы не показали эту декларацию на стороне C #. Кроме того, ваша Delphi-функция использует соглашение о вызовах, совместимое с C #? Кажется, это не так. Он должен использовать толькоcdecl
илиstdcall
и быть соответствующим образом объявлен на стороне C #.