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

2

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

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

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

Функция Delphi Library:

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. Но когда я пытаюсь вызвать его из С#, получен вывод null.

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

[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);
Теги:
string

1 ответ

8
Лучший ответ

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

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

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

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

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

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

На самом деле, вы даже не возвращаете указатель на С#! На стороне С# вы объявили 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;

С#:

[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;

С#:

[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 вместо использования CoTaskMemAlloc(), просто обязательно CoTaskMemAlloc() ее как BSTR на стороне С#:

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 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;

С#:

[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;

С#:

[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);
  • 0
    Спасибо @ Реми. Это очень помогло и добавило моего обучения. Спасибо!

Ещё вопросы

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