|
|
Welcome to Microsoft Developer Support, Languages team blog! You will find a lot of language related troubleshooting resources here.
-
In this blog I am going to talk about Marshal.StructuretoPtr , especially its last parameter fDeleteOld. Although its msdn description is pretty concise but I have seen it to be a source of confusion a lot of times. Following is what it says:
fDeleteOld
Type: System..::.Boolean true to have the Marshal..::.DestroyStructure method called on the ptr parameter before this method executes. Note that passing false can lead to a memory leak.
Lets start with an example , lets say that you have a structure Interop_Struct that you would like to marshal across managed/native boundary ie from C# to C+ + typically.
Following is the structure definition in C++ :
struct Interop_Struct
{
int num;
char* ptr;
}
Following is corresponding definition in c#:
[StructLayout(LayoutKind.Sequential)]
struct Interop_Struct
{
int num;
[MarshalAs(UnmanagedType.LPStr)] string ptr;
}
Now say you would like to marshal it using Marshal.StructuretoPtr ,
//Allocate managed structure
Interop_Struct op= new Interop_Struct() ;
//Allocate corresponding native structure
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(op));
//Marshal the structure
Marshal.StructureToPtr(op, ptr, true);
Now what would happen once you run the above code. There is something gravely wrong with it that would go unnoticed most of the times.
To illustrate what I am saying, enable page heap for the process, using gfalgs utillity from Debugging Tools for Windows. Now run the process again , under the debugger and it would crash consistently everytime. Following is the callstack :
08 0026f080 64b06f95 00061000 c0c0c0c0 00060000 verifier!AVrfpDphFindBusyMemoryNoCheck+0xb8
09 0026f0a4 64b07240 00061000 c0c0c0c0 0026f114 verifier!AVrfpDphFindBusyMemory+0x15
0a 0026f0c0 64b09080 00061000 c0c0c0c0 00d6ed7e verifier!AVrfpDphFindBusyMemoryAndRemoveFromBusyList+0x20
0b 0026f0dc 77dd5674 00060000 01000002 c0c0c0c0 verifier!AVrfDebugPageHeapFree+0x90
0c 0026f124 77d97aca 00060000 01000002 c0c0c0c0 ntdll!RtlDebugFreeHeap+0x2f
0d 0026f218 77d62d68 00000000 c0c0c0c0 013213f0 ntdll!RtlpFreeHeap+0x5d
0e 0026f238 769a3ffa 00060000 00000000 c0c0c0c0 ntdll!RtlFreeHeap+0x142
0f 0026f24c 769a4016 76a966bc c0c0c0c0 0026f268 ole32!CRetailMalloc_Free+0x1c
10 0026f25c 6d90fa18 c0c0c0c0 0026f27c 6d8e0224 ole32!CoTaskMemFree+0x13
11 0026f268 6d8e0224 03826ffc 032e2c38 01323078 mscorwks!FieldMarshaler_StringUni::DestroyNativeImpl+0x16
12 0026f27c 6d8f36c7 03826ff8 01323078 a0af14fa mscorwks!LayoutDestroyNative+0x3a
13 0026f32c 01e1010d 00000001 03c7331c 03c7331c mscorwks!MarshalNative::StructureToPtr+0x13f
WARNING: Frame IP not in any known module. Following frames may be wrong.
14 0026f360 6d7f1b6c 0026f3ac 00000000 0026f3f0 0x1e1010d
15 0026f370 6d802209 0026f440 00000000 0026f410 mscorwks!CallDescrWorker+0x33
16 0026f3f0 6d816511 0026f440 00000000 0026f410 mscorwks!CallDescrWorkerWithHandler+0xa3
17 0026f528 6d816544 0132c030 0026f5f4 0026f5c0 mscorwks!MethodDesc::CallDescr+0x19c
18 0026f544 6d816562 0132c030 0026f5f4 0026f5c0 mscorwks!MethodDesc::CallTargetWorker+0x1f
19 0026f55c 6d880c45 0026f5c0 a0af1116 00000000 mscorwks!MethodDescCallSite::CallWithValueTypes_RetArgSlot+0x1a
1a 0026f6c0 6d880b65 01322ffc 00000001 0026f6fc mscorwks!ClassLoader::RunMain+0x223
1b 0026f928 6d8810b5 00000000 a0af1a2e 00000001 mscorwks!Assembly::ExecuteMainMethod+0xa6
1c 0026fdf8 6d88129f 012e0000 00000000 a0af199e mscorwks!SystemDomain::ExecuteMainMethod+0x456
1d 0026fe48 6d8811cf 012e0000 a0af1946 00000000 mscorwks!ExecuteEXE+0x59
1e 0026fe90 6be6571d 6d881137 0026fea8 6bf5ae72 mscorwks!_CorExeMain+0x15c
1f 0026fe9c 6bf5ae72 6be60000 0026febc 6bf55121 mscoreei!CorExeMain+0x20
20 0026fea8 6bf55121 00000000 77921174 7ffd3000 MSCOREE!GetMetaDataInternalInterface+0x2d5
21 0026febc 77d6b3f5 7ffd3000 78d7eb0a 00000000 MSCOREE!CorExeMain+0x8
22 0026fefc 77d6b3c8 6bf55119 7ffd3000 00000000 ntdll!__RtlUserThreadStart+0x70
23 0026ff14 00000000 6bf55119 7ffd3000 00000000 ntdll!_RtlUserThreadStart+0x1b
As is evident from call-stack CoTaskMemFree is being called on an address which is not pointing to process heap (c0c0c0). Now the reason why it is called is also evident from callstack, Marshal.StructuretoPtr is calling LayoutDestroyNative which internally is calling CoTaskMemFree.
So for native structures which have pointers as members , if you pass TRUE as the value of fdeleteOld parameter to Marshal.structureToPtr, CoTaskMemFree will be called on the member variables of structure which are pointers so that the corresponding allocated memory is freed , before the corresponding new value of that member variable from managed structure is copied to native structure.
If you are allocating native structure and not assigning values to it so that member variables which are pointers are pointing to allocated memory on process heap , you should be pasing false as the third parameter and not true.
In the above sample if you pass false as last parameter to Marshal.structuretoPtr , it would not crash even with page heap enabled.
As for the reason why without enabling page heap crash is random is , because many times the pointer member variable might be holding a valid address(although not correct) , and so calling CoTaskMemFree on it does not cause an immediate AV however note that it has corrupted the heap and eventually it would cause problems , which would be difficult to diagnose.
|
-
This is one of the interesting scenario in which an unmanaged application built with Non-Microsoft technology was crashing during application shutdown with BOOTUP_EXCEPTION_COMPLUS exception (c0020001). This unmanaged application happened to be using unmanaged dll (built with Microsoft compiler) which in turn used IJW (a.k.a. C++ Interop) to interact with windows forms and create some ActiveX controls.
Now as you might know BOOTUP_EXCEPTION_COMPLUS occurs if a call is made in the managed code before the execution engine has finished initializing, or after it has started shutting down. Have a look at cbrume’s blog to know more about BOOTUP_EXCEPTION_COMPLUS. To confirm the cause of the problem and troubleshoot thoroughly, WinDbg was an obvious choice.
After aligning symbols correctly for Microsoft modules, the call stack showed the exception being raised during a call to a window procedure. See frame 5 below which shows a function call to user32!UserCallWinProcCheckWow. This indicates that a window procedure registered during the window creation is trying to process a message. Observe the below call stack carefully:
0:000> .lastevent Last event: 1084.1150: Unknown exception - code c0020001 (first/second chance not available) debugger time: Thu Aug 27 00:09:57.524 2009 (GMT+6)
0:000> knL100 # ChildEBP RetAddr 00 0012f99c 7a0967c9 kernel32!RaiseException+0x53 01 0012f9b8 7a0257a8 mscorwks!COMPlusThrowBoot+0x4a 02 0012f9c8 79e71e6d mscorwks!UMThunkStubRareDisableWorker+0x25 03 0012f9e8 7739b6e3 mscorwks!UMThunkStubRareDisable+0xa 04 0012fa14 7739b874 user32!InternalCallWinProc+0x28 05 0012fa8c 7739c8b8 user32!UserCallWinProcCheckWow+0x151 06 0012fae8 7739c9c6 user32!DispatchClientMessage+0xd9 07 0012fb10 7c828556 user32!__fnDWORD+0x24 08 0012fb3c 7739d1ec ntdll!KiUserCallbackDispatcher+0x2e 09 0012fb90 77393c27 user32!NtUserMessageCall+0xc 0a 0012fbac 77393c91 user32!RealDefWindowProcA+0x47 0b 0012fbf4 0127b962 user32!DefWindowProcA+0x72 …[Snip]
Now, the next question was to know which window is processing the messages and why. To determine these details with public symbols, you have to manually see the raw call stack. This can be done by running WinDbg command dds. See below output:
0:000> dds 0012fa14 0012fa14 0012fa8c <Unloaded_.DLL>+0x124d57 0012fa18 7739b874 user32!UserCallWinProcCheckWow+0x151 0012fa1c 015b5052 <Unloaded_ure.dll>+0x15b5051 0012fa20 000202b6 <Unloaded_.DLL>+0x15581 0012fa24 0000001c <Unloaded_ure.dll>+0x1b 0012fa28 00000000 0012fa2c 0000178c <Unloaded_ure.dll>+0x178b
One of the values we are interested in is the handle to the window which is processing the message. In this case it is 000202b6. As you read further, you will come to know what this value resembles to.
Now if you analyze the disassembly for mscorwks!UMThunkStubRareDisableWorker, you will notice that even before routing the message to the window procedure the stub function first confirms if it is safe to run the managed code. It does this by calling mscorwks!CanRunManagedCode. If this call returns a value indicating “No”, the stub method throws the BOOTUP_EXCEPTION_COMPLUS. This was exactly the same in this case.
0:000> uf mscorwks!UMThunkStubRareDisableWorker mscorwks!UMThunkStubRareDisableWorker: 7a025783 55 push ebp 7a025784 8bec mov ebp,esp 7a025786 56 push esi 7a025787 6a00 push 0 7a025789 6a01 push 1 7a02578b e8896cf2ff call mscorwks!CanRunManagedCode (79f4c419) 7a025790 85c0 test eax,eax 7a025792 8b7508 mov esi,dword ptr [ebp+8] 7a025795 7511 jne mscorwks!UMThunkStubRareDisableWorker+0x25 (7a0257a8)
mscorwks!UMThunkStubRareDisableWorker+0x14: 7a025797 682b040780 push 8007042Bh 7a02579c c7460800000000 mov dword ptr [esi+8],0 7a0257a3 e8ff0f0700 call mscorwks!COMPlusThrowBoot (7a0967a7)
...[Snip]
Now if the managed code is not allowed to execute, why the managed window is still alive and registered for receiving the messages? Well, a quick look into the managed heap reveals that “Microsoft.Win32.SystemEvents” object is still alive! Ideally this object is destroyed during application cleanup phase; however this was not the case. Now, SystemEvents actually provides access to system event notifications like display setting changed, user preference changed, etc. This is achieved by creating a hidden window having a specific “windowHandle”. If you carefully analyze below command you will notice the “windowHandle” for these events is 202b6 which is exactly the same value for the window trying to process the message. This window is actually the “.NET-BroadcastEventWindow”, a special window, created for monitoring the system events.
0:000> .loadby sos mscorwks
0:000> !DumpHeap -stat total 101243 objects Statistics: MT Count TotalSize Class Name 7a5d2608 1 20 Microsoft.Win32.SystemEvents
…[Snip]
0:000> !DumpHeap -MT 0x7a5d2608 Address MT Size 017eacd8 7a5d2608 20 total 1 objects Statistics: MT Count TotalSize Class Name 7a5d2608 1 20 Microsoft.Win32.SystemEvents Total 1 objects
0:000> !do 017eacd8 Name: Microsoft.Win32.SystemEvents MethodTable: 7a5d2608 EEClass: 7a4724fc Size: 20(0x14) bytes (C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll) Fields: MT Field Offset Type VT Attr Value Name 793332c8 40015b8 c System.IntPtr 1 instance 202b6 windowHandle 79330a00 40015b2 568 System.String 0 static 017ead88 className …[Snip]
0:000> du 017ead88+c 017ead94 ".NET-BroadcastEventWindow.2.0.0." 017eadd4 "0.9585cb.0"
The next step was to find why the window did not get destroyed. Reflecting the code for “Microsoft.Win32.SystemEvents.Initialize” shows that this window gets destroyed during the call to “SystemEvents.Shutdown()”. See below code snippet:
private void Initialize() { this.consoleHandler = new NativeMethods.ConHndlr(this.ConsoleHandlerProc); if (!UnsafeNativeMethods.SetConsoleCtrlHandler(this.consoleHandler, 1)) { this.consoleHandler = null; } this.windowHandle = this.CreateBroadcastWindow(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(SystemEvents.Shutdown); AppDomain.CurrentDomain.DomainUnload += new EventHandler(SystemEvents.Shutdown); }
Further, SystemEvents.Shutdown() method is called when either AppDomain.CurrentDomain.ProcessExit() or AppDomain.CurrentDomain.DomainUnload() events are served. Now these events are not guaranteed to be called always, mostly during rude exit (application directly calling kernel32!TerminateProcess/ExitProcess or StackOverFlow). So to confirm if the application is not running into a rude exit you can set breakpoints on above methods and see if it hits. It was confirmed that the problem was indeed with ProcessExit/DomainUnload events not getting called, due to which the broadcast window remains alive and thus executes the window procedure even when CLR is about to shutdown.
A crude way to get around with the problem is to manually call SystemEvents.Shutdown() using System.Reflection just before closing the application. However, the reliable solution to the problem is to make sure the CRT/CLR cleanup code is correctly executed. For a mixed mode application, one of the crt functions which does this is exit(). It makes sure CRT/CLR code is correctly cleaned up and ProcessExit/DomainUnload events are called thus ensuring a correct cleanup.
- Gaurav Patole,
Technical Lead, Developer Support Languages Team.
|
-
1. How do I know which dll/control needs to be rebuilt?
Please check the guidelines to understand if your dll/control could be affected or not. Installing the Visual Studio ATL Security patch and rebuilding the binaries alone will not mitigate the vulnerability. You may need to make code changes as given in the document.
2. I rebuilt my application and then deployed it. The application doesn’t launch and the event viewer shows Side-by-side error to load msvcrxx.dll (xx could be 80 or 90).
You would need to install the new VS200X Redistributable:
For app built with VS2005 SP1 + ATL Security Package: Download Link
For app built with VS2008 RTM + ATL Security Package: Download Link
For app built with VS2008 Sp1 + ATL Security Package: Download Link
3. Can I install all the three new redistributable (refer to the Q2 for all the new redistributable available) on a single machine.
Yes. You can. The redistributable installs the necessary files in <windows>\WinSXS folder. The application will find the required runtime files it needs.
4. How is the MS09-035 Security Update pushed to the various machines (end user and developer machines):
[Developer Machine-> Which has the Visual Studio2003/2005/2008 installed.
End user-> which has the VS2003/2005/2008 redistributable installed.]
Refer to the explanation: http://blogs.msdn.com/vcblog/archive/2009/08/05/active-template-library-atl-security-updates.aspx#9859710
5. I see in my build machine that Visual Studio ATL Security Update is available via Microsoft Update. Will the end user machine can get the full CRT/MFC/ATL redistributable from Microsoft Update.
No. For an end user, only if he has a previous vulnerable redistributable installed on his machine, the Microsoft Update will show up to install KB973923 or KB973924. For example, if you see Microsoft Visual Studio 2005 SP1 redistributable in Add/Remove programs, then visiting the http://udpate.microsoft.com will show KB973923 in the list (provided Microsoft Update is enabled).
6. When trying to install the ATL Security Update for Visual Studio I get the error message “The feature you are trying to use is on a network resource that is unavailable” and asks you to point to an alternate path.
Refer to KB944298 for solution.
7. While installing Visual Studio 2008 SP1 Security Patch KB971092, I am getting following error message.
“VC Libraries QFE Patch does not apply, or is blocked by another condition on your system. Please click the link below for more details.”
This is known issue and it’s been fixed in latest patch release dates 8/3/2009. Reinstalling this latest patch should resolve this error.
8. While installing Visual Studio 2005 SP1 Security Update KB971090 on Windows 2003 Sp2, I am getting the following errors
“Product: Microsoft Visual Studio 2005 Professional Edition - ENU -- Configuration failed.
Product: Microsoft Visual Studio 2005 Professional Edition - ENU -- Error 1718.File C:\WINDOWS\Installer\78a5028.msp did not pass the digital signature check. For more information about a possible resolution for this problem, see http://go.microsoft.com/fwlink/?LinkId=73863.
Product: Microsoft Visual Studio 2005 Professional Edition - ENU - Update 'Security Update for Microsoft Visual Studio 2005 Team Suite - ESN (KB971090)' could not be installed. Error code 1603.
The installation of C:\WINDOWS\Installer\78a5028.msp is not permitted due to an error in software restriction policy processing. The object cannot be trusted.”
This problem occurs if the Windows Installer process has insufficient contiguous virtual memory to verify that the .msi package or the .msp package is correctly signed. Installing hotfix for KB925336 should resolve this issue.
9. After installing Visual Studio 2k8 SP1 security patch KB971092, I am getting the following
errors.
error C2039: '_Swap_adl' : is not a member of 'std' c:\program files\microsoft visual studio 9.0\vc\include\xutility 2764
error C3861: '_Swap_adl': identifier not found c:\program files\microsoft visual studio 9.0\vc\include\xutility 2764.
Installing windows SDK for server 2008 (v6.1) after VS2008 SP1 causes conflicts with this security update which eventually leads to these compiler errors. Workaround is to reinstall VS 2008 SP1 and then on top installing this security patch should resolve this issue.
More information is available at
http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/96f5d066-01cb-48c5-bba3-9df8120d06cc
If you need more help, feel free to post here or call Microsoft support.
10. My build was updated (with Visual Studio 200X ATL security update) and so was my customers machine with the ATL Security update (Kb973923/4). But then in my customer’s machine I get this error when launching:
“the application failed to start because the application configuration is incorrect’ reinstalling the application”
KB973923&KB973924 just updates the corresponding ATL libraries and it doesn’t update MFC /CRT dlls. You need to install the corresponding redist so that the new versions of CRT/MFC /ATL are available on target machine. See point 2 in this article for the links.
11. Is there any tool or software to scan if my ATL control is affected?
You could try using http://codetest.verizonbusiness.com/. Additionally you should also review your code and make necessary changes mentioned in http://msdn.microsoft.com/en-us/visualc/ee309358.aspx
|
-
Most of the developers work with CToolBarCtrl but they get stuck when it comes to display chevron button. This article is all about how to get chevron button displayed on CToolBarCtrl control while resizing its content window.
There is a good MSDN article on handling chevrons but no good sample implementation on it. I wrote a small sample using CDialog, CToolBarCtrl and CReBarCtrl classes to illustrate the same.
Since the main focus would be on handling chevron button, I am not going to cover tool bar control implementation details. Following are the steps to achieve chevron button on CToolBarCtrl.
1. Using class wizard, create a new class CCustReBarCtrl deriving it from CReBarCtrl MFC class. Add m_wndReBar and m_wndToolBar of type CCustReBarCtrl and CToolBarCtrl respectively in dialog class. Create rebar and tool bar controls in OnInitDialog() dialog method as shown below.
m_wndReBar.CreateEx(WS_EX_TOOLWINDOW, WS_CHILD | WS_BORDER | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NOPARENTALIGN | CCS_NODIVIDER | RBS_VARHEIGHT | RBS_BANDBORDERS, CRect(0, 0, 0, 0), this, 10);
m_wndToolBar.Create(WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CCS_NOPARENTALIGN | CCS_NODIVIDER | CCS_NORESIZE | TBSTYLE_TRANSPARENT | TBSTYLE_FLAT, CRect(0,0,0,0), &m_wndReBar, IDR_MAINFRAME); //Note that rebar control is parent for tool bar control.
/*Make sure that tool bar resource is loaded here*/
m_wndToolBar.AutoSize();
Rebar control provides chevron control when RBBS_USECHEVRON flag in the fStyle member of the band's REBARBANDINFO structure is set. Before defining the band info, define the following macro in stdafx.h file.
#define REBARBANDINFO_SIZE CCSIZEOF_STRUCT(REBARBANDINFO, cxHeader)
SIZE sizeButtons;
m_wndToolBar.GetMaxSize(&sizeButtons);
REBARBANDINFO rbi;
ZeroMemory((void*)&rbi, sizeof(rbi));
rbi.cbSize = REBARBANDINFO_SIZE; // V5 structure size. Insert fails if the struct is too big
rbi.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_STYLE;
rbi.fStyle = RBBS_GRIPPERALWAYS | RBBS_USECHEVRON;
rbi.cyMaxChild = rbi.cyMinChild = rbi.cyChild = sizeButtons.cy;
rbi.cxIdeal = rbi.cx = sizeButtons.cx;
rbi.hwndChild = (HWND)m_wndToolBar;
m_wndReBar.InsertBand(-1, &rbi);
RECT rcWindow;
GetClientRect(&rcWindow);
m_wndReBar.MoveWindow(0, 0, rcWindow.right - rcWindow.left, sizeButtons.cy);
2. It’s very important to size the rebar control accordingly when dialog is resized otherwise chevron button will not be displayed. So handle WM_SIZE message (OnSize() method) in the dialog class and add the following code as shown below.
if (m_wndReBar.GetSafeHwnd())
{
RECT rcRebar;
m_wndReBar.GetWindowRect(&rcRebar);
m_wndReBar.MoveWindow(0, 0, cx, rcRebar.bottom - rcRebar.top);
}
3. Now if you run the code and resize the dialog, chevron will be displayed for tools that have been covered. When a user clicks a chevron, the rebar control sends RBN_CHEVRONPUSHED notification. The NMREBARCHEVRON structure that is passed with the notification contains the band's identifier and a RECT structure with the rectangle occupied by the chevron. Your handler must determine which buttons are hidden and display the associated commands on a pop-up menu.
Add RBN_CHEVRONPUSHED notification in CCustReBarCtrl, after adding the message map should look like as shown below
BEGIN_MESSAGE_MAP(CCustRebarCtrl, CReBarCtrl)
ON_NOTIFY_REFLECT(RBN_CHEVRONPUSHED, & CCustRebarCtrl::OnRbnChevronPushed)
END_MESSAGE_MAP()
Following is the code that goes into the chevron handler 'void CCustRebar::OnRbnChevronPushed(NMHDR *pNMHDR, LRESULT *pResult)' method
LPNMREBARCHEVRON pnmrc = reinterpret_cast<LPNMREBARCHEVRON>(pNMHDR);
RECT rcBand;
RECT rcButton;
RECT rcIntersect;
REBARBANDINFO rbi;
TBBUTTON tbb;
UINT cButtons;
UINT iButton;
UINT iMenu;
HMENU hmenuPopup;
MENUITEMINFO mii;
TCHAR szText[200];
HRESULT hr;
CImageList* pimlToolbar;
//
// Get the child window for this band.
ZeroMemory((void*)&rbi, sizeof(rbi));
rbi.cbSize = REBARBANDINFO_SIZE;
rbi.fMask = RBBIM_CHILD;
GetBandInfo(pnmrc->uBand, &rbi);
CToolBarCtrl *pTBarCtrl = (CToolBarCtrl *)CToolBarCtrl::FromHandle ( rbi.hwndChild );
ASSERT(pTBarCtrl);
ASSERT(pTBarCtrl->IsKindOf(RUNTIME_CLASS(CToolBarCtrl)));
//
// Get the client rectangle of the child window
pTBarCtrl->GetClientRect(&rcBand);
//
// Get the number of buttons in the toolbar. This should fail when the
// child window is not a toolbar.
if ((cButtons = pTBarCtrl->GetButtonCount()) == 0)
return;
//
// Starting from the leftmost button, retrieve each button's bounding
// rectangle until we find a button that is at least partially hidden
for (iButton = 1; iButton < cButtons; iButton++)
{
//
// Get the button's rectangle
ZeroMemory((void*)&rcButton, sizeof(rcButton));
pTBarCtrl->GetItemRect(iButton, &rcButton);
CRect rtTabButton(rcButton);
CRect rtToolBarCtr(rcBand);
CPoint ptCenter = rtTabButton.CenterPoint();
if(!rtToolBarCtr.PtInRect(ptCenter))
break;
}
//
// Nothing to do if we didn't find a hidden button
if (iButton == cButtons)
return;
//
// Allocate an array of bitmaps to hold the images used in menu items. An
// alternate approach is to ownerdraw the menu
UINT cImages = cButtons - iButton;
size_t cb = sizeof(HBITMAP) * cImages;
HBITMAP* ahbmpImages = (HBITMAP*)malloc(cb);
if (!ahbmpImages)
return;
ZeroMemory((void*)ahbmpImages, cb);
//
// Create a memory DC that we use to render the toolbar images
HDC hdcMem = NULL;
HDC hdcWindow = ::GetDC(m_hWnd);
if (hdcWindow)
{
hdcMem = CreateCompatibleDC(hdcWindow);
}
//
// Create a popup menu for the hidden toolbar items
hmenuPopup = CreatePopupMenu();
//
// Get the image list for the toolbar
int cxImage = 0;
int cyImage = 0;
pimlToolbar = pTBarCtrl->GetImageList();
if (pimlToolbar)
{
ImageList_GetIconSize(*pimlToolbar, &cxImage, &cyImage);
}
//
// Create a menu item for each hidden toolbar item
iMenu = 0;
for ( ; iButton < cButtons; iButton++)
{
ZeroMemory((void*)szText, sizeof(szText));
ZeroMemory((void*)&tbb, sizeof(tbb));
if (pTBarCtrl->GetButton(iButton, &tbb))
{
if(tbb.idCommand != 0)
{
LoadString(GetModuleHandle(NULL), tbb.idCommand, szText, sizeof(szText)-1);
int cch = strlen(szText);
szText[cch+1] = '\0';
}
//
// Create a bitmap for the toolbar icon and draw the icon into the bitmap
if (hdcMem && pimlToolbar->GetSafeHandle() && ahbmpImages)
{
ahbmpImages[iMenu] = CreateCompatibleBitmap(hdcWindow, cxImage, cyImage);
if (ahbmpImages[iMenu])
{
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, ahbmpImages[iMenu]);
RECT rcImage = {0, 0, cxImage, cyImage};
//
// First, fill the background with the menu background
FillRect(hdcMem, &rcImage, GetSysColorBrush(COLOR_MENU));
//
// Then draw the icon
ImageList_Draw(pimlToolbar->GetSafeHandle(), tbb.iBitmap, hdcMem, 0, 0, ILD_NORMAL);
SelectObject(hdcMem, hbmpOld);
}
}
//
// Add a menu item for this toolbar item
ZeroMemory((void*)&mii, sizeof(mii));
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING;
mii.dwTypeData = szText;
mii.wID = tbb.idCommand;
if (ahbmpImages[iMenu])
{
mii.fMask |= MIIM_BITMAP;
mii.hbmpItem = ahbmpImages[iMenu];
}
InsertMenuItem(hmenuPopup, iMenu++, TRUE, &mii);
}
}
//
// Display the popup menu if it has menu items
if (0 < GetMenuItemCount(hmenuPopup))
{
::MapWindowPoints(pnmrc->hdr.hwndFrom, NULL, (LPPOINT)&pnmrc->rc, 2);
TrackPopupMenu(hmenuPopup,
TPM_LEFTALIGN | TPM_TOPALIGN,
pnmrc->rc.left, pnmrc->rc.bottom,
0, m_hWnd, NULL);
}
//
// Destroy the popup menu
DestroyMenu(hmenuPopup);
//
// Cleanup the bitmaps
if (ahbmpImages)
{
while (cImages)
{
cImages--;
if (ahbmpImages[cImages])
DeleteObject(ahbmpImages[cImages]);
}
free(ahbmpImages);
}
//
// Release the DCs used to handle the bitmaps
if (hdcMem)
{
DeleteDC(hdcMem);
}
if (hdcWindow)
{
::ReleaseDC(m_hWnd, hdcWindow);
}
// TODO: Add your control notification handler code here
*pResult = 0;
|
-
Internal Mechanism
Internally the Visual Studio IDE interacts with editor and the language parsers to create a list of tokens that are updated frequently. In this process the background threads continuously update the Intellisense database. The priority of threads change automatically to make sure that the database is updated as and when user writes the code and also the delay on the IDE is minimized.
In a VC++ application, the Intellisense parser engine (feacp.dll) creates a database .ncb file and keeps it up-to-date. This ncb file can be located in the Project directory.
Intellisense Features
1. Quick Info – When we place the mouse pointer over an identifier, we can see a yellow popup box that appears on the screen. This window gives a brief description about the identifier.
2. Auto Completion – When we use period sign (.) after an object, or -> sign after a pointer, we automatically get the member lists. This is also generated if we use the scope resolution operator.
3. Parameter Help – Put a ‘(‘ sign after the function name and we get function definition which helps us write the function call. In case there are multiple overloaded functions, we get a drop down button as well.
4. Other features
• GD – Go to Definition
• XML – XML Doc Comments
• LB – Live Browsing
• CC – Case Correct
• NB – Navigation Bar
• CV – Class View
• OL – Outlining
• GR – Graying if-def code
VC++ Intellisense components
Vcpkg.dll - VC++ Language Service DLL - This dll is responsible to collect data from ncb file and perform operations like AC, QI, PH etc.
Feacp.dll – This dll is the frontend parser responsible to generate PDB data.
Symptoms
1. Loading project takes a long time showing “Updating Intellisense… (xxx)” in status bar. The number in parentheses shows how many background thread work items are in progress. For customers that aren’t seeing Intellisense ever complete, this information may be useful.
2. Quick-Info, Auto-Completion or Parameter-Help doesn’t work or show correctly.
3. Class-view doesn’t show-up.
4. Go-to Definition doesn’t work.
5. Visual Studio Hangs.
6. XML documentation doesn’t work.
Troubleshooting
1. Verifying Intellisense issue (or Disabling Intellisense)
There are chances that we are misinterpreting some issues with Intellisense. We can verify this by disabling Intellisense in VC++. There is a file called feacp.dll in <VS_INSTALL>\VC\vcpackages folder. Renaming this file will disable Intellisense feature.
After disabling, if we can work with the IDE without any other issue then we can say for sure that Intellisense is causing the problem.
2. NCB is write-protected
If the NCB file is write-protected, or the project is opened simultaneously by two or more instances of Visual Studio, the Intellisense may not work as expected.
3. Updating Intellisense… / IDE hangs
This is a very common issue with Intellisense and it may occur due to a variety of reasons. There are two scenarios, either the Intellisense returns after a long time or it doesn’t return at all. The recommended way is to check for NCB corruption and also getting the ACPLOG.
4. NCB file corruption
In some circumstances, NCB file becomes corrupted and thus the PARSER is unable to continue resulting hangs. Sometimes by deleting the NCB, the problem is resolved. But it may be a temporary workaround. The permanent solution is to find out which part of our code is causing failure.
5. ACPlog
We can generate a log file to find out the reasons behind the failure of parser. The ACPLOG can be generated from the Visual Studio IDE by adding the following option under Project Properties->Configuration Properties->C/C++->Command Line->Additional options: /acplog:<file_name_with_complete_path>
Save and close the solution. Reopen it and try to reproduce the scenario. The log will be created.
6. Intellisense and network build
Building a project/solution over network is unsupported. It may encounter Intellisense issue.
7. Is there any add-in that you are using?
Removing/disabling the add-in might resolve the issue.
8. Opening a project when Intellisense is busy?
If Intellisense is in the middle of updating (typically in case of a huge solution) and if we open a different project by File->Recent projects, the IDE hangs and never comes back. It is suggested to wait for Intellisense to finish its work.
9. Is there any antivirus installed?
Try disabling the antivirus. There might be some conflict.
10. Running Visual Studio in safemode.
The safemode option in Visual Studio(Start->Run->devenv /safemode) ensures that no additional third party add-ins are loaded when VS starts. This helps us to isolate Visual Studio conflicts with other third party applications/add-ins.
/SafeMode (devenv.exe)
11. Pre-Compiled Headers
It may happen that the project is building correctly and thus you imagine that there is no error in the code. Then why Intellisense is not working? Are you using precompiled header files in your VC++ projects? Let’s try building the project without using PCH and see if build gives us any error. If yes, resolving those errors may fix Intellisense.
12. Cyclic inclusion of header files
Try identifying the header files which are creating a cyclic graph and get it fixed.
13. Is it a bug?
Check for available fixes. A simple search here will give you a list of related fixes available.
14. Still not resolved?
If by considering the above points, your issue is not resolved, you can take 3-4 hang-dumps for devenv at an interval of say 3-4 minutes along with acplogs and contact Microsoft Support.
In Visual Studio 2010, we are completely changing the Intellisense mechanism. You can find more details here.
|
-
Suppose you are calling some native code from your managed code, and you observe an Access Violation crash soon after returning from the native code inside your managed code. if you run the application inside the Visual Studio debugger you will see the following exceptions in the output window if IDE.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000090: Floating-point invalid operation.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000090: Floating-point invalid operation.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000090: Floating-point invalid operation.
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000005: Access violation writing location 0x00133fec.
<crash>
OR
First-chance exception at 0x5eaf30b0 in TestApp.exe: 0xC0000091: Floating-point overflow. First-chance exception at 0x79f958e3 in TestApp.exe: 0xC0000091: Floating-point overflow. First-chance exception at 0x79f958e3 in TestApp.exe: 0xC0000091: Floating-point overflow.
First-chance exception at 0x79f958e3 in TestApp.exe: 0xC0000005: Access violation writing location 0x001331ec.
.... <crash>
Or other Floating point exceptions with a Access violation at the end
These floating point exceptions gives a clue that somehow the FPCW (Floating Point Control Word) register has been changed inside the native code. Because its value has been changed so it results in floating point exceptions inside the managed code. Its stated in the ECMA specifications for IL that IL floating point operations never throw exceptions. So CLR is not hardened against floating point exceptions. Although it is allowed in native code to change the FPCW register, but you need to restore it back to the old state as it was in before it was changed.
So the expectation here is that the FPCW should be restored to the same value as it was earlier before entering into the native code. In scenarios where the native code is not restoring this register we can call CRT’s _controlfp() inside the managed code to reset FPCW soon after returning from native code.
Below is a sample code snippet which shows how to call _controlfp() using pInvoke:
{
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int _controlfp(int newControl, int mask);
const int _RC_NEAR = 0x00000000;
const int _PC_53 = 0x00010000;
const int _EM_INVALID = 0x00000010;
const int _EM_UNDERFLOW = 0x00000002;
const int _EM_ZERODIVIDE = 0x00000008;
const int _EM_OVERFLOW = 0x00000004;
const int _EM_INEXACT = 0x00000001;
const int _EM_DENORMAL = 0x00080000;
const int _CW_DEFAULT = (_RC_NEAR + _PC_53 +
_EM_INVALID + _EM_ZERODIVIDE +
_EM_OVERFLOW + _EM_UNDERFLOW +
_EM_INEXACT + _EM_DENORMAL);
public SomeCode()
{
Some_unmanahed_code(); /*this is the native code which alters the FPCW regiester */
int value = _controlfp(_CW_DEFAULT, 0xfffff); /* Calling _controlfp()using pInvoke */
Some_managed_call() /*Calling the maanged code using pInvoke */
}
}
I found this “initial Control Word” value as _CW_DEFAULT in the float.h file.
/* initial Control Word value */
#if defined(_M_IX86)
#define _CW_DEFAULT ( _RC_NEAR + _PC_53 + _EM_INVALID + _EM_ZERODIVIDE + _EM_OVERFLOW + _EM_UNDERFLOW + _EM_INEXACT + _EM_DENORMAL)
Regards,
Bhupendra Dhyani
|
-
I am back with some more PInvoke Stuff. Recently I was working on a PInvoke issue which I found interesting.
I have a C++ dll which has a function whose signature is
int TestFunc(IN_STRUCT in_Params, RET_STRUCT * pret_Par).
I wanted to call this function from C#. Function has two arguments. First argument is input structure which will be filled from C# code and passed to C++ code. Second argument is output structure which is filled in C++ code and output to C# code.
Here are the C struct definitions and a function that needs to be marshaled
#include "stdafx.h"
#include <stdio.h>
#include "Objbase.h"
#include <malloc.h>
typedef struct IN_STRUCT
{
BYTE CMD_PType;
BYTE CMD_PTType;
BYTE CMD_OC;
BYTE CMD_Seq;
};
typedef struct RET_STRUCT
{
BYTE RET_OC;
BYTE RET_Seq;
BYTE RET_RetBytes;
char *str;
BYTE RET_PD[10];
};
extern "C" __declspec(dllexport) \
int TestFunc(IN_STRUCT in_Params, RET_STRUCT * pret_Par)
{
int iRet = 0;
pret_Par->RET_OC = in_Params.CMD_OC;
pret_Par->RET_Seq = in_Params.CMD_Seq;
pret_Par->RET_RetBytes = 6;
pret_Par->RET_PD[0] = 0;
pret_Par->RET_PD[1] = 10;
pret_Par->RET_PD[2] = 20;
pret_Par->RET_PD[3] = 30;
pret_Par->RET_PD[4] = 40;
pret_Par->RET_PD[5] = 50;
pret_Par->str = new char(30);
strcpy(pret_Par->str,"This is sample PInvoke app");
return iRet;
}
Managed Structure equivalent to Native Structure:
namespace ConsoleApplication1
{
class Program
{
//This structure will be filled up by C++ Test.dll and returned back
With values to C# code.
[StructLayout(LayoutKind.Sequential)]
public struct RET_STRUCT
{
public byte RET_OC;
public byte RET_Seq;
public byte RET_RetBytes;
[MarshalAs(UnmanagedType.LPStr)]
public String RET_STR;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] RET_PD;
};
//The values of this structure will be used to fill up IN_STRUCT and
passed to C#
[StructLayout(LayoutKind.Sequential)]
public struct IN_STRUCT
{
public byte CMD_PT;
public byte CMD_PTType;
public byte CMD_OC;
public byte CMD_Seq;
};
//C++ dll containing the func
[DllImport("Test.dll")]
public static extern int TestFunc(IN_STRUCT i, ref RET_STRUCT r);
static void Main(string[] args)
{
IN_STRUCT cmd_params = new IN_STRUCT();
RET_STRUCT ret_Params = new RET_STRUCT();
//Fill up the cmd_params
cmd_params.CMD_OC = 0x02;
cmd_params.CMD_PTType = 0x00;
cmd_params.CMD_Seq = 1;
//Call the C++ function to fill ret_params
int iRet = TestFunc(cmd_params, ref ret_Params);
//Print out the returned values
Console.WriteLine("Returned Values\n");
Console.WriteLine(ret_Params.RET_OC + " " + ret_Params.RET_Seq +
" ");
for (int i = 0; i < ret_Params.RET_RetBytes; i++)
Console.WriteLine("\n" + ret_Params.RET_PD[i]);
Console.WriteLine(ret_Params.RET_STR);
Console.ReadLine();
}
}
}
After executing the code I was expecting a valid output. But I ended up with Access Violation. I used windbg to troubleshoot this issue.
I spawned exe from windbg and tried to see call stack.
0:000> kv
ChildEBP RetAddr Args to Child
002cec30 76fc5883 006515c8 00000001 00000000 ntdll!RtlpLowFragHeapFree+0x31 (FPO: [0,10,4])
002cec44 76b9c56f 000b0000 00000000 049a3a48 ntdll!RtlFreeHeap+0x101 (FPO: [3,0,4])
002cec58 7565dc2c 000b0000 00000000 049a3a50 KERNEL32!HeapFree+0x14 (FPO: [3,0,0])
002cec6c 7565dc53 7573e6f4 049a3a50 002cec88 ole32!CRetailMalloc_Free+0x1c (FPO: [2,0,0])
002cec7c 6c7e8410 049a3a50 002cec9c 6c8084bd ole32!CoTaskMemFree+0x13 (FPO: [1,0,0])
002cec88 6c8084bd 00109d34 00000001 00109d48 mscorwks!FieldMarshaler_StringAnsi::DestroyNativeImpl+0x16 (FPO: [1,0,0])
002cec9c 6c8088e5 00109d30 0065340c 1670b1d2 mscorwks!LayoutDestroyNative+0x3a (FPO: [2,0,0])
002cee8c 6c73539b 002cef58 00000000 1670b182 mscorwks!CleanupWorkList::Cleanup+0x2ea (FPO: [2,116,4])
002ceedc 001cad4c 002cef18 01020000 00109d30 mscorwks!NDirectSlimStubWorker2+0x120 (FPO: [1,12,4])
WARNING: Frame IP not in any known module. Following frames may be wrong.
002cefa4 6c7013a4 00700876 002cefd8 00000000 0x1cad4c
002cefe0 6c6f1b4c 010d2816 00000003 002cf070 mscorwks!PreStubWorker+0x141 (FPO: [1,13,4])
002ceff0 6c7021b1 002cf0c0 00000000 002cf090 mscorwks!CallDescrWorker+0x33
……….
0:000> da 049a3a50
049a3a50 "My Name is Jyoti Patel"
From the call stack it’s clear that InteropMarshaller ( NDirectSlimStubWorker2) is trying to deallocate string using CoTaskMemFree.
There are two solutions to this problem.
1. As deallocation is done using CoTaskMemFree, Allocate the memory using CoTaskMemAlloc.
Changing line of code in C++ from
pret_Par->str = new char(30);
pret_Par->str = (char*)CoTaskMemAlloc(30);
Issue was resolved.
(When a string buffer allocated by native code is marshaled to managed code, CLR Interop marshaller will allocate a managed string object and copy the contents of native buffer to the managed string object. Now in order to prevent a potential memory leak, CLR Interop Marshaller will try to free allocated native memory. It does so by calling CoTaskMemFree. The decision to call CoTaskMemFree is by-design. This can at times lead to crash, if memory was allocated by the called-native function using any API other than CoTaskMemAlloc family of API’s as custom allocators may allocate on different heaps.)
In this case, memory was allocated by malloc, and ends up being freed by CoTaskMemFree and hence we see an AV)
2. If you are not able to change the C++ code and you want to allocate memory using new/malloc other solution is to use Intptr and do custom marshalling by calling corresponding methods from Marshal class to do marshalling.
[StructLayout(LayoutKind.Sequential)]
public struct RET_STRUCT
{
public byte RET_OC;
public byte RET_Seq;
public byte RET_RetBytes;
public IntPtr RET_STR;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public byte[] RET_PD;
};
……….
………
………
Console.WriteLine(Marshal.PtrToStringAnsi(ret_Params.RET_STR));
Jyoti Patel and Manish Jawa
Developer Support VC++ and C#
|
-
Recently I worked with one of my colleague on an interesting scenario in which the MFC application was crashing on startup. The next step was to run the application under WinDbg. After running the application under WinDbg we saw that we are actually access violating an address which was indeed a NULL pointer. The access violation was coming within mfc90u!AfxWinMain().
(ce8.1018): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=7ffdf000 ecx=77224e10 edx=00000000 esi=00000000 edi=00000000
eip=5749b48a esp=001cf824 ebp=001cf840 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mfc90ud!AfxWinMain+0x7a:
5749b48a 8b10 mov edx,dword ptr [eax] ds:0023:00000000=????????
The obvious question was why AfxWinMain() would fail with an access violation? We analyzed the callstack to find the reason of the access violation (AV). Looking at the file source information we saw the application was failing in winmain.cpp @ line 37.
0:000> kbn100
*** WARNING: Unable to verify checksum for Test.exe
# ChildEBP RetAddr Args to Child
00 001cf840 010e70ea 010d0000 00000000 001e20ec mfc90ud!AfxWinMain+0x7a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\winmain.cpp @ 37]
01 001cf858 010e35fb 010d0000 00000000 001e20ec Test!wWinMain+0x1a [f:\dd\vctools\vc7libs\ship\atlmfc\src\mfc\appmodul.cpp @ 30]
02 001cf908 010e335f 001cf91c 773a4911 7ffdf000 Test!__tmainCRTStartup+0x28b [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 574]
03 001cf910 773a4911 7ffdf000 001cf95c 7721e4b6 Test!wWinMainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 399]
04 001cf91c 7721e4b6 7ffdf000 7748c3a5 00000000 kernel32!BaseThreadInitThunk+0xe
05 001cf95c 7721e489 010e178a 7ffdf000 00000000 ntdll!__RtlUserThreadStart+0x23
06 001cf974 00000000 010e178a 7ffdf000 00000000 ntdll!_RtlUserThreadStart+0x1b
Looking at the source code, “C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\winmain.cpp” we saw that the application was failing while accessing pThread. Since pThread was NULL we were getting AV. Now if you carefully see the source code, pThread is being returned from AfxGetThread().
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// Snip
// AV on below line
if (!pThread->InitInstance())
{
// Snip
}
// Snip
}
Above findings were confirmed by displaying the value of pThread using display type command (dt) in WinDbg. CWinThread pointer was in fact NULL.
0:000> dt pThread
Local var @ 0x1cf838 Type CWinThread*
(null)
The question was why AfxGetThread() returns NULL? Especially when it’s called from mfc90u!AfxWinMain() where we do not have direct control, unless we override AfxWinMain(). Looking at the source code of AfxGetThread(), it was clear that pThread was coming from the module thread state, AFX_MODULE_THREAD_STATE. For the ones who are wondering what is it? Have a look at afxstat_.h. below lines show where exactly pThread pointer is assigned.
“C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\thrdcore.cpp” @ line 138
CWinThread* AFXAPI AfxGetThread()
{
// check for current thread in module thread state
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
CWinThread* pThread = pState->m_pCurrentWinThread;
return pThread;
}
Now when and where the CWinThread pointer is getting assigned for the module thread state? A further search in the MFC source reveals that the CWinThread pointer is getting assigned from CWinApp constructor. See below line.
“C:\Program Files\Microsoft Visual Studio 9.0\VC\atlmfc\src\mfc\appcore.cpp” @ 381
CWinApp::CWinApp(LPCTSTR lpszAppName)
{
// Snip
// below code correctly sets CWinThread with this pointer
pThreadState->m_pCurrentWinThread = this;
// Snip
}
Wow! Does that mean the constructor was never called? Yes, the crux of the problem was that someone had accidently commented the global CWinApp constructor. As a result the constructor never got called. Compiler never complained about it being commented, and why should it? Since CWinApp was not correctly set, the application crashed by throwing an AV on pThread. So, as a rule of thumb you should always create the global CWinApp object to make sure it's constructor gets called and the AFX_MODULE_STATE structure is correctly setup. If you see your code works without the correct initialization, probably you are not using enough MFC in your code! This is the reason behind AfxGetThread() returning NULL in AfxWinMain().
- Gaurav Patole,
Technical Lead, Developer Support VC++/C# and Robotics
|
-
Recently, we came across a very interesting issue. A purely native application written in C++ was failing with "Not a valid win32 application" error at the startup on Windows XP machine.
There are a few basic checks that we do to troubleshoot this error. We did the following checks:
1) Checked if all the modules are built for the same platform. (no mixing of modules intended for x86 and x64 platforms)
2) Checked the Event Log entry. Sometimes EventLog gives very good clues to move towards resolution. There was no information available for this error.
3) Tried spawning the exe from a debugger. It didn’t break inside the application but crashed even before it. Had enabled Loader snaps using gflags that come with “Debugging Tools for Windows” to check which DLL fails to load. But the application was crashing before any dependent DLL got loaded.
4) For the first time even Process Monitor failed to give any clues.
Then we thought of getting the dumpbin header output (dumpbin /headers test.exe > DumpBinTest.txt). Analyzing the output file showed that target Operating System was 5.02. It means that the exe was intended for Windows Server 2003 and higher version of Operating Systems and not for XP. We tried running the exe on Windows Server 2003 machine and it worked fine. So that was the root cause. Changing the winver from 502 to 501 resolved the issue. J
Here is a part of the DumpBin header that confirmed the issue:
=============================
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file TestWin32.exe
PE signature found
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
14C machine (x86)
7 number of sections
48CFDFAC time date stamp Tue Sep 16 22:02:44 2008
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
102 characteristics
Executable
32 bit word machine
OPTIONAL HEADER VALUES
10B magic # (PE32)
9.00 linker version
3200 size of code
4200 size of initialized data
0 size of uninitialized data
11073 entry point (00411073) @ILT+110(_wmainCRTStartup)
1000 base of code
1000 base of data
400000 image base (00400000 to 0041AFFF)
1000 section alignment
200 file alignment
5.02 operating system version
0.00 image version
<snip>
==================================
Here are a few links for your information:
1) For more information on Minimum value for _WIN32_WINNT and WINVER, please check this MSDN Link.
2) The /SUBSYSTEM option specifies the environment for the executable. Here is the link which describes it more.
3) This blog discusses different windows version symbols. It is a nice read.
4) This link describes how to modify the WINVER and _WIN32_WINNT and also describes what change happened in Visual Studio 2008.
Ravi Kumar
Microsoft Developer Support - VC++ & C#
|
-
In native world, one interacts with OS directly by calling Win32 APIs for managing resources (like allocating/deallocating memory,opening/closing handles). In managed world one relies on CLR totally or at times partially for doing the same( eg GC does memory management for us).Due to lack of understanding of the way CLR does the work for us, we may at times conclude false theories. I was recently working on a test application, which seemed to leak threads when compiled with /clr switch . Following are the functions that were used to create threads.
unsigned int __stdcall WorkerThread(void * ptr)
{
THREADINFO *ti = (THREADINFO*)ptr;
::Sleep(100);
delete ti;
return 1;
}
void CTest::CreateWorkers(void *ptr)
{
int counter=0;
while (_run)
{
::Sleep(100);
THREADINFO *ti = new THREADINFO;
DWORD ui;
HANDLE threadHandle = CreateThread(NULL,4096,(LPTHREAD_START_ROUTINE )WorkerThread,(unsigned(_stdcall *)(void *))ti, CREATE_SUSPENDED,&ui);
ti->threadID = threadHandle;
ResumeThread(threadHandle);
WaitForSingleObject(threadHandle,INFINITE);
int a = CloseHandle(threadHandle);
counter++;
}
}
Note that it is C++ code which was being compiled with /clr for testing purpose as a step towards porting the application to .net. This is not a recommended way to create managed threads in an application. If we run this application after compiling it with /clr it seems to leak thread handles. If an application seems to leak resources, first thing to do is to confirm that resource indeed is being leaked and to find out what resource the application is leaking.
STEP 1. Use process explorer/performance monitor to see what type of resource application is leaking. For memory one should look at virtual bytes and private bytes counter in process monitor , for managed memory one should look at Total # bytes in GC heap. For handles process explorer gives a good idea as to which type of handles application is leaking. It was found that it looked like application was leaking thread handles.
STEP 2. Once it is established that application is leaking handles and we know which type of handles application is leaking. Next step is to enable stack trace for opening and closing of handles. Htrace extension command comes in handy to enable stacktrace for opening and closing of handles.Stack traces are generated by ntdll , htrace just enables it. Launch the application under windbg and use !htrace –enable to enable stack tracing for handles.
0:002> !htrace -enable
Handle tracing enabled.
Handle tracing information snapshot successfully taken.
STEP 3. After running the application for some time , when you are sure that enough of the handles have been leaked , run !htrace –diff , Output of this command , is stack trace of handles that have been opened but not yet closed. In this case it showed us stack trace of thread creation. We are creating threads using Win32 API createthread and then running managed code on them. In order to proceed further ,since we take help of runtime to manage system resources for us , it is a good idea to have an understanding of how runtime manages resources for us, in this case , resource is threads.
There are two types of threads that CLR needs to keep track of , one which start execution from within managed code by calling Thread.Start , other type are threads which were created in native world but which later execute managed code . CLR needs to maintain information about either of these threads in-order to performs operations like GC, CAS security checks , etc. In order to see a list of threads which belong to either of these categories , and some of the information that CLR maintains about them, run the following command after loading sos.dll.
0:004> !threads
ThreadCount: 183
UnstartedThread: 0
BackgroundThread: 182
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
0 1 1fc6c 0028b500 6020 Enabled 02c750e8:02c75fe8 00255be0 0 STA
2 2 21640 00294730 b220 Enabled 00000000:00000000 00255be0 0 MTA (Finalizer)
3 3 abcc 002d01a8 220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX 4 abb0 002d1888 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX 5 b7ec 002d2070 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX 6 2177c 002d2880 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX 7 abc0 002d30b8 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX 8 ab98 002d38f0 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX 9 216e8 002d4128 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX a 1fb78 002d4960 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX b 216b8 002d5198 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX c 216f8 002d59d0 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
XXXX d 216c8 002d6208 80010220 Enabled 00000000:00000000 00255be0 0 Ukn
</snip>
You may want to run !help threads in order to see what exactly all these columns indicate. ThreadObj is the address of the data-structure , in which CLR keeps the necessary information about these threads. PreemptiveGC indicates whether the thread can be preempted by the any other thread on which has to perform a garbage collection . As a developer it is not required to understand how CLR maintains this information and it is an implementation detail that can change over time but having a fair understanding of it helps in debugging.
XXX in first column indicates that the thread is a dead thread ,ie it has been detached from its corresponding os thread . There were a lot of such dead threads in output of !threads. Note that these dead threads are cleaned up at a later time by finalizer thread of the application.
In this particular case , GC was not yet triggered and finalizer thread was not getting invoked and hence these dead thread objects were getting accumulated giving an impression that thread handles are being leaked . In order to prevent dead threads in this test scenario we periodically called Gc.Collect. It is important to realize that GC is generational and self tuning in nature and hence it is not a good idea to call GC.Collect in code , however at times it may be justified to call GC.Colect , this blog talks about it in detail.
In order to prevent dead threads in this test scenario we periodically called GC.Collect. However for creating worker threads in managed applications we have following better options :
1)ThreadPool.QueueUserWorkItem.
2)CCR’s dispatcher object.
-Manish Jawa
|
-
This article talks about marshaling structures using PInvoke which has a pointer to an array of another structure as a member.
When calling native functions in a managed application, a frequent question that comes to mind is how to marshal a nested complicated structure. Recently I came across a scenario where we have to marshal a structure which contains a pointer to an array of another structure as a member.
When doing PInvoke, it’s essential to know what is the layout of the native structure and how it can be marshaled. During marshaling, one of the most important steps is converting unmanaged types to managed types. You can use the below link for conversion table to match basic data types.
http://msdn.microsoft.com/en-us/library/aa719104(VS.71).aspx#docum_topic3
Here is an example of C struct definitions that need to be marshaled and a function to marshal them
// NativeDLL.cpp : Defines the exported functions for the DLL application.
#include "stdafx.h"
#include <strsafe.h>
#include <stdio.h>
#define _EXPORTS_API __declspec(dllexport)
#define HASHSIZE 151
typedef struct CELL
{
char *name;
char *label;
char *fmt;
double amount;
unsigned int precision;
unsigned int flags;
unsigned int rank;
} CELL;
typedef struct CELLTABLE
{
CELL *table[HASHSIZE];
void *userData; // reserved for future use
} CELLTABLE;
extern "C" _EXPORTS_API void NativeFunction(CELLTABLE * pFinance)
{
char buffer[1024];
for ( int ix = 0 ; pFinance->table[ix] != NULL ; ix++ )
{
CELL * pCell = pFinance->table[ix] ;
StringCbPrintf(buffer, sizeof(buffer), "%d:\n%f\n%s\n%u\n%u\n%u\n",ix, pCell->amount, pCell->name, pCell->precision, pCell->flags, pCell->rank);
MessageBox(NULL,buffer,"Test",MB_OK);
}
}
Note: Char set of native application is set to Multi-Byte Character Set.
Managed Structure equivalent to Native Structure:
//MyManagedApp.cs file
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace MyManagedAPP
{
public class MyClass
{
private const int HASHSIZE = 151;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class CELL
{
public string name;
public string label;
public string fmt;
public double amount;
public UInt32 precision;
public UInt32 flags;
public UInt32 rank;
};
[StructLayout(LayoutKind.Sequential)]
public class CELLTABLE
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = HASHSIZE)]
public IntPtr[] table;
public IntPtr userData;
}
[DllImport("PInvokeDll.DLL")]
public static extern void NativeFunction(IntPtr finance);
public void Test()
{
CELL[] cells = new CELL[HASHSIZE];
cells[0] = new CELL();
cells[0].amount = 123.45;
cells[0].name = "First Record";
cells[0].precision = 50;
cells[0].flags = 0;
cells[0].rank = 0;
cells[1] = new CELL();
cells[1].amount = 9876.54;
cells[1].name = "Second Record";
cells[1].precision = 3;
cells[1].flags = 0;
cells[1].rank = 1;
cells[2] = new CELL();
cells[2].amount = 24680.1357;
cells[2].name = "Third Record";
cells[2].precision = 2;
cells[2].flags = 255;
cells[2].rank = 4;
CELLTABLE ct = new CELLTABLE();
ct.table = new IntPtr[HASHSIZE];
for (int ix = 0; ix < HASHSIZE && cells[ix] != null; ix++)
{
int nSizeCell = Marshal.SizeOf(cells[ix]);
ct.table[ix] = Marshal.AllocHGlobal(nSizeCell);
Marshal.StructureToPtr(cells[ix], ct.table[ix], false);
}
int nSizeTable = Marshal.SizeOf(ct);
IntPtr pCellTable = Marshal.AllocHGlobal(nSizeTable);
Marshal.StructureToPtr(ct, pCellTable, false);
NativeFunction(pCellTable);
}
static void Main()
{
MyClass md = new MyClass();
md.Test();
}
}
}
Note: Marshaling the structure in C#, it’s essential to set CharSet to Ansi if the C++ application expects an Ansi type string. If it’s not set to Ansi, output can be unexpected (Mostly first character of string passed). If you don't explicitly set the CharSet property, then its default is CharSet.Ansi. If the native application is built with UNICODE defined, the CharSet should be set to CharSet.Unicode.
Jyoti Patel
Developer Support VC++ and C#
|
-
After migrating to Visual Studio 2008 SP1 and building the application, the manifest file of application and all the dependent DLL’s contain RTM version.
In Visual Studio 2008 SP1, by design, the manifest for the created exe links it to the RTM version of the CRT.
In Visual Studio 2005 SP1, by design, the manifest is linked to the current version of the CRT. This was a design change that happened in Visual Studio 2008; by default, it always links to the RTM version of the CRT (no matter if we are using SP1 to build it).
In order to link with SP1 version, rebuild the application with _BIND_TO_CURRENT_MFC_VERSION=1, _BIND_TO_CURRENT_CRT_VERSION=1, and/or _BIND_TO_CURRENT_ATL_VERSION=1 in stdafx.h.
http://blogs.msdn.com/vcblog/archive/2008/05/15/vc-runtime-binding.aspx
Recently, I came across the scenario where building an ATL project with _BIND_TO_CURRENT_MFC_VERSION=1, _BIND_TO_CURRENT_CRT_VERSION=1, _BIND_TO_CURRENT_ATL_VERSION=1 in stdafx.h, the manifest file contains dependency of both RTM and SP1 version of CRuntime library.
The manifest file will look like:
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.MFC" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.ATL" version="9.0.30729.1" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.VC90.CRT" version="9.0.21022.8" processorArchitecture="x86" publicKeyToken="1fc8b3b9a1e18e3b"></assemblyIdentity>
</dependentAssembly>
If you’re seeing multiple manifest entries, the easiest way I’ve found to diagnose this is to do something like this:
1. Set LINK_REPRO=c:\repro [or any other directory]
2. Build your app.
3. All objs and libs that your application depends on are present in c:\repro.
4. In c:\repro, do “link /dump /directives *.lib *.obj > direct.txt”
5. Open up ‘direct.txt’ in your any text editor and search for the “bad” version number (9.0.21022.8). That will tell you which object or library is responsible for the RTM dependency in your manifest.
This may be caused by one of the following:
1. Inconsistent use of ‘BIND_TO_CURRENT_VERSION’ in your app.
2. An external static library that you are using which wants RTM, while the rest of your objects want SP1.
In my scenario, it was the .obj file that corresponds to the _i.c file which is auto generated file after compiling an IDL file. As IDL files are compiled before stdafx.h, and if IDL file is referring to certain header files which can introduce dependency to RTM version of CRT, finding such dependency is very difficult. Also we cannot add _BIND_TO_CURRENT_MFC_VERSION=1, _BIND_TO_CURRENT_CRT_VERSION=1, _BIND_TO_CURRENT_ATL_VERSION=1 _i.c to the _i.h file, as they are auto-generated files.
Work Around:
To work around this issue, we can use preprocessor definitions and add _BIND_TO_CURRENT_VCLIBS_VERSION
1. Right Click _i.c file.
2. Go to Configuration Property.
3. Select C/C++ Preprocessor
4. Add _BIND_TO_CURRENT_VCLIBS_VERSION to Preprocessor Definitions.
Jyoti Patel
Developer Support VC++ and C#
|
-
C++ code which contains an STL vector containing types derived from the_com_ptr_t COM smart pointer class and using the std:sort() function to sort interface pointers may give unexpected sort output._com_ptr_t COM smart pointer derived types are commonly created when using the '#import' keyword in C++ to import a COM type library. For example:
#import "msxml4.dll"
The _com_ptr_t class implementation overrides the reference (&) operator and returns a null interface pointer. This is in conflict with STL reference operator rules and may result in incorrect sorting order when std::sort () is called.
Sample Code:
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
// Create smart pointer wrappers for msxml com object
#import "msxml4.dll"
// Helper Function to create XML
_bstr_t CreateXML()
{
int nums[] = {1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,4,5,6,7,8,9};
std::stringstream tempstr;
tempstr << "<NodesCol>" << std::endl;
for(int i=0; i<34 ;i++)
tempstr << "\t<Node><NodeNum>" << nums[i] << "</NodeNum></Node>" << std::endl;
tempstr << "</NodesCol>" << std::endl;
return static_cast<_bstr_t>(tempstr.str().c_str());
}
// Compare Function for std::swap
bool myCompare(MSXML2::IXMLDOMNodePtr& node1, MSXML2::IXMLDOMNodePtr& node2)
{
int nNode1No(-1),nNode2No(-1);
if( ( NULL == node1.GetInterfacePtr() ) || ( NULL == node2.GetInterfacePtr() ) )
return false;
_stscanf_s( node1->text, _T("%d"), &nNode1No );
_stscanf_s( node2->text, _T("%d"), &nNode2No );
if( nNode1No < nNode2No )
return true;
return false;
};
int _tmain(int argc, _TCHAR* argv[])
{
// A vector of IXMLDOMNode smart pointers
std::vector<MSXML2::IXMLDOMNodePtr> vecNodesList;
CoInitialize( NULL );
// Create and Load the XML document
MSXML2::IXMLDOMDocumentPtr docPtr;
docPtr.CreateInstance(__uuidof(MSXML2::DOMDocument40));
docPtr->loadXML(CreateXML());
MSXML2::IXMLDOMNodeListPtr nodes = docPtr->selectNodes( _T("NodesCol/Node/NodeNum") );
std::cout << "Before Sorting Nodes" << std::endl;
// Initialize the vector of smart pointers
for( int i = 0; i < nodes->length; i++ )
{
int nNodeNo = -1;
vecNodesList.push_back( nodes->item[i] );
std::cout << nodes->item[i]->text << std::endl;
}
// Sort the vector of smart pointers
std::sort( vecNodesList.begin(), vecNodesList.end(), myCompare );
// Display sorted results
std::cout << "\nAfter Sorting Nodes\n" << std::endl;
std::vector<MSXML2::IXMLDOMNodePtr>::iterator iter;
for(iter = vecNodesList.begin(); iter!=vecNodesList.end();iter++)
{
if(NULL != (*iter).GetInterfacePtr())
std::cout << (*iter)->text << std::endl;
else
std::cout << "-1" << std::endl;
*iter = NULL; // Release smart pointer before destroying vector;
}
// Release smart pointers before CoUninitialize
nodes = NULL;
docPtr = NULL;
CoUninitialize( );
return 0;
}
The sort() method uses the utility function std::swap():
template<class _Ty> inline
void swap(_Ty& _Left, _Ty& _Right)
{
// exchange values stored at _Left and _Right
if (&_Left != &_Right)
{
// different, worth swapping
_Ty _Tmp = _Left;
_Left = _Right;
_Right = _Tmp;
}
}
The "if (&_Left != &_Right)" line of code calls the overloaded operator & from _com_ptr_t comip.h header file, which returns a NULL interface pointer:
Interface** operator&() throw()
{
_Release();
m_pInterface = NULL;
return &m_pInterface;
}
This is a known issue.
All STL containers, including vector, forbid their elements from overloading operator&(). (This is C++ Specification 03 23.1/3 "The type of objects stored in these components must meet the requirements of CopyConstructible types (20.1.3) and the additional requirements of Assignable types", and 20.1.3/1 requires &t to have type T * and denote the address of t.)
The best solution is to never overload operator&() when using STL containers. If that is not possible (e.g. because the operator overload is coming from code that you don't control), an alternative is to wrap the class in another wrapper class that doesn't overload operator&() which you can then add to the vector or other container class. CAdapt is one example of such a class.
Fixed Sample Code example using CAdapt
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <algorithm>
#include <sstream>
#include "atlcomcli.h"
// Create smart pointer wrappers for msxml com object
#import "msxml4.dll"
// Helper Function to create XML
_bstr_t CreateXML()
{
int nums[] = {1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,24,25,26,27,28,29,3,30,31,32,33,34,4,5,6,7,8,9};
std::stringstream tempstr;
tempstr << "<NodesCol>" << std::endl;
for(int i=0; i<34 ;i++)
tempstr << "\t<Node><NodeNum>" << nums[i] << "</NodeNum></Node>" << std::endl;
tempstr << "</NodesCol>" << std::endl;
return static_cast<_bstr_t>(tempstr.str().c_str());
}
// Compare Function for std::swap
bool myCompare(MSXML2::IXMLDOMNodePtr& node1, MSXML2::IXMLDOMNodePtr& node2)
{
int nNode1No(-1),nNode2No(-1);
if( ( NULL == node1.GetInterfacePtr() ) || ( NULL == node2.GetInterfacePtr() ) )
return false;
_stscanf_s( node1->text, _T("%d"), &nNode1No );
_stscanf_s( node2->text, _T("%d"), &nNode2No );
if( nNode1No < nNode2No )
return true;
return false;
};
int _tmain(int argc, _TCHAR* argv[])
{
// A vector of IXMLDOMNode smart pointers
std::vector<CAdapt<MSXML2::IXMLDOMNodePtr>> vecNodesList;
CoInitialize( NULL );
// Create and Load the XML document
MSXML2::IXMLDOMDocumentPtr docPtr;
docPtr.CreateInstance(__uuidof(MSXML2::DOMDocument40));
docPtr->loadXML(CreateXML());
MSXML2::IXMLDOMNodeListPtr nodes = docPtr->selectNodes( _T("NodesCol/Node/NodeNum") );
std::cout << "Before Sorting Nodes" << std::endl;
// Initialize the vector of smart pointers
for( int i = 0; i < nodes->length; i++ )
{
int nNodeNo = -1;
vecNodesList.push_back( nodes->item[i] );
std::cout << nodes->item[i]->text << std::endl;
}
// Sort the vector of smart pointers
std::sort( vecNodesList.begin(), vecNodesList.end(), myCompare );
// Display sorted results
std::cout << "\nAfter Sorting Nodes\n" << std::endl;
std::vector<CAdapt<MSXML2::IXMLDOMNodePtr>>::iterator iter;
for(iter = vecNodesList.begin(); iter!=vecNodesList.end();iter++)
{
if(NULL != (*iter).m_T.GetInterfacePtr())
std::cout << (*iter).m_T->text << std::endl;
else
std::cout << "-1" << std::endl;
*iter = NULL; // Release smart pointer before destroying vector;
}
// Release smart pointers before CoUninitialize
nodes = NULL;
docPtr = NULL;
CoUninitialize( );
return 0;
}
Jyoti Patel
Developer Support VC++ and C#
|
-
This article focuses on troubleshooting a heap corruption caused by writing into the next allocated block. Heap corruption comes into notice when the overridden memory is accessed, leaving in a state where it becomes hard to figure out the original code that is overriding the memory.
In this scenario, call stack could look like
0:000> kp
ChildEBP RetAddr
0:000> kb
ChildEBP RetAddr Args to Child
0012c2f4 7c91a3bc 01660000 01d0b000 0012c320 ntdll!RtlpFindAndCommitPages+0x116
0012c32c 7c911917 01660000 00000300 0012eb04 ntdll!RtlpExtendHeap+0xa6
0012c55c 00f4103e 01660000 00000000 000002f4 ntdll!RtlAllocateHeap+0x623
0012c578 00f4fd76 000002f4 5a7fcbb3 0012eb04 msvcr90d!_heap_alloc_base+0x5e
0012c5c0 00f4fb2f 000002d0 00000001 00000000 msvcr90d!_nh_malloc_dbg+0x2c6
0012c5e0 00f4fadc 000002d0 00000000 00000001 msvcr90d!_nh_malloc_dbg+0x7f
0012c608 00f5b25b 000002d0 00000000 00000001 msvcr90d!_nh_malloc_dbg+0x2c
0012c628 00f3d691 000002d0 cccccccc cccccccc msvcr90d!malloc+0x1b
0012c644 007ab543 000002d0 cccccccc 0012c690 msvcr90d!operator new+0x11
0012eb14 0073b367 MyApp!DummyFun
01660000 is the heap used by msvcr90d.dll in this case
0:000> !heap -av 01660000
Index Address Name Debugging options enabled
12: 01660000
Segment at 01660000 to 01670000 (00010000 bytes committed)
Segment at 01d00000 to 01e00000 (00011000 bytes committed)
Flags: 00001002
ForceFlags: 00000000
Granularity: 8 bytes
Segment Reserve: 00200000
Segment Commit: 00002000
DeCommit Block Thres: 00000200
DeCommit Total Thres: 00002000
Total Free Size: 00000089
-
-
-
0166e8e0: 00048 . 00300 [01] - busy (2f4)
0166ebe0: 00300 . 00048 [01] - busy (3c)
0166ec28: 00048 . 00048 [01] - busy (3c)
0166ec70: 00048 . 00048 [01] - busy (3c)
0166ecb8: 00048 . 00300 [01] - busy (2f4)
0166efb8: 00300 . 00030 [01] - busy (25)
0166efe8: 281a8 . 17188 [01] - busy (1717c)
##CORRUPTION FOUND at 0x0166EFE8
PreviousSize field does not match Size field in previous entry
Entry->PreviousSize == 0x2e31
PreviousEntry->Size == 0x60
##The above errors were found in segment at 0x01660640
Looking at the 0166efe8 memory block, it is clear that the current size of previous block ‘00030’ is not matching with the previous size ‘281a8’. This infers that 0166efe8 is corrupted and previous size field is overridden by some data.
_ CrtMemBlockHeader is the block header structure used to store the debug heap's bookkeeping information, for more information http://msdn.microsoft.com/en-us/library/bebs9zyz.aspx
0:000> dt msvcr90d!_CrtMemBlockHeader 0166efb8+8
+0x000 pBlockHeaderNext : 0x0166ecc0 _CrtMemBlockHeader
+0x004 pBlockHeaderPrev : 0x0166eff0 _CrtMemBlockHeader
+0x008 szFileName : (null)
+0x00c nLine : 0
+0x010 nDataSize : 1
+0x014 nBlockUse : 1
+0x018 lRequest : 3686
+0x01c gap : [4] "???"
0:000> dt msvcr90d!_CrtMemBlockHeader 0166efe8+8
+0x000 pBlockHeaderNext : 0x0166efc0 _CrtMemBlockHeader
+0x004 pBlockHeaderPrev : 0x0166f028 _CrtMemBlockHeader
+0x008 szFileName : (null)
+0x00c nLine : 0
+0x010 nDataSize : 8
+0x014 nBlockUse : 1
+0x018 lRequest : 3687
+0x01c gap : [4] "???"
The reason behind adding 8 bytes is that, each memory block in a given segment has 8 bytes metadata associated with it. The metadata is used by the heap manager to effectively manage the heap blocks within a segment.
lRequest is the allocation count. The above msvcr90d!_CrtMemBlockHeader structures show that they are adjacent allocations (3686 & 3687). If the corruption is in the same spot and allocation numbers are sequential, _CrtSetBreakAlloc(3686) can be used to determine the code that is overriding the memory 0166efe8 and how that block is being corrupted.
Call _CrtSetBreakAlloc(3686) at the code startup for ex: main(), start debugging(F5) the code and this should break the debugger when 0166efb8 memory block is allocated. Once the debugger breaks, code could be reviewed for two things
1) How many bytes are allocated
2) Are there any chances that suspected buffer could overflow.
For Example:
char *ptr = new char;
strcpy(ptr, source); //where ‘source’ variable holds more than one character.
Please make a note that 3686 & 3687 may be different when its debugged second time, but it’s very rare case.
|
-
Robots will soon be in action moving around you, to make your life easier. This is not the tagline from the Hollywood movie “I, Robot” but is a real life fact! Yes, this is a dream come true. In the third week of November 2008, we proudly introduced Microsoft® Robotics Developer Studio 2008 (RDS), which is a Windows-based environment for hobbyist, academic and commercial developers to create robotics applications for a variety of hardware platforms. This is definitely a boon to all those who wish to program their Robots.
RDS includes a lightweight REST-style, service-oriented runtime, a set of visual authoring and simulation tools, as well as tutorials and sample code to help get started. RDS primarily helps the hobbyist to focus more on programming robots rather than just focusing on building a robotic framework.
The great news is that even Non-Programmers can create robot applications using Visual Programming Environment. RDS features Microsoft Visual Programming Language, for creating and debugging robot applications, web-based and windows-based interfaces, 3D simulation. It allows you to program your robot in a number of languages including C# and Visual Basic .NET, JScript, and IronPython.
Underneath, it is based on very powerful concurrent library implementation called “Concurrency and Coordination Runtime (CCR) and Decentralized Software Services (DSS)”. The CCR and DSS application model makes it simple to achieve concurrency, coordinate and responds to, a robot’s state using a Web browser or Windows-based application from any machine thus making it distributed.

