Using the SAFEARRAY data type

I am using the IE 4.01 WebBrowser control in my MFC/Visual C++ 5.0 SDI application in the View, and it has two functions that require manipulating the SAFEARRAY data type. One needs to set data in it, the other needs to read data from it.

Can someone show me how to do both?

In addition, if you can show me an example of how to use the PostData and Headers field in the Navigate2 method and BeforeNavigate2 event handler, I'll double the points!

Murali Devarakonda4WeekStartup.guruAsked:
Who is Participating?
mikeblasConnect With a Mentor Commented:
Meant that as an answer, of course.
Go to the online help topic "Array Manipulation API Functions" and check the subtopics following it.  Tere are descriptions of APIs like SafeArrayAccessData(), SafeArrayGetElement(), SafeArrayPutElement(), etc.

In general, the functions you need are:

SafeArrayAccessData      Increments the lock count of an array and returns a pointer to array data.
SafeArrayAllocData      Allocates memory for a safe array based on a descriptor created with SafeArrayAllocDescriptor.
SafeArrayAllocDescriptor      Allocates memory for a safe array descriptor.
SafeArrayCopy      Copies an existing array.
SafeArrayCopyData      Copies a source array to a target array after releasing source resources.
SafeArrayCreate      Creates a new array descriptor.
SafeArrayCreateVector      Creates a one-dimensional array whose lower bound is always zero.
SafeArrayDestroy      Destroys an array descriptor.
SafeArrayDestroyData      Frees memory used by the data elements in a safe array.
SafeArrayDestroyDescriptor      Frees memory used by a safe array descriptor.
SafeArrayGetDim      Returns the number of dimensions in an array.
SafeArrayGetElement      Retrieves an element of an array.
SafeArrayGetElemsize      Returns the size of an element.
SafeArrayGetLBound      Retrieves the lower bound for a given dimension.
SafeArrayGetUBound      Retrieves the upper bound for a given dimension.
SafeArrayLock      Increments the lock count of an array.
SafeArrayPtrOfIndex      Returns a pointer to an array element.
SafeArrayPutElement      Assigns an element into an array.
SafeArrayRedim      Resizes a safe array.
SafeArrayUnaccessData      Frees a pointer to array data and decrements the lock count of the array.
SafeArrayUnlock      Decrements the lock count of an array.

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Also, there's a sample program:

Murali Devarakonda4WeekStartup.guruAuthor Commented:
I appreciate your time, but I'm familiar with all the "Resources" that you are suggesting. I even have the sample and have compiled and run it. It's for sorting.

None of these resources actually make it easy to understand how to best use the SAFEARRAY (it's preferable to use COleSafeArray).

What I asked for, and need badly is code snippet that shows me simply how to set data, and retrieve data correctly, and efficiently. Specifically, in the WebBrowser supported method Navigate and the BeforeNavigate event (which deserves bonus points!).
I can't believe these "experts" didn't suggest using COleSafeArray. Experts Exchange is a race for points these days, instead of a quality contest.

This function calls the navigate method after converting the C++ data types in the function's prototype to the appropriate automation types. Note the use of COleSafeArray for the post data.

void CYourWindow::Navigate(LPCTSTR lpszURL, DWORD dwFlags,
   LPCTSTR lpszTargetFrameName,
   LPCTSTR lpszHeaders, LPVOID lpvPostData,
   DWORD dwPostDataLen)
   CString strURL(lpszURL);
   BSTR bstrURL = strURL.AllocSysString();

   COleSafeArray vPostData;
   if (lpvPostData != NULL)
      if (dwPostDataLen == 0)
         dwPostDataLen = lstrlen((LPCTSTR) lpvPostData);

      vPostData.CreateOneDim(VT_UI1, dwPostDataLen, lpvPostData);

   // call the browser control (you might have a different name)

      COleVariant((long) dwFlags, VT_I4),
      COleVariant(lpszTargetFrameName, VT_BSTR),
      COleVariant(lpszHeaders, VT_BSTR));

You can hook this function up as your BeforeNavigate2() event handler. The code I've supplied here translates the automation data types to real C++ data types that you can conveniently use.

