/*
 * vbox_MSCOMGlue.c: glue to the MSCOM based VirtualBox API
 *
 * Copyright (C) 2013 Red Hat, Inc.
 * Copyright (C) 2010-2011 Matthias Bolte <matthias.bolte@googlemail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#ifdef HAVE_WINSOCK2_H
# include <winsock2.h>
#endif
#include <windows.h>

#define nsCID CLSID

#include "internal.h"
#include "viralloc.h"
#include "virlog.h"
#include "virerror.h"
#include "virstring.h"
#include "virutil.h"
#include "vbox_MSCOMGlue.h"

#define VIR_FROM_THIS VIR_FROM_VBOX

VIR_LOG_INIT("vbox.vbox_MSCOMGlue");

#define VBOX_REGKEY_ORACLE "Software\\Oracle\\VirtualBox"
#define VBOX_REGKEY_SUN "Software\\Sun\\xVM VirtualBox"

#define IVIRTUALBOX_IID_STR_v2_2 "779264f4-65ed-48ed-be39-518ca549e296"
#define ISESSION_IID_STR_v2_2 "12F4DCDB-12B2-4ec1-B7CD-DDD9F6C5BF4D"



typedef struct _VBOXXPCOMC_v1 VBOXXPCOMC_v1;
typedef struct _VBOXXPCOMC_v2 VBOXXPCOMC_v2;

struct _VBOXXPCOMC_v1 {
    unsigned cb;
    unsigned uVersion;
    unsigned int (*pfnGetVersion)(void);
    void (*pfnComInitialize)(IVirtualBox **virtualBox, ISession **session);
    void (*pfnComUninitialize)(void);
    void (*pfnComUnallocMem)(void *pv);
    void (*pfnUtf16Free)(PRUnichar *pwszString);
    void (*pfnUtf8Free)(char *pszString);
    int (*pfnUtf16ToUtf8)(const PRUnichar *pwszString, char **ppszString);
    int (*pfnUtf8ToUtf16)(const char *pszString, PRUnichar **ppwszString);
    unsigned uEndVersion;
};

struct _VBOXXPCOMC_v2 {
    unsigned cb;
    unsigned uVersion;
    unsigned int (*pfnGetVersion)(void);
    void (*pfnComInitialize)(const char *pszVirtualBoxIID,
                             IVirtualBox **ppVirtualBox,
                             const char *pszSessionIID,
                             ISession **ppSession);
    void (*pfnComUninitialize)(void);
    void (*pfnComUnallocMem)(void *pv);
    void (*pfnUtf16Free)(PRUnichar *pwszString);
    void (*pfnUtf8Free)(char *pszString);
    int (*pfnUtf16ToUtf8)(const PRUnichar *pwszString, char **ppszString);
    int (*pfnUtf8ToUtf16)(const char *pszString, PRUnichar **ppwszString);
    void (*pfnGetEventQueue)(nsIEventQueue **eventQueue);
    unsigned uEndVersion;
};



PFNVBOXGETXPCOMCFUNCTIONS g_pfnGetFunctions = NULL;

static unsigned long vboxVersion;
static IVirtualBox *vboxVirtualBox;
static ISession *vboxSession;



/*
 * nsISupports dummy implementation
 */

