Передача строки из Delphi в C # возвращает ноль.Однако, это прекрасно работает, когда я вызываю Delphi lib из Delphi.Как получить строку из Delphi - PullRequest
0 голосов
/ 29 ноября 2018

Я совершенно новичок в Delphi и пытаюсь создать несколько библиотек DLL для .NET.

Чего я хочу добиться, так это отправлять и получать txt-вывод из моих библиотек DLL.

Вот что я сделал до сих пор:

Функция библиотеки Delphi:

function DBConnet(inputStr: PChar; connStr: PChar): PAnsiChar; stdcall; export;
var
  conStr: string;
  s: string;
begin  
  inputStr := PChar('Hello from Delphi! How are you ' + inputStr + connStr);
  try
    Result := PAnsiChar(inputStr);    
  except
    on e: Exception do
    begin
      Result := 'exception';
    end;
  end;
end;

Exports
  DBConnet;

end.

Вот моя функция вызова в Delphi:

function DBConnet(inputStr: PChar; connStr: PChar): PChar; stdcall; external 'NewLib.dll';

procedure TUseDLLForm.functionxClick(Sender: TObject);
var
  a: string;
  conStr: string;
  i: integer;
begin
  a := 'firstname';
  conStr := 'lastname';
  ShowMessage(DBConnet(pchar(a), pchar(conStr)));
end;

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

Вот мой блок кода C #:

[DllImport("NewLib.dll",
        CallingConvention = CallingConvention.StdCall,
        CharSet = CharSet.Unicode)]
public static extern void DBConnet(string inputString,  string 
connectionString, [MarshalAs(UnmanagedType.BStr)] out string dbStrObj);

А затем в Main я называю это так:

DBConnet(inputString, connectionString, out dbStrObj);

1 Ответ

0 голосов
/ 29 ноября 2018

Код DLL, который вы показали, НЕ совместим с вашим кодом C #.

Ваш код C # полагается на стандартное string поведение маршалинга , которому ваша DLL не соответствует.

A string передается в DLL как указатель PWideChar по умолчанию (если вы используете Delphi 2009+, PChar отображается на PWideChar, в противном случае вместо этого отображается PAnsiChar).

Кроме того, ваша функция DLL возвращает PAnsiChar, но маршаллер ожидает PWideChar по умолчанию, поскольку вы не применили атрибут [return: MarshalAs(UnmanagedType.LPStr)] в объявлении функции DLL на стороне C #.

Но что еще более важно, когда DLL возвращает указатель на память, которой маршаллер становится владельцем, память ДОЛЖНА быть выделена с CoTaskMemAlloc() или эквивалентным, так как маршаллер освобождает память с CoTaskMemFree() по умолчанию(см. Управление памятью с помощью маршалера взаимодействия ).

Вы возвращаете указатель на динамически распределенную память, однако эта память не выделяется с помощью CoTaskMemAlloc().Фактически, память фактически управляется компилятором Delphi и освобождается автоматически при выходе из функции.Итак, вы фактически возвращаете неверный указатель на C #.

На самом деле, вы даже не возвращаете указатель на C #!На стороне C # вы объявили DLL как имеющую параметр out, но такого параметра на стороне DLL нет!

С учетом всего сказанного, попробуйте что-то более похожее на это:

DLL:

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

function UnicodeStringToCoTaskMemStr(const s: UnicodeString): PWideChar;
var
  Size: Integer;
begin
  Size := (Length(s) + 1) * SizeOf(WideChar);
  Result := PWideChar(CoTaskMemAlloc(Size));
  if Result <> nil then
    Move(PWideChar(s)^, Result^, Size);
end;

function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; export;
var
  sInput: UnicodeString;
  sConn: UnicodeString;
begin  
  try
    sInput := inputStr;
    sConn := connStr;
    Result := UnicodeStringToCoTaskMemStr('Hello from Delphi! How are you ' + sInput + sConn);
  except
    Result := UnicodeStringToCoTaskMemStr('exception');
  end;
end;

Приложение Delphi:

function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; external 'NewLib.dll';

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

procedure TUseDLLForm.functionxClick(Sender: TObject);
var
  a: UnicodeString;
  conStr: UnicodeString;
  ret: PWideChar;
begin
  a := 'firstname';
  conStr := 'lastname';
  ret := DBConnet(PWideChar(a), PWideChar(conStr));
  if ret <> nil then
  begin
    try
      ShowMessage(ret);
    finally
      CoTaskMemFree(ret);
    end;
  end else
    ShowMessage('nil');
