views:

203

answers:

3

I have a Visual Studio 2008 C++ Windows Mobile 6 application where I'm using a FindFirst() / FindNext() style API to get a collection of items. I do not know how many items will be in the list ahead of time. So, I would like to dynamically allocate an array for these items.

Normally, I would use a std::vector<>, but, for other reasons, that's not an option for this application. So, I'm using LocalAlloc() and LocalReAlloc().

What I'm not clear on is if this memory should be marked fixed or moveable. The application runs fine either way. I'm just wondering what's 'correct'.

int count = 0;
INFO_STRUCT* info = ( INFO_STRUCT* )LocalAlloc( LHND, sizeof( INFO_STRUCT ) );
while( S_OK == GetInfo( &info[ count ] )
{
    ++count;
    info = ( INFO_STRUCT* )LocalRealloc( info, sizeof( INFO_STRUCT ) * ( count + 1 ), LHND );
}

if( count > 0 )
{
    // use the data in some interesting way...
}

LocalFree( info );

Thanks, PaulH


Edit: Responders are (not unreasonably) getting hung up on the use of LocalAlloc() over other better options. So I will provide more context.

This chunk of code is being executed from within a RAPI invokable DLL. So, in that context, it looks more like this:

FOO_API int RapiInvokable_Foo( DWORD /*cbInput*/, 
                               BYTE* /*pInput*/,
                               DWORD* pcbOutput, 
                               BYTE** ppOutput,
                               IRAPIStream* /*pStream*/ )
{
    int count = 0;
    INFO_STRUCT* info = ( INFO_STRUCT* )LocalAlloc( LPTR, sizeof( INFO_STRUCT ) );
    while( S_OK == GetInfo( &info[ count ] )
    {
        ++count;
        info = ( INFO_STRUCT* )LocalRealloc( info, sizeof( INFO_STRUCT ) * ( count + 1 ), LHND );
    }

    *ppOutput = ( BYTE* )info;
    *pcbOutput = sizeof( INFO_STRUCT ) * ( count + 1 );
    return S_OK;
}

From the CeRapiInvoke() documentation:

An application should allocate memory for the pInput parameter with the LocalAlloc function. The caller is responsible for freeing pInput. The system allocates memory for the ppOutput parameter. When the application is completed with the buffer, it should free the memory with the LocalFree function.

+1  A: 

According to the MSDN there's no benefit to using the Local functions: http://msdn.microsoft.com/en-us/library/aa366723(VS.85).aspx. Why not just use regular malloc and free or new and delete. They're probably your best bet.

miked
True for big windows, but the documentation for Windows Mobile doesn't say the same thing: http://msdn.microsoft.com/en-us/library/bb202785.aspx
PaulH
@PaulH: Actually that link confirms what miked is saying. `LocalAlloc is equivalent to HeapAlloc(GetProcessHeap, …).`. The `Local` functions are a holdover from the days of 16 bit windows, and should be avoided when possible. You should use either the CRT provided functions, or the Heap functions instead.
Billy ONeal
@Billy ONeal - see my edit above.
PaulH
@PaulH: I would trust the LocalAlloc documentation over the CeRapiInvoke documentation.
Billy ONeal
@Billy ONeal - Right. My concern is about how that memory is freed. `LocalAlloc()` is only allocating memory on the device. I assume the RAPI framework is freeing that memory internally with `LocalFree()` on the device. So, if I allocate it with `new` or `malloc` can it be safely freed with `LocalFree()`?
PaulH
Absolutely not, Paul. Things allocated with `new` should *only* be released with `delete`. Likewise, `malloc` and `free` form a pair, as do `LocalAlloc` and `LocalFree`. Never mix and match.
Rob Kennedy
+1  A: 

From what I can tell, LHND is not even a valid flag to use in the Windows Mobile version of LocalAlloc.

When you call the non-mobile version of LocalAlloc with LMEM_MOVEABLE, the return type is not INFO_STRUCT*. The return type is HLOCAL — a handle to the memory that you've allocated. It's not a pointer itself, so it is incorrect to dereference it like a pointer. To get a pointer, you need to use LocalLock to tell the OS that it mustn't move the memory around for the time being.

Consider what MSDN says about movable memory:

The movable-memory flags LHND, LMEM_MOVABLE, and NONZEROLHND add unnecessary overhead and require locking to be used safely. They should be avoided unless documentation specifically states that they should be used.

So, if you really must use LocalAlloc, then allocate fixed memory, not movable. That's the same behavior you'd get from calling plain old malloc.

The LMEM_MOVEABLE flag means something different with LocalReAlloc. Whereas with LocalAlloc it specifies whether the memory is locked, with LocalReAlloc it specifies whether the function is allowed to move the memory in order to satisfy a request for a larger block of memory. If you don't include that flag with LocalReAlloc, then the function is restricted to changing the block's size in-place. If there's no room there, then the function will fail, even if there are larger blocks of memory available elsewhere in the heap.

To get the effect of malloc, call LocalAlloc(LMEM_FIXED). To get the effect of realloc, call LocalReAlloc(LMEM_MOVEABLE). Include LMEM_ZEROINIT in either case if you wish.

One thing to take away from all this seems to be that you should only use the flags that the documentation specifically says you can use for each function. For LocalAlloc, it doesn't mention LMEM_MOVEABLE, and for LocalReAlloc, it doesn't mention LPTR.

Rob Kennedy
If I change `LHND` to `LPTR`, the `LocalRealloc()` return NULL (contrary to what I said earlier about it working either way). Any idea on why?
PaulH
Nevermind. LPTR isn't defined for `LocalRealloc()` the way LHND isn't defined for `LocalAlloc()`, so if I use LHND for `LocalRealloc()` and LPTR for `LocalAlloc()`, it works. But, now I'm back to needing the `LocalLock()` functions you mentioned.
PaulH
`LHND` isn't defined for *either* function in Windows Mobile. It doesn't look like `LocalLock` is defined, either.
Rob Kennedy
@Rob Kennedy - LHND is a combination LMEM_MOVEABLE and LMEM_ZEROINIT. both of which are defined for Windows Mobile.
PaulH
But the Windows Mobile documentation doesn't say anything about LHND. LHND is for when you want the memory-allocation function to return a movable *handle* — that's what the *HND* name indicates. But Windows Mobile doesn't support memory handles here. In the context where LMEM_MOVEABLE is supported, it doesn't mean "return a handle." It means "you're allowed to move this for re-allocation." Even though LHND's *numeric value* happens to what you want, its name tells us it's meant for something else, which `LocalReAlloc` can't provide on Windows Mobile.
Rob Kennedy
+1  A: 

From the page you link to n your comment to miked's response

"In the linear Windows Embedded CE API environment, there is no difference between the local heap and the global heap. LocalAlloc is equivalent to HeapAlloc(GetProcessHeap, …)."

Clicking the link to go look at CE 6.0 (or Win Mobile 6.0) and you see the same thing.

In fact neww/malloc etc all boild down to a HeapAlloc internally anyway. So I'm really not sure what the issue with just using the C/C++ runtime is ...

Edit:

From this Dr Dobbs link it says:

Using LocalAlloc and LocalReAlloc in this manner exposed a quirk in the Windows CE implementation. In the past, when I used them in a loop like this on NT, I used LPTR as the flag to LocalReAlloc, which allowed me to allocate up to about 512 KB. On Windows CE, this same scenario never allocates more than one KB. This size limitation it too small even for our process list. Changing the LocalReAlloc flag to LMEM_MOVEABLE works on both NT and CE and allows memory up to the size of the biggest free block to be allocated since the location can be moved after the call to LocalAlloc rather than having to be expanded in place.

Goz
@goz - see my edit above.
PaulH
Argh .. bloody microsoft. Updated my answer!
Goz