MapWindow / MapWindow5

MW5 is a desktop GIS which is extendable using the plug-in architecture. It is using the MapWinGIS mapping control and is created from scratch in 2015 using new technologies like MEF and Dependency Injection making it small in size, robust and fast.
290 stars 100 forks source link

Crashing w/out notification #36

Closed IslandHydro closed 4 years ago

IslandHydro commented 6 years ago

As of yesterday, MapWinGIS started crashing on one pc (my main). I had not changed anything on the pc (although a Windows 10 update happened the same morning....). The exact same code still works on other pc's. The application is a MsAccess database, several forms utilizing MapWinGIS. This code has been working fine for the last 4 or 5 years. I'm running MapWinGIS version 4.8. The line of code that crashes is:

SelWellsLayer.Categories.ApplyExpressions

The .ApplyExpressions function causes crashes in all forms that have that code. When that line of code is run, the database just disappears (closes) without any messages. I'm at a bit of a loss on how to diagnose this. I tried uninstalling and re-installing MapWinGIS, no change. Thoughts?

Thanks, Doug

pmeems commented 6 years ago

I just looked at the C++ code and I don't see anything that could explain this:

void CShapefileCategories::ApplyExpressionCore(long CategoryIndex)
{
    if (!_shapefile)
        return;

    CComPtr<ITable> tbl = NULL;
    _shapefile->get_Table(&tbl);
    if ( !tbl ) return;

    long numShapes;
    _shapefile->get_NumShapes(&numShapes);

    // vector of numShapes size with category index for each shape
    std::vector<int> results;
    results.resize(numShapes, -1);

    bool uniqueValues = true;
    for (unsigned int i = 0; i < _categories.size(); i++) {
        tkCategoryValue value;
        _categories[i]->get_ValueType(&value);
        if (value != cvSingleValue) {
            uniqueValues = false;
            break;
        }
    }

    // ----------------------------------------------------------------
    // we got unique values classification and want to process it fast
    // ----------------------------------------------------------------
    bool parsingIsNeeded = true;    
    if (_classificationField != -1 && uniqueValues)
    {
        parsingIsNeeded = false;    // in case there are unique values only we don't need any parsing

        std::map<CComVariant, long> myMap;              // variant value as key and number of category as result
        for (unsigned int i = 0; i < _categories.size(); i++)
        {
            if (i == CategoryIndex || CategoryIndex == -1 )
            {
                CComVariant val;
                _categories[i]->get_MinValue(&val);
                if (val.vt != VT_EMPTY)
                {
                    CComVariant val2;
                    VariantCopy(&val2, &val);
                    myMap[val2] = i;
                }
            }
        }

        // applying categories to shapes
        VARIANT val;
        VariantInit(&val);
        for (long i = 0; i < numShapes; i++)
        {
            tbl->get_CellValue(_classificationField, i, &val);
            if (myMap.find(val) != myMap.end())
            {
                results[i] = myMap[val];    // writing the index of category
            }
        }
        VariantClear(&val);
    }

    // -------------------------------------------------------------
    //      Analyzing expressions
    // -------------------------------------------------------------
    if (parsingIsNeeded)
    {
        // building list of expressions
        std::vector<CString> expressions;
        for (unsigned int i = 0; i < _categories.size(); i++)
        {
            if (i == CategoryIndex || CategoryIndex == -1 )
            {
                CComBSTR expr;
                _categories[i]->get_Expression(&expr);
                USES_CONVERSION;
                CString str = OLE2CA(expr);
                expressions.push_back(str);
            }
            else
            {
                // we don't need this categories, so dummy strings for them
                CString str = "";
                expressions.push_back(str);
            }
        }

        // adding category indices for shapes in the results vector

        TableHelper::Cast(tbl)->AnalyzeExpressions(expressions, results);
    }

    // saving results
    if (CategoryIndex == -1 )
    {
        for (unsigned long i = 0; i < results.size(); i++)
        {
            _shapefile->put_ShapeCategory(i, results[i]);
        }
    }
    else
    {
        for (unsigned long i = 0; i < results.size(); i++)
        {
            if (results[i] == CategoryIndex)
                _shapefile->put_ShapeCategory(i, CategoryIndex);
        }
    }
}

I don't have MapWinGIS v4.8 anymore, I'm using v4.9-dev and I'm all up-to-date with Win10.

