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; }