Skip to main content
Known Participant
April 21, 2019
Answered

External Library Entry Point to Free Memory

  • April 21, 2019
  • 1 reply
  • 2159 views

I've created an After Effects script that extracts data from JSON files downloaded from an HTTPS URL. I'm new to C++ and I've had a hard time putting together a DLL that does the download, based on the examples provided with the After Effects installation (samplelib and basicexternalobject) and Microsoft's documentation. The problem now is that I don't know how to use the entry point "ESFreeMem()". The JavaScript Tools Guide says that it is "called to free memory allocated for a null-terminated string passed to or from library functions".

I'm  using After Effects CC 15.0.0 (build 180) on Windows 7.

This is the C++ function that gets some parameters from the javascript caller and returns a string with the JSON contents. If it fails it returns a bool (FALSE) so that the script can do what is necessary in this case.

  1. extern "C" TvgAfx_Com_API long DownloadJson(TaggedData* argv, long argc, TaggedData * result) 
  2.  
  3.      //... first I check the arguments passed 
  4.  
  5. // The returned value type 
  6. result->type = kTypeString; 
  7.  
  8.  
  9. //Converts from string into LPCWSTR --------------------------------------------------- 
  10. std::wstring stemp = s2ws(argv[0].data.string); 
  11. LPCWSTR jsonLink = stemp.c_str(); 
  12.  
  13.  
  14. std::wstring stemp02 = s2ws(argv[1].data.string); 
  15. LPCWSTR jsonHeader = stemp02.c_str(); 
  16. //-------------------------------------------------------------------------------------- 
  17.  
  18.  
  19. //Class that does the HTTP request 
  20. WinHttpClient client(jsonLink, jsonHeader); 
  21.  
  22.  
  23. //Synchronous request  
  24. if (client.SendHttpsRequest()) 
  25.      string httpResponse = client.GetHttpResponse(); 
  26.  
  27.  
  28.      if (httpResponse.length() > 0
  29.      { 
  30.           //Sends response string back to javascript 
  31.           result->data.string = getNewBuffer(httpResponse); 
  32.      } 
  33.      else 
  34.      { 
  35.            //Sends FALSE back to javascript 
  36.            result->type = kTypeBool; 
  37.            result->data.intval = 0
  38.      } 
  39. else 
  40.      //Sends FALSE back to javascript 
  41.      result->type = kTypeBool; 
  42.      result->data.intval = 0
  43.  
  44.  
  45. return kESErrOK; 

The C++ class WinHttpClienttthat does the actual request and frees the memory allocated to the buffer that holds the response. Here's a piece of code:

  1. // Read the data. 
  2. ZeroMemory(pszOutBuffer, dwSize + 1); 
  3.  
  4. if (!WinHttpReadData(hRequest, (LPVOID)pszOutBuffer, dwSize, &dwDownloaded)) 
  5. //Log error 
  6. else 
  7. resource.append(pszOutBuffer).c_str(); 
  8.  
  9.  
  10. // Free the memory allocated to the buffer. 
  11. delete[] pszOutBuffer;
  12.  

This is the function that the Adobe example uses to hold the string that will be returned to javascript:

  1. //brief Utility function to handle strings and memory clean up 
  2. static char* getNewBuffer(string& s) 
  3. // Dynamically allocate memory buffer to hold the string  
  4. // to pass back to JavaScript
  5. char* buff = new char[1 + s.length()]; 
  6.    
  7. memset(buff, 0, s.length() + 1); 
  8. strcpy(buff, s.c_str()); 
  9.    
  10. return buff; 

Now, the manual says this method must be implemented:

  1. /**
  2. * \brief Free any string memory which has been returned as function result.
  3. * JavaScipt calls this function to release the memory associated with the string.
  4. * Used for the direct interface.
  5. *
  6. * \param *p Pointer to the string
  7. */ 
  8. extern "C" SAMPLIB void ESFreeMem (void* p) 
  9. if (p) 
  10. free (p); 

What I understand from this is that the memory associated with the json string returned must be released. But didn't the request class already do it? I just don't know where to call this method and what to pass on to it. Can anybody provide me with an example, please? Thanks a lot!

PS: You will probably find flaws in my C++ code and I would appreciate if you could show me the dangerous ones (at least), if any.

This topic has been closed for replies.
Correct answer

Hello, Toby.

Thank you so much for such a detailed explanation! I was at a loss to understand who was responsible for what. And the concept of entry points as bridges between processes was very good. Your reply has surely made things clearer to me. However, since C++ and memory allocation are still new to me let me ask you a few more questions to make sure I've got it right .

First of all, the memory-release function I've posted was wrong in that I wrote "free" instead of "delete". This is the function I had declared, following Adobe's samples:

extern "C" SAMPLIB void ESFreeMem (void* p)

{

    delete (char*)(p);

}

You said the above function will be called automatically by the javascript engine, and the Adobe doc says the same:  "If you want to return a string value, you need to provide a FreeMem() entry point in your DLL so ExtendScript can free the memory after usage."

So, I understand I just have to declare this function using the right matching function for memory release, such as above, and the javascript engine will call it when it deems necessary.

Question 1: This is exactly what I did and even so I got the After Effects warning: "String Memory Leak".  What am I missing?

Question 2: Just out of curiosity, how can the javascript engine know which pointer to pass to this function?

Thank you so much!


1. Try using delete[](char*)p; It shouldn't matter though, strange you still get this error. Make sure you are not leaking anywhere else.

2. You pass a char* to JS, this is the pointer the JS engine saves and passes back to your function.

1 reply

Community Expert
April 21, 2019

when your dll creates a new string to be passed back to the javascript

side, it must allocate some memory for that string to be stored in.

once passed, your dll doesn't know when that string has gone out of scope

and is no longer used. that's when the javascript engine calls your dll,

and says "were' done with this piece of memory. please dispose".

so it's not your dll that calls that function. it's the javascript side

that calls it with a pointer to a mem chunk your dll passed back earlier.

On Sun, Apr 21, 2019 at 3:43 PM artepesquisa <forums_noreply@adobe.com>

Known Participant
April 22, 2019

Thanks for the explanation! It's a little clearer for me now.

So the javascript engine disposes of it automatically through this C++ function I'm required to implement: ESFreeMem (void *p). (It's a function that must be in the DLL, not in javascript).

Now, you said that  "it's the javascript side that calls it with a pointer to a mem chunk your dll passed back earlier".  Earlier when? Where? I still don't know what that pointer (*p) should be.

As I have already shown, the string is passed back to the  javascript engine through the function getNewBuffer::

result->data.string = getNewBuffer(httpResponse);

This function creates a buffer (buff) to store the string and returns it.

//brief Utility function to handle strings and memory clean up

static char* getNewBuffer(string& s)

{

     // Dynamically allocate memory buffer to hold the string to pass back to JavaScript

     char* buff = new char[1 + s.length()];

     memset(buff, 0, s.length() + 1);

     strcpy(buff, s.c_str());

     return buff;

}

I understand that "buff" is the piece of memory that should be disposed of, but how can I pass it to ESFreeMem()?

Thank you so much for taking the time to answer me.

April 22, 2019

Aren't you passing your newly created buffer from C++ to JS within your function?

result->data.string = getNewBuffer(httpResponse); 

If so, this is the very data that the JS runtime will later ask you to remove again when it is calling ESFreeMem() with this pointer. You don't need to pass this to ESFreeMem(), this function is called with your pointer data, and you remove it, since your process also allocated it.