[ccppbrasil] Qual o perigo de se chamar uma DLL em C++ em um código C#?

205 views
Skip to first unread message

Matheus Lima

unread,
Jan 17, 2012, 7:48:28 PM1/17/12
to ccppbrasil
Olá à todos. 
Esses dias fiquei sabendo que não é recomendado acessar uma DLL em C++ em um código em C#. O motivo é que pelo fato de o C++ não ter gerenciamento de memória, um programa em C# não pode chamar uma DLL em C++, já que poderiam haver problemas com memory leaks ou coisas do tipo. Alguém poderia me explicar o porquê disso? Não se deve mesmo acessar uma DLL em C++ em um código C#?
Até mais.

--
Matheus Lima
LDS - Laboratório de Desenvolvimento de Software - IFCE
Graduando em Engenharia de Teleinformática - UFC

Bruno Sanches

unread,
Jan 17, 2012, 8:03:40 PM1/17/12
to ccppb...@googlegroups.com
Depende de como você chama, eu uso isso num sistema que trabalho sem problemas.

Você tem que apenas saber que o GC do .Net não vai cuidar dos objetos C++ e outras alocações que você fizer no lado do C++, no código C# vai ter que explicitamente destruir os objetos.

Pode-se também encapsular os objetos em classes C# e implementar IDisposable para encapsular esse gerenciamento, mas de qualquer forma, vai ter que invocar o Dispose para limpar tudo.

T+
Bruno Sanches
========================
http://www.pontov.com.br


2012/1/17 Matheus Lima <matheus...@gmail.com>
--
Antes de enviar um e-mail para o grupo leia:
http://www.ccppbrasil.org/wiki/Lista:AntesdePerguntar
--~--~---------~--~----~---------------------------------~----------~--~----~
[&] Colabore com a Pesquisa de Preferência de Conteúdo
para Eventos do Grupo C & C++ Brasil:
http://www.surveymonkey.com/s/GBBGTXN
------~----~-------~---~---~---~---~----------------~------------~---------~
[&] C & C++ Brasil - http://www.ccppbrasil.org/
Para sair dessa lista, envie um e-mail para ccppbrasil-...@googlegroups.com
Para mais opções, visite http://groups.google.com/group/ccppbrasil
--~--~---------~--~----~--~-~--~---~----~-----------------~--~----------~
Emprego & carreira: vag...@ccppbrasil.org
http://groups.google.com/group/dev-guys?hl=en

Matheus Lima

unread,
Jan 17, 2012, 8:11:50 PM1/17/12
to ccppb...@googlegroups.com
Entendi. Então implementando essa interface IDisposable eu posso manipular os objetos não-gerenciados do C++ e assim posso trabalhar com eles sem problema?

Tiago Alves

unread,
Jan 17, 2012, 8:19:03 PM1/17/12
to ccppb...@googlegroups.com
Vazamento de memória pode ocorrer se você alocar memória na DLL e não liberar.
Se uma função da DLL precisa alocar memória e essa memória deve ser usada no C#, sem problemas, desde que sua DLL tenha uma função exportada para liberar essa memória depois que sua aplicação C# terminar de usar aquela memória. 
---------------------------------------------------
 Tiago Alves de Oliveira
 Engenheiro de Biometria
 
Blog: CodaBR
 
 Cel:  (19) 8175-2513
-------------------------------------------------------

Djalma Rosa dos Santos Filho

unread,
Jan 17, 2012, 8:32:16 PM1/17/12
to ccppb...@googlegroups.com
Mateus,

Quem trabalha com C# e outras linguagens .NET pode precisar por exemplo acessar diretamente funcoes da API do Windows ou outras bibliotecas escritas em código nativo e não há nada de errado nisso.

A Microsoft tem várias opções de interoperabilidade, uma delas é o PInvoke, mais indicada para acessar DLLs que exportam funcões em C, como é o caso da Win32 API.

Você vai encontrar também a linguagem C++ CLI que a partir do VS 2005 substituiu a Managed C++, com certeza o recomendado para acessar DLL ou libs estáticas escritas em C++.

