Les Forums

Les Forums

Les forums sont fermés. Ils restent présent pour consultation et archivage.
Vous pouvez désormais poser vos questions directement dans les commentaires en bas de chaque page du site.
Alors n'hésitez pas à participer

C# : API Ras sur modem GPRS = plantage svchost

Bonjour,

Voilà, je développe une application en C# appli dont l'affichage graphique dépend de données reçues en permanence d'un serveur via une liaison par modem externe GPRS (branché sur le port série) et protocole Tcp. L'appli doit pouvoir tourner sans discontinuer pendant plusieurs mois.

J'utilise pour la connexion GPRS la librairie Rasapi32.dll, en particulier les méthodes suivantes :
RasDial
RasHangUp
RasEnumEntries
RasEnumConnections
RasConnectionNotification

Avec le marshalling des structures :
RASDIALPARAMS
RASENTRYNAME
RASCONN

J'utilise la méthode RasDial en mode synchrone et guette la déconnexion grâce à un évènement que je transmets à RasConnectionNotification et que je surveille par WaitForSingleObject.

J'avais auparavant configuré la connexion GPRS dans les connexions réseau de Windows, ainsi que le modem utilisé.

Mon appli se déroule normalement sauf que de temps à autres apparaît ce message d'erreur :

"svchost.exe Application Error L'instruction à l'adresse 0x77c43dbd référence l'adresse 0x90909090 la mémoire ne peut pas être écrite"

L'erreur se produit au bout d'un temps d'éxécution qui varie de 20 mn à 22 h. A chaque fois qu'elle se produit Windows me signale ensuite que la connexion GPRS vient d'être établie, alors que mon programme n'avait rien demandé au préalable (toutes les actions de mon appli sont tracées, et je n'ai pas trouvé la survenue de l'évènement de déconnexion ni la tentative de reconnexion dans le fichier de trace dans les minutes qui précèdent l'apparition de l'erreur).

Ce qui me pousse à incriminer l'API Ras dans l'apparition de cette erreur, c'est le fait que svchost s'occuppe apparemment des connexions réseau sur un ordi, ainsi que de l'utilisation de dll. C'est aussi la seule dll native que j'utilise.

Supposant donc que le problème venait de la suppression par le GC de pointeurs transmis à la dll Ras, j'ai fait, dans l'ordre, les modifs de code suivantes :

->Tentative de passer toutes les variables transmises à l'API en statique dans la classe alors utilisée : aucun effet

->Tentative de créer un projet en C++ natif pour utiliser la dll Ras, avec un autre projet en C++ .Net managé utilisable directement depuis C# et utilisant cette ma dll native : Peine perdue, dans mon projet natif j'incluais pourtant le ras.h et utilisais directement ses structures, mais la méthode RasDial me renvoyait une erreur au motif que la taile de la structure déclarée dans ras.h était mauvaise, apparement à cause d'un #ifdef WINVER >= 0x500 qui rajoute 2 variables superflues dans la structure RasDialParams. J'ai alors essayé de mettre "#define WINVER 0x4ee" avant l'inclusion de ras.h, mais je me ramassais une erreur de compilation à cause de la structure jugée du coup trop petite. J'ai dû renoncer à cette option.

