Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Почему в Delphi для работы с C# DLL нужно использовать PWideString, а не WideString, и зачем передавать строки в качестве PWideString?

Delphi , Файловая система , DLL и PlugIns

 

В данной статье мы рассмотрим проблему, связанную с взаимодействием Delphi и C# через DLL, и почему для передачи строковых данных между этими языками лучше использовать PWideString, а не WideString. Мы также рассмотрим, как правильно организовать взаимодействие между Delphi и C# для обеспечения корректной передачи данных.

Введение

В современных системах программирования часто возникает необходимость интегрировать компоненты, написанные на разных языках программирования. В данном случае мы рассматриваем взаимодействие между Delphi и C#. Delphi и C# используют разные подходы к работе с памятью и типами данных, что может привести к проблемам при взаимодействии между ними.

Проблема

В вашем примере вы пытаетесь использовать WideString для передачи строк между Delphi и C#. Однако при этом возникают проблемы с передачей параметров. В частности, параметры version и pstr1 оказываются поврежденными, а pstr2 передается корректно. Это связано с тем, что WideString в Delphi и BSTR в C# имеют разные внутренние представления и механизмы управления памятью.

Решение

Для решения этой проблемы необходимо использовать PWideString вместо WideString при передаче строк между Delphi и C#. PWideString представляет собой указатель на строку, что позволяет корректно передавать данные между языками. В C# строковые данные передаются в виде BSTR, а в Delphi — в виде указателя на строку (PWideString).

Вот как можно изменить ваш код, чтобы он работал корректно:

Delphi:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  PAttachToDLL = procedure();
  PTest = function(version : Integer; str1: PWideString; str2 : PWideString; version2 : integer): PWideString; stdcall;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    dllAttachToDLL: PAttachToDLL;
    dllTest : PTest;
    procedure makeTestCall;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  dllhandle: THandle;
  str1: PWideString;
  str2: PWideString;
  result: PWideString;
begin
  dllhandle := SafeLoadLibrary('C:pathtoDNNETestsbinDebugnet8.0-windows7.0DNNETestsNE.dll');
  @dllAttachToDLL := GetProcAddress(dllhandle, 'Attach');
  @dllTest := GetProcAddress(dllhandle, 'Test');
  dllAttachToDLL();
  makeTestCall();
  Application.Terminate();
end;

procedure TForm1.makeTestCall;
var
  str1: PWideString;
  str2: PWideString;
  result: PWideString;
begin
  str1 := PWideString(WideString('normal widestring'));
  str2 := PWideString(WideString('and here pwidestring'));
  result := dllTest(8, str1, str2, 7);
  try
    ShowMessage(result);
  finally
    if Assigned(result) then
      Dispose(result);
  end;
end;

end.

C#:

using System.Runtime.InteropServices;
using System.Windows;

namespace DNNETests
{
    public class Class1
    {
        [UnmanagedCallersOnly]
        public static void Attach()
        {
            MessageBox.Show("Hallo c# debugger");
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct IsInputFileVersionCompatibleResult
        {
            public int resultCode;
            public IntPtr msg;
            public IntPtr mostRecentRelatedPPPVersion;
        }

        [UnmanagedCallersOnly]
        public static IntPtr Test(int version, IntPtr pstr1, IntPtr pstr2, int version2)
        {
            string str1 = Marshal.PtrToStringBSTR(pstr1) ?? string.Empty;
            string str2 = Marshal.PtrToStringBSTR(pstr2) ?? string.Empty;
            MessageBox.Show("parameter: " + version + Environment.NewLine +
                str1 + Environment.NewLine +
                str2 + Environment.NewLine +
                version2);
            return Marshal.StringToBSTR("return value");
        }
    }
}

Объяснение

  1. Параметры передачи строк: В Delphi строки передаются в виде указателей на строки (PWideString), а не самих строк (WideString). Это позволяет корректно передавать данные между языками.

  2. Calling Convention: В Delphi необходимо указать stdcall как соглашение о вызове для функций, которые будут вызваны из C#. Это соглашение о вызове используется в Windows API и гарантирует корректную передачу параметров и управление стеком.

  3. Управление памятью: В Delphi строки, переданные в виде указателей (PWideString), должны быть освобождены после использования. Это можно сделать с помощью функции Dispose.

Альтернативное решение

Если вы хотите избежать использования PWideString и Dispose, можно использовать TBStr, который является оберткой для BSTR и автоматически управляет памятью. Вот как можно изменить ваш код для использования TBStr:

Delphi:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  PAttachToDLL = procedure();
  PTest = function(version : Integer; str1: TBStr; str2 : TBStr; version2 : integer): TBStr; stdcall;

  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    dllAttachToDLL: PAttachToDLL;
    dllTest : PTest;
    procedure makeTestCall;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
var
  dllhandle: THandle;
  str1: TBStr;
  str2: TBStr;
  result: TBStr;
begin
  dllhandle := SafeLoadLibrary('C:pathtoDNNETestsbinDebugnet8.0-windows7.0DNNETestsNE.dll');
  @dllAttachToDLL := GetProcAddress(dllhandle, 'Attach');
  @dllTest := GetProcAddress(dllhandle, 'Test');
  dllAttachToDLL();
  makeTestCall();
  Application.Terminate();
end;

procedure TForm1.makeTestCall;
var
  str1: TBStr;
  str2: TBStr;
  result: TBStr;
begin
  str1 := SysAllocString(PWideChar('normal widestring'));
  str2 := SysAllocString(PWideChar('and here pwidestring'));
  result := dllTest(8, str1, str2, 7);
  try
    ShowMessage(result);
  finally
    if Assigned(result) then
      SysFreeString(result);
  end;
end;

end.

Заключение

В данной статье мы рассмотрели проблему, связанную с передачей строк между Delphi и C# через DLL, и предложили несколько решений для ее решения. Использование PWideString или TBStr позволяет корректно передавать строки между языками и избежать проблем с управлением памятью.

Создано по материалам из источника по ссылке.

Статья рассматривает проблему взаимодействия Delphi и C# через DLL, и предлагает использовать `PWideString` вместо `WideString` для корректной передачи строковых данных между этими языками.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: DLL и PlugIns ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-04-09 16:56:32/0.0036561489105225/0