Figure 1: A robot’s eye showing the images from DSS using a Web browser
Even if you do not have a physical robot, RDS allows simulating robotics applications in 3D physics-based virtual environments using realistic 3D simulated models. Microsoft Visual Simulation Environment (VSE) includes AGEIA™ PhysX™ Technology from AGEIA Technologies Inc., a pioneer in hardware-accelerated physics, enabling real-world physics simulation for robot models. RDS supports almost all robot platforms including Aldebaran, CoroWare CoroBot, Lego Mindstroms NXT, iRobot Create, KUKA Robots, Kondo, etc.

Figure 2: An iRobot Create simulation sample in Visual Simulation Environment
To get you started, robotics samples and tutorials cover how to get started building robotics applications.
They include:
Setting Up Your Hardware: RDS can be used with a variety of robot platforms. Follow the instructions for setting up your hardware and its means of communication to your PC running Windows XP or Windows Vista.
Basic Robotics Tutorials: The basic robotics tutorials help the user get started writing the very first service for a robot. The tutorials guide the user from getting input from a single sensor, to controlling an actuator, and to being able to write a "drive-by-wire" application where the robot can be moved around.
Now that you are ready to drive your robot, “What will you do with yours?”
- Gaurav Patole.
Developer Support VC++ and C#
|
|
|
|