Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"No Disk" error using GDAL from C#/.NET

I am using Tamas Szekeres builds of GDAL including the C# bindings in a desktop GIS application using C# and .net 4.0

I am including the entire GDAL distribution in a sub-directory of my executable with the following folder structure:

\Plugins\GDAL
\Plugins\GDAL\gdal
\Plugins\GDAL\gdal-data
\Plugins\GDAL\proj

We are using EPSG:4326, and the software is built using 32-bit target since the GDAL C# API is using p/invoke to the 32-bit libraries (could try 64 bit since Tamas provides these, haven't gotten around to it yet).

When I run my application I get the following error

enter image description here

This error typically happens when software tries to access a device that is no longer attached, such as a removable drive. It is not possible to "catch" this exception because it pops up a system dialog.

After dismissing the dialog using any of the buttons, the software continues to execute as designed.

The error occurs the first time I call the following method

OSGeo.OSR.CoordinateTransformation.TransformPoint(double[] inout);

The strange stuff:

  • The error occurs on one, and only one computer (so far)
  • I've run this software in several other computers both 32 and 64 bit without problems
  • The error does not ocurr on the first run after compiling the GDAL shim library I am using, it only occurrs on each subsequent run
  • it happens regardless of release, or debug builds
  • it happens regardless of whether the debugger is attached or not
  • it happens regardless of whether I turn on or off Gdal.UseExceptions or Osr.UseExceptions();
  • disabling removable drives causes the bug to disappear. This is not what I consider a real solution as I will not be able to ask a customer to do this.

I have tried the following:

  • catching the error
  • changing GDAL directories and environment settings
  • changing computers and operating systems: this worked
  • used SysInternals ProcMon to trace what files are being opened with no luck, they all appear to be files that exist
  • I re-built the computer in question when the hard drive failed, to no avail.
  • "cleaning" the registry using CCleaner
  • files in GDAL Directory are unchanged on execution

Assumptions

  • Error is happening in unmanaged code
  • During GDAL initialization, some path is referring to a drive on the computer that is no longer attached.
  • I am also working on the assumption this is limited to a computer configuration error

Configuration

  • Windows 7 Pro
  • Intel Core i7 920 @ 2,67GHz
  • 12.0 GB RAM
  • 64-bit OS
  • Drive C: 120 GB SSD with OS, development (Visual Studio 10), etc
  • Drive D: 1 TB WD 10,000k with data, not being accessed for data.

The Question

I either need a direction to trap the error, or a tool or technique that will allow me to figure out what is causing it. I don't want to release the software with the possibility that some systems will have this behaviour.

like image 622
Blue Toque Avatar asked Jul 14 '12 21:07

Blue Toque


3 Answers

I have no experience with this library, but perhaps some fresh eyes might give you a brainwave...

Firstly, WELL WRITTEN QUESTION! Obviously this problem really has you stumped...

Your note about the error not occurring after a rebuild screams out: Does this library generate some kind of state file, in its binary directory, after it runs? If so, it is possible that it is saving incorrect path information into that 'configuration' file, in a misguided attempt to accelerate its next start-up.

Perhaps scan this directory for changes between a 'fresh build' and 'first run'?

At very least you might find a file you can clean up on shut-down to avoid this alert...

HTH

like image 52
Adam Avatar answered Oct 24 '22 18:10

Adam


Maybe you can try this:

  • Run diskmgmt.msc
  • Change the driveletter for Disk 2 (right click) if my assumption that Disk 2 is a Removable Disk is true
  • Run your application
  • If this removes the error, something in the application is referring to the old driveletter
  • It could be in the p/invoked libs
  • Maybe see: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46501 It talks about gcc somehow compiling a driveletter into a binary
like image 42
Daniel van Os Avatar answered Oct 24 '22 17:10

Daniel van Os


+1 Great question, but It is not possible to "catch"

Its one of these awful solutions that will turn up on DailyWTF in 5 years. But for now it is stored here http://www.pinvoke.net/default.aspx/user32.senddlgitemmessage

using Microsoft.VisualBasic;  //this reference is for the Constants.vbNo;  

public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SendDlgItemMessage(IntPtr hDlg, int nIDDlgItem, uint Msg, UIntPtr wParam, IntPtr lParam);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);

// For Windows Mobile, replace user32.dll with coredll.dll
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

[DllImport("user32.dll", SetLastError = true)]
static extern uint GetDlgItemText(IntPtr hDlg, int nIDDlgItem,[Out] StringBuilder lpString, int nMaxCount);

public void ClickSaveBoxNoButton()
{
    //In this example, we've opened a Notepad instance, entered some text, and clicked the 'X' to close Notepad.
    //Of course we received the 'Do you want to save...' message, and we left it sitting there. Now on to the code...
    //
    //Note: this example also uses API calls to FindWindow, GetDlgItemText, and SetActiveWindow.
    //    You'll have to find those separately.

    //Find the dialog box (no need to find a "parent" first)
    //classname is #32770 (dialog box), dialog box title is Notepad
    IntPtr theDialogBoxHandle; // = null;
    string theDialogBoxClassName = "#32770";
    string theDialogBoxTitle = "Notepad";
    int theDialogItemId = Convert.ToInt32("0xFFFF", 16);
    StringBuilder theDialogTextHolder = new StringBuilder(1000);
    //hardcoding capacity - represents maximum text length
    string theDialogText = string.Empty;
    string textToLookFor = "Do you want to save changes to Untitled?";
    bool isChangeMessage = false;
    IntPtr theNoButtonHandle; // = null;
    int theNoButtonItemId = (int)Constants.vbNo;
    //actual Item ID = 7
    uint theClickMessage = Convert.ToUInt32("0x00F5", 16);
    //= BM_CLICK value
    uint wParam = 0;
    uint lParam = 0;

    //Get a dialog box described by the specified info
    theDialogBoxHandle = FindWindow(theDialogBoxClassName, theDialogBoxTitle);
    //a matching dialog box was found, so continue
    if (theDialogBoxHandle != IntPtr.Zero)
    {

        //then get the text
        GetDlgItemText(theDialogBoxHandle, theDialogItemId, theDialogTextHolder, theDialogTextHolder.Capacity);
        theDialogText = theDialogTextHolder.ToString();

    }

    //Make sure it's the right dialog box, based on the text we got.
    isChangeMessage = Regex.IsMatch(theDialogText, textToLookFor);


    if ((isChangeMessage))
    {
        //Set the dialog box as the active window
        SetActiveWindow(theDialogBoxHandle);

        //And, click the No button
        SendDlgItemMessage(theDialogBoxHandle, theNoButtonItemId, theClickMessage, (System.UIntPtr)wParam, (System.IntPtr)lParam);

    }

}
like image 1
Jeremy Thompson Avatar answered Oct 24 '22 16:10

Jeremy Thompson