C# 在没有Win32 API调用的WinForms程序中向路径添加省略号(重新访问)

C# 在没有Win32 API调用的WinForms程序中向路径添加省略号(重新访问),c#,.net-4.0,C#,.net 4.0,我正在寻找在C路径中插入省略号的方法,并在stackoverflow上找到了答案: 使用VS2010和.NET4.0的RTM版本,我无法使建议的方法工作。我搜索了“Net”,找到了使用相同方法的示例代码,但以相同的方式失败 您可以在下面的代码中看到我试图缩短的字符串 调用MeasureText方法后,输入字符串(OriginalName)和输出字符串(EllipSidName)如下所示: d:\abcd\efgh\ijkl\mnop\qrst\…\test.txt\0F\GHIJ\KLMN\OP

我正在寻找在C路径中插入省略号的方法,并在stackoverflow上找到了答案:

使用VS2010和.NET4.0的RTM版本,我无法使建议的方法工作。我搜索了“Net”,找到了使用相同方法的示例代码,但以相同的方式失败

您可以在下面的代码中看到我试图缩短的字符串

调用MeasureText方法后,输入字符串(OriginalName)和输出字符串(EllipSidName)如下所示:

d:\abcd\efgh\ijkl\mnop\qrst\…\test.txt\0F\GHIJ\KLMN\OPQR\STIV\WXYZ\test.txt

两个问题:

1) 生成的字符串是narfed(路径按预期被截断,但后面是看起来像C风格的终止null和原始路径的一块)

2) 我的原始字符串更改为与输出字符串相同

我做错什么了吗

namespace WindowsFormsApplication2 {
   public partial class Form1 : Form {
      public Form1()
      {
         InitializeComponent();

         string OriginalPath = @"d:\abcd\efgh\ijkl\mnop\qrst\uvwx\yzAB\CDEF\GHIJ\KLMN\OPQR\STIV\WXYZ\test.txt";
         string ellipsisedPath = OriginalPath;

         Size proposedSize = new Size(label1.Width, label1.Height);

         TextRenderer.MeasureText(ellipsisedPath, label1.Font, proposedSize, TextFormatFlags.ModifyString | TextFormatFlags.PathEllipsis);
      }
   }
}
我的原始字符串更改为与输出字符串相同

您已经通过指定
TextFormatFlags.ModifyString
(文档中说

修改指定的字符串以匹配显示的文本。除非还指定了EndEllipsis或PathEllipsis,否则此值无效

在我看来,这是一种不寻常的.NETFramework调用操作方式,但它确实明确表示会这样做。“原始”字符串和“输出”字符串最终都会被修改,因为
string
是引用类型(尽管通常具有不可变的值语义)

string ellipsisedPath = OriginalPath;
实际上,您只是让
ellipsedpath
引用与
OriginalPath
相同的字符串实例。当这个实例被API调用修改时,对它的两个引用都将看到修改

至于

路径按预期被截断,但后面是一个C风格的终止null和原始路径的一块


我的猜测是,这个托管包装器围绕Win32 API调用提供的抽象有点泄漏,因为抽象很容易泄漏——它并没有让您避免底层调用使用C样式字符串的事实。这可能是你必须自己处理的问题。

天哪,你发现了一只巨大的虫子。调用DrawTextEx()的TextRenderer类中使用的P/Invoke是borke。该API函数正在写回字符串,这是允许的,因为cchText参数是LPTSTR,而不是LPCTSTR。这会破坏这两个变量的.NET字符串内容,因为该字符串已被插入

这个bug不是特定于.NET4.0的,我在.NET3.5SP1的ReferenceSource中也看到了错误,可以在VS2008上重新设置它。问题出在内部WindowsGraphics.MeasureText函数中。您可以在connect.microsoft.com上报告此错误

一种可能的解决方法是更改字符串,使其得到复制,并且不会影响原始字符串:

  string ellipsisedPath = OriginalPath + '\0';

但在这种情况下,更好的解决方法是不通过ModifyString选项,因为它没有任何用途。更安全的是,在第一个解决方案中仍然有可能销毁垃圾收集堆。微软的解决方案同样简单,它应该屏蔽ModifyString选项。有文件证明它没有任何效果。

我不知道对MeasureText()的调用如何可能修改原始路径。如果真的发生了这种情况,那么MeasureText()方法正在做一些非常有趣的事情。“当你说string ellipsedpath=OriginalPath时,实际上只是让ellipsedpath引用与OriginalPath相同的字符串实例。[…]”这是错误的。将string1指定给string2将生成string1的值副本。对string1的任何更改都不会对string2产生影响。尽管string是一个类,但它是不可变的,其行为类似于值类型。字符串从不更改,它们总是经过修改后复制到新字符串。@Hans:它可能描述了由于错误而发生的情况,但它也给出了在任何正常情况下将一个字符串分配给另一个字符串的明显错误信息,并且没有指出这只是因为TextRenderer中的一个主要错误。请至少承认这一点,因为你的代表不应该造成这样的误会。@Charles,你必须嗅出这个世界并不完美。只有当你真正了解虫子是如何产生的,你才能享受启蒙。你关于System.String是不可变的假设在这里引起了极大的反响。找出原因。我对这个错误理解得很好(没有你那么好,但足够好),我的观点是正确的。这一答复的案文特别具有误导性。然而,这不值得争论。@HansPassant可能与8.2.1有关,该条款规定
string
值是不可变的,11.2.3条款规定
string
System.string
的别名,附录D第483页似乎没有定义任何字符串变异方法。。。以及C语言规范中的许多其他部分……您可以使用
string.Copy
来明确从插入的字符串复制到新缓冲区的步骤。感谢您的解释。您的原始工作(和Ron的)都解决了覆盖原始字符串的问题,但都没有使MeasureText按我所希望的那样工作,因为输出字符串仍然没有正确地省略。如果我没有指定ModifyString,则输出字符串不会被省略(如预期的那样),这与我的目的(即获取省略的路径字符串)背道而驰。我想做的可能是滥用ModifyString。我使用了这个技巧,正如有人建议的那样:@castle:Ugh。您的代码实际上依赖于bug。我想知道,当您实际需要DrawText来显示椭圆时,为什么要使用MeasureText。这就是你需要做的,