Perhaps you could try to reinstall MapWinGIS?

You could try to create a small new application in VB.NET (or C#) with 1 form with MapWinGIS and open a shapefile and to the Categories.ApplyExpressions. When it is not crashing it suggests it is something with MS-Access, if it is still crashing you could share the code and I can have a look.

You could also add a try-catch around the method and log the exception. That might help as well.

IslandHydro commented 6 years ago

Thanks for the reply Paul. I did re-install MapWinGIS to no avail. I don't have MS Studio (Vb.net / C#) so I can't try that approach. VBA doesn't include try-catch but does have on-error routines. The application shuts down without triggering any error trapping routines. I'm at a bit of a loss on what to do, so any thoughts would be appreciated.

pmeems commented 6 years ago

Can you save your data as Shapefile and share it with me? And show the code that makes the categories? I can try to make a small program that does the same. But from tomorrow I'll be offline for a week, so I can't work on it until next week.

IslandHydro commented 6 years ago

Thank you so much for the offer Paul, I really appreciate it. I had a breakthrough just now, which solved the problem but makes me wonder if I am doing something fundamentally wrong in how I deal with this situation. First the fix: I noticed that some other layers that I am using (although less commonly) also use the offending "Layer.Categories.ApplyExpressions" that was crashing the application. Since the code utilized by those layers was identical, just pointing at a different layer, I began to suspect something was wrong with my layer. So I created a new, empty layer and re-ran the code and all is fine!

This apparent corruption of the layer leads me to wonder if I am doing something incorrectly. The layer in question is a layer that is continuously emptied and re-filled based on search criteria (spatial and other). The unloading of the layer is accomplished via something like this:

            SelWellsLayer.StartEditingShapes (True)
            While SelWellsLayer.NumShapes > 0
                SelWellsLayer.EditDeleteShape (0)
            Wend
            SelWellsLayer.Categories.Clear

Reloading is accomplished via:

             While Not Recs.EOF
                    Call AddPointToShapefile(SelWellsLayer, Recs("WellKey"), Recs("WellEast"), Recs("WellNorth"), FnWellIcon(Recs("WellKey")))
                    Recs.MoveNext
             Wend
            SelWellsLayer.StopEditingShapes
            SelWellsLayer.Save

I then go on to apply expressions to the layer with the following:

            Set Icons = CurrentDb.OpenRecordset(SearchSQL)
            While Not Icons.EOF
                Set SfImage = New MapWinGIS.Image
                BmpName = Icons("WellIconCode") & ".bmp"
                If SfImage.Open(FnDbFolderPath & "MapData\IconsNew\" & BmpName, USE_FILE_EXTENSION, True) = True Then
                    Set ShapeCat = SelWellsLayer.Categories.Add(Left(BmpName, InStr(BmpName, ".") - 1))
                    ShapeCat.Expression = "[GE_Code] =" & Chr(34) & Left(BmpName, InStr(BmpName, ".") - 1) & Chr(34)
                    ShapeCat.DrawingOptions.PointType = ptSymbolPicture
                    ShapeCat.DrawingOptions.PictureScaleX = WellIconSize
                    ShapeCat.DrawingOptions.PictureScaleY = WellIconSize
                    ShapeCat.DrawingOptions.Picture = SfImage
                End If
                Icons.MoveNext
            Wend

            hndSelWells = mapCtl.AddLayer(SelWellsLayer, True)
            SelWellsLayer.Categories.ApplyExpressions

This has been working for a number of years, so perhaps it really was just some sort of corruption of the .shp file. But if there is a better way to 're-use' a shapefile for selected data I'd like to know. Thanks again, I appreciate your willingness to help, and your (and everyone elses) efforts in creating this product, it is truly very useful.

pmeems commented 6 years ago

Do you need the layer/shapefile to be disk-based? Creating an in-memory shapefile might speed up things if you change the shapes a lot.

I would also recommend removing shapes in reverse order. Something like:

var numShapes = SelWellsLayer.NumShapes;
for (var i=numShapes; i>0;i--) {
  SelWellsLayer.EditDeleteShape(i);
}
pmeems commented 4 years ago

If this is still an issue with the latest release (https://github.com/MapWindow/MapWindow5/releases) could you create a new issue? For now, I close this old issue.