A host computer may have virtual machines on its hard drive that are not currently registered with the Parallels Service. This can happen when a virtual machine is removed from the Parallels Service registry but its files are kept on the drive, or when a virtual machine files are manually copied to the drive from another computer. Parallels C API provides the
PrlSrv_StartSearchVms
function that can be used to find such virtual machines on the specified host at the specified location on the hard drive. The function accepts a string containing a directory name and path and searches the directory and all its subdirectories for unregistered virtual machines. It then returns a list of
PHT_FOUND_VM_INFO
handles, each containing information about an individual virtual machine that it finds. You can then decide whether you want to keep the machine as-is, register it, or remove it from the hard drive.
Since the search operation may take a long time (depending on the size of the specified directory tree), the
PrlSrv_StartSearchVms
function should be executed using the
callback functionality
. The callback function will be called for every virtual machine found and a single instance of the
PHT_FOUND_VM_INFO
handle will be passed to it. As we discussed
earlier in this guide
, a callback function can receive two types of objects:
jobs
(
PHT_JOB
) and
events
(
PHT_EVENT
). In this instance, the information is passed to the callback function as an event of type
PET_DSP_EVT_FOUND_LOST_VM_CONFIG
. To following steps are involved in processing the event inside the callback function:
PrlHandle_GetType
function. If it is
PET_DSP_EVT_FOUND_LOST_VM_CONFIG
then the data passed to the callback function contains information about an unregistered virtual machine. If not, then the event was generated by some other function and contains the data relevant to that function.
PrlEvent_GetParam
function to obtain a handle of type
PHT_EVENT_PARAMETER
(this is a standard event processing step).
PrlEvtPrm_ToHandle
function to obtain a handle of type
PHT_FOUND_VM_INFO
containing the virtual machine information.
PHT_FOUND_VM_INFO
object to determine the location of the virtual machine files, the virtual machine name, guest OS version, and some other information.
The following is an implementation of the steps above:
static PRL_RESULT callback(PRL_HANDLE hEvent, PRL_VOID_PTR pUserData)
{
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_HANDLE_TYPE nHandleType;
PrlHandle_GetType(hEvent, &nHandleType);
// If this is a job, release the handle and exit.
// Normally, we would process this, if we were after
// a job.
if (nHandleType == PHT_JOB)
{
PrlHandle_Free(hEvent);
return 0;
}
// If it's not a job, then it is an event (PHT_EVENT).
// Get the type of the event received.
PRL_EVENT_TYPE eventType;
PrlEvent_GetType(hEvent, &eventType);
// Check the event type. If it's what we are looking for, process it.
if (eventType == PET_DSP_EVT_FOUND_LOST_VM_CONFIG)
{
PRL_UINT32 nParamsCount = 0;
PRL_HANDLE hParam; // this will receive the event parameter handle.
// The PrlEvent_GetParam function obtains a handle of type
// PHT_EVENT_PARAMETER.
ret = PrlEvent_GetParam(hEvent, 0, &hParam);
if (PRL_FAILED(ret))
{
fprintf(stderr, "[4]%.8X: %s\n", ret,
prl_result_to_string(ret));
PrlHandle_Free(hParam);
PrlHandle_Free(hEvent);
return ret;
}
PRL_HANDLE hFoundVmInfo;
ret = PrlEvtPrm_ToHandle(hParam, &hFoundVmInfo);
if (PRL_FAILED(ret))
{
fprintf(stderr, "[9]%.8X: %s\n", ret,
prl_result_to_string(ret));
PrlHandle_Free(hParam);
PrlHandle_Free(hEvent);
return ret;
}
// Get the virtual machine name.
PRL_CHAR sName[1024];
PRL_UINT32 nBufSize = sizeof(sName);
ret = PrlFoundVmInfo_GetName(hFoundVmInfo, sName, &nBufSize);
printf("VM name: %s\n", sName);
// Get the name and path of the virtual machine directory.
PRL_CHAR sPath[1024];
nBufSize = sizeof(sPath);
ret = PrlFoundVmInfo_GetConfigPath(hFoundVmInfo, sPath, &nBufSize);
printf("Path: %s\n\n", sPath);
PrlHandle_Free(hFoundVmInfo);
PrlHandle_Free(hEvent);
return 0;
}
// The received event handler MUST be freed.
PrlHandle_Free(hEvent);
}
To begin the search operation, place the following code into your main program:
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Register the event handler.
PrlSrv_RegEventHandler(hServer, &callback, NULL);
// Create a string list object and populate it
// with the name and path of the directory to search.
PRL_HANDLE hStringList = PRL_INVALID_HANDLE;
ret = PrlApi_CreateStringsList(&hStringList );
ret = PrlStrList_AddItem(hStringList, "/Users/Shared/Parallels/");
// Begin the search operation.
hJob = PrlSrv_StartSearchVms(hServer, hStringList);
PrlHandle_Free(hJob);
In order for the callback function to be called, your program should have a global loop (the program never exits on its own). The callback function will be called as soon as the first virtual machine is found. If there are no unregistered virtual machines in the specified directory tree, then the function will never be called as a
PET_DSP_EVT_FOUND_LOST_VM_CONFIG
event (it will still be called at least once as a result of the started job and will receive the job object but this and possibly other callback invocations are irrelevant in the context of this example).
Receiving the search results synchronously
It is also possible to use this function synchronously using the
PrlJob_Wait
function
. In this case, the information is returned as a list of
PHT_FOUND_VM_INFO
objects contained in the job object returned by the
PrlSrv_StartSearchVms
function. The following example demonstrates how to call the function and to process results synchronously.
PRL_RESULT SearchVMsSample(PRL_HANDLE hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hFoundVmInfo = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Create a string list object and populate it
// with the name and path of the directory to search.
PRL_HANDLE hStringList = PRL_INVALID_HANDLE;
PrlApi_CreateStringsList(&hStringList );
PrlStrList_AddItem(hStringList, "/Users/Shared/Parallels/");
// Begin the search operation.
hJob = PrlSrv_StartSearchVms(hServer, hStringList);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_StartSearchVms.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Iterate through the returned list obtaining a
// handle of type PHT_FOUND_VM_INFO in each iteration containing
// the information about an individual virtual machine.
PRL_UINT32 nIndex, nCount;
PrlResult_GetParamsCount(hJobResult, &nCount);
for(nIndex = 0; nIndex < nCount ; nIndex++)
{
PrlResult_GetParamByIndex(hJobResult, nIndex, &hFoundVmInfo);
// Get the virtual machine name.
PRL_CHAR sName[1024];
PRL_UINT32 nBufSize = sizeof(sName);
ret = PrlFoundVmInfo_GetName(hFoundVmInfo, sName, &nBufSize);
printf("VM name: %s\n", sName);
// Get the name and path of the virtual machine directory.
PRL_CHAR sPath[1024];
nBufSize = sizeof(sPath);
ret = PrlFoundVmInfo_GetConfigPath(hFoundVmInfo, sPath, &nBufSize);
printf("Path: %s\n\n", sPath);
PrlHandle_Free(hFoundVmInfo);
}
PrlHandle_Free(hJobResult);
PrlHandle_Free(hStringList);
}