Вызов Delphi COM-объекта в C # создает исключение AccessViolationException

2

Я пытаюсь отправить строку в 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;

Я должен добавить, что это работает на моем компьютере и компьютере сотрудника, но не на третьем компьютере. Любые предложения о том, как я могу решить эту проблему?

  • 0
    Delphi WideString - это оболочка для COM BSTR . Является ли ваша string кода C # маршалингом s как BSTR при PInvoking для Send() ? Вы не показали эту декларацию на стороне C #. Кроме того, ваша Delphi-функция использует соглашение о вызовах, совместимое с C #? Кажется, это не так. Он должен использовать только cdecl или stdcall и быть соответствующим образом объявлен на стороне C #.
  • 0
    @RemyLebeau, код не выполняет маршалинг строк, но так как он работает в некоторых системах, я предполагаю, что в этом нет необходимости. То же самое для соглашения о вызовах, в настоящее время оно не определено, но, похоже, работает без него.
Показать ещё 2 комментария
Теги:
com

2 ответа

1
Лучший ответ
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.

  • 0
    out параметров автоматического inializates управляемого типа, как WideString , допустимого значения по умолчанию при входе в функцию. Таким образом, WideString будет инициализирован в ноль / пустую строку в этом случае. См. В чем разница между параметрами "var" и "out"? ,
  • 0
    @RemyLebeau Да, но это вещь Delphi, не так ли? Инициализация составитель удалось out параметров и возвращаемых значений осуществляется абонентом, подумал я. В случае вызова этого метода из кода Delphi, да, компилятор вставит инициализацию на вызывающей стороне, но сам метод не делает этого ... или, по крайней мере, это было мое понимание. Поскольку это вызывается оболочкой C #, эта оболочка должна была решить, что делать с переменной при входе. Или я что-то здесь совершенно неправильно понимаю?
Показать ещё 1 комментарий
0

@J... комментарий о том, что параметр ответа не был назначен, был, в моем случае, проблемой. Я попросил коллегу изменить dll, чтобы присвоить значение параметру, и это, кажется, исправляет ошибку, и теперь она работает так, как положено.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню