【问题标题】:"No Disk" error using GDAL from C#/.NET使用 C#/.NET 中的 GDAL 时出现“无磁盘”错误
【发布时间】:2012-07-14 21:05:25
【问题描述】:

我正在使用 GDALTamas Szekeres 构建,包括使用 C# 和 .net 4.0 的桌面 GIS 应用程序中的 C# 绑定

我将整个 GDAL 分发包包含在我的可执行文件的子目录中,其文件夹结构如下:

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

我们正在使用 EPSG:4326,并且该软件是使用 32 位目标构建的,因为 GDAL C# API 使用 p/invoke 到 32 位库(可以尝试 64 位,因为 Tamas 提供了这些,还没有得到还没到)。

当我运行我的应用程序时,我收到以下错误

此错误通常发生在不再连接的软件tries to access a device(例如可移动驱动器)时。无法“捕捉”此异常,因为它会弹出一个系统对话框。

使用任何按钮关闭对话框后,软件将继续按设计执行。

第一次调用下面的方法时出现错误

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

奇怪的东西:

  • 错误发生在一台计算机上(目前为止)
  • 我已经在其他几台 32 位和 64 位计算机上运行该软件,没有任何问题
  • 编译我正在使用的 GDAL shim 库后,第一次运行时不会发生错误,它只会在每次后续运行时发生
  • 无论发布还是调试版本都会发生这种情况
  • 无论是否附加调试器,都会发生这种情况
  • 无论我打开还是关闭 Gdal.UseExceptions 或 Osr.UseExceptions(),它都会发生;
  • 禁用可移动驱动器会导致错误消失。我认为这不是真正的解决方案,因为我无法要求客户这样做。

我尝试了以下方法:

  • 发现错误
  • 更改 GDAL 目录和环境设置
  • 更改计算机和操作系统:这行得通
  • 使用 SysInternals ProcMon 来跟踪正在打开的文件没有运气,它们似乎都是存在的文件
  • 当硬盘驱动器发生故障时,我重新构建了有问题的计算机,但无济于事。
  • 使用CCleaner“清理”注册表
  • GDAL 目录中的文件在执行时保持不变

假设

  • 非托管代码中发生错误
  • 在 GDAL 初始化期间,某些路径指的是计算机上不再连接的驱动器
  • 我也在假设这仅限于计算机配置错误

配置

  • Windows 7 专业版
  • 英特尔酷睿 i7 920 @ 2,67GHz
  • 12.0 GB 内存
  • 64 位操作系统
  • 驱动器 C:120 GB SSD,带操作系统、开发 (Visual Studio 10) 等
  • 驱动器 D:1 TB WD 10,000k 有数据,未访问数据。

问题

我要么需要一个方向来捕获错误,要么需要一个工具或技术来让我找出导致它的原因。我不想发布软件,因为某些系统可能会有这种行为。

【问题讨论】:

  • 更新1:根据下面的第一个答案,我查看了GDAL目录中的文件数,文件数稳定。更新 2:我接受了自己的建议,并使用设备管理器禁用了系统上的所有可移动设备。这“解决”了问题!这不是一个真正的解决方案,所以我将保留这个问题。如何追踪哪个软件正在访问哪个可移动设备?
  • 可能是方法调用导致了异常,所以 Visual Studio 尝试加载库的原始源文件,这些文件可能是从 PC 上指向空驱动器的驱动器编译的?不过,您应该能够使用 ProcMon 看到这一点。
  • 嘿!在这最后 8 小时内您需要任何帮助吗?让我们知道,因为这似乎有点像 PITA
  • 大家好;我觉得我无法奖励赏金,因为所提供的解决方案都没有真正找到错误。优秀的想法;关于编写代码来点击按钮的方法很有创意,但绝对是一个可怕的骇人听闻的黑客行为。我将使用此处概述的算法让系统奖励一半的赏金:meta.stackexchange.com/questions/16065/…

标签: c# .net windows gis gdal


【解决方案1】:

我对这个库没有经验,但也许一些新鲜的眼睛可能会给你一个脑波......

首先,问题写得很好!显然这个问题真的把你难住了……

您对重建后未发生错误的注释大声疾呼:此库在运行后是否在其二进制目录中生成某种状态文件? 如果是这样,它可能会将不正确的路径信息保存到该“配置”文件中,这是在错误地尝试加速其下一次启动。

也许扫描此目录以查找“新构建”和“首次运行”之间的更改?

至少您可能会找到一个可以在关机时清理的文件以避免此警报...

HTH

【讨论】:

  • 如果您愿意,可以对问题进行投票,以补偿我的赏金。我将检查目录中是否有在启动期间写出的某种文件。考虑到这一点,可能会有一些更难以拦截的内容写入注册表。谁能推荐一个拦截注册表写入的工具?我猜 SysInternals 有一个。
  • 绝对 SysInternals Proces Monitor technet.microsoft.com/en-us/sysinternals/bb896645
【解决方案2】:

也许你可以试试这个:

  • 运行 diskmgmt.msc
  • 如果我认为磁盘 2 是可移动磁盘的假设成立,请更改磁盘 2 的驱动器号(右键单击)
  • 运行您的应用程序
  • 如果这消除了错误,则表明应用程序中的某些内容引用了旧的驱动器号
  • 它可能在 p/invoked 库中
  • 也许看:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46501 它谈到 gcc 以某种方式将驱动器号编译成二进制文件

【讨论】:

  • 我昨晚添加了一条评论,我尝试了这个(对此表示赞同)。我禁用了所有外部驱动器。我不认为这是一个解决方案,因为我无法告诉客户永久禁用驱动器。由于该库不会在其他计算机上引起问题,因此我不确定是否是驱动器号问题。
  • 仅当驱动器具有可移动存储且仅当存储已被移除时才会发生。如果它是一个“低”驱动器号,例如“E”,它不太可能出现在许多系统上,因为大多数人都会有多个硬盘驱动器/分区用完的小写字母,这不会导致问题。但我承认这是一个长远的目标。
【解决方案3】:

+1 好问题,但 不可能“抓住”

它是这些糟糕的解决方案之一,将在 5 年内出现在 DailyWTF 上。但现在它存储在这里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);

    }

}

【讨论】:

    【解决方案4】:

    您可以将自定义错误处理程序添加到 gdal。这可能会有所帮助:

    http://www.gdal.org/ogr/cpl__error_8h.html

    http://trac.osgeo.org/gdal/ticket/2895

    【讨论】:

    • 您好,在 StackOverflow 的答案中通常会包含链接内容的摘要或专门回答问题的亮点。 SE 网站的目标是成为未来几年的知识和答案资源。对于仅链接的答案,操作员必须挖掘另一个资源以找到他/她可能不确定的答案。最重要的是,如果您的链接永远中断,那么您的回答对于将来访问此页面的任何人都是无用的。考虑在您的答案中添加和edit 以添加更多详细信息。祝你好运!
    • 感谢 GDAL 错误处理程序提示;我忘了提到我也已经尝试过了。
    【解决方案5】:

    事实证明,没有办法肯定地回答这个问题。 我最终通过找出系统上注册的一些硬件不存在来“解决”问题。为什么几年后只有 GDAL 设法引发了这个错误,对我来说仍然是个谜。

    我将无法捕获此异常归结为 p/invoke 所涉及的特性以及在系统上以非常低的级别引发的硬件错误。

    【讨论】:

      猜你喜欢
      • 2014-11-29
      • 2016-06-02
      • 2013-04-19
      • 2019-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-04
      • 1970-01-01
      相关资源
      最近更新 更多