Com relação ao gerenciamento de memória existe mesmo a necessidade de se tomar alguns cuidados para desalocar a memória nativa, usando corretamente os destrutores e finalizers, sabendo conviver com o comportamento não determinístico do Garbage Collector, interessante prover realmente o método dispose para que o cliente force a desalocação imediata dos recursos alocados e existe também a função AddMemoryPressure que ajuda o GC a contabilizar a alocação não gerenciada e consequentemente passar mais vezes. Ou seja, o problema de memory leak com wrappers .NET nem sempre é porque você esqueceu de desalocar, mas as vezes porque o GC está trabalhando menos do que você gostaria para a sua aplicação.

Outro problema relacionado à memória é a necessidade de pinagem de objetos para que o GC não mova o seu endereço de memória nativo quando ele estiver compactando o heap gerenciado. Se você não pinar vai sofrer com erros de Access violation intermitentes (attempted to read or write protected memory).

Djalma



2012/1/17 Matheus Lima <matheus...@gmail.com>

Bruno Sanches

unread,
Jan 17, 2012, 8:32:53 PM1/17/12
to ccppb...@googlegroups.com
Só um detalhe sobre o IDisposable, não é totalmente sem problemas.

Se você implementar o IDisposable corretamente, quando invocar o metodo Dispose do seu objeto, ele vai invocar a função na dll (conforme citado pelo Tiago) que vai destruir / liberar, etc o objeto do C++.

Mas você tem que invocar o Dispose. 

No pior dos casos, se não invocar o Dispose, talvez um dia o GC colete o seu objeto e invoque ele para você, mas nesse caso você tem que garantir que o código c++ seja thread safe, pois o GC provavelmente vai fazer isso em outra thread.

T+

Bruno Sanches
========================
http://www.pontov.com.br


2012/1/17 Tiago Alves <tiago...@gmail.com>

Rafael Uchôa

unread,
Jan 17, 2012, 8:49:44 PM1/17/12
to ccppb...@googlegroups.com
Bom, quiser entender todas as regras:

http://msdn.microsoft.com/en-us/magazine/cc164193.aspx

Agora, se quiser somente usar o que você tem em C++ no C#, exporte as funções principais como um facade ou service e use o PInvoke para mapear as funções e os parametros necessários.

Exemplo: http://www.dotnetperls.com/dllimport

Matheus Lima

unread,
Jan 18, 2012, 6:47:54 AM1/18/12
to ccppb...@googlegroups.com
Desculpe a ignorância Djalma, mas o que seria essa pinagem de objeto que você se refere?

Djalma Rosa dos Santos Filho

unread,
Jan 18, 2012, 6:52:27 AM1/18/12
to ccppb...@googlegroups.com
Se você tiver sorte da sua DLL exportar interface COM fica mais fácil e a interoperabilidade é transparente. Basta adicionar referência da dll no Solution Explorer para automagicamente enxergar os objetos no .NET e começar a instanciar as classes COM como se fosse C#.

2012/1/17 Rafael Uchôa <rafaeluc...@gmail.com>

Djalma Rosa dos Santos Filho

unread,
Jan 18, 2012, 7:01:49 AM1/18/12
to ccppb...@googlegroups.com
A pinagem é simplesmente uma maneira de proteger a chamado do código nativo (C/C++) que não espera uma repentina mudança de endereço. O GC precisa fazer isso as vezes para desfragmentar o heap que ele gerencia, mas se o objeto estiver "pinado" aquele endereço de memória nao é movido.

Em CLI usa-se um smart pointer chamado pin_ptr para pinar os objetos .NET que precisam ser passados para funções nativas.

Veja explicação mais detalhada e exemplo:

http://msdn.microsoft.com/en-us/library/1dz8byfh%28v=vs.80%29.aspx


2012/1/18 Matheus Lima <matheus...@gmail.com>

Matheus Lima

unread,
Jan 18, 2012, 8:25:11 AM1/18/12
to ccppb...@googlegroups.com
Em CLI usa-se esse smart pointer, mas em C++ padrão, como eu faria essa proteção do meu objeto nativo?

Bruno Sanches

unread,
Jan 18, 2012, 8:27:36 AM1/18/12
to ccppb...@googlegroups.com
Essa questão da pinagem é apenas para quando passo ponteiro / referencia de um objeto managed para um codigo não managed certo?

T+
Bruno Sanches
========================
http://www.pontov.com.br


2012/1/18 Matheus Lima <matheus...@gmail.com>