->Enfin, j'ai refait le code en mettant toute l'utilisation de l'API Ras dans une seule méthode encapsulée dans un thread, qui fait :
- initialisation des structures RAS
- test de l'existence de la connexion
- test de l'activité de la connexion
- connexion synchrone sans délégué
- création, notification et surveillance de l'évènement de déconnexion
- utilisation de la méthode GC.KeepAlive sur toutes les variables transmises à la dll interdisant leur ramassage par le GC depuis le début de la méthode jusqu'à l'appel de cette méthode KeepAlive (enfin c'est ce qui est dit dans msdn).
Résultat : idem que précédemment, erreur dans svchost.exe.

Je me suis documenté sur l'API Ras, le PInvoke, le Marshall de structures. J'ai appliqué toutes les recommandations que j'ai pu trouver à ce propos, pour chaque fois un résultat inchangé.


Cela fait 2 semaines que je bataille avec cette erreur sans obtenir le moindre résultat.

Quelqu'un pourrait-il m'aider ?

Merci.

P.S Voici le code que j'utilise :

using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.Runtime.InteropServices;

using System.Threading;

using System.Diagnostics;

using XPe_Win32;

namespace XPe_Ras

{

public static class RasSimple

{

public interface MBTClient

{

bool Connecte();

void Deconnecte();

void Erreur();

string RasName { get; }

string RasPseudo { get;}

string RasMdp { get;}

bool Ecoute { get;}

}

#region Structures

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

public struct RASCONN

{

public uint dwSize;

public IntPtr hrasconn;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (RAS_MaxEntryName + 1))]

public string szEntryName;

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

unsafe public struct RASDIALEXTENSIONS

{

public uint dwSize;

public uint dwfOptions;

public IntPtr hwndParent;

public uint reserved;

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

unsafe public struct RASDIALPARAMS

{

public uint dwSize;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]

public string szEntryName;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxPhoneNumber + 1)]

public string szPhoneNumber;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxCallbackNumber + 1)]

public string szCallbackNumber;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = UNLEN + 1)]

public string szUserName;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = PWLEN + 1)]

public string szPassword;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = DNLEN + 1)]

public string szDomain;

}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]

unsafe public struct RASENTRYNAME

{

public uint dwSize;

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = (int)RAS_MaxEntryName + 1)]

public string szEntryName;

}

#endregion

#region Constants

private const int MAX_PATH = 260;

private const int DNLEN = 15;

internal const int ERROR_INVALID_HANDLE = RASBASE + 1;

private const int PWLEN = 256;

private const int RAS_MaxAreaCode = 10;

private const int RAS_MaxCallbackNumber = 48;

private const int RAS_MaxDeviceName = 32;

private const int RAS_MaxDeviceType = 16;

private const int RAS_MaxEntryName = 20;

private const int RAS_MaxFacilities = 200;

private const int RAS_MaxIpAddress = 15;

private const int RAS_MaxIpxAddress = 21;

private const int RAS_MaxPadType = 32;

private const int RAS_MaxParamKey = 32;

private const int RAS_MaxParamValue = 128;

private const int RAS_MaxPhoneNumber = 128;

private const int RAS_MaxUserData = 200;

private const int RAS_MaxX25Address = 200;

internal const int RASBASE = 600;

private const int RASCS_DONE = 0x2000;

private const int RASCS_PAUSED = 0x1000;

private const int RASCF_AllUsers = 1;

private const int RASCF_GlobalCreds = 2;

private const int REN_AllUsers = 1;

private const int UNLEN = 256;

internal const int WM_RASDIALEVENT = 0xCCCD;

private const int ATTENTE_EVTS = 1000;

private const int PAUSE_EVTS = 100;

private const uint RASCN_Disconnection = 2;

#endregion

#region Imports Dll

[DllImport("rasapi32.dll", SetLastError = true, EntryPoint = "RasDialW")]

public static extern uint RasDialW([In] ref RASDIALEXTENSIONS lpRasDialExtensions,[In] IntPtr lpszPhoneBook,

[In] ref RASDIALPARAMS lpRasDialParams,[In] uint dwNotifierType,[In] IntPtr lpNotifier,[In, Out] ref IntPtr lphRasConn);

[DllImport("rasapi32.dll", SetLastError=true, EntryPoint="RasEnumConnectionsW")]

public static extern uint RasEnumConnections([In, Out] RASCONN[] lprasconn,[In, Out] ref uint lpcb,[Out] out uint lpcConnections);

[DllImport("rasapi32.dll", SetLastError=true, EntryPoint="RasEnumEntriesW")]

public static extern uint RasEnumEntries([In] IntPtr reserved,[In] IntPtr lpszPhoneBookPath,[In,Out] RASENTRYNAME[] lprasentryname,[In,Out] ref uint lpcb,[Out] out uint lpcEntries);

[DllImport("rasapi32.dll", CharSet = CharSet.Auto)]

