Pergunta

** grande atualização ** Eu cometi um pequeno erro, mas eu ainda estou curioso sobre exatamente o que está acontecendo.

A função que estou chamando é realmente "fooV", uma função com essa assinatura:

foo(const char *, const char *, EnumType, va_list)

este esclarece o AccessViolationExceptions eu estava ficando, mas não explica por que os parâmetros params trabalhar para qualquer outro tipo .NET exceto para cordas que terão de ser convertido em caracteres multibyte ANSI. Eu estou indo para obter o desenvolvedor da DLL para expor a versão que realmente usa ... na lista de parâmetros, quaisquer sugestões para PInvoking se va_list é o parâmetro?

** post antigo **

Esta é relacionado, mas diferente de uma pergunta recente perguntei .

Eu tenho que usar PInvoke para chamar uma função de biblioteca C que tem a seguinte assinatura:

foo(const char *, const char *, EnumType, ...)

A função configura um recurso de uma forma que varia de acordo com StructType; o caso, eu estou interessado em configura algo que espera que o varargs para ser uma única seqüência ANSI multibyte. Eu usei essa assinatura PInvoke para chamar a função:

    [DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
    public static extern int Foo(string s1, string s2, EnumType st1, params string[] s3);

O params string[] parecia estranho para mim, mas as assinaturas anteriores que chamou esta função foram usá-lo para outros tipos, tais como bool então eu seguiu o padrão.

Eu envolvo isso com um método mais amigável NET:

public void Foo(string s1, string s2, string s3)
{
    int error = Dll.Foo(s1, s2, EnumType.Foo, s3);
    // handle errors
}

Recentemente eu mudei a assinatura para incluir "BestFitMapping = false, ThrowOnUnmappableChar = true" no atributo DLLImport para cumprir com as sugestões do FxCop. Este é um arenque vermelho como vou descrever mais tarde.

O que esperar de fazer essa alteração suporte limitado para Unicode. Por exemplo, em uma máquina com uma página de código Inglês uma exceção será lançada se você passar uma cadeia que contém um carácter japonês. Em uma máquina japonesa, eu esperaria para ser capaz de passar uma cadeia japonesa para a função. O teste de Inglês funcionou como esperado, mas o teste japonês lança um System.Runtime.InteropServices.COMException com HRESULT 0x8007007A. Isso acontece mesmo sem as definições BestFitMapping e ThrowOnUnmappableChar.

Eu fiz um pouco de olhar ao redor e vi alguns sites que sugerem que você poderia PInvoke varargs apenas especificando argumentos normais, então eu tentei esta assinatura:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo")]
public static extern int Foo(string s1, string s2, EnumType st1, string s3);

Este lança uma AccessViolationException quando eu usá-lo em qualquer máquina Inglês ou japonês.

Eu não posso usar UTF-8 ou outra codificação Unicode porque este C espera biblioteca e alças única multibyte ANSI. Eu descobri que especificar CharSet.Unicode para esta função funciona, mas eu estou preocupado que seja apenas por coincidência e não algo sobre o qual eu deveria confiar. Tenho pensado em converter a string para um byte [] usando a página de código do sistema, mas não consigo descobrir como especificar eu gostaria de passar uma matriz de bytes para o parâmetro varargs.

O que está acontecendo? Na máquina do Inglês, muito bem Inglês personagens trabalho e charactes japoneses lançar um ArgumentException como esperado. Na máquina japonesa, bem Inglês personagens trabalho e caracteres japoneses jogar a COMException. Existe errado alguma coisa com a minha assinatura PInvoke? Eu tentei usar o atributo MarshalAs para especificar um tipo de LPArray e subtipo de LPSTR, mas isso falhar da mesma maneira. UnmanagedType.LPStr indica que é uma cadeia ANSI de um byte; há uma maneira para especificar uma string multibyte ANSI?

** ATUALIZAÇÃO ** Aqui está uma adição de material tomar observações atuais em conta.

Se eu especificar uma convenção de chamada de CDECL e tomar parâmetros normais como esta:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.Cdecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

Quando eu uso esta especificação, recebo uma AccessViolationException. Então, eu tentei isso:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", CallingConvention = CallingConvention.CDecl)]
public static extern int Foo(string s1, string s2, EnumType e1, string s3) 

Isso funciona para Inglês e joga o COMException quando eu usar caracteres japoneses.

A única coisa que eu achei que works consistente é o seguinte:

[DllImport(DllName, CharSet = CharSet.Ansi, EntryPoint = "foo", BestFitMapping = false, ThrowOnUnmappableChar = true)]
public static extern int Foo(string s1, string s2, EnumType e1, params IntPtr[] s3)

Para fazer este trabalho, eu uso Marshal.StringToHGlobalAnsi () e passar esse ponteiro. Isso funciona para Inglês e Japonês. Eu não estou confortável com não entender por que esta é a solução, mas funciona.

Foi útil?

Solução

Você provavelmente também precisará especificar CallingConvention=CallingConvention.Cdecl desde um varargs função utiliza uma convenção _cdecl chamando (o padrão é Winapi que por sua vez o padrão é stdcall).

Você pode precisar de mais alguma coisa, mas eu tenho certeza que você vai precisar de pelo menos isso.

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