Matheus Lima

unread,
Jan 18, 2012, 8:34:04 AM1/18/12
to ccppb...@googlegroups.com
Eu tinha entendido que era o contrário, quando você passa um ponteiro de um objeto não managed para um objeto managed. Não é isso?

Rodrigo Madera

unread,
Jan 18, 2012, 8:34:33 AM1/18/12
to ccppb...@googlegroups.com
Matheus,

Eu não uso objetos COM, mas uso funções simples exportadas que podem ser chamadas sem nenhum perigo (até onde eu sei).

Se houver alocação, utilizo o pattern de deixar o C++ criar o objeto, e retornar um handle (que será um número inteiro simples) que o C# poderá usar.

Exemplo:

DllDoMatheus.dll
  + const int OpenXyz(param0, param1, etc)
  + const bool DoSomething(const int handle, param0, paramN)
  + void Close(const int handle)


Eu uso assim, dai separo completamente o C# do C++.

Abraços,
Mx


2012/1/18 Bruno Sanches <bcsa...@gmail.com>

Bruno Sanches

unread,
Jan 18, 2012, 8:47:36 AM1/18/12
to ccppb...@googlegroups.com
Se você manda o objeto não managed para o código managed, sem problemas, o código managed não vai controlar ele, por isso vem aqueles problemas que citei, onde você, no código managed vai ter que gerenciar o tempo de vida dele e talvez implementar a interface IDisposable para simplificar um pouco o seu trabalho na hora de usar o objeto não managed.

O seu código C# vai ter que dizer ao código C++ que não precisa mais do objeto e o código C++ cuida de destruir ele.

Nos trabalhos que fiz para uma empresa, tinha uma interface C++ +/- assim (supondo aqui que temos um objeto com um nome muito criativo de "Object"):

int ObjectCreate(....);
void ObjectDestroy(int id);
bool ObjectOperationX(int id);

No lado C# criamos uma classe para fazer os acessos na dll:
class ObjectDll
    {
        [DllImport("ObjectImpl.dll", EntryPoint = " ObjectCreate",
            SetLastError = true,
            CharSet = CharSet.Unicode, ExactSpelling = true,
            CallingConvention = CallingConvention.StdCall)]        
        public static extern  Int32  Create();

    //demais fucnoes da dll
}

E o uso do objeto era encapsulado numa classe C#:
class Object: IDisposable
{
     Int32 mId;

     public Object()
     {
          mId = ObjectDll.Create();
     }

~ Object ()
        {
            this.Dispose(false);
        }

        public void Dispose()
        {
            GC.SuppressFinalize(this);

            this.Dispose(true);           
        }

        public void Dispose(bool disposing)
        {
            if (disposing)
            {
                //no managed
            }
            if(mId != 0)
            {
                ObjectDll.Destroy(mId);
                mId = 0;
            }
        }
}

Dessa forma, no codigo C# poderiamos simplemente fazer:

Object obj = new Object();
obj.FazAlgo();
obj.Dispose();

ou então:
using(Object obj = new Object())
{
     obj.FazAlgo();
}

O codigo não deve estar 100% correto, fiz de cabeça com copy e paste modificado, mas acho que já da para você ter uma idéia.

Outro detalhe, no nosso caso, era conveniente armazenar no C++ os objetos num array, por isso os objetos eram identificados com inteiros.

Djalma Rosa dos Santos Filho

unread,
Jan 18, 2012, 8:49:07 AM1/18/12
to ccppb...@googlegroups.com
É exatamente como o Bruno explicou. Eu pino o objeto managed para passar um ponteiro ou referencia para o código unmanaged.
É que eu prefiro o termo nativo no lugar de unmanaged.
O pin_ptr é um template que faz parte da linguagem CLI e nao do C++ padrão.
O CLI realmente é mais intrusivo, voce pode misturar no mesmo código recursos do C++ e do framework .NET.
Essa interoperabilidade pode ser uma bênção ou uma maldição, aí vai da necessidade do seu projeto.

2012/1/18 Matheus Lima <matheus...@gmail.com>

Matheus Lima

unread,
Jan 18, 2012, 9:08:12 AM1/18/12
to ccppb...@googlegroups.com
Entendi agora. Obrigado à todos pelas explicações.
Reply all
Reply to author
Forward
0 new messages