Pergunta

A resposta de

David a shows Outra questão uma função Delphi DLL retornando um WideString. Eu nunca pensei que seria possível sem o uso de ShareMem.

Meu DLL de teste:

function SomeFunction1: Widestring; stdcall;
begin
  Result := 'Hello';
end;

function SomeFunction2(var OutVar: Widestring): BOOL; stdcall;
begin
  OutVar := 'Hello';
  Result := True;
end;

Meu programa chamador:

function SomeFunction1: WideString; stdcall; external 'Test.dll';
function SomeFunction2(var OutVar: Widestring): BOOL; stdcall; external 'Test.dll';

procedure TForm1.Button1Click(Sender: TObject);
var
  W: WideString;
begin
  ShowMessage(SomeFunction1);
  SomeFunction2(W);
  ShowMessage(W);
end;

Ele funciona , e eu não entendo como. A convenção que eu conheço é o utilizado pela API do Windows, por exemplo, o Windows GetClassNameW:

function GetClassNameW(hWnd: HWND; lpClassName: PWideChar; nMaxCount: Integer): Integer; stdcall;

Significado o chamador fornece o tampão, e o comprimento máximo. A DLL do Windows escreve a esse tampão com a limitação do comprimento. O chamador é aloca e desaloca a memória.

Outra opção é que a DLL alocar a memória, por exemplo, usando LocalAlloc, eo Caller desaloca a memória chamando LocalFree.

Como é que a alocação de memória e trabalho deallocation com o meu exemplo DLL? Será que a "mágica" acontecer, porque o resultado é WideString (BSTR)? E por que não são do Windows APIs declarado com tal convenção conveniente? (Há algum conhecido APIs Win32 que usa essa convenção?)


EDIT:

Eu testei a DLL com C #.
Chamando SomeFunction1 provoca um AV (Attempted to read or write protected memory).
SomeFunction2 funciona bem.

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string SomeFunction1();

[DllImport(@"Test.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SomeFunction2([MarshalAs(UnmanagedType.BStr)] out string res);

...

string s;
SomeFunction2(out s);
MessageBox.Show(s); // works ok
MessageBox.Show(SomeFunction1()); // fails with AV!

Aqui é um acompanhamento .

Foi útil?

Solução

A WideString é o mesmo que um BSTR, é apenas o nome Delphi para ele. A alocação de memória é tratado pelo alocador COM compartilhado, CoTaskMemAlloc. Porque todas as partes usar o mesmo alocador você pode alocar de forma segura em um módulo e desalocar em outro.

Assim, a razão que você não precisa usar Sharemem é que a pilha de Delphi não está sendo usado. Em vez da pilha COM é usado. E que é compartilhada entre todos os módulos em um processo.

Se você olhar para a implementação Delphi de WideString você vai ver as chamadas para as seguintes APIs: SysAllocStringLen , SysFreeString e SysReAllocStringLen . Estes são o sistema previsto BSTR funções API .

Muitas das APIs do Windows que você se refere ao pré-data a invenção do COM. Além do mais, existem benefícios de desempenho ao uso de um buffer de comprimento fixo, alocados pelo chamador. Ou seja, que pode ser alocada na pilha ao invés de uma pilha. Eu também posso imaginar que os designers do Windows não quer forçar cada processo ter que apontam para OleAut32.dll e pagar o preço de manter o COM heap. Lembre-se que, quando a maior parte da API do Windows foi concebido, as características do hardware típico de desempenho era muito diferente a partir de agora.

Outra possível razão para não usar BSTR mais amplamente é que a API do Windows é destinado a C. E gerir a vida de BSTR de C é muito mais complicada do que a partir de linguagens de nível mais alto, como C ++, C #, Delphi etc.

Há uma complicação adicional no entanto. A Delphi ABI para valores de retorno WideString não é compatível com ferramentas da Microsoft. Você não deve usar WideString como um tipo de retorno, em vez devolvê-lo através de um parâmetro out. Para mais detalhes veja Por que não um WideString ser usado como um valor de retorno de função para interoperabilidade?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top