none
Appel asynchrone+multithreading = NullReferenceException :( RRS feed

  • Question

  • Bonsoir,

    je désire faire un appel asynchrone vers un webservice mappoint, j'opère donc de la manière suivante:


       Stream imageStream; // variable globale
       void DisplayMap(double latitude,double longitude)
       {
           // du code...
           RenderServiceSoap rss;
           rss.BeginGetMap(mapSpecif, new AsyncCallback(GetMapThread), rss);
       }

       private void GetMapThread(IAsyncResult state)
       {
                RenderServiceSoap rss = state.AsyncState as RenderServiceSoap;
                if (rss == null)
                    return;
                MapImage[] images = rss.EndGetMap(state);
                imageStream = new System.IO.MemoryStream(images[0].MimeData.Bits);

        }

           // puis au niveau de ma fct principale
           DisplayMap(pos_lat,pos_long);
           pictureBox1.Image=new System.Drawing.Bitmap(imageStream); //la varia ble globale devrait être mis à jour à l'intérieur de GetMapThread


    A l'execution j'obtiens une exception de type NullReferenceException, donc voici le détail :

            StackTrace    "   at System.Drawing.Bitmap..ctor(Stream stream)\r\n   at PickMeUp.RiderStartSearch..ctor()\r\n   at PickMeUp.RiderMainMenu.startsearch_SelectedIndexChanged(Object sender, EventArgs e)\r\n   at System.Windows.Forms.ListView.OnSelectedIndexChanged(EventArgs e)\r\n   at System.Windows.Forms.ListView.UpdateSelectedIndex(Int32 iItem)\r\n   at System.Windows.Forms.ListView.WnProc(WM wm, Int32 wParam, Int32 lParam)\r\n   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)\r\n   at Microsoft.AGL.Forms.EVL.EnterModalDialog(IntPtr hwnModal)\r\n   at System.Windows.Forms.Form.ShowDialog()\r\n   at PickMeUp.SelectMode.RiderMode_SelectedIndexChanged(Object sender, EventArgs e)\r\n   at System.Windows.Forms.ListView.OnSelectedIndexChanged(EventArgs e)\r\n   at System.Windows.Forms.ListView.UpdateSelectedIndex(Int32 iItem)\r\n   at System.Windows.Forms.ListView.WnProc(WM wm, Int32 wParam, Int32 lParam)\r\n   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)\r\n   at Microsoft.AGL.Forms.EVL.EnterModalDialog(IntPtr hwnModal)\r\n   at System.Windows.Forms.Form.ShowDialog()\r\n   at PickMeUp.FLogin.Login_Click(Object sender, EventArgs e)\r\n   at System.Windows.Forms.Control.OnClick(EventArgs e)\r\n   at System.Windows.Forms.Button.OnClick(EventArgs e)\r\n   at System.Windows.Forms.ButtonBase.WnProc(WM wm, Int32 wParam, Int32 lParam)\r\n   at System.Windows.Forms.Control._InternalWnProc(WM wm, Int32 wParam, Int32 lParam)\r\n   at Microsoft.AGL.Forms.EVL.EnterMainLoop(IntPtr hwnMain)\r\n   at System.Windows.Forms.Application.Run(Form fm)\r\n   at PickMeUp.Program.Main()\r\n"    string


    Ce qui est bizarre c'est qu'un coup j'obtiens ça, un coup j'obtiens un NullReferenceException mais avec un message comme quoi je devrais utiliser Invoke..
    "Control.Invoke must be used to interact with controls created on a separate thread."    string

    Je tiens à signaler que j'ai tenté d'utiliser un delegate de la manière suivante :


            private delegate void GetMapThreadDelegate(IAsyncResult state);
            private void DoUpdate2(IAsyncResult state)
            {

                if (this.InvokeRequired)
                {
        
                    this.Invoke(new GetMapThreadDelegate(DoUpdate2),new object[] { state });
                    return;
                }
            
                MapImage[] images = rss.EndGetMap(state);
                imageStream = new System.IO.MemoryStream(images[0].MimeData.Bits);

            }


    Avec la mise à jour effectuée dans GetMapThread ( qui appelle la méthode DoUpdate2 )

    J'ai cherché sur le net de l'aide sur ce genre de msg d'erreur apparemment c'est assez connu comme problème, mais je n'ai pas su adapter les solutions trouvées à mon cas Sad

    Merci pour votre aide grandement appréciée !
    dimanche 27 avril 2008 18:10

Réponses

  • Bonjour,

    Pour le problème de la NullReferenceException, cela est du au fait que l'instruction "pictureBox1.Image = ..." est appelée avant que la variable globale soit mise à jour. En effet la méthode DisplayMap fait un appel asynchrone mais vous n'attendez pas la fin de cette appel pour utiliser imageStream.

    Voila un proposition qui devrait marcher :

     

    Code Snippet

    private Stream imageStream;

     

    private void DisplayMap(double latitude, double longitude)

    {

    RenderServiceSoap rss;

    rss.BeginGetMap(mapSpecif, new AsyncCallback(GetMapThread), rss);

    }

     

    private void GetMapThread(IAsyncResult state)

    {

    RenderServiceSoap rss = state.AsyncState as RenderServiceSoap;

    if (rss == null)

    {

    return;

    }

     

    MapImage[] images = rss.EndGetMap(state);

    imageStream = new System.IO.MemoryStream(images[0].MimeData.Bits);

     

    if (pictureBox1.InvokeRequired)

    {

    pictureBox1.Invoke(new UpdateMap(UpdatePictureBox));

    }

    else

    {

    UpdatePictureBox();

    }

    }

     

    private delegate void UpdateMapDelegate();

    private void UpdatePictureBox()

    {

    pictureBox1.Image = new System.Drawing.Bitmap(imageStream);

    }

     

     

    Il ne vous reste plus qu'a appeler DisplayMap et attendre

    lundi 28 avril 2008 08:27

Toutes les réponses

  • Bonjour,

    Pour le problème de la NullReferenceException, cela est du au fait que l'instruction "pictureBox1.Image = ..." est appelée avant que la variable globale soit mise à jour. En effet la méthode DisplayMap fait un appel asynchrone mais vous n'attendez pas la fin de cette appel pour utiliser imageStream.

    Voila un proposition qui devrait marcher :

     

    Code Snippet

    private Stream imageStream;

     

    private void DisplayMap(double latitude, double longitude)

    {

    RenderServiceSoap rss;

    rss.BeginGetMap(mapSpecif, new AsyncCallback(GetMapThread), rss);

    }

     

    private void GetMapThread(IAsyncResult state)

    {

    RenderServiceSoap rss = state.AsyncState as RenderServiceSoap;

    if (rss == null)

    {

    return;

    }

     

    MapImage[] images = rss.EndGetMap(state);

    imageStream = new System.IO.MemoryStream(images[0].MimeData.Bits);

     

    if (pictureBox1.InvokeRequired)

    {

    pictureBox1.Invoke(new UpdateMap(UpdatePictureBox));

    }

    else

    {

    UpdatePictureBox();

    }

    }

     

    private delegate void UpdateMapDelegate();

    private void UpdatePictureBox()

    {

    pictureBox1.Image = new System.Drawing.Bitmap(imageStream);

    }

     

     

    Il ne vous reste plus qu'a appeler DisplayMap et attendre

    lundi 28 avril 2008 08:27
  •  

    yess ça marche Smile

    Je vous dois une fière chandelle, ça va faire une semaine que j'étais dessus merci bcp Wink

    lundi 28 avril 2008 18:39
  •  

    Re bonsoir,

     

    Je vais encore avoir besoin de votre aide si possible Smile

     

    J'ai été confronté à un cas similaire mais cette fois il n'y a ni pictureBox ni rien.

     

    Je vous montre l'ossature de mon code:

    PixelCoord[] pixels = new PixelCoord[3]; //variable globale

    private void ConvertToPointThread(IAsyncResult state)

    {

    RenderServiceSoap rss = state.AsyncState as RenderServiceSoap;

    if (rss == null)

    return;

    pixels = rss.EndConvertToPoint(state);

     

    }

     

    private void DisplayMapFromCoordinates()

    {

    // du code..

    rss.BeginConvertToPoint(Coord, viewByBoundingLocation, width, height,new AsyncCallback(ConvertToPointThread), rss);

     

     

    for (int j = 0; j < taille; j++)

    {

    PuhspinsPixels[j] = new PickMeUp_Mobile_BusinessLayers.PixelsCoordinates();

    PuhspinsPixels[j].idDriverCarList = DriverCarList[j].idMotorist;

    PuhspinsPixels[j].X = pixels[j].X;

    PuhspinsPixels[j].Y = pixels[j].Y;

    PuhspinsPixels[j].SetSizeOfTheObject(taille);

     

    }

     

    }

    private void pictureBox2_Click_1(object sender, EventArgs e)

    {

    int i = 0;

    bool ClickDetected = false;

    int taille = Pixels[0].ObjectSize;

    while (i < taille)

    {

    menuItem2.Text = "" + MousePosition.X;

    menuItem1.Text = "" + MousePosition.Y;

    if ((MousePosition.X > (PixelsIdea.X - 10)) && (MousePosition.X < (PixelsIdea.X + 5)) && ((MousePosition.Y > (PixelsIdea.Y + 21)) && (MousePosition.Y < (PixelsIdea.Y + 27))))

    {

    ClickDetected = true;

    }

    if (ClickDetected)

    {

    DriverDisplayed = i;

    DisplayFirstPanel();

    i = taille;

    }

    i++;

    }

     

     

    }

     

    private delegate void CallPositionsThreadingDelegate(int nb_positions);

    private delegate void GetMapThreadDelegate();

    private void DoUpdate2()

    {

    if (this.InvokeRequired)

    {

     

    this.Invoke(new GetMapThreadDelegate(DoUpdate2));

    return;

    }

    pictureBox2.Image = new System.Drawing.Bitmap(imageStream);

    }

    private void DoUpdate(int nb_positions)

    {

    if (this.InvokeRequired)

    {

    this.Invoke(new CallPositionsThreadingDelegate(DoUpdate), new object[] { nb_positions });

    return;

    }

    DisplayMapFromCoordinates();

     

    }

    private void CallPositionsThreading()

    {

    for (nb_positions = 0; nb_positions < 15; nb_positions++)

    {

    DoUpdate(nb_positions);

    Thread.Sleep(0);

    }

    }

    Il me sort encore une exception de type : NullReferenceException. ( qui doit venir de Pixels qui doit être non intialisé.. ça doit être le même problème que la dernière fois, mais là je vois vraiment pas comment le résoudre ?

     

    Merci bcp en tous cas !!

    lundi 28 avril 2008 19:52
  • Bonjour,

    Le problème est toujours le même, vous utilisez les données générées par votre fonction asynchrone avant que celle-ci n'est finit. Voila un exemple d'exécution pour vous expliquer le problème :

    1. appel à rss.BeginConvertToPoint  : pixels est null et un thread est lancé.
    2. le système ne donne pas la main au nouveau thread mais continue le thread courant : début de la boucle for, pixels est null !

    Comme pour le cas précédent, déplacé la boucle for après l'appel à BeginConvertToPoint à la fin de la méthode ConvertToPointThread. Dans ce cas vous êtes sur que pixels sera rempli.

    mardi 29 avril 2008 08:00
  •  

    Bonjour,

     

    Merci encore une fois pour votre aide précieuse et efficace. Le problème du NullReference a été totalement réglé.

     

    Mais, les thread n'ont malheureusement pas terminé de m'embêter!

     

    Je vous expose mon nouveau problème (qui j'espère sera le dernier):

     

    J'ai un thread : monThread qui appelle à chaque fois la fonction DisplayMap ( qui effectue des appels asynchrones aux webservices MapPoint comme vous avez pu le constater).

     

    Le problème est qu'après deux appels de monThread, ce dernier n'est plus appelé ..

     

    Je vous mets un bout de code:

     

           int nb_positions=0; // variable globale qui sera mise à jour à chaque cycle du threa

          private void menuItem2_Click(object sender, EventArgs e)
            {
      
                 // Le thread est lancé lors de l'évenement : clic sur menuItem2
                  monThread = new Thread(new ThreadStart(CallPositionsThreading));

                  monThread.Start();
            }

           private void CallPositionsThreading()
            {
        
                if (nb_positions < 15)
                {
                    //tant que nb_positions est <15 on continue d'appeler DoUpdate
                    DoUpdate(nb_positions);
                    nb_positions = nb_positions + 1;
                }

          
            }

            private delegate void CallPositionsThreadingDelegate(int nb_positions);

            private void DoUpdate(int nb_positions)
            {
                i = i + 1;
                if (this.InvokeRequired)
                {
                    // we were called on a worker thread
                    // marshal the call to the user interface thread
                    this.Invoke(new CallPositionsThreadingDelegate(DoUpdate), new object[] { nb_positions });
                    return;
                }


                menuItem1.Text = "Appel"+" "+i.ToString(); // Le compteur se bloque à 2, ce qu signifie que
                 // le thread n'est appelé que deux fois


                DisplayMapFromCoordinates(DriverCarList,pictureBox2.Height, pictureBox2.Width);
        

            }

            DisplayMapFromCoordinates(...)
           {
               // Appel asynchrone vers GetMap
           } 

          private void GetMapThread(IAsyncResult state)
            {
                RenderServiceSoap rss = state.AsyncState as RenderServiceSoap;
                if (rss == null)
                    return;
                MapImage[] images = rss.EndGetMap(state);
                imageStream = new System.IO.MemoryStream(images[0].MimeData.Bits);

                if (pictureBox2.InvokeRequired)
                {
                    pictureBox2.Invoke(new UpdatePictureBoxDelegate(UpdatePictureBox));
                }
                else
                {
                    UpdatePictureBox();
                }

            }

            private delegate void UpdatePictureBoxDelegate();

            private void UpdatePictureBox()
            {

                pictureBox2.Image = new System.Drawing.Bitmap(imageStream);

            }

     

     

     

    Une idée? pourquoi le thread n'est pas appelé 15fois ? pourquoi se bloque t'il à  2 appels? pourquoi se bloque t'il tout court?

     

    Je vous remercie pour votre aide et votre patience !!

    (HS: Comment insérer une balise Snippet ? )

    mardi 29 avril 2008 15:14
  • Problème réglé, un nettoyage de mon code a pu y venir à bout Smile
    vendredi 2 mai 2008 10:38