static nsresult __stdcall
vboxSupports_QueryInterface(nsISupports *pThis ATTRIBUTE_UNUSED,
                            const nsID *iid ATTRIBUTE_UNUSED,
                            void **resultp ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxSupports_AddRef(nsISupports *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxSupports_Release(nsISupports *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxSupports_GetTypeInfoCount(nsISupports *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxSupports_GetTypeInfo(nsISupports *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxSupports_GetIDsOfNames(nsISupports *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxSupports_Invoke(nsISupports *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}



/*
 * nsIEventTarget dummy implementation
 */

static nsresult __stdcall
vboxEventTarget_PostEvent(nsIEventTarget *pThis ATTRIBUTE_UNUSED,
                          PLEvent *aEvent ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventTarget_IsOnCurrentThread(nsIEventTarget *pThis ATTRIBUTE_UNUSED,
                                  PRBool *_retval ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}



/*
 * nsIEventQueue dummy implementation
 */

static nsresult __stdcall
vboxEventQueue_InitEvent(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                         PLEvent *aEvent ATTRIBUTE_UNUSED,
                         void *owner ATTRIBUTE_UNUSED,
                         PLHandleEventProc handler ATTRIBUTE_UNUSED,
                         PLDestroyEventProc destructor ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_PostSynchronousEvent(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                                    PLEvent *aEvent ATTRIBUTE_UNUSED,
                                    void **aResult ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_PendingEvents(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                             PRBool *_retval ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_ProcessPendingEvents(nsIEventQueue *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_EventLoop(nsIEventQueue *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_EventAvailable(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                              PRBool *aResult ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_GetEvent(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                        PLEvent **_retval ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_HandleEvent(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                           PLEvent *aEvent ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_WaitForEvent(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                            PLEvent **_retval ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static PRInt32 __stdcall
vboxEventQueue_GetEventQueueSelectFD(nsIEventQueue *pThis ATTRIBUTE_UNUSED)
{
    return -1;
}

static nsresult __stdcall
vboxEventQueue_Init(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                    PRBool aNative ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_InitFromPRThread(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                                PRThread *thread ATTRIBUTE_UNUSED,
                                PRBool aNative ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_InitFromPLQueue(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                               PLEventQueue *aQueue ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_EnterMonitor(nsIEventQueue *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_ExitMonitor(nsIEventQueue *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_RevokeEvents(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                            void *owner ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_GetPLEventQueue(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                               PLEventQueue **_retval ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_IsQueueNative(nsIEventQueue *pThis ATTRIBUTE_UNUSED,
                             PRBool *_retval ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static nsresult __stdcall
vboxEventQueue_StopAcceptingEvents(nsIEventQueue *pThis ATTRIBUTE_UNUSED)
{
    return NS_ERROR_NOT_IMPLEMENTED;
}

static struct nsIEventQueue_vtbl vboxEventQueueVtbl = {
    {
        {
            vboxSupports_QueryInterface,
            vboxSupports_AddRef,
            vboxSupports_Release,

            vboxSupports_GetTypeInfoCount,
            vboxSupports_GetTypeInfo,
            vboxSupports_GetIDsOfNames,
            vboxSupports_Invoke
        },

        vboxEventTarget_PostEvent,
        vboxEventTarget_IsOnCurrentThread
    },

    vboxEventQueue_InitEvent,
    vboxEventQueue_PostSynchronousEvent,
    vboxEventQueue_PendingEvents,
    vboxEventQueue_ProcessPendingEvents,
    vboxEventQueue_EventLoop,
    vboxEventQueue_EventAvailable,
    vboxEventQueue_GetEvent,
    vboxEventQueue_HandleEvent,
    vboxEventQueue_WaitForEvent,
    vboxEventQueue_GetEventQueueSelectFD,
    vboxEventQueue_Init,
    vboxEventQueue_InitFromPRThread,
    vboxEventQueue_InitFromPLQueue,
    vboxEventQueue_EnterMonitor,
    vboxEventQueue_ExitMonitor,
    vboxEventQueue_RevokeEvents,
    vboxEventQueue_GetPLEventQueue,
    vboxEventQueue_IsQueueNative,
    vboxEventQueue_StopAcceptingEvents,
};

static nsIEventQueue vboxEventQueue = {
    &vboxEventQueueVtbl
};



static char *
vboxLookupRegistryValue(HKEY key, const char *keyName, const char *valueName)
{
    LONG status;
    DWORD type;
    DWORD length;
    char *value = NULL;

    status = RegQueryValueEx(key, valueName, NULL, &type, NULL, &length);

    if (status != ERROR_SUCCESS) {
        VIR_ERROR(_("Could not query registry value '%s\\%s'"),
                  keyName, valueName);
        goto cleanup;
    }

    if (type != REG_SZ) {
        VIR_ERROR(_("Registry value '%s\\%s' has unexpected type"),
                  keyName, valueName);
        goto cleanup;
    }

    if (length < 2) {
        VIR_ERROR(_("Registry value '%s\\%s' is too short"),
                  keyName, valueName);
        goto cleanup;
    }

    /* +1 for the null-terminator if it's missing */
    if (VIR_ALLOC_N(value, length + 1) < 0)
        goto cleanup;

    status = RegQueryValueEx(key, valueName, NULL, NULL, (LPBYTE)value, &length);

    if (status != ERROR_SUCCESS) {
        VIR_FREE(value);
        VIR_ERROR(_("Could not query registry value '%s\\%s'"),
                  keyName, valueName);
        goto cleanup;
    }

    if (value[length - 1] != '\0')
        value[length] = '\0';

 cleanup:
    return value;
}

static int
vboxLookupVersionInRegistry(void)
{
    int result = -1;
    const char *keyName = VBOX_REGKEY_ORACLE;
    LONG status;
    HKEY key;
    char *value = NULL;

    status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);

    if (status != ERROR_SUCCESS) {
        keyName = VBOX_REGKEY_SUN;
        status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, KEY_READ, &key);

        if (status != ERROR_SUCCESS) {
            /* Both keys aren't there, or we cannot open them. In general this
             * indicates that VirtualBox is not installed, so we just silently
             * fail here making vboxRegister() register the dummy driver. */
            return -1;
        }
    }

    /* The registry key layout changed around version 4.0.8. Before the version
     * number was in the Version key, now the Version key can contain %VER% and
     * the actual version number is in the VersionExt key then. */
    value = vboxLookupRegistryValue(key, keyName, "Version");

    if (value == NULL)
        goto cleanup;

    if (STREQ(value, "%VER%")) {
        VIR_FREE(value);
        value = vboxLookupRegistryValue(key, keyName, "VersionExt");

        if (value == NULL)
            goto cleanup;
    }

    if (virParseVersionString(value, &vboxVersion, false) < 0) {
        VIR_ERROR(_("Could not parse version number from '%s'"), value);
        goto cleanup;
    }

    result = 0;

 cleanup:
    VIR_FREE(value);
    RegCloseKey(key);

    return result;
}

static unsigned int
vboxGetVersion(void)
{
    return vboxVersion;
}

static void
vboxComUnallocMem(void *pv)
{
    SysFreeString(pv);
}

static void
vboxUtf16Free(PRUnichar *pwszString)
{
    SysFreeString(pwszString);
}

static void
vboxUtf8Free(char *pszString)
{
    VIR_FREE(pszString);
}

static int
vboxUtf16ToUtf8(const PRUnichar *pwszString, char **ppszString)
{
    int length = WideCharToMultiByte(CP_UTF8, 0, pwszString, -1, NULL, 0,
                                     NULL, NULL);

    if (length < 1)
        return -1;

    if (VIR_ALLOC_N(*ppszString, length) < 0)
        return -1;

    return WideCharToMultiByte(CP_UTF8, 0, pwszString, -1, *ppszString,
                               length, NULL, NULL);
}

static int
vboxUtf8ToUtf16(const char *pszString, PRUnichar **ppwszString)
{
    int length = MultiByteToWideChar(CP_UTF8, 0, pszString, -1, NULL, 0);

    if (length < 1)
        return -1;

    *ppwszString = SysAllocStringLen(NULL, length);

    if (*ppwszString == NULL)
        return -1;

    return MultiByteToWideChar(CP_UTF8, 0, pszString, -1, *ppwszString, length);
}

static void
vboxGetEventQueue(nsIEventQueue **eventQueue)
{
    *eventQueue = &vboxEventQueue;
}

static void
vboxComInitialize_v2(const char *pszVirtualBoxIID, IVirtualBox **ppVirtualBox,
                     const char *pszSessionIID, ISession **ppSession)
{
    int result = -1;
    HRESULT hrc;
    IID virtualBoxIID;
    IID sessionIID;
    char *mbsVirtualBoxIID = NULL;
    char *mbsSessionIID = NULL;
    PRUnichar *wcsVirtualBoxIID = NULL;
    PRUnichar *wcsSessionIID = NULL;

    *ppVirtualBox = NULL;
    *ppSession = NULL;

    CoInitialize(NULL);

    if (virAsprintf(&mbsVirtualBoxIID, "{%s}", pszVirtualBoxIID) < 0 ||
        virAsprintf(&mbsSessionIID, "{%s}", pszSessionIID) < 0)
        goto cleanup;

    if (vboxUtf8ToUtf16(mbsVirtualBoxIID, &wcsVirtualBoxIID) < 0 ||
        vboxUtf8ToUtf16(mbsSessionIID, &wcsSessionIID) < 0) {
        goto cleanup;
    }

    hrc = IIDFromString(wcsVirtualBoxIID, &virtualBoxIID);

    if (FAILED(hrc)) {
        VIR_ERROR(_("Could not parse IID from '%s', rc = 0x%08x"),
                  pszVirtualBoxIID, (unsigned int)hrc);
        goto cleanup;
    }

    hrc = IIDFromString(wcsSessionIID, &sessionIID);

    if (FAILED(hrc)) {
        VIR_ERROR(_("Could not parse IID from '%s', rc = 0x%08x"),
                  pszVirtualBoxIID, (unsigned int)hrc);
        goto cleanup;
    }

    hrc = CoCreateInstance(&CLSID_VirtualBox, NULL, CLSCTX_LOCAL_SERVER,
                           &virtualBoxIID, (void**)&vboxVirtualBox);

    if (FAILED(hrc)) {
        VIR_ERROR(_("Could not create VirtualBox instance, rc = 0x%08x"),
                  (unsigned int)hrc);
        goto cleanup;
    }

    hrc = CoCreateInstance(&CLSID_Session, NULL, CLSCTX_INPROC_SERVER,
                           &sessionIID, (void**)&vboxSession);

    if (FAILED(hrc)) {
        VIR_ERROR(_("Could not create Session instance, rc = 0x%08x"),
                  (unsigned int)hrc);
        goto cleanup;
    }

    *ppVirtualBox = vboxVirtualBox;
    *ppSession = vboxSession;

    result = 0;

 cleanup:
    if (result < 0) {
        if (vboxVirtualBox != NULL) {
            vboxVirtualBox->vtbl->nsisupports.Release((nsISupports *)vboxVirtualBox);
            vboxVirtualBox = NULL;
        }

        if (vboxSession != NULL) {
            vboxSession->vtbl->nsisupports.Release((nsISupports *)vboxSession);
            vboxSession = NULL;
        }
    }

    vboxUtf16Free(wcsVirtualBoxIID);
    vboxUtf16Free(wcsSessionIID);
}

static void
vboxComInitialize_v1(IVirtualBox **ppVirtualBox, ISession **ppSession)
{
    vboxComInitialize_v2(IVIRTUALBOX_IID_STR_v2_2, ppVirtualBox,
                         ISESSION_IID_STR_v2_2, ppSession);
}

static void
vboxComUninitialize(void)
{
    if (vboxVirtualBox != NULL) {
        vboxVirtualBox->vtbl->nsisupports.Release((nsISupports *)vboxVirtualBox);
        vboxVirtualBox = NULL;
    }

    if (vboxSession != NULL) {
        vboxSession->vtbl->nsisupports.Release((nsISupports *)vboxSession);
        vboxSession = NULL;
    }

    CoUninitialize();
}



static VBOXXPCOMC_v1 vboxXPCOMC_v1 = {
    sizeof(VBOXXPCOMC_v1),      /* cb */
    0x00010000U,                /* uVersion */
    vboxGetVersion,             /* pfnGetVersion */
    vboxComInitialize_v1,       /* pfnComInitialize */
    vboxComUninitialize,        /* pfnComUninitialize */
    vboxComUnallocMem,          /* pfnComUnallocMem */
    vboxUtf16Free,              /* pfnUtf16Free */
    vboxUtf8Free,               /* pfnUtf8Free */
    vboxUtf16ToUtf8,            /* pfnUtf16ToUtf8 */
    vboxUtf8ToUtf16,            /* pfnUtf8ToUtf16 */
    0x00010000U                 /* uEndVersion */
};

static VBOXXPCOMC_v2 vboxXPCOMC_v2 = {
    sizeof(VBOXXPCOMC_v2),      /* cb */
    0x00020000U,                /* uVersion */
    vboxGetVersion,             /* pfnGetVersion */
    vboxComInitialize_v2,       /* pfnComInitialize */
    vboxComUninitialize,        /* pfnComUninitialize */
    vboxComUnallocMem,          /* pfnComUnallocMem */
    vboxUtf16Free,              /* pfnUtf16Free */
    vboxUtf8Free,               /* pfnUtf8Free */
    vboxUtf16ToUtf8,            /* pfnUtf16ToUtf8 */
    vboxUtf8ToUtf16,            /* pfnUtf8ToUtf16 */
    vboxGetEventQueue,          /* pfnGetEventQueue */
    0x00020000U                 /* uEndVersion */
};

static PCVBOXXPCOM
vboxGetFunctions(unsigned int version)
{
    if (version == 0x00010000U) {
        return (PCVBOXXPCOM)&vboxXPCOMC_v1;
    } else if (version == 0x00020000U) {
        return (PCVBOXXPCOM)&vboxXPCOMC_v2;
    } else {
        return NULL;
    }
}



int
VBoxCGlueInit(unsigned int *version)
{
    if (vboxLookupVersionInRegistry() < 0)
        return -1;

    *version = vboxGetVersion();
    g_pfnGetFunctions = vboxGetFunctions;

    return 0;
}

void
VBoxCGlueTerm(void)
{
}



/*
 * In MSCOM an array is represented by a SAFEARRAY pointer. To access the items
 * in the array the SafeArrayAccessData function is used to lock the array and
 * get its contents. When the items aren't needed anymore the
 * SafeArrayUnaccessData function is used to unlock the array. The pointer
 * retuned by SafeArrayAccessData function becomes invalid. Finally the
 * SafeArrayDestroy function is called to destroy the array, it also releases
 * or frees all items in the array according to their type.
 */

typedef HRESULT __stdcall (*SafeArrayGetter)(void *self, SAFEARRAY **array);
typedef HRESULT __stdcall (*SafeArrayGetterWithPtrArg)(void *self, void *arg, SAFEARRAY **array);
typedef HRESULT __stdcall (*SafeArrayGetterWithUintArg)(void *self, PRUint32 arg, SAFEARRAY **array);

static nsresult
vboxArrayGetHelper(vboxArray *array, HRESULT hrc, SAFEARRAY *safeArray)
{
    void **items = NULL;

    array->items = NULL;
    array->count = 0;
    array->handle = NULL;

    if (FAILED(hrc))
        return hrc;

    hrc = SafeArrayAccessData(safeArray, (void **)&items);

    if (FAILED(hrc)) {
        SafeArrayDestroy(safeArray);
        return hrc;
    }

    array->items = items;
    array->count = safeArray->rgsabound[0].cElements;
    array->handle = safeArray;

    return hrc;
}

/*
 * Call the getter with self as first argument and fill the array with the
 * returned items.
 */
nsresult
vboxArrayGet(vboxArray *array, void *self, void *getter)
{
    HRESULT hrc;
    SAFEARRAY *safeArray = NULL;

    hrc = ((SafeArrayGetter)getter)(self, &safeArray);

    return vboxArrayGetHelper(array, hrc, safeArray);
}

/*
 * Call the getter with self as first argument and arg as second argument
 * and fill the array with the returned items.
 */
nsresult
vboxArrayGetWithPtrArg(vboxArray *array, void *self, void *getter, void *arg)
{
    HRESULT hrc;
    SAFEARRAY *safeArray = NULL;

    hrc = ((SafeArrayGetterWithPtrArg)getter)(self, arg, &safeArray);

    return vboxArrayGetHelper(array, hrc, safeArray);
}

/*
 * Call the getter with self as first argument and arg as second argument
 * and fill the array with the returned items.
 */
nsresult
vboxArrayGetWithUintArg(vboxArray *array, void *self, void *getter, PRUint32 arg)
{
    HRESULT hrc;
    SAFEARRAY *safeArray = NULL;

    hrc = ((SafeArrayGetterWithUintArg)getter)(self, arg, &safeArray);

    return vboxArrayGetHelper(array, hrc, safeArray);
}

/*
 * Release all items in the array and reset it.
 *
 * SafeArrayDestroy is aware of the item's type and calls release or free
 * for each item according to its type. Therefore, vboxArrayUnalloc and
 * vboxArrayRelease are the same for MSCOM.
 */
void
vboxArrayRelease(vboxArray *array)
{
    if (array->handle == NULL)
        return;

    SafeArrayUnaccessData(array->handle);
    SafeArrayDestroy(array->handle);

    array->items = NULL;
    array->count = 0;
    array->handle = NULL;
}
