1

In my own program I'm trying to use this code here to add tooltip balloon windows to my application: http://www.codeproject.com/Articles/4991/Balloon-Tips-Galore (Source code available here)

I tried compiling the demo program and it works fine on 32-bit Windows 7, but when I try to use it on 64-bit Windows 7 the program crashes. If I try to debug the crash in VS2010 I get this message:

enter image description here

The debugger is in some area where the source code isn't available and it says Call stack location: ntdll.dll!0000000076fe40f2()

How can I fix this so it won't crash on 64-bit?

Kyle V.
  • 4,752
  • 9
  • 47
  • 81
  • Im impressed, never seen anyone corrupt a heap before... –  Feb 14 '13 at 16:49
  • That article dates back to 2003. Solution: trash winforms, send the grandpa to rest, use WPF. There's a control called `CallOut` in the Expression SDK which does what you're looking for. – Federico Berasategui Feb 14 '13 at 16:50
  • @HighCore My program already exists though and is a Windows Forms app on .NET 4.0, Can I still use WPF? Is the Expression SDK part of WPF? – Kyle V. Feb 14 '13 at 17:18
  • You can put WPF content inside an existing winforms app using the [`ElementHost`](http://msdn.microsoft.com/en-us/library/system.windows.forms.integration.elementhost.aspx). The Expression SDK is a installed with Blend, but you can [Download it separately](http://www.microsoft.com/en-us/download/details.aspx?id=10801). Then you can include the `Microsoft.Expression.Drawing` assembly in your application binaries. – Federico Berasategui Feb 14 '13 at 17:21
  • @HighCore Okay I downloaded the SDK and pulled out WPF.Microsoft.Expression.Drawing.dll and added it as a reference in a test project. Now how do I use this `CallOut` control? – Kyle V. Feb 14 '13 at 17:32
  • @stickfigs Since I never did any winforms, I couldn't tell, guess you'll have to follow the MSDN stuff, it shouldn't be difficult, though – Federico Berasategui Feb 14 '13 at 17:34
  • 5
    @HighCore Are you just going through all of the questions tagged [tag:winforms] and advising people to stop using it and switch to WPF? I'm not even sure if that's good advice, but I certainly don't think it's welcomed by those people who are asking perfectly valid questions about WinForms. This makes three times I've seen you post almost the exact same comment. It's *particularly* offensive when you go on to admit that you've never even used WinForms. That explains why your sweeping conclusions about its inadequacies have thrice been incorrect. Please stop leaving these comments. – Cody Gray - on strike Feb 14 '13 at 18:34
  • @codygray `your sweeping conclusions about its inadequacies have thrice been incorrect` can you please show how and where and why? – Federico Berasategui Feb 14 '13 at 18:40
  • 1
    @HighCore I could, but this question is not about you. If you don't like WinForms, don't use it. If you don't know how to do something using WinForms, then don't answer the question. It's not that hard. I don't know anything about Java, but I don't go around telling people that Java sucks and they should use C++ instead. If you want advice on doing something in WinForms, post a question about it. – Cody Gray - on strike Feb 14 '13 at 18:43
  • @codygray I can show at least 3 posts of WPF solutions to winforms problems, where the OP finally accepted my solution, understood that winforms is deprecated and switched over to WPF, and Im sure they're much much happier now. Im just trying to create a better world, a world where winforms does not exist anymore. – Federico Berasategui Feb 14 '13 at 18:45
  • The article's discussion contain information abou this issue [here](http://www.codeproject.com/Messages/3326161/Re-Crash-on-Windows-7-64bit.aspx) an link to a possible solution [here](http://blogs.msdn.com/b/dsvc/archive/2009/11/02/p-invoke-marshal-structuretoptr.aspx). Have seen and tried that? – Christian.K Feb 14 '13 at 18:53
  • @highcore I have the feeling, nothing is going to change your attitude regarding WPF vs WinForms, and you don't have to. But as a service for you, an others reading this heap of comments, i' lik to poin you to [this](http://stackoverflow.com/a/913709/21567). – Christian.K Feb 14 '13 at 18:59
  • @christian.k that answer dates back to 2009, and sounds too much like the kind of opinion that thinks that a LOB must be a battleship-gray torture to the end users' eyes. As a matter of fact, you are unable to show me 1 (One) scenario where winforms outperforms WPF be it in performance, ease of development, scalability, and whatever other aspects you wish. – Federico Berasategui Feb 14 '13 at 19:07
  • 3
    @HighCore Please feel free to search out any and all questions that ask variations of "Should I use WinForms or WPF?" and "Why should I not start a new project in WinForms?" and share your wisdom there. But you really need to stop spamming everyone who asks a WinForms question with your "advice" not to use WinForms. Most people have good reasons for using WinForms, not the least of which might be thousands of lines of established, *working* code that it makes absolutely no business sense to re-write in WPF. – Cody Gray - on strike Feb 14 '13 at 19:14

1 Answers1

4

I can't make the C# demo crash on Windows Server 2003 x64 (which is the only 64-bit environment I have handy at the moment), but the code is faulty so it makes sense that you're seeing unexpected behavior.

Edit: Reproduced the crash in Windows Server 2008 R2 x64 using the original code, and verified the efficacy of the fix.

As Christian.K points out, the problem has been noted before. When calling the Marshal.StructureToPtr method, you should pass true for the third fDeleteOld parameter only when the specified memory block does not contain valid data. This is called out pretty explicitly in the documentation, so I'm not sure how the original writer got it wrong.

In this case, since the data was just allocated the line before by calling Marshal.AllocHGlobal, it does not contain valid data and should not be deleted/freed. The change is simple: change the third parameter true to false. Unfortunately, because the interop code is scattered across three different classes in the sample project, you'll have to make the change multiple places. The pattern you're looking for is this:

IntPtr ptrStruct = Marshal.AllocHGlobal(Marshal.SizeOf(ti));
Marshal.StructureToPtr(ti, ptrStruct, false /* <-- change this from true to false */);

Just as a general observation: the code tries to handle a lot of the interop stuff manually (by using methods of the Marshal class) rather than just letting the CLR handle it automatically. I much prefer the latter method. Even though I fully understand how to do all of the interop manually, letting the system manage it for me keeps down the number of mistakes I make and resulting heap corruption.

RhysW says he's never encountered heap corruption before, but it becomes very common when you start doing interop between .NET code and the Win32 API. The .NET Framework is no longer protecting you.

For an example of what I mean, notice that the FMSBalloonTip.SetToolTip method uses the Marshal.StringToHGlobalAuto method to marshal the string containing the tooltip's title as a pointer. While that certainly works (and the author was thankfully careful to free the pointer after they're finished), it would be much easier and less error-prone to declare an overload of the SendMessage function that accepts a string object as the fourth parameter. That way, the framework will handle all of the necessary interop stuff for you transparently.

The real question, of course, is why you need this code at all. It's way easier to just use the built-in ToolTip class, which has been available since the beginning. I'm not sure if you just didn't mention some feature you need that ToolTip doesn't provide or you just don't know about it, but I strongly recommend reconsidering your design so that you can make use of the built-in class and let the Microsoft programmers handle all of the interop stuff.

If it's the balloon part you're looking for, make sure to set the IsBalloon property of the ToolTip class. That wasn't introduced until .NET 2.0, but that's the same version the sample project is targeting.

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574