void CYourWindow::BeforeNavigate2(LPDISPATCH /* pDisp */, VARIANT* URL,
      VARIANT* Flags, VARIANT* TargetFrameName,
      VARIANT* PostData, VARIANT* Headers, BOOL* Cancel)

   VARIANT* vtPostedData = V_VARIANTREF(PostData);
   CByteArray array;
   if (V_VT(vtPostedData) & VT_ARRAY)
      // must be a vector of bytes
      ASSERT(vtPostedData->parray->cDims == 1 && 
         vtPostedData->parray->cbElements == 1);

      vtPostedData->vt |= VT_UI1;
      COleSafeArray safe(vtPostedData);

      DWORD dwSize = safe.GetOneDimSize();
      LPVOID pVoid;

      LPBYTE lpByte = array.GetData();

      memcpy(lpByte, pVoid, dwSize);

   // make real parameters out of the notification

   CString strTargetFrameName(V_BSTR(TargetFrameName));
   CString strURL = V_BSTR(URL);
   CString strHeaders = V_BSTR(Headers);
   DWORD nFlags = V_I4(Flags);

   //**** at this point,
   //   strURL is the URL
   //    nFlags contains the flags
   //    strTargetFrameName has the name of the target frame
   //    array is a CByteArray containing the POST data
   //    strHeaders contains the headers

   // ... write your handler code here...

I'm sure you'll let me know if you have questions.

B ekiM

Murali Devarakonda4WeekStartup.guruAuthor Commented:
Thanks for your answer. It's exactly what I wanted. But I do have a few simple questions.
I have been using the TargetFrame, and URL fields, but I never used "USES_CONVERSION"- in fact I really don't know what it does or if I need it here. My existing code has been working fine (so far!) without it.

Also, you're doing      CString strURL = V_BSTR(URL);
             I did CString strURL( URL->bstrVal );
Similarily, you have
I suppose they are both equivalent, although I've never used V_BSTR.

Why didn't you use COleVariant here?
   VARIANT* vtPostedData = V_VARIANTREF(PostData);  
I feel more comfortable using MFC classes whenever possible, unless there's a reason not to.

Finally, I'd be really grateful if you can explain the PostData field to me. As I understand it, it's what I have to use to send, essentially a string made of the form data. I may get this from a dialog I present to the user, and build the string. I did gather from the docs that PostData is a single dimensional array of unsigned integer bytes. I hope I'm assuming correctly here that I can pass a pointer to a string for the parameter: LPVOID lpvPostData, in CYourWindow::Navigate function.

USES_CONVERSION isn't necessary in the code I posted; it was left over from the original application of the code.

The BSTR assignment constructor for CString is equivalently invoked both by operator equals and by the constructor syntax.

I don't have time to double-check at this instant, but I think I used VARIANT instead of COleVariant because COleVariant didn't have a constructor to initialize from a variant byref. That just made using VARIANT directly more convenient. Otheriwse, the code is equivalent.

You can pass a pointer to a string for PostData, yes.  If you read the HTTP spec, it wil lgive you info on formulating the string.

B ekiM

Murali Devarakonda4WeekStartup.guruAuthor Commented:
Thanks for your patience. I am going to close out this question.

I guess you'll give me some info that I asked for in my other question.

I tried to increase the points. Looks like I don't have enough points(only 10 available). I have 400 points locked between these two questions.
Once you're done with the other question, I'll close that out too. That'll give you 400 points for both of them together, which is twice the points I initially allocated to this question (and what I promised for a complete answer, which you've given me).

Please let me know what you think.

Thank you,
Murali Krishna Devarakonda
I think I skipped using COleVariant in the instance you describe because COleVariant doesn't have a constructor which accepts a pointer to a byref variant. (I could be wrong, because I'm not in a position to double-check at the moment.)

The string constructor is the same as the assignment, yes.

The posted data is normally just a string; you can pass along whatever your server expects. What the server expects is completely implementation dependent: if you're talking to an HTML form, or working with a CGI extension, or some other custom software, the exact content of the POST data may be different.

B ekiM
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.