end;

C #:

[DllImport("NewLib.dll",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPWStr)]
public static extern string DBConnet(string inputString, string connectionString);

Или с использованием параметра out вместо:

DLL:

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

function UnicodeStringToCoTaskMemStr(const s: UnicodeString): PWideChar;
var
  Size: Integer;
begin
  Size := (Length(s) + 1) * SizeOf(WideChar);
  Result := PWideChar(CoTaskMemAlloc(Size));
  if Result <> nil then
    Move(PWideChar(s)^, Result^, Size);
end;

function DBConnet(inputStr: PWideChar; connStr: PWideChar; out outputStr: PWideChar): boolean; stdcall; export;
var
  sInput: UnicodeString;
  sConn: UnicodeString;
begin  
  Result := False;
  try
    sInput := inputStr;
    sConn := connStr;
    outputStr := UnicodeStringToCoTaskMemStr('Hello from Delphi! How are you ' + sInput + sConn);
    Result := outputStr <> nil;
  except
  end;
end;

Приложение Delphi:

function DBConnet(inputStr: PWideChar; connStr: PWideChar, out outputStr: PWideChar): boolean; stdcall; external 'NewLib.dll';

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

procedure TUseDLLForm.functionxClick(Sender: TObject);
var
  a: UnicodeString;
  conStr: UnicodeString;
  ret: PWideChar;
begin
  a := 'firstname';
  conStr := 'lastname';
  if DBConnet(PWideChar(a), PWideChar(conStr), ret) then
  begin
    try
      ShowMessage(ret);
    finally
      CoTaskMemFree(ret);
    end;
  end else
    ShowMessage('fail');
end;

C #:

[DllImport("NewLib.dll",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool DBConnet(string inputString, string connectionString,
  [MarshalAs(UnmanagedType.LPWStr)] out outputString string);

Кроме того, вы можете выделить возвращенную память как BSTR string вместо использования CoTaskMemAlloc(), просто обязательно выделите ее как BSTR на стороне C #:

DLL:

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; export;
var
  sInput: UnicodeString;
  sConn: UnicodeString;
begin  
  try
    sInput := inputStr;
    sConn := connStr;
    // the RTL's StringToOleStr() function returns a BSTR...
    Result := StringToOleStr('Hello from Delphi! How are you ' + sInput + sConn);
  except
    Result := StringToOleStr('exception');
  end;
end;

Приложение Delphi:

function DBConnet(inputStr: PWideChar; connStr: PWideChar): PWideChar; stdcall; external 'NewLib.dll';

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

procedure TUseDLLForm.functionxClick(Sender: TObject);
var
  a: UnicodeString;
  conStr: UnicodeString;
  ret: WideString; // NOT UnicodeString!
begin
  a := 'firstname';
  conStr := 'lastname';
  Pointer(ret) := DBConnet(PWideChar(a), PWideChar(conStr));
  if ret <> '' then
    ShowMessage(ret)
  else
    ShowMessage('nil');
end;

C #:

[DllImport("NewLib.dll",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string DBConnet(string inputString, string connectionString);

Или, используя параметр out:

DLL:

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

function DBConnet(inputStr: PWideChar; connStr: PWideChar; out outputStr: WideString): boolean; stdcall; export;
var
  sInput: UnicodeString;
  sConn: UnicodeString;
begin  
  Result := False;
  try
    sInput := inputStr;
    sConn := connStr;
    outputStr := 'Hello from Delphi! How are you ' + sInput + sConn;
    Result := True;
  except
  end;
end;

Приложение Delphi:

function DBConnet(inputStr: PWideChar; connStr: PWideChar; out outputStr: WideString): boolean; stdcall; external 'NewLib.dll';

// uncomment this if you are NOT using D2009+ ...
{
type
  UnicodeString = WideString;
}

procedure TUseDLLForm.functionxClick(Sender: TObject);
var
  a: UnicodeString;
  conStr: UnicodeString;
  ret: WideString;
begin
  a := 'firstname';
  conStr := 'lastname';
  if DBConnet(PWideChar(a), PWideChar(conStr), ret) then
    ShowMessage(ret)
  else
    ShowMessage('fail');
end;

C #:

[DllImport("NewLib.dll",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool DBConnet(string inputString, string connectionString,
  [MarshalAs(UnmanagedType.BStr)] out string outputStr);
...