public static extern uint RasHangUp(IntPtr hRasConn);

[DllImport("rasapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]

public static extern uint RasConnectionNotification([In] IntPtr hrasconn,[In] IntPtr hEvent,[In] uint dwFlags);

[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "CreateEventW")]

public static extern IntPtr CreateEvent([In] IntPtr lpEventAttributes, [In] bool bManualReset, [In] bool bInitialState, [In] string lpName);

[DllImport("kernel32.dll", SetLastError = true)]

public static extern uint WaitForSingleObject(IntPtr handle, uint milliseconds);

[DllImport("kernel32.dll", SetLastError = true)]

public static extern bool CloseHandle(IntPtr hObject);

[DllImport("kernel32.dll", SetLastError = true)]

public static extern void Sleep(uint dwMilliseconds);

#endregion



public static void EcouteRasSure(MBTClient mbt)

{

Thread t = new Thread(new ParameterizedThreadStart(Ras));

t.Start((object)mbt);

}

private static void Ras(object o)

{

unsafe

{

MBTClient mbt = (MBTClient)o;

string nomRas = mbt.RasName;

IntPtr hdleRas = IntPtr.Zero;

IntPtr evtDisc = IntPtr.Zero;

List<IntPtr> poubellePtr = new List<IntPtr>();

RASCONN[] rasConnTest = new RASCONN[1];

RASCONN[] rasConnsToutes = null;

RASENTRYNAME[] rasEntryTest = new RASENTRYNAME[1];

RASENTRYNAME[] rasEntryToutes = null;

RASDIALPARAMS dialParams = new RASDIALPARAMS();

RASDIALEXTENSIONS dialExt = new RASDIALEXTENSIONS();

uint cbC = (uint)Marshal.SizeOf(typeof(RASCONN)),

cbE = (uint)Marshal.SizeOf(typeof(RASENTRYNAME)),

nbEntries = 0,

nbRasConn = 0,

ret = 0;

int i;

//Initialisation des paramètres d'appel

dialParams.dwSize = (uint)Marshal.SizeOf(typeof(RASDIALPARAMS));

dialParams.szEntryName = nomRas;

dialParams.szCallbackNumber = null;

dialParams.szDomain = "";

dialParams.szPhoneNumber = null;

dialParams.szPassword = mbt.RasMdp;

dialParams.szUserName = mbt.RasPseudo;

//Initialisation des extensions d'appel

dialExt.dwSize = (uint)Marshal.SizeOf(typeof(RASDIALEXTENSIONS));

dialExt.dwfOptions = 0;

dialExt.hwndParent = IntPtr.Zero;

dialExt.reserved = 0;

//Test l'existence de l'entrée choisie

rasEntryTest.Initialize();

rasEntryTest[0].dwSize = cbE;

// Teste la version de la structure passée en paramètre (si ret=0 la version est bonne, fausse sinon)

ret = RasEnumEntries(IntPtr.Zero, IntPtr.Zero, rasEntryTest, ref cbE, out nbEntries);

if (ret != 0)

{

TraceExecution.NoteErreur(ret, "RasEnumEntries");

mbt.Erreur();

goto fin;

}

if (nbEntries == 0)

{

TraceExecution.NoteAlerte("Aucune entrée RAS détectée");

mbt.Erreur();

goto fin;

}

// Ajustement du nombre d'entrées RAS

rasEntryToutes = new RASENTRYNAME[nbEntries];

for (i = 0; i < nbEntries; i++)

rasEntryToutes[i].dwSize = (uint)Marshal.SizeOf(typeof(RASENTRYNAME));

// Récupère les entrées RAS

ret = RasEnumEntries(IntPtr.Zero, IntPtr.Zero, rasEntryToutes, ref cbE, out nbEntries);

if (ret != 0)

{

TraceExecution.NoteErreur(ret, "RasEnumEntries");

mbt.Erreur();

goto fin;

}

//Recheche si l'entrée voulue existe

for (i = 0; i < rasEntryToutes.Length && rasEntryToutes[i].szEntryName != nomRas; i++) ;

if (i == rasEntryToutes.Length)

{

TraceExecution.NoteAlerte("L'entrée RAS " + nomRas + " n'existe pas");

mbt.Erreur();

goto fin;

}

//Test la connexion

rasConnTest.Initialize();

rasConnTest[0].dwSize = cbC;

// Teste la version de la structure passée en paramètre (si ret=0 la version est bonne, fausse sinon)

ret = RasEnumConnections(rasConnTest, ref cbC, out nbRasConn);

if (ret != 0)

{

TraceExecution.NoteErreur(ret, "RasEnumConnections");

mbt.Erreur();

goto fin;

}

if (nbRasConn != 0)

{

// Ajustement du nombre de connections RAS actives

rasConnsToutes = new RASCONN[nbRasConn];

for (i = 0; i < nbRasConn; i++)

rasConnsToutes[i].dwSize = (uint)Marshal.SizeOf(typeof(RASCONN));

// Recherche si l'entrée voulue est active

ret = RasEnumConnections(rasConnsToutes, ref cbC, out nbRasConn);

if (ret != 0)

{

TraceExecution.NoteErreur(ret, "RasEnumConnections");

mbt.Erreur();

goto fin;

}

for (i = 0; i < rasConnsToutes.Length && rasConnsToutes[i].szEntryName != nomRas; i++) ;

if (i < rasConnsToutes.Length)

{

TraceExecution.NoteInfo("La connexion " + nomRas + " est déjà active");

goto connexion;

}

}

appel:

TraceExecution.NoteInfo("Appel de la connexion " + nomRas);

for (i = 0; i < 5000; i++)

{

ret = RasDialW(ref dialExt, IntPtr.Zero, ref dialParams, 1, IntPtr.Zero, ref hdleRas);

if (ret == 0) goto connexion;

if (!mbt.Ecoute) goto fin;

TraceExecution.NoteErreur(ret, "RasDial");

Kernel32.Sleep(PAUSE_EVTS);

}

TraceExecution.NoteAlerte("Echec de la connexion RAS");

mbt.Erreur();

goto fin;

connexion:

mbt.Connecte();

TraceExecution.NoteInfo("Création de l'évènement");

evtDisc = Kernel32.CreateEvent(IntPtr.Zero, false, false, null);

for (i = 0; i < 100; i++)

{

ret = RasConnectionNotification(hdleRas, evtDisc, RASCN_Disconnection);

if (ret == 0) goto attente;

TraceExecution.NoteErreur(ret, "RasConnectionNotification");

Kernel32.Sleep(PAUSE_EVTS);

}

attente:

do

{

ret = Kernel32.WaitForSingleObject(evtDisc, ATTENTE_EVTS);

Kernel32.Sleep(PAUSE_EVTS);

if (!mbt.Ecoute) goto deconnexion;

} while (ret != 0);

TraceExecution.NoteInfo("Déclenchement de l'évènement déconnexion");

mbt.Deconnecte();

poubellePtr.Add(evtDisc);

poubellePtr.Add(hdleRas);

hdleRas = IntPtr.Zero;

if (mbt.Ecoute) goto appel;

goto fin;

deconnexion:

Kernel32.CloseHandle(evtDisc);

for (i = 0; i < 100; i++){

ret = RasHangUp(hdleRas);

if (ret == 0) goto fin;

TraceExecution.NoteErreur(ret, "RasHangUp");

}

TraceExecution.NoteAlerte("Erreur dans la fermeture de la connexion");

fin:

GC.KeepAlive(mbt);

GC.KeepAlive(nomRas);

GC.KeepAlive(dialExt);

GC.KeepAlive(dialParams);

GC.KeepAlive(hdleRas);

GC.KeepAlive(evtDisc);

GC.KeepAlive(poubellePtr);

GC.KeepAlive(rasConnTest);

GC.KeepAlive(rasConnsToutes);

GC.KeepAlive(rasEntryTest);

GC.KeepAlive(rasEntryToutes);

GC.KeepAlive(cbE);

GC.KeepAlive(cbC);

GC.KeepAlive(nbRasConn);

poubellePtr.Clear();

}}