开发者

Some Windows API calls fail unless the string arguments are in the system memory rather than local stack

We have an older massive C++ application and we have been converting it to support Unicode as well as 64-bits. The following strange thing has been happening:

Calls to registry functions and windows creation functions, like the following, have been failing:

   hWnd = CreateSysWindowExW( ExStyle, ClassNameW.StringW(), Label2.StringW(), Style,
                             Posn.X(), Posn.Y(),
                             Size.X(), Size.Y(),
                             hParentWnd, (HMENU)Id,
                             AppInstance(), NULL);

ClassNameW and Label2 are instances of our own Text class which essentially uses malloc to allocate the memory used to store the string.

Anyway, when the functions fail, and I call GetLastError it returns the error code for "invalid memory access" (though I can inspect and see the string arguments fine in the debugger). Yet if I change the code as follows then it works perfectly fine:

   BSTR Label2S = SysAllocString(Label2.StringW());
   BSTR ClassNameWS = SysAllocString(ClassNameW.StringW());

   hWnd = CreateSysWindowExW( ExStyle, ClassNameWS, Label2S, Style,
                             Posn.X(), Posn.Y(),
                             Size.X(), Size.Y(),
                             hParentWnd, (HMENU)Id,
                             AppInstance(), NULL);
   SysFreeString(ClassNameWS); ClassNameWS = 0;
   SysFreeString(Label2S); Label2S = 0;

So what gives? Why would the original functions work fine with the arguments in local memory, but when used with Unicode, the registry function require SysAllocString, and when used in 64-bit, the Windows creation functions also require SysAllocString'd string arguments? Our Windows procedure functions have all been converted to be Unicode, always, and yes we use SetWindowLogW call the correct default Unicode DefWindowProcW etc. That all seems to work fine and handles and draws Unicode properly etc.

The documentation at http://msdn.microsoft.com/en-us/library/ms632679%28v=vs.85%29.aspx does not say anything about this. While our application is massive we do use debug heaps and tools like Purify to check for and clean up any memory corruption. Also at the time of this failure, there is still only one main system thread. So it is not a thread issue.

So what is going on? I have read that if string arguments are marshalled anywhere or passed across process boundaries, then you have to use SysAllocString/BSTR, yet we call lots of API functions and there is lots of code out there which calls these func开发者_StackOverflow中文版tions just using plain local strings?

What am I missing? I have tried Googling this, as someone else must have run into this, but with little luck.

Edit 1: Our StringW function does not create any temporary objects which might go out of scope before the actual API call. The function is as follows:

Class Text {
 const wchar_t* StringW () const
   {
   return TextStartW;
   }

wchar_t*  TextStartW; // pointer to current start of text in DataArea


I have been running our application with the debug heap and memory checking and other diagnostic tools, and found no source of memory corruption, and looking at the assembly, there is no sign of temporary objects or invalid memory access.

BUT I finally figured it out:

We compile our code /Zp1, which means byte aligned memory allocations. SysAllocString (in 64-bits) always return a pointer that is aligned on a 8 byte boundary. Presumably a 32-bit ANSI C++ application goes through an API layer to the underlying Unicode windows DLLs, which would also align the pointer for you.

But if you use Unicode, you do not get that incidental pointer alignment that the conversion mapping layer gives you, and if you use 64-bits, of course the situation will get even worse.

I added a method to our Text class which shifts the string pointer so that it is aligned on an eight byte boundary, and viola, everything runs fine!!!

Of course the Microsoft people say it must be memory corruption and I am jumping the wrong conclusion, but there is evidence it is not the case.

Also, if you use /Zp1 and include windows.h in a 64-bit application, the debugger will tell you sizeof(BITMAP)==28, but calling GetObject on a bitmap will fail and tell you it needs a 32-byte structure. So I suspect that some of Microsoft's API is inherently dependent on aligned pointers, and I also know that some optimized assembly (I have seen some from Fortran compilers) takes advantage of that and crashes badly if you ever give it unaligned pointers.

So the moral of all of this is, dont use "funky" compiler arguments like /Zp1. In our case we have to for historical reasons, but the number of times this has bitten us...

Someone please give me a "this is useful" tick on my answer please?


Using a bit of psychic debugging, I'm going to guess that the strings in your application are pooled in a read-only section.

It's possible that the CreateSysWindowsEx is attempting to write to the memory passed in for the window class or title. That would explain why the calls work when allocated on the heap (SysAllocString) but not when used as constants.

The easiest way to investigate this is to use a low level debugger like windbg - it should break into the debugger at the point where the access violation occurs which should help figure out the problem. Don't use Visual Studio, it has a nasty habit of being helpful and hiding first chance exceptions.

Another thing to try is to enable appverifier on your application - it's possible that it may show something.


Calling a Windows API function does not cross the process boundary, since the various Windows DLLs are loaded into your process.

It sounds like whatever pointer that StringW() is returning isn't valid when Windows is trying to access it. I would look there - is it possible that the pointer returned it out of scope and deleted shortly after it is called?

If you share some more details about your string class, that could help diagnose the problem here.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