PInvoke - como representar um campo a partir de uma interface COM
Pergunta
Eu estou fazendo referência uma estrutura COM que começa da seguinte forma:
[scriptable, uuid(ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e)]
interface nsICacheSession : nsISupports
{
/**
* Expired entries will be doomed or evicted if this attribute is set to
* true. If false, expired entries will be returned (useful for offline-
* mode and clients, such as HTTP, that can update the valid lifetime of
* cached content). This attribute defaults to true.
*/
attribute PRBool doomEntriesIfExpired;
...
Eu encontrei código para importar essa interface em meu aplicativo C #. O código deve ser embora errado, como o método set
não parece ser útil e também lança um erro quando tento chamá-lo só para ver o que acontece:
[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession
{
[return: MarshalAs(UnmanagedType.Bool)]
void set_doomEntriesIfExpired();
[return: MarshalAs(UnmanagedType.Bool)]
bool get_doomEntriesIfExpired();
...
O que é a maneira correta para definir o valor de doomEntriesIfExpired
e como faço referência a esse do meu código?
Editar
Eu mudei meu código para o seguinte, que produziu "System.AccessViolationException: Tentativa de ler ou protegido contra gravação Memória yada yada ...":
[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession
{
void set_doomEntriesIfExpired(bool enabled);
bool get_doomEntriesIfExpired();
...
Solução
A resposta você inseriu é bom. Em COM bools Interop são empacotados como VARIANT_BOOL por padrão, então sua adição do atributo MarshalAs para dizer o empacotador de usar um 4 byte tipo BOOL padrão é correto, embora a parte getter da equação precisa do atributo adicionado também.
Em geral, eu gostaria de deixar propriedades definidas na interface como propriedades em vez de quebrá-los para fora em seus getters e setters. Ele corresponde a semântica da definição de interface melhor e é geralmente mais fácil de ler também. Você deve ser capaz de voltar a escrever a sua definição de importação COM da seguinte forma para manter a natureza atributo de doomEntriesIfExpired:
[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession
{
bool doomEntriesIfExpired
{
[param:MarshalAs(UnmanagedType.Bool)]set;
[return:MarshalAs(UnmanagedType.Bool)]get;
}
...
Outras dicas
O fato de que você indicar um [return: MarshalAs(UnmanagedType.Bool)]
para o seu método void set
é, obviamente, uma fonte de erro.
Dito isso, eu consegui o código de um C ++ mozilla plugin sem o [retorno ...] tags no .idl, por exemplo:
[scriptable, uuid(ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e)]
interface nsICacheSession : nsISupports
{
void set_doomEntriesIfExpired(in bool value);
bool get_doomEntriesIfExpired();
}
A propósito, você tem certeza que pode codificar a NSI plugin no C #?
Acontece que a resposta foi a seguinte:
[Guid("ae9e84b5-3e2d-457e-8fcd-5bbd2a8b832e"), ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface nsICacheSession
{
void set_doomEntriesIfExpired([In, MarshalAs(UnmanagedType.Bool)] ref bool enabled);
bool get_doomEntriesIfExpired();