views:

160

answers:

5

I've got some code that worked fine under Delphi 2007 but breaks under D2010. It involves passing in a string, converting it to a PWideChar (specifically, a WideString pointer, not a UnicodeString pointer), doing some processing, and then calling SysFreeString on it. It works fine until a blank string is passed in, then SysFreeString breaks. It calls a bunch of things that end up raising an Int 3 breakpoint inside NTDLL.DLL. Continuing past this point results in

Project raised exception class $C0000005 with message 'access violation at 0x7747206e: read of address 0x539b8dba'.

Which, if you look closely, is not the standard Access Violation message.

The top of the stack trace when it hits the Int 3 looks like this:

:774e475d ; ntdll.dll
:774afad0 ; ntdll.dll
:774e5de9 ; ntdll.dll
:774a6dff ; ntdll.dll
:76fc1075 ; C:\Windows\system32\ole32.dll
:770e443a ; C:\Windows\system32\oleaut32.dll
:770e3ea3 oleaut32.SysFreeString + 0x4a

Does anyone have any idea what's going on here?

Edit (from the comments):

This isn't a WideString, though. It's a PWideChar generated by StringToOleStr, and there are no double-free errors when a non-blank string is passed in. Unfortunately, I can't really post a code sample because this is a third-party component that's under copyright. (And I can't ask them for support because it's no longer supported. Basically, the whole thing's one big mess.)

+1  A: 

It is hard to diagnose without seeing your actual code, however WideString automatically calls SysFreeString() when it goes out of scope. It sounds like your code may be making a second call to SysFreeString() on memory that has already been freed. WideString itself has not changed at all between D2007 and D2010, but other aspects of Delphi's string handling have. Maybe you are not managing the strings correctly. Can you please show your actual code?

Remy Lebeau - TeamB
This isn't a WideString, though. It's a PWideChar generated by StringToOleStr, and there are no double-free errors when a non-blank string is passed in. Unfortunately, I can't really post a code sample because this is a third-party component that's under copyright. (And I can't ask them for support because it's no longer supported. Basically, the whole thing's one big mess.)
Mason Wheeler
Then you really need to show a code snippet that shows the problem in action. If you cannot show the component's code, then please provide a separate example that reproduces the same failure.
Remy Lebeau - TeamB
+3  A: 

I'm going to try psychic debugging. You've got some kind of heap corruption in your application and SysFreeString is the unfortunate victim (it's hard to tell without OS symbols, you should probably install the MSFT symbol packages for your OS).

Try enabling application verifier (in particular pageheap) for your app and see if it crashes earlier.

Larry Osterman
A: 

+1 for Larry Osterman's answer.

Some Windows memory functions behave slightly different under debugger: if they detect some kind of misuse - they trigger breakpoint to notify debugger. So, basically, your code is doing something wrong.

You can install hooks on SysAllocString/SysFreeString and redirect them to your memory manager (which should be in full debug mode) to collect more info. Or you can just pass these calls through to original functions, installing only a filter, which watches for memory actions.

And you can install debug symbols to get more info too (I'm not sure if Delphi debugger can use it, but Process Explorer - can. You can connect it to your process and see call stack).

Alexander
A: 
Jeroen Pluimers
There is a call to SysFreeString() being done inside of StringToOleStr() itself (specifically, inside of System._WStrFromPWCharLen()), not at the end of Allocate() (which does not implicitally call anything after StringToOleStr() exits). That call to SysFreeString() is not being passed the WideString/BSTR that StringToOleStr() allocates. It is being pass a temporary WideString/BSTR that SysFreeString() uses internally for its Result, and that temporary is blank anyway so the SysFreeString() is effectively a no-op.
Remy Lebeau - TeamB
@Remy: You are right, my bad, I had my breakpoints one moved one line because of some cut/paste outside of the IDE.
Jeroen Pluimers
A: 

It can be different reasons of such kind of errors:

  1. You try to free with SysFreeString a memory which are allocated not with SysAllocString, but for example with CoTaskMemAlloc.
  2. You have heap correct.

Heap corruptions are difficult to localize. The function HeapSetInformation can be very helpful. For example you can use

HeapSetInformation(NULL,HeapEnableTerminationOnCorruption,NULL,0);

Other good way is usage of HeapValidate function. For example you can define a function which verify all heaps pf the process (code in C, which can be easy rewritten in Delphi):

BOOL MyHeapValidate (void)
{
    HANDLE  hProcessHeaps[1024];
    DWORD   i;
    DWORD   dwNumberOfHeaps;
    BOOL    bSuccess = FALSE;

    dwNumberOfHeaps = GetProcessHeaps (sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0]),
                                       hProcessHeaps);
    if (dwNumberOfHeaps > sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0])) {
        MessageBox(NULL, TEXT("GetProcessHeaps()"),
                   TEXT("Error in MyHeapValidate()"), MB_OK);
        return FALSE;
    }

    for (i=0; i<dwNumberOfHeaps; i++) {
        bSuccess = HeapValidate (hProcessHeaps[i], 0, NULL);
        if (!bSuccess)
            return bSuccess;
    }

    return bSuccess;
}

The usage of this function can be like following:

void BadFunction(BSTR bstr)
{
    LPOLESTR psz = OLESTR("Test12");
    lstrcpy (bstr, psz);
}

int main()
{
    LPOLESTR psz = OLESTR("Test");

    BSTR bstr = SysAllocString (psz);

    // verify that before call of BadFunction() all process heaps are OK!
    if (!MyHeapValidate()) {
        _tprintf(TEXT("heap is corrupted after the step 1.\n"));
        return 1;
    }

    BadFunction(bstr);

    if (!MyHeapValidate()) {
        _tprintf(TEXT("heap is corrupted after the step 1.\n"));
        return 1;
    }
    SysFreeString (bstr);

    return 0;
}

With respect of inserting MyHeapValidate() in different suspected places you can very quickly local the place of corruption.

Oleg