Generates properly formatted and structured answer to a question included in the event.
PRL_RESULT PrlEvent_CreateAnswerEvent( PRL_HANDLE hEvent, PRL_HANDLE_PTR phEvent, PRL_RESULT nAnswer );
PrlApiCore.h
PRL_RESULT. Possible values:
PRL_ERR_INVALID_ARG - invalid handle or null pointer was passed.
PRL_ERR_OUT_OF_MEMORY - not enough memory to instantiate event object.
PRL_ERR_SUCCESS - function completed successfully.
One of the event types that a callback function can receive is PET_DSP_EVT_VM_QUESTION. The event of this type is generated on the Parallels Service side when a user interaction is required. For example, let's say you've sent a request to the Parallels Service to create a virtual machine but specified the size of the virtual hard drive that exceeds the free space available on the physical hard drive. In such a case, the Parallels Service will generate an event containing a question (and possible answers) about how the user would like to proceed. In this particular case, the question will be similar to "The size of the specified disk is larger than the free space available. Would you like to create the disk anyway?" and the possible answers will be "Yes" or "No". The user must choose one of the available options, compose an answer, and send it to the Parallels Service. Based on the answer received, the Parallels Service will take the appropriate action.
To obtain a string containing the question, use the PrlEvent_GetErrString function. Answer choices are acquired by obtaining a handle of type PHT_EVENT_PARAMETER using the PrlEvent_GetParam function (a single object will contain an individual answer choice). After the user selects an answer, the PrlEvent_CreateAnswerEvent function must be called to populate another object of type PHT_EVENT with the properly formatted answer data. The resulting object must then be passed to the PrlSrv_SendAnswer function which will send the answer to the Parallels Service.
The following is a complete example that demonstrates how to handle events and how to answer to Parallels Service questions. In the example, we create a blank virtual machine and try to add a virtual hard drive to it with the size larger than the free disk space available on the physical drive. This will trigger an event on the Parallels Service side and a question will be sent to the client asking if we really want to create a drive like that. The virtual machine creation operation will not continue unless we send an answer to the Parallels Service.
#include "Parallels.h" #include "Wrappers/SdkWrap/SdkWrap.h" #include <stdio.h> #include <stdlib.h> #ifdef _WIN_ #include <windows.h> #else #include <unistd.h> #endif #define MY_JOB_TIMEOUT 10000 // Default timeout to use in the PrlJob_Wait function. #define MY_HDD_SIZE 70*1024 // The size of the new hard drive. #define MY_STR_BUF_SIZE 1024 // The default buffer size to use for string output. //////////////////////////////////////////////////////////////////////////////// // A helper function that will attempt to crate a hard drive larger // than the free space available, thus triggering an event on the // Parallels Service, which will result in Service sending us a question. static PRL_RESULT create_big_hdd(PRL_HANDLE hVm); // The callback function (event handler). static PRL_RESULT callback(PRL_HANDLE, PRL_VOID_PTR); //////////////////////////////////////////////////////////////////////////////// int main(int argc, char* argv[]) { // Pick the correct dynamic library file depending on the platform. #ifdef _WIN_ #define SDK_LIB_NAME "prl_sdk.dll" #elif defined(_LIN_) #define SDK_LIB_NAME "libprl_sdk.so" #elif defined(_MAC_) #define SDK_LIB_NAME "libprl_sdk.dylib" #endif // Load the dynamic library. if (PRL_FAILED(SdkWrap_Load(SDK_LIB_NAME)) && PRL_FAILED(SdkWrap_Load("./" SDK_LIB_NAME))) { // Error handling goes here... return -1; } PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT rc = PRL_ERR_UNINITIALIZED; PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hServer = PRL_INVALID_HANDLE; // Initialize API library. err = PrlApi_Init(PARALLELS_API_VER); if (PRL_FAILED(err)) { // Error handling goes here... return -1; } // Create server object. PrlSrv_Create(&hServer); // Log in. hJob = PrlSrv_Login( hServer, // Server handle "10.30.22.82", // Host IP address "jdoe", // User "secret", // Password 0, // Previous session ID 0, // Port number 0, // Timeout PSL_NORMAL_SECURITY); // Security ret = PrlJob_Wait(hJob, MY_JOB_TIMEOUT); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n", PRL_RESULT_TO_STRING(ret)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; } // Analyze the result of PrlSrv_Login. PRL_RESULT nJobResult; ret = PrlJob_GetRetCode( hJob, &nJobResult ); if (PRL_FAILED( nJobResult)) { PrlHandle_Free(hJob); PrlHandle_Free(hServer); printf( "Login job returned with error: %s\n", PRL_RESULT_TO_STRING(nJobResult)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; } // Create a new virtual machine. PRL_HANDLE hVm; PrlSrv_CreateVm(hServer, &hVm); PrlVm_SetName(hVm, "My simple VM"); // Register the virtual machine with the Parallels Service. hJob = PrlVm_Reg(hVm, "", PRL_FALSE); PrlJob_Wait(hJob, MY_JOB_TIMEOUT); PrlHandle_Free(hJob); // Register the event handler with the Service. // The second parameter is a pointer to our callback function. PrlSrv_RegEventHandler(hServer, &callback, NULL); // Try creating a virtual hard drive larger than the // free space available (increase MY_HDD_SIZE value if needed). // This should produce an event that will // contain a question from the Parallels Service. create_big_hdd(hVm); // // At this point, the background thread should call the // callback function. // // We can now clean up and exit the program. // Unregister the event handler and log off. PrlSrv_UnregEventHandler(hServer, &callback, NULL); hJob = PrlSrv_Logoff(hServer); PrlJob_Wait(hJob, MY_JOB_TIMEOUT); PrlHandle_Free( hJob ); PrlHandle_Free( hServer ); PrlApi_Deinit(); SdkWrap_Unload(); return 0; } //////////////////////////////////////////////////////////////////////////////// // The callback function implementation. // The event handling is demonstrated here. // static PRL_RESULT callback(PRL_HANDLE hEvent, PRL_VOID_PTR pUserData) { PRL_HANDLE_TYPE nHandleType; PrlHandle_GetType(hEvent, &nHandleType); // A callback function will be called more than once. // It will be called for every job that we initiate and it // will be called for the event that we intentionally trigger. // In this example, we are interested in events only. if (nHandleType != PHT_EVENT) { return PrlHandle_Free(hEvent); } // Get the type of the event received. PRL_EVENT_TYPE type; PrlEvent_GetType(hEvent, &type); // See if the received event is a "question". if (type == PET_DSP_EVT_VM_QUESTION) { PRL_UINT32 nParamsCount = 0; PRL_RESULT err = PRL_ERR_UNINITIALIZED; // Extract the text of the question and display it on the screen. // The question is stored in the object in the "event message" property. PRL_BOOL bIsBriefMessage = true; char errMsg [MY_STR_BUF_SIZE]; PRL_UINT32 nBufSize = MY_STR_BUF_SIZE; PrlEvent_GetErrString(hEvent, bIsBriefMessage, errMsg, &nBufSize); printf("Question: %s\n\n", errMsg); // Extract answer choices. They are stored in the // hEvent object as event parameters. // First, determine the number of parameters. err = PrlEvent_GetParamsCount(hEvent, &nParamsCount); if (PRL_FAILED(err)) { fprintf(stderr, "[3]%.8X: %s\n", err, PRL_RESULT_TO_STRING(err)); PrlHandle_Free(hEvent); return err; } // Declare an array to hold the choices information. PRL_UINT32_PTR choices =(PRL_UINT32_PTR) malloc(nParamsCount * sizeof(PRL_UINT32)); // Now, itereate through the parameter list obtaining a // handle of type PHT_EVENT_PARAMETER that will contain // an individual parameter data. for(PRL_UINT32 nParamIndex = 0; nParamIndex < nParamsCount; ++nParamIndex) { PRL_HANDLE hParam; // this will receive the event parameter handle. PRL_RESULT err = PRL_ERR_UNINITIALIZED; // The PrlEvent_GetParam function obtains a handle of type // PHT_EVENT_PARAMETER containing (in this case) an answer choice. err = PrlEvent_GetParam(hEvent, nParamIndex, &hParam); if (PRL_FAILED(err)) { fprintf(stderr, "[4]%.8X: %s\n", err, PRL_RESULT_TO_STRING(err)); PrlHandle_Free(hParam); PrlHandle_Free(hEvent); return err; } // Get the answer description that can be shown to the user. // First, obtain the event parameter value. err = PrlEvtPrm_ToUint32(hParam, &choices[nParamIndex]); if (PRL_FAILED(err)) { fprintf(stderr, "[9]%.8X: %s\n", err, PRL_RESULT_TO_STRING(err)); PrlHandle_Free(hParam); PrlHandle_Free(hEvent); return err; } // Now, get the answer description using the // event parameter value as input in the following call. char sDesc [MY_STR_BUF_SIZE]; err = PrlApi_GetResultDescription(choices[nParamIndex], true, sDesc, &nBufSize); if (PRL_FAILED(err)) { fprintf(stderr, "[8]%.8X: %s\n", err, PRL_RESULT_TO_STRING(err)); PrlHandle_Free(hParam); PrlHandle_Free(hEvent); return err; } // Display the answer choice on the screen. printf("Answer choice: %s\n", sDesc); PrlHandle_Free(hParam); } // Select an answer choice (we are using the "No" answer here) and // create a valid answer object (hAnswer). PRL_HANDLE hAnswer; err = PrlEvent_CreateAnswerEvent(hEvent, &hAnswer, choices[1]); if (PRL_FAILED(err)) { fprintf(stderr, "[A]%.8X: %s\n", err, PRL_RESULT_TO_STRING(err)); PrlHandle_Free(hEvent); return err; } // Obtain a server handle. PRL_HANDLE hServer; PrlEvent_GetServer(hEvent, &hServer); // Send the answer handle to the Parallels Service. PrlSrv_SendAnswer(hServer, hAnswer); free(choices); PrlHandle_Free(hServer); PrlHandle_Free(hAnswer); } else // other event type { PrlHandle_Free(hEvent); } return PRL_ERR_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// // A helper function that will attempt to crate a hard drive larger // than the free space available, thus triggering an event. PRL_RESULT create_big_hdd(PRL_HANDLE hVm) { PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE; PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode = PRL_ERR_UNINITIALIZED; PRL_RESULT err = PRL_ERR_UNINITIALIZED; // Timestamp the beginning of the configuration changes operation. hJobBeginEdit = PrlVm_BeginEdit(hVm); err = PrlJob_Wait(hJobBeginEdit, MY_JOB_TIMEOUT); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "[B]%.8X: %s\n", nJobRetCode, PRL_RESULT_TO_STRING(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; } // Create a new device handle. // This will be our new virtual hard disk. PRL_HANDLE hHDD; err = PrlVm_CreateVmDev( hVm, // Target virtual machine. PDE_HARD_DISK, // Device type. &hHDD ); // Device handle. // Set disk type to "expanding". err = PrlVmDevHd_SetDiskType(hHDD, PHD_EXPANDING_HARD_DISK); // Set max disk size, in megabytes. err = PrlVmDevHd_SetDiskSize(hHDD, MY_HDD_SIZE); // This option determines whether the image file will be split // into chunks or created as a single file. err = PrlVmDevHd_SetSplitted(hHDD, PRL_FALSE); // Choose and set the name for the new image file. err = PrlVmDev_SetFriendlyName(hHDD, "harddisk4.hdd"); err = PrlVmDev_SetSysName(hHDD, "harddisk4.hdd"); // Set the emulation type. err = PrlVmDev_SetEmulatedType(hHDD, PDT_USE_IMAGE_FILE); // Enable the new disk on successful creation. err = PrlVmDev_SetEnabled(hHDD, PRL_TRUE); // Create the new image file. hJob = PrlVmDev_CreateImage(hHDD, PRL_TRUE, // Do not overwrite if file exists. PRL_FALSE ); // Use non-interactive mode. err = PrlJob_Wait(hJob, MY_JOB_TIMEOUT); if (PRL_FAILED(err)) { fprintf(stderr, "[C]%.8X: %s\n", err, PRL_RESULT_TO_STRING(err)); PrlHandle_Free(hJob); return err; } // Commit the changes. hJobCommit = PrlVm_Commit(hVm); err = PrlJob_Wait(hJobCommit, MY_JOB_TIMEOUT); PrlJob_GetRetCode(hJobCommit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "[D]%.8X: %s\n", nJobRetCode, PRL_RESULT_TO_STRING( nJobRetCode)); PrlHandle_Free(hJobCommit); return nJobRetCode; } return PRL_ERR_SUCCESS; }