//
//
// Copyright (c) 2012, Daniel Cornel. Published on drivenbynostalgia.com.
// All rights reserved.
//
//
//
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the copyright holder nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//
#ifndef _SOP_HPP
#define _SOP_HPP
#include
#include
// Return values
#define SOP_RESULT_NO_CHANGE 0
#define SOP_RESULT_CHANGE 1
#define SOP_RESULT_ERROR -1
// Descriptor for an application bound to an application profile
struct Application {
unsigned long version;
unsigned long isPredefined;
unsigned short appName[2048];
unsigned short userFriendlyName[2048];
unsigned short launcher[2048];
unsigned short fileInFolder[2048];
};
// Application profile descriptor
struct Profile {
unsigned long version;
unsigned short profileName[2048];
unsigned long* gpuSupport;
unsigned long isPredefined;
unsigned long numOfApps;
unsigned long numOfSettings;
};
// Setting descriptor
struct Setting {
struct BinarySetting {
unsigned long valueLength;
unsigned char valueData[4096];
};
unsigned long version;
unsigned short settingName[2048];
unsigned long settingID;
unsigned long settingType;
unsigned long settingLocation;
unsigned long isCurrentPredefined;
unsigned long isPredefinedValid;
union {
unsigned long u32PredefinedValue;
BinarySetting binaryPredefinedValue;
unsigned short strPredefinedValue[2048];
};
union {
unsigned long u32CurrentValue;
BinarySetting binaryCurrentValue;
unsigned short strCurrentValue[2048];
};
};
// Definitions for required NvAPI functions
typedef int (*CreateApplicationT)(int session, int profile, Application* application);
CreateApplicationT CreateApplication = NULL;
typedef int (*CreateProfileT)(int session, Profile* profileInfo, int* profile);
CreateProfileT CreateProfile = NULL;
typedef int (*CreateSessionT)(int* session);
CreateSessionT CreateSession = NULL;
typedef int (*DeleteProfileT)(int session, int profile);
DeleteProfileT DeleteProfile = NULL;
typedef int (*DestroySessionT)(int session);
DestroySessionT DestroySession = NULL;
typedef int (*EnumApplicationsT)(int session, int profile, unsigned long startIndex, unsigned long* appCount, Application* application);
EnumApplicationsT EnumApplications = NULL;
typedef int (*FindProfileByNameT)(int session, unsigned short profileName[2048], int* profile);
FindProfileByNameT FindProfileByName = NULL;
typedef int (*GetProfileInfoT)(int session, int profile, Profile* profileInfo);
GetProfileInfoT GetProfileInfo = NULL;
typedef int (*LoadSettingsT)(int session);
LoadSettingsT LoadSettings = NULL;
typedef int (*SaveSettingsT)(int session);
SaveSettingsT SaveSettings = NULL;
typedef int (*SetSettingT)(int session, int profile, Setting* setting);
SetSettingT SetSetting = NULL;
typedef int (*InitializeT)();
InitializeT Initialize = NULL;
typedef int* (*QueryInterfaceT)(unsigned int offset);
QueryInterfaceT QueryInterface = NULL;
bool CheckForError(int status) {
if (status != 0) {
#if _DEBUG
fprintf(stderr, "NvAPI error in SetOptimusProfile: %i\n", status);
#endif
return true;
}
else {
return false;
}
}
bool UnicodeStringCompare(unsigned short firstString[2048], unsigned short secondString[2048]) {
for (int i = 0; i < 2048; i++) {
if (firstString[i] != secondString[i]) {
return false;
}
}
return true;
}
void GetUnicodeString(std::string sourceString, unsigned short (* destinationString)[2048]) {
for (unsigned int i = 0; i < 2048; i++) {
if (i < sourceString.length()) {
(*destinationString)[i] = (unsigned short) sourceString[i];
}
else {
(*destinationString)[i] = 0;
}
}
}
bool GetProcs() {
// Check if this is a 32 bit application
if (sizeof(void*) != 4) {
#if _DEBUG
fprintf(stderr, "Only 32 bit applications are supported.");
#endif
return false;
}
HMODULE hMod = LoadLibraryA("nvapi.dll");
// Check if the nvapi.dll is available
if (hMod == NULL) {
#if _DEBUG
fprintf(stderr, "The nvapi.dll could not be found.");
#endif
return false;
}
// This function is used to get all other function procs
QueryInterface = (QueryInterfaceT) GetProcAddress(hMod, "nvapi_QueryInterface");
// Query the procs with an ID
// the IDs can be retrieved by parsing the nvapi.lib such that no library has to be linked
CreateApplication = (CreateApplicationT) (*QueryInterface)(0x4347A9DE);
CreateProfile = (CreateProfileT) (*QueryInterface)(0xCC176068);
CreateSession = (CreateSessionT) (*QueryInterface)(0x0694D52E);
DeleteProfile = (DeleteProfileT) (*QueryInterface)(0x17093206);
DestroySession = (DestroySessionT) (*QueryInterface)(0xDAD9CFF8);
EnumApplications = (EnumApplicationsT) (*QueryInterface)(0x7FA2173A);
FindProfileByName = (FindProfileByNameT) (*QueryInterface)(0x7E4A9A0B);
GetProfileInfo = (GetProfileInfoT) (*QueryInterface)(0x61CD6FD6);
LoadSettings = (LoadSettingsT) (*QueryInterface)(0x375DBD6B);
SaveSettings = (SaveSettingsT) (*QueryInterface)(0xFCBC7E14);
SetSetting = (SetSettingT) (*QueryInterface)(0x577DD202);
Initialize = (InitializeT) (*QueryInterface)(0x0150E828);
return true;
}
bool ContainsApplication(int session, int profile, Profile profileDescriptor, unsigned short applicationName[2048], Application* application) {
if (profileDescriptor.numOfApps == 0) {
return false;
}
// Iterate over all applications bound to the profile
unsigned long numAppsRead = profileDescriptor.numOfApps;
Application* allApplications = new Application[profileDescriptor.numOfApps];
allApplications[0].version = 147464; // Calculated from the size of the descriptor
if (CheckForError((*EnumApplications)(session, profile, 0, &numAppsRead, allApplications))) {
delete[] allApplications;
return false;
}
for (unsigned int i = 0; i < numAppsRead; i++) {
if (UnicodeStringCompare(allApplications[i].appName, applicationName)) {
application = &allApplications[i];
return true;
}
}
delete[] allApplications;
return false;
}
// Call this from your application to check if an application profile with
// the name provided exists.
bool SOP_CheckProfile(std::string profileNameString) {
bool result = false;
int session = NULL;
int profile = NULL;
// Initialize NvAPI
if ((!GetProcs()) || (CheckForError((*Initialize)()))) {
return false;
}
// Create a unicode string from the profile name
unsigned short profileName[2048];
GetUnicodeString(profileNameString, &profileName);
// Create the session handle to access driver settings
if (CheckForError((*CreateSession)(&session))) {
return false;
}
// Load all the system settings into the session
if (CheckForError((*LoadSettings)(session))) {
return false;
}
// Check if the application profile with the specified name exists
result = ((*FindProfileByName)(session, profileName, &profile) == 0);
(*DestroySession)(session);
session = NULL;
return result;
}
// Call this from your application to delete the application profile with
// the name provided. Note that only one application profile per name exists
// for all applications bound to it.
// After the profile has been erased, the application will use the default GPU
// the next time it is started, usually the integrated GPU.
int SOP_RemoveProfile(std::string profileNameString) {
int result = SOP_RESULT_NO_CHANGE;
int status = 0;
int session = NULL;
int profile = NULL;
// Initialize NvAPI
if ((!GetProcs()) || (CheckForError((*Initialize)()))) {
return SOP_RESULT_ERROR;
}
// Create a unicode string from the profile name
unsigned short profileName[2048];
GetUnicodeString(profileNameString, &profileName);
// Create the session handle to access driver settings
if (CheckForError((*CreateSession)(&session))) {
return SOP_RESULT_ERROR;
}
// Load all the system settings into the session
if (CheckForError((*LoadSettings)(session))) {
return SOP_RESULT_ERROR;
}
// Check if the application profile with the specified name already exists
status = (*FindProfileByName)(session, profileName, &profile);
if (status == 0) {
// The application profile with the specified name exists and can be deleted
if ((CheckForError((*DeleteProfile)(session, profile))) ||
CheckForError((*SaveSettings)(session))) {
return SOP_RESULT_ERROR;
}
else {
result = SOP_RESULT_CHANGE;
}
}
else if (status == -163 /* Profile not found */) {
// The application profile does not exist and does not have to be deleted
result = SOP_RESULT_NO_CHANGE;
}
else {
return SOP_RESULT_ERROR;
}
(*DestroySession)(session);
session = NULL;
return result;
}
// Call this from your application to create a generic application profile with
// the name provided and add the application name to it. The application profile is
// set to start all bound applications with the discrete (NVIDIA) GPU. If the profile
// already exists because it is shared among several applications, the provided
// application name is bound to the existing profile.
// The changes take effect the next time the application is started.
int SOP_SetProfile(std::string profileNameString, std::string applicationNameString) {
int result = SOP_RESULT_NO_CHANGE;
int status = 0;
int session = NULL;
int profile = NULL;
// Initialize NvAPI
if ((!GetProcs()) || (CheckForError((*Initialize)()))) {
return SOP_RESULT_ERROR;
}
// Create a unicode string from the profile name
unsigned short profileName[2048];
GetUnicodeString(profileNameString, &profileName);
// Create a unicode string from the application name
std::transform(applicationNameString.begin(), applicationNameString.end(), applicationNameString.begin(), ::tolower);
unsigned short applicationName[2048];
GetUnicodeString(applicationNameString, &applicationName);
// Create the session handle to access driver settings
if (CheckForError((*CreateSession)(&session))) {
return SOP_RESULT_ERROR;
}
// Load all the system settings into the session
if (CheckForError((*LoadSettings)(session))) {
return SOP_RESULT_ERROR;
}
// Check if the application profile with the specified name already exists
status = (*FindProfileByName)(session, profileName, &profile);
if (status == -163 /* Profile not found */) {
// The application profile does not yet exist and has to be created
Profile newProfileDescriptor = { NULL };
newProfileDescriptor.version = 69652; // Calculated from the size of the descriptor
newProfileDescriptor.isPredefined = 0;
memcpy(&newProfileDescriptor.profileName, &profileName, 2048);
newProfileDescriptor.gpuSupport = new unsigned long[32];
newProfileDescriptor.gpuSupport[0] = 1;
// Create the application profile
if (CheckForError((*CreateProfile)(session, &newProfileDescriptor, &profile))) {
delete newProfileDescriptor.gpuSupport;
return SOP_RESULT_ERROR;
}
// Create the application settings. This is where the discrete GPU for Optimus is set.
Setting optimusSetting = { NULL };
optimusSetting.version = 77856; // Calculated from the size of the descriptor
optimusSetting.settingID = 0x10F9DC81; // Shim rendering mode ID
optimusSetting.u32CurrentValue = 0x00000001 | 0x00000010; // Enable | Auto select
if (CheckForError((*SetSetting)(session, profile, &optimusSetting))) {
delete newProfileDescriptor.gpuSupport;
return SOP_RESULT_ERROR;
}
delete newProfileDescriptor.gpuSupport;
}
else if (CheckForError(status)) {
return SOP_RESULT_ERROR;
}
// Retrieve the profile information of the application profile
Profile profileDescriptor = { NULL };
profileDescriptor.version = 69652; // Calculated from the size of the descriptor
if (CheckForError((*GetProfileInfo)(session, profile, &profileDescriptor))) {
return SOP_RESULT_ERROR;
}
// Application descriptor
Application applicationDescriptor = { NULL };
if (!ContainsApplication(session, profile, profileDescriptor, applicationName, &applicationDescriptor)) {
applicationDescriptor.version = 147464; // Calculated from the size of the descriptor
applicationDescriptor.isPredefined = 0;
memcpy(&applicationDescriptor.appName, &applicationName, 2048);
// Add the current application to the new profile
if ((CheckForError((*CreateApplication)(session, profile, &applicationDescriptor))) ||
(CheckForError((*SaveSettings)(session)))) {
return SOP_RESULT_ERROR;
}
else {
result = SOP_RESULT_CHANGE;
}
}
(*DestroySession)(session);
session = NULL;
return result;
}
#endif