Page 1 of 1

How to free memory allocated by external DLL

Posted: 28 Jan 2017, 14:27
by Sam_
I make a DllCall to an external 3rd-party DLL (zopfli), and one of the parameters calls for a pointer to a pointer to a block of memory. For reference, the function I am calling is ZopfliZlibCompress which is defined as:

Code: Select all

/*
Compresses according to the zlib specification and append the compressed
result to the output.

options: global program options
out: pointer to the dynamic output array to which the result is appended. Must
  be freed after use.
outsize: pointer to the dynamic output array size.
*/
void ZopfliZlibCompress(const ZopfliOptions* options,
                        const unsigned char* in, size_t insize,
                        unsigned char** out, size_t* outsize);
where 'options' is a structure containing 6 Integers (see below).

My code looks like:

Code: Select all

VarSetCapacity(options,24,0)
	NumPut(0,&options,0,"Int")							; verbose
	NumPut(0,&options,4,"Int")							; verbose_more
	NumPut(Settings.zopfliIterations,&options,8,"Int")	; numiterations
	NumPut(1,&options,12,"Int")							; blocksplitting
	NumPut(0,&options,16,"Int")							; blocksplittinglast
	NumPut(15,&options,20,"Int")						; blocksplittingmax
insize:=this.Stats.FileSize
VarSetCapacity(out,DllCall(PS_DirArch "\zlib1.dll\compressBound","UInt",insize,"Cdecl"))
out:=0
outsize:=0
VarSetCapacity(outptr,A_PtrSize)	; ZopfliZlibCompress function wants a pointer to a pointer to the first byte of the "out" variable.
NumPut(&out,outptr,0,"ptr")
DllCall(PS_DirArch "\libzopfli.dll\ZopfliZlibCompress","ptr",&options,"ptr",this.GetAddress("Raw"),"UInt",insize,"ptr",&outptr,"UIntP",outsize,"Cdecl")
OffsetToOut:=NumGet(outptr,0,"ptr")
;Then I read the data at 'OffsetToOut' of size 'outsize' using a wrapper around NumGet()
Now here is my real question: the block of memory located at 'OffsetToOut' was allocated by the external DLL not AHK, so presumably I have to free it manually when I am done with it (code comments in the quoted function definition claim "Must be freed after use."). I don't have a variable name for the block of memory, just a pointer to it and its size. How do I free it?

TIA,
Sam.

Re: How to free memory allocated by external DLL

Posted: 29 Jan 2017, 13:25
by qwerty12
From your link, it uses malloc, so look at the import table of libzopfli.dll (assuming your copy of libzopfli.dll is not statically linked to a CRT) and then DllCall free from the same DLL it gets its malloc from.

Re: How to free memory allocated by external DLL

Posted: 30 Jan 2017, 14:31
by Sam_
qwerty12 wrote:From your link, it uses malloc, so look at the import table of libzopfli.dll (assuming your copy of libzopfli.dll is not statically linked to a CRT) and then DllCall free from the same DLL it gets its malloc from.
That was the clue I needed, thanks! I had tried free before but it wasn't found in the AHK default DLLs and I couldn't figure out the name of the dll free would be in from its documentation. Looking at the imports of libzopfli.dll, malloc and free are called from msvcrt.dll which is in my system path. I call

Code: Select all

DllCall("msvcrt.dll\free","ptr",OffsetToOut,"Cdecl")]
and it does not throw an exception, but is there a way to tell if it actually worked as desired?

In my above code, should I be calling

Code: Select all

DllCall("msvcrt.dll\free","ptr",&outptr,"Cdecl")
instead?

Re: How to free memory allocated by external DLL

Posted: 30 Jan 2017, 15:10
by qwerty12
Sam_ wrote:is there a way to tell if it actually worked as desired?
I talk out of a position of noobishness, but I don't have any ideas better than running ZopfliZlibCompress in a loop, watching the private memory usage of your AutoHotkey script go up and then seeing if the memory is freed when free is called on all the allocated memory (though I've read this might not be an accurate way of checking anyway - I don't know if HeapAlloc does this, but the memory might not actually be freed but the blocks might be set aside for later memory allocations to reuse for saving time). (To solve this, I used MinHook to intercept the smart card functions to determine what was actually being passed to them by my AutoHotkey script and the compiled C example I had, but I don't know if that's entirely applicable here or if it's the quickest way to check. As this is open source, a hooking library wouldn't be required, but in the C code, for lack of a better idea, you can check the address of the malloc pointer ZopfliZlibCompress returns and compare it with the one you're passing to free. But I'm going off on a tangent there)
In my above code, should I be calling

Code: Select all

DllCall("msvcrt.dll\free","ptr",&outptr,"Cdecl")
instead?
I'm sorry, but I was intentionally vague in my initial post to avoid answering specifically how you should use it because

Code: Select all

VarSetCapacity(out,DllCall(PS_DirArch "\zlib1.dll\compressBound","UInt",insize,"Cdecl"))
out:=0
outsize:=0
VarSetCapacity(outptr,A_PtrSize)	; ZopfliZlibCompress function wants a pointer to a pointer to the first byte of the "out" variable.
NumPut(&out,outptr,0,"ptr")
doesn't look right to me (ZopfliZlibCompress is allocating new memory based on the data passed to in so I don't think it really cares about what was there before), and yet given that I know absolutely nothing of Zopfli, it doesn't seem my place to say and I could be wrong, but nevertheless I don't know if this would work or if it's even correct, but I would personally attempt it like this:

Code: Select all

VarSetCapacity(options,24,0)
	NumPut(0,&options,0,"Int")							; verbose
	NumPut(0,&options,4,"Int")							; verbose_more
	NumPut(Settings.zopfliIterations,&options,8,"Int")	; numiterations
	NumPut(1,&options,12,"Int")							; blocksplitting
	NumPut(0,&options,16,"Int")							; blocksplittinglast
	NumPut(15,&options,20,"Int")						; blocksplittingmax
insize:=this.Stats.FileSize
DllCall(PS_DirArch "\libzopfli.dll\ZopfliZlibCompress","ptr",&options,"ptr",this.GetAddress("Raw"),"UInt",insize,"ptrP",outptr,"UIntP",outsize,"Cdecl")
firstByte:=NumGet(outptr+0, 0, "UChar") ; (for demo purposes) and so on...
DllCall("msvcrt\free", "Ptr", outptr, "Cdecl")
But with your code, I also think passing OffsetToOut is correct (I think the pointer to the memory ZopfliZlibCompress allocates would be inside outptr)