This product is protected by United States and international copyright laws. The product’s underlying technology,
patents, and trademarks are listed at http://www.parallels.com/about/legal/.
Microsoft, Windows, Windows Server, Windows Vista are registered trademarks of Microsoft Corporation.
Apple, Mac, the Mac logo, OS X, macOS, iPad, iPhone, iPod touch are trademarks of Apple Inc., registered in the US
and other countries.
Linux is a registered trademark of Linus Torvalds.
All other marks and names mentioned herein may be trademarks of their respective owners.
Contents
Getting Started .......................................................................................................... 6
To develop applications on Mac computers using the SDK, the following requirements must be
met.
Hardware Requirements
• Mac computer with Intel Core 2 Duo, Core i3, Core i5, Core i7, or Xeon processor
• Minimum 2 GB RAM
Software Requirements
• macOS 10.12 or newer
• Python 2.7 or 3.0 to develop applications in Python
C HAPTER 2
Parallels C API Concepts
This chapter describes the basics of the Parallels C API. It contains information on how to compile
client applications that use the API and explains basic API concepts.
You can use the framework just like any other Apple framework when creating development
projects and compiling applications. Alternately, you can compile and build your applications
without using the framework. In such a case, you will have to specify all the necessary paths to the
SDK source files manually.
When using the framework, the dynamic library, which is supplied with the SDK, will be directly
linked to the application. If you would like to load the dynamic library at runtime, the Parallels
Virtualization SDK includes a convenient dlopen wrapper for this purpose called SdkWrap. Using
the wrapper, you can load and unload the library symbols at any time with one simple call. Please
note that in order to use SdkWrap, you must compile your application without using the framework.
The wrapper source files are located in the Helpers/SdkWrap directory, which is located in the
main SDK installation directory.
The following subsections describe various compilation scenarios in detail and provide code
samples.
Parallels C API Concepts
Compiling with SdkWrap
When using SdkWrap, your program must contain the following:
• The #include "SdkWrap.h" directive. This header file defines the wrapper functions.
• The #define SDK_LIB_NAME "libprl_sdk.dylib" directive. This is the name of the
dynamic library included in the SDK.
• The SdkWrap_Load(SDK_LIB_NAME) function call that will load the dynamic library symbols.
• The SdkWrap_Unload() function call that will unload the dynamic library when it is no longer
needed.
To compile a program, the following compiler options and instructions must be used:
• The DYN_API_WRAP preprocessor macro must be defined.
• Full paths to the Headers and the Helpers/SdkWrap directories must be specified. Both
directories are located in the main SDK installation directory.
• The SdkWrap.cpp file must be included in the project and must be built together with the
main target.
• The libdl library must be linked to the application. This is the standard dynamic linking
interface library needed to load the SDK library.
Using Makefile
The following is a sample Makefile that demonstrates the implementation of the requirements
described above. To compile a program and to build an executable, type make in the Terminal
window. To clean up the project, type make clean. Please note that the SOURCE variable must
contain the name of your source file name.
# Source file name.
# Substitute the file name with your own.
SOURCE = HelloWorld
# Target executable file name.
# Here we are using the same name as the source file name.
TARGET = $(SOURCE)
# Path to the Parallels Virtualization SDK files.
SDK_PATH = /Library/Frameworks/ParallelsVirtualizationSDK.framework
# Relative path to the SdkWrap directory containing
# the SDK helper files. The files are used to load
# the dynamic library.
SDK_WRAP_PATH = Helpers/SdkWrap
If you are using the Xcode IDE, follow these steps to set up your project:
1 Add the SdkWrap.h and the SdkWrap.cpp files to your project.
2 In the Search Paths collection, specify:
• a full path to the Helpers/SdkWrap directory (contains the wrapper source files)
• a full path to the Headers directory (contains the SDK header files)
• a full path to the Libraries directory (contains the dynamic library)
3In the Preprocessor collection, add the DYN_API_WRAP preprocessor macro.
Example
The following is a complete sample program that demonstrates the usage of the SdkWrap
wrapper. The program loads the dynamic library, initializes the API, and then logs in to the local
Parallels Service. You can copy the entire program into a file on your Mac and try building and then
running it. The program uses a cross-platform approach, so it can also be compiled on Windows
and Linux machines.
// Log in to Parallels Service.
err = LoginLocal(hServer);
// Log off.
err = LogOff(hServer);
printf( "\nEnd of program.\n\n" );
printf("Press Enter to exit...");
getchar();
exit(0);
}
// Initializes the SDK library and
// logs in to the local Parallels Service.
//
PRL_RESULT LoginLocal(PRL_HANDLE &hServer)
{
// Variables for handles.
PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result
// Initialize the API. In this example, we are initializing the
// API for Parallels Desktop.
// To initialize in the Parallels Workstation mode, pass PAM_WORKSTATION
// as the second parameter.
// To initialize for Parallels Server, pass PAM_SERVER.
// See the PRL_APPLICATION_MODE enumeration for all possible options.
err = PrlApi_InitEx(PARALLELS_API_VER, PAM_DESKTOP, 0, 0);
if (PRL_FAILED(err))
{
10
Parallels C API Concepts
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Create a server handle (PHT_SERVER).
err = PrlSrv_Create(&hServer);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Wait for a maximum of 10 seconds for
// the job to complete.
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Analyze the result of PrlSrv_Login.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Now check the Login operation success/failure.
if (PRL_FAILED(nJobReturnCode))
{
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
printf("Login job returned with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
// Logs off the Parallels Service and
// deinitializes the SDK library.
//
PRL_RESULT LogOff(PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Get the Logoff operation return code.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// Check the PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode failed for PrlSrv_Logoff with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Report success or failure of PrlSrv_Logoff.
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_Logoff failed with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
12
Parallels C API Concepts
}
else
{
printf( "Logoff was successful.\n" );
}
// Free handles that are no longer required.
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
// De-initialize the Parallels API, and unload the SDK.
PrlApi_Deinit();
SdkWrap_Unload();
return 0;
}
Compiling with Framework
If you are using the ParallelsVirtualizationSDK framework, the program must contain the following
include directive:
#include "ParallelsVirtualizationSDK/Parallels.h"
Parallels.h is the main SDK header file. Please note the framework name in front of the SDK
header file name. This is a common requirement when using a framework.
Note: The difference between the SdkWrap scenario (described in the previous subsection) and the
framework scenario is that Parallels.h must be included when using the framework, while
SdkWrap.h must be included when using SdkWrap. The two files must never be included together.
Please also note that you don't have to load the dynamic library manually in your program when using the
framework.
The only compiler option that must be specified when using the framework is:
-framework ParallelsVirtualizationSDK
Using Makefile
The following sample Makefile can be used to compile a program using the
ParallelsVirtualizationSDK framework:
# Source file name.
# Substitute the file name with your own.
SOURCE = HelloWorld
# Target executable file name.
# Here we are using the same name as the source file name.
TARGET = $(SOURCE)
When setting up an Xcode project, the only thing that you have to do is add the
ParallelsVirtualizationSDK framework to the project. No other project modifications are necessary.
Sample
The following is a complete sample program that demonstrates the usage of the
ParallelsVirtualizationSDK framework.
int main(int argc, char* argv[])
{
// Variables for handles.
PRL_HANDLE hServer = PRL_INVALID_HANDLE; // server handle
// Variables for return codes.
PRL_RESULT err = PRL_ERR_UNINITIALIZED;
// Log in.
err = LoginLocal(hServer);
// Log off
err = LogOff(hServer);
printf( "\nEnd of program.\n\n" );
printf("Press Enter to exit...");
getchar();
exit(0);
}
14
Parallels C API Concepts
// Intializes the SDK library and
// logs in to the local Parallels Service.
//
PRL_RESULT LoginLocal(PRL_HANDLE &hServer)
{
// Variables for handles.
PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle
// Initialize the API. In this example, we are initializing the
// API for Parallels Workstation.
// To initialize in the Parallels Desktop mode, pass PAM_DESKTOP
// as the second parameter.
// To initialize for Parallels Server, pass PAM_SERVER.
// See the PRL_APPLICATION_MODE enumeration for all possible options.
err = PrlApi_InitEx(PARALLELS_API_VER, PAM_DESKTOP, 0, 0);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
return -1;
}
// Create a server handle (PHT_SERVER).
err = PrlSrv_Create(&hServer);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(err));
PrlApi_Deinit();
return -1;
}
// Wait for a maximum of 10 seconds for
// the job to complete.
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
// Analyze the result of PrlSrv_Login.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
15
Parallels C API Concepts
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
// Now check the Login operation success/failure.
if (PRL_FAILED(nJobReturnCode))
{
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
printf("Login job returned with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
else
{
printf( "Login was successful.\n" );
}
return 0;
}
// Log off the Parallels Service and
// deinitializes the SDK library.
//
PRL_RESULT LogOff(PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
// Get the Logoff operation return code.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// Check the PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode failed for PrlSrv_Logoff with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
// Report success or failure of PrlSrv_Logoff.
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_Logoff failed with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
else
{
printf( "Logoff was successful.\n" );
}
// Free handles that are no longer required.
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
// De-initialize the Parallels API, and unload the SDK.
PrlApi_Deinit();
return 0;
}
Handles
The Parallels C API is a set of functions that operate on objects. Objects are not accessed directly.
Instead, references to these objects are used. These references are known as handles.
Handle Types
PRL_HANDLE is the only handle type used in the C API. It is a pointer to an integer and it is defined
in PrlTypes.h.
PRL_HANDLE can reference any type of object within the API. The type of object that
PRL_HANDLE references determines the PRL_HANDLE type. A list of handle types can be found
in the PRL_HANDLE_TYPE enumeration in PrlEnums.h.
A handles' type can be extracted using the PrlHandle_GetType function. A string
representation of the handle type can then be obtained using the handle_type_to_string
function.
17
Parallels C API Concepts
Obtaining a Handle
A handle is usually obtained by calling a function belonging to another handle, which we may call a
"parent". For example, a virtual machine handle is obtained by calling a function that operates on
the Server handle. A virtual device handle is obtained by calling a function that operates on the
virtual machine handle, and so forth. The Parallels C API Reference guide contains a description
of every available handle and explains how each particular handle type can be obtained. The
examples in this guide also demonstrate how to obtain handles of different types.
Freeing a Handle
Parallels API handles are reference counted. Each handle contains a count of the number of
references to it held by other objects. A handle stays in memory for as long as the reference count
is greater than zero. A program is responsible for freeing any handles that are no longer needed. A
handle can be freed using the PrlHandle_Free function. The function decreases the reference
count by one. When the count reaches zero, the object is destroyed. Failing to free a handle after it
has been used will result in a memory leak.
Multithreading
Parallels API handles are thread safe. They can be used in multiple threads at the same time. To
maintain the proper reference counting, the count should be increased each time a handle is
passed to another thread by calling the PrlHandle_AddRef function. If this is not done, freeing a
handle in one thread may destroy it while other threads are still using it.
Example
The following code snippet demonstrates how to obtain a handle, how to determine its type, and
how to free it when it's no longer needed. The code is a part of the bigger example that
demonstrates how to log in to a Parallels Service (the full example is provided later in this guide).
ret = PrlSrv_Create(&hServer);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(ret));
return PRL_ERR_FAILURE;
}
// Determine the type of the hServer handle.
PRL_HANDLE_TYPE nHandleType;
PrlHandle_GetType(hServer, &nHandleType);
printf("Handle type: %s\n",
handle_type_to_string(nHandleType));
// Free the handle when it is no longer needed.
PrlHandle_Free(hServer);
18
Parallels C API Concepts
Synchronous Functions
The Parallels C API provides synchronous and asynchronous functions. Synchronous functions run
in the same thread as the caller. When a synchronous function is called it completes executing
before returning control to the caller. Synchronous functions return PRL_RESULT, which is a
integer indicating success or failure of the operation. Consider the PrlSrv_Create function. The
purpose of this function is to obtain a handle of type PHT_SERVER. The handle is required to
access most of the functionality within the Parallels C API. The syntax of PrlSrv_Create is as
follows:
The following is an example of the PrlSrv_Create function call:
// Declare a handle variable.
PRL_HANDLE hServer = PRL_INVALID_HANDLE;
// Call the PrlSrv_Create to obtain the handle.
PRL_RESULT res = PrlSrv_Create(&hServer);
// Examine the function return code.
// PRL_FAILED is a macro that evaluates a variable of type PRL_RESULT.
// A return value of True indicates success; False indicates failure.
if (PRL_FAILED(res))
{
printf("PrlSrv_Create returned error: %s\n",
prl_result_to_string(res));
exit(ret);
}
Asynchronous Functions
An asynchronous operation is executed in its own thread. An asynchronous function that started
the operation returns to the caller immediately without waiting for the operation to complete. The
results of the operation can be verified later when needed. Asynchronous functions return
PRL_HANDLE, which is a pointer to an integer and is a handle of type PHT_JOB. The handle is
used as a reference to the asynchronous job executed in the background. The general procedure
for calling an asynchronous function is as follows:
1 Register an event handler (callback function).
2 Call an asynchronous function.
3 Analyze the results of events (jobs) within the callback function.
4 Handle the appropriate event in the callback function.
5 Un-register the event handler when it is no longer needed.
19
Parallels C API Concepts
The Callback Function (Event Handler)
Asynchronous functions return data to the caller by means of an event handler (or callback
function). The callback function could be called at any time, depending on how long the
asynchronous function takes to complete. The callback function must have a specific signature.
The prototype can be found in PrlApi.h and is as follows:
typedef PRL_METHOD_PTR(PRL_EVENT_HANDLER_PTR) (
PRL_HANDLE hEvent,
PRL_VOID_PTR data
);
The following is an example of the callback function implementation:
// You must always release the handle before exiting.
PrlHandle_Free(handle);
}
A handle received by the callback function can be of type PHT_EVENT or PHT_JOB. The type can
be determined using the PrlHandle_GetType function. The PHT_EVENT type indicates that the
callback was called by a system event. If the type is PHT_JOB then the callback was called by an
asynchronous job started by the program.
To handle system events within a callback function:
1 Get the event type using PrlEvent_GetType.
2 Examine the event type. If it is relevant, a handle of type PHT_EVENT_PARAMETER can be
extracted using PrlEvent_GetParam.
3Convert the PHT_EVENT_PARAMETER handle to the appropriate handle type using
PrlEvtPrm_ToHandle.
To handle jobs within a callback function:
1 Get the job type using PrlJob_GetType. A job type can be used to identify the function that
started the job and to determine the type of the result it contains. For example, a job of type
PJOC_SRV_GET_VM_LIST is started by PrlSrv_GetVmList function call, which returns a
list of virtual machines.
2 Examine the job type. If it is relevant, proceed to the next step.
3 Get the job return code using PrlJob_GetRetCode. If it doesn't contain an error, proceed to
the next step.
4Get the result (a handle of type PHT_RESULT) from the job handle using
PrlJob_GetResult.
20
Parallels C API Concepts
5 Get a handle to the result using PrlResult_GetParam. Note that some functions return a list
(ie. there can be more than a single parameter in the result). For example,
PrlSrv_GetVmList returns a list of available virtual machines. In such cases, use
PrlResult_GetParamCount and PrlResult_GetParamByIndex.
6 Implement code to use the handle obtained in step 5.
Note: You must always free the handle that was passed to the callback function before exiting,
regardless of whether you actually used it or not. Failure to do so will result in a memory leak.
The following skeleton code demonstrates implementation of the above steps. In this example, the
objective is to handle events of type PET_DSP_EVT_HOST_STATISTICS_UPDATED that are
generated by a call to function PrlSrv_SubscribeToHostStatistics, and to obtain the
result from a job of type PJOC_SRV_GET_VM_LIST.
// Check if the event type is a statistics update.
if (EventType == PET_DSP_EVT_HOST_STATISTICS_UPDATED)
{
// Get handle to PHT_EVENT_PARAMETER.
PRL_HANDLE hEventParameters = PRL_INVALID_HANDLE;
PrlEvent_GetParam(hHandle, 0, &hEventParameters);
// Get handle to PHT_SYSTEM_STATISTICS.
PRL_HANDLE hServerStatistics = PRL_INVALID_HANDLE;
PrlEvtPrm_ToHandle(hEventParameters, &hServerStatistics);
// Code goes here to extract the statistics data
// using hServerStatistics.
PrlHandle_Free(hServerStatistics);
PrlHandle_Free(hEventParameters);
}
}
else if (nHandleType == PHT_JOB) // Job handle
{
// Get the job type.
PrlJob_GetOpCode(hHandle, &nJobType);
// Check if the job type is PJOC_SRV_GET_VM_LIST.
21
Parallels C API Concepts
if (nJobType == PJOC_SRV_GET_VM_LIST)
{
// Check the job return code.
PRL_RESULT nJobRetCode;
PrlJob_GetRetCode(hHandle, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "[B]%.8X: %s\n", nJobRetCode,
prl_result_to_string(nJobRetCode));
PrlHandle_Free(hHandle);
return nJobRetCode;
}
err = PrlJob_GetResult(hHandle, &hJobResult);
// if (err != PRL_ERR_SUCCESS), process the error here.
// Determine the number of parameters in the result.
PrlResult_GetParamsCount(hJobResult, &nParamsCount);
// Iterate through the parameter list.
for(nParamIndex = 0; nParamIndex < nParamsCount ; nParamIndex++)
{
// Obtain a virtual machine handle (PHT_VIRTUAL_MACHINE).
PrlResult_GetParamByIndex(hJobResult, nParamIndex, &hVm);
// Code goes here to obtain virtual machine info from hVm.
// Free the handle when done using it.
PrlHandle_Free(hVm);
}
PrlHandle_Free(hJobResult);
}
}
The PrlSrv_RegEventHandler function is used to register an event handler,
PrlSrv_UnregEventHandler is used to unregister an event handler.
Note: When an event handler is registered, it will receive all of the events/jobs regardless of their origin. It
is the responsibility of the program to identify the type of the event and to handle each one accordingly.
// Register an event handler.
ReturnDataClass rd; // some user-defined class.
PrlSrv_RegEventHandler(hServer, OurCallbackFunction, &rd);
// Make a call to an asynchronous function here.
// OurCallbackFunction will be called by the background thread
// as soon as the job is completed, and code within
// OurCallbackFunction can populate the ReturnDataClass instance.
// For example, we can make the following call here:
// Please note that we still have to obtain the
// job object (hJob above) and free it; otherwise
// we will have memory leaks.
// Unregister the event handler when it is no longer needed.
PrlSrv_UnregEventHandler(hServer, OurCallbackFunction, &rd);
Calling Asynchronous Functions Synchronously
It is possible to call an asynchronous function synchronously by using the PrlJob_Wait function.
The function takes two parameters: a PHT_JOB handle and a timeout value in milliseconds. Once
you call the function, the main thread will be suspended and the function will wait for the
asynchronous job to complete. The function will return when the job is completed or when timeout
value is reached, whichever comes first. The following code snippet illustrates how to call an
asynchronous function PrlServer_Login synchronously:
// Log in (PrlSrv_Login is asynchronous).
PRL_HANDLE hJob = PrlSrv_Login(
hServer,
szHostnameOrIpAddress,
szUsername,
szPassword,
0,
0,
0,
PSL_LOW_SECURITY);
// Wait for a maximum of 10 seconds for
// asynchronous function PrlSrv_Login to complete.
ret = PrlJob_Wait(hJob, 10000);
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);
return -1;
}
// Analyse the result of the PrlServer_Login call.
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));
return -1;
}
else
{
printf("login successfully performed\n");
}
23
Parallels C API Concepts
Strings as Return Values
Sting values in the Parallels C API are received by passing a char pointer to a function which
populates it with data. It is the responsibility of the caller to allocate the memory required to receive
the value, and to free it when it is no longer needed. Since in most cases we don't know the string
size in advance, we have to either allocate a chunk of memory large enough for any possible value
or to determine the exact required size. To determine the required buffer size, the following two
approaches can be used:
1 Calling the same function twice: first, to obtain the required buffer size, and second, to receive
the actual string value. To get the required buffer size, call the function passing a null pointer as
a value of the output parameter, and pass 0 (zero) as a value of the variable that is used to
specify the buffer size. The function will calculate the required size and will populate the variable
with the correct value, which you can use to initialize a variable that will receive the string. You
can then call the function again to get the actual string value.
2 It is also possible to use a static buffer. If the length of the buffer is large enough, you will simply
receive the result. If the length is too small, a function will fail with the
PRL_ERR_BUFFER_OVERRUN error but it will populate the "buffer_size" variable with the
required size value. You can then allocate the memory using the received value and call the
function again to get the results.
The PrlVmCfg_GetName function above is a typical Parallels API function that returns a string
value (in this case, the name of a virtual machine). The hVmCfg parameter is a handle to an object
containing the virtual machine configuration information. The sVmName parameter is a char
pointer. It is used as output that receives the virtual machine name. The variable must be initialized
on the program side with enough memory allocated for the expected string. The size of the buffer
must be specified using the pnVmNameBufLength variable.
The following example demonstrates how to call the function using the first approach:
PRL_RESULT ret;
PRL_UINT32 nBufSize = 0;
// Get the required buffer size.
ret = PrlVmCfg_GetName(hVmCfg, 0, &nBufSize);
// Allocate the memory.
PRL_STR pBuf = (PRL_STR)malloc(sizeof(PRL_CHAR) * nBufSize);
// Get the virtual machine name.
ret = PrlVmCfg_GetName(hVmCfg, pBuf, &nBufSize);
printf("VM name: %s\n", pBuf);
24
Parallels C API Concepts
// Deallocate the memory.
free(pBuf);
The following example uses the second approach. To test the buffer-overrun scenario, set the
// Get the virtual machine name.
ret = PrlVmCfg_GetName(hVmCfg, sVmName, &nBufSize);
// Check for errors.
if (PRL_SUCCEEDED(ret))
{
// Everything's OK, print the machine name.
printf("VM name: %s\n", sVmName);
}
else if (ret == PRL_ERR_BUFFER_OVERRUN)
{
// The sVmName array size is too small.
// Get the required size, allocate the memory,
// and try getting the VM name again.
PRL_UINT32 nSize = 0;
PRL_STR pBuf;
// Get the required buffer size.
ret = PrlVmCfg_GetName(hVmCfg, 0, &nSize);
// Allocate the memory.
pBuf = (PRL_STR)malloc(sizeof(PRL_CHAR) * nSize);
// Get the virtual machine name.
ret = PrlVmCfg_GetName(hVmCfg, pBuf, &nSize);
printf("VM name: %s\n", pBuf);
// Dallocate the memory.
free(pBuf);
}
Error Handling
Synchronous Functions
All synchronous Parallels C API functions return PRL_RESULT, which is an integer indicating
success or failure of the operation.
25
Parallels C API Concepts
Error Codes for Asynchronous Functions
All asynchronous functions return PRL_HANDLE. The error code (return value) in this case can be
extracted with PrlJob_GetRetCode after the asynchronous job has finished.
Analyzing Return Values
Parallels C API provides the following macros to work with error codes:
PRL_FAILED
PRL_SUCCEEDED
prl_result_to_string
Returns True if the return value indicates failure, or False if the
return value indicates success.
Returns True if the return value indicates success, or False if
the return value indicates failure.
Returns a string representation of the error code.
The following code snippet attempts to create a directory on the host and analyzes the return value
(error code) of asynchronous function PrlSrv_CreateDir.
// Attempt to create directory /tmp/TestDir on the host.
char *szRemoteDir = "/tmp/TestDir";
hJob = PrlSrv_FsCreateDir(hServer, szRemoteDir);
// Wait for a maximum of 5 seconds for asynchronous
// function PrlSrv_FsCreateDir to complete.
PRL_RESULT resWaitForCreateDir = PrlJob_Wait(hJob, 5000);
if (PRL_FAILED(resWaitForCreateDir))
{
fprintf(stderr, "PrlJob_Wait for PrlSvr_FsCreateDir failed with error: %s\n",
prl_result_to_string(resWaitForCreateDir));
PrlHandle_Free(hJob);
return -1;
}
// Extract the asynchronous function return code.
PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
fprintf(stderr, "Error creating directory %s. Error returned: %s\n",
szRemoteDir, prl_result_to_string(nJobResult));
PrlHandle_Free(hJob);
return -1;
}
Descriptive error messages can sometimes be obtained using the PrlJob_GetError function.
This function will return a handle to an object of type PHT_EVENT. In cases where PrlJob_GetError is unable to return error information, PrlApi_GetResultDescription
can be used. Although it is possible to avoid using PrlJob_GetError and use
PrlJob_GetResultDescription instead, it is recommended to first use PrlJob_GetError,
and if this doesn't return additional descriptive error information then use
PrlApi_GetResultDescription. The reason is that sometimes errors contain dynamic
parameters. The following example demonstrates how to obtain descriptive error information:
// Check if additional error information is available.
if (PRL_SUCCEEDED(ret)) // Additional error information is available.
{
// Additional error information is available.
ret = PrlEvent_GetErrString(hError, PRL_FALSE, PRL_FALSE, szErrBuff,
&nErrBuffSize);
if (PRL_FAILED(ret))
{
printf("PrlEvent_GetErrString returned error: %.8x %s\n",
ret, prl_result_to_string(ret));
}
else
{
// Extra error information is available, display it.
printf("Error returned: %.8x %s\n", nJobResult,
prl_result_to_string(nJobResult));
printf("Descriptive error: %s\n", szErrBuff);
}
}
else
{
// No additional error information available, so use
PrlApi_GetResultDescription.
ret = PrlApi_GetResultDescription(nJobResult, PRL_FALSE, PRL_FALSE, szErrBuff,
&nErrBuffSize);
if (PRL_FAILED(ret))
{
printf("PrlApi_GetResultDescription returned error: %s\n",
prl_result_to_string(ret));
}
else
{
printf("Error returned: %.8x %s\n", nJobResult,
prl_result_to_string(nJobResult));
printf("Descriptive error: %s\n", szErrBuff);
}
}
// Free handles, return the error code.
PrlHandle_Free(hJob);
27
Parallels C API Concepts
PrlHandle_Free(hError);
return nJobResult;
}
28
C HAPTER 3
Parallels C API by Example
This chapter contains examples of using the Parallels C API. The examples include tasks such as
performing general Parallels Desktop tasks, creating and managing virtual machines, handling
event, collecting performance data, and others.
In This Chapter
Obtaining Server Handle and Logging In ................................................................. 30
The following steps are required in any program using the Parallels C API:
1 Load the Parallels Virtualization SDK library using the SdkWrap_Load function.
2 Initialize the API using the PrlApi_InitEx function. The API must be initialized properly for
the given Parallels product, such as Parallels Server, Parallels Desktop. The initialization mode is
determined by the value of the nAppMode parameter passed to the PrlApi_InitEx
function. The value must be one of the enumerators from the PRL_APPLICATION_MODE
enumeration.
3 Create a server handle using the PrlSrv_Create function.
4 Call PrlSrv_LoginLocal or PrlSrv_Login to login to the Parallels Virtualization Service
(or simply Parallels Service). Parallels Service is a combination of processes running on the host
machine that comprise the Parallels virtualization product. The first function is used when the
program and the target Parallels Service are running on the same host. The second function
(PrlSrv_Login) is used to log in to a remote Parallels Service. Please note that remote login
is supported in Parallels Server-based virtualization products only.
If the above steps are executed without errors, the handle created in step 3 will reference a Server
object (a handle of type PHT_SERVER) identifying the Parallels Service. A handle to a valid Server
object is required to access most of the functionality within the Parallels C API. The
PrlSrv_LoginLocal function (step 4) establishes a connection with a specified Parallels Service
and performs a login operation using the specified credentials. The function operates on the Server
object created in step 3. Upon successful login, the object can be used to perform other
operations.
To end the session with the Parallels Service, the following steps must be performed before exiting
the application:
1 Call PrlSrv_Logoff to log off the Parallels Service.
2 Free the Server handle using PrlHandle_Free.
3 Call PrlApi_Deinit to de-initialize the library.
4 Call SdkWrap_Unload to unload the API.
Example
The following sample functions demonstrates how to perform the steps described above.
// Intializes the SDK library and
// logs in to the local Parallels Service.
// Obtains a handle of type PHT_SERVER identifying
// the Parallels Service.
Loading...
+ 133 hidden pages
You need points to download manuals.
1 point = 1 manual.
You can buy points or you can get point for every manual you upload.