none
P/Invoke .. C++ dll dans un code C# ... "System.AccessViolationException" RRS feed

  • Discussion générale

  • Bonjour, 

    Je reviens vers vous après quelques jours de galère avec le chargement d'une Dll c++ dans un code C#.

    Je n'ai pas le code de la Dll, par contre j'ai un exemple d'un code C++ qui utilise cette Dll, et je veux faire pareil mais en C#

    int main()
    {
    	HINSTANCE hinst = NULL;
    
    	typedef bool ( *GetTokenProto )( char ** );
    	typedef void ( *FreeTokenProto )( char * );
    
    	GetTokenProto GetToken;
    	FreeTokenProto FreeToken;
    
    	std::string str = "DllName.dll";
    	std::string token;
    
    	if ( (hinst = LoadLibraryA(str.c_str()) ) )
    	{
    		GetToken = (GetTokenProto) GetProcAddress(hinst, "GetToken");
    		FreeToken = (FreeTokenProto) GetProcAddress(hinst, "FreeToken");
    
    		if (GetToken && FreeToken)
    		{
    			char *buf;
    			if (GetToken(&buf))
    			{
    				token = buf;
    				FreeToken(buf);
    
    				std::cout << "Token:" << token << std::endl;
    			}
    			else
    			{
    				std::cerr << "DLL loaded but no token" << std::endl;
    				exit(1);
    			}
    		}
    		else
    		{
    			std::cerr << "DLL loaded but missing proc address(es)" << std::endl;
    			exit(1);
    		}
    
    		FreeLibrary(hinst);
    	}
    	else
    	{
    		std::cerr << "Failed to load DLL" << std::endl;
    		exit(1);
    	}
    	return 0;
    }


    Et mon code en C# est le suivant : 

    [DllImport("DllName.dll", EntryPoint = "GetToken", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool GetToken(ref StringBuilder token);
    [DllImport("DllName.dll", EntryPoint = "FreeToken", CallingConvention = CallingConvention.Cdecl)]
    public static extern void FreeToken(StringBuilder token);
    
    public string token;
    public void get_token()
    {
        StringBuilder buf = new StringBuilder();
        try
        {
            if (GetToken(ref buf))
            {
                token = buf.ToString();
                FreeToken(buf);
            }
            else
            {
                 Debug.WriteLine("DLL Loaded but no token");
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("\n" + ex.Message);
        }
    }

    Quand il arrive à : 

    if (GetToken(ref buf)

    il me sort une exception de type : System.AccessViolationException avec le message : Tentative de lecture ou d'écriture de mémoire protégée. Cela indique souvent qu'une autre mémoire est endommagée. je suppose que c'est la fonction GetProcAddress de P/Invoke qui n'arrive pas à trouver l'adresse de la fonction GetToken() dans la Dll, et je n'arrive pas à le résoudre.

    Si quelqu'un pourra me dire comment, je serai très reconnaissante.

    mardi 20 février 2018 13:44

Toutes les réponses

  • Bonjour, 

    Je reviens vers vous après quelques jours de galère avec le chargement d'une Dll c++ dans un code C#.

    Je n'ai pas le code de la Dll, par contre j'ai un exemple d'un code C++ qui utilise cette Dll, et je veux faire pareil mais en C#

    int main()
    {
    	HINSTANCE hinst = NULL;
    
    	typedef bool ( *GetTokenProto )( char ** );
    	typedef void ( *FreeTokenProto )( char * );
    
    	GetTokenProto GetToken;
    	FreeTokenProto FreeToken;
    
    	std::string str = "DllName.dll";
    	std::string token;
    
    	if ( (hinst = LoadLibraryA(str.c_str()) ) )
    	{
    		GetToken = (GetTokenProto) GetProcAddress(hinst, "GetToken");
    		FreeToken = (FreeTokenProto) GetProcAddress(hinst, "FreeToken");
    
    		if (GetToken && FreeToken)
    		{
    			char *buf;
    			if (GetToken(&buf))
    			{
    				token = buf;
    				FreeToken(buf);
    
    				std::cout << "Token:" << token << std::endl;
    			}
    			else
    			{
    				std::cerr << "DLL loaded but no token" << std::endl;
    				exit(1);
    			}
    		}
    		else
    		{
    			std::cerr << "DLL loaded but missing proc address(es)" << std::endl;
    			exit(1);
    		}
    
    		FreeLibrary(hinst);
    	}
    	else
    	{
    		std::cerr << "Failed to load DLL" << std::endl;
    		exit(1);
    	}
    	return 0;
    }


    Et mon code en C# est le suivant : 

    [DllImport("DllName.dll", EntryPoint = "GetToken", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool GetToken(ref StringBuilder token);
    [DllImport("DllName.dll", EntryPoint = "FreeToken", CallingConvention = CallingConvention.Cdecl)]
    public static extern void FreeToken(StringBuilder token);
    
    public string token;
    public void get_token()
    {
        StringBuilder buf = new StringBuilder();
        try
        {
            if (GetToken(ref buf))
            {
                token = buf.ToString();
                FreeToken(buf);
            }
            else
            {
                 Debug.WriteLine("DLL Loaded but no token");
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("\n" + ex.Message);
        }
    }

    Quand il arrive à : 

    if (GetToken(ref buf)

    il me sort une exception de type : System.AccessViolationException avec le message : Tentative de lecture ou d'écriture de mémoire protégée. Cela indique souvent qu'une autre mémoire est endommagée. je suppose que c'est la fonction GetProcAddress de P/Invoke qui n'arrive pas à trouver l'adresse de la fonction GetToken() dans la Dll, et je n'arrive pas à le résoudre.

    Si quelqu'un pourra me dire comment, je serai très reconnaissante.

    Je précise que parfois j'ai cette Exception et parfois mon programme d'arrête avec ce message "Chargé 'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\IDE\Remote Debugger\x64\Runtime\Microsoft.VisualStudio.Debugger.Runtime.dll'. Chargement des symboles ignoré. Le module est optimisé et l'option du débogueur 'Uniquement mon code' est activée."

    Et aussi je veux préciser que lors de l'exception, dans l'onglet Variable locales, je vois bien que "buf" contient un string, et c'est exactement ce que je suis censée avoir en utilisant la Dll

    mardi 20 février 2018 15:18
  • Essaie sans le ref

    Exemple avec 2 méthodes (StringBuilder et IntPtr) avec une api Win32 renvoyant une chaine de caractères :

    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
    
    [DllImport("User32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern int GetClassName(IntPtr hWnd, IntPtr lpClassName, int nMaxCount);
    
    
    StringBuilder sbClassName = new StringBuilder(260);                                
    string sClassName = null;                                           
                                                                        
    GetClassName(this.Handle, sbClassName, (int)(sbClassName.Capacity));
    sClassName = sbClassName.ToString();                                
                                                                        
    IntPtr pClassName = Marshal.AllocHGlobal(260);                      
    GetClassName(this.Handle, pClassName, 260); 
    // PtrToStringAnsi si chaine Ansi                        
    sClassName = Marshal.PtrToStringUni(pClassName);                    
    Marshal.FreeHGlobal(pClassName); 

    mercredi 21 février 2018 07:47
  • Et pour ref StringBuilder ??si vous remarquez j'en ai une méthode avec un char* et l'autre avec un char**



    • Modifié J-Go mercredi 21 février 2018 08:17
    mercredi 21 février 2018 08:17