Run MsiExec.exe from c++? Windows - c++

MsiExec.exe /X{9BA100BF-B59D-4657-9530-891B6EE24E31};
I need to run this command through my cpp project in main. This is a new version of a piece of software that needs to remove the older version before installing. I want to do this using the Uninstall String from the application's registry. Is there a way to do this in cpp? I'm using Qt 5.5. Thanks.

One of the simplest ways is to use the system function.
I.e.:
int result = system("MsiExec.exe /X{9BA100BF-B59D-4657-9530-891B6EE24E31}");
Other more Windows specific ways involve the use of CreateProcess or ShellExecute Windows Win32 API functions.

Is there a way to search out the uninstall key by looking in the registry for a matching DisplayName? Then, if you find the GUID by DisplayName, run the uninstall string like above? – RGarland
Of course there is. You can use native Windows API for manipulating registry or if you prefer you can use some existing C++ wrapper around that API.
I wrote small easy to use Registry wrapper which supports enumerating registry keys.
I think you may find it useful to solve your problem.
#include <Registry.hpp>
using namespace m4x1m1l14n;
std::wstring GetProductCodeByDisplayName(const std::wstring& displayName)
{
std::wstring productCode;
auto key = Registry::LocalMachine->Open(L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall");
key->EnumerateSubKeys([&](const std::wstring& subKeyName) -> bool
{
auto subKey = key->Open(subKeyName);
if (subKey->HasValue(L"DisplayName"))
{
if (displayName == subKey->GetString(L"DisplayName"))
{
// Product found! Store product code
productCode = subKeyName;
// Return false to stop processing
return false;
}
}
// Return true to continue processing subkeys
return true;
});
return productCode;
}
int main()
{
try
{
auto productCode = GetProductCodeByDisplayName(L"VMware Workstation");
if (!productCode.empty())
{
// Uninstall package
}
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
return 0;
Also you should be aware, that some packages is not stored by its package code under Uninstall registry key, but by their names and to uninstall them you must search for UninstallString value within specific subkey and call this package instead.

Related

How to convert the output of boost::interprocess::winapi::get_last_bootup_time() to the unix time?

I need to get the time of the latest system boot. I'm using the following boost function:
#include <boost/interprocess/detail/win32_api.hpp>
std::string SystemLastBootTime = "";
if (boost::interprocess::winapi::get_last_bootup_time(SystemLastBootTime)) {
MessageBox(NULL, SystemLastBootTime.c_str(), "Last Boot Time", MB_OK | MB_ICONEXCLAMATION);
}
The output of SystemLastBootTime is:
AA000000_0E030000
What's exactly in this string, and how to convert it to a "normal" unix time, so I can compare and see if the system booted during the last 5 minutes or later?
The implementation is right there. There's actually different implementations:
when BOOST_INTERPROCESS_BOOTSTAMP_IS_SESSION_MANAGER_BASED is defined
when BOOST_INTERPROCESS_BOOTSTAMP_IS_EVENTLOG_BASED is defined
From Session Manager
inline bool get_last_bootup_time(std::string &stamp)
{
unsigned dword_val = 0;
std::size_t dword_size = sizeof(dword_val);
bool b_ret = get_registry_value_buffer( hkey_local_machine
, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters"
, "BootId", &dword_val, dword_size);
if (b_ret)
{
char dword_str[sizeof(dword_val)*2u+1];
buffer_to_narrow_str(&dword_val, dword_size, dword_str);
dword_str[sizeof(dword_val)*2] = '\0';
stamp = dword_str;
b_ret = get_registry_value_buffer( hkey_local_machine
, "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Power"
, "HybridBootAnimationTime", &dword_val, dword_size);
//Old Windows versions have no HybridBootAnimationTime
if(b_ret)
{
buffer_to_narrow_str(&dword_val, dword_size, dword_str);
dword_str[sizeof(dword_val)*2] = '\0';
stamp += "_";
stamp += dword_str;
}
b_ret = true;
}
return b_ret;
}
From Event Log
This is even grittier, and way more code since apparently they chose not to use an existing library. They end up looking for event ID 6005, and then reading the
unsigned long TimeGenerated; // Seconds since 1-1-1970
from that record.
Summarizing
As you can see the whole thing is proprietary, and you might just want to implement it yourself without (ab)using implementation details from Boost Interprocess.
The "good news" is that you might be able to get some documentation from Microsoft about either the Session Manager registry key, or System event 6005.
Oh, and don't forget that defining BOOST_INTERPROCESS_BOOTSTAMP_IS_EVENTLOG_BASED could we result in a readable timestamp to begin with, since it just formats the UNIX timestamp with %u to the output.

Has anyone used Appium/WinAppDriver for automating desktop applications.

I am looking for automating a windows application,and researching on what tools to be used. I have come across Appium/WinAppDriver, but not sure if it has a good usage anywhere so far....Appreciate suggestions on this.
I'm currently using the WinAppDriver to automate a WPF program. It's very similar to Selenium, if you have any experience with that then I'd advise using the WinAppDriver over something like White. You also get to use the Selenium WebDriverWait which was a massive bonus.
There is also a tool known as 'Inspect' that comes with the Windows SDK that allows you to inspect a windows application similar to the web-browser dev tools.
You simply initiate a driver (similar to Selenium) however you also need to start the WinApp process.
C# example:
protected WindowsDriver<WindowsElement> GetWindowsDriver()
{
var appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("app",
PathHelper.GetClientInstallPath() + "APPLICATION.exe");
appCapabilities.SetCapability("deviceName", "WindowsPC");
if (!IsWinAppDriverProcesssRunning())
{
StartWinAppProcessRunning();
}
var driver = new WindowsDriver<WindowsElement>(new Uri("http://127.0.0.1:4723"), appCapabilities);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
return driver;
}
private static bool IsWinAppDriverProcesssRunning()
{
const string processName = "WinAppDriver";
var existingProcesses = Process.GetProcessesByName(processName);
return existingProcesses.Any();
}
private static void StartWinAppProcessRunning()
{
const string winAppDirectory = #"C:\Program Files (x86)\Windows Application Driver";
var winAppProcess =
new Process
{
StartInfo =
{
FileName = Path.Combine(winAppDirectory, "WinAppDriver.exe"),
WindowStyle = ProcessWindowStyle.Hidden,
WorkingDirectory = winAppDirectory
}
};
winAppProcess.Start();
}

How to get Game Controller name (Windows 10 / C++)

I've seen lots of info on how to read game controller input using XInput but I really want to know the name of the controller that is connected.
How can I find out the name of connected controllers on a PC or more specifically the name of the controller I am reading XInput from?
You can do this by calling the joyGetDevCaps function which returns a JOYCAPS structure containing all information (including name) of the connected controller.
You can use DirectInput to get the name of the device. You need to do that using a callback:
pDirectInput->EnumDevices(DI8DEVCLASS_GAMECTRL, EnumJoystickCallbackStatus, &joynum, DIEDFL_ATTACHEDONLY);
Then you have to be a bit creative: on Startup detect all devices using the callback and store their name/GUID... and then when a device is hot-plugged (which you detect with XInputGetState) look for the device which you don't know about yet, with a modified version of that earlier callback, something similar to this:
BOOL CALLBACK EnumJoystickCallbackStatus(LPCDIDEVICEINSTANCE pdevinst, LPVOID pref)
{
DWORD devtype = GET_DIDEVICE_TYPE(pdevinst->dwDevType);
DWORD subtype = GET_DIDEVICE_SUBTYPE(pdevinst->dwDevType);
if (devtype == DI8DEVTYPE_KEYBOARD || (devtype == DI8DEVTYPE_SUPPLEMENTAL && subtype == DI8DEVTYPESUPPLEMENTAL_UNKNOWN)) {
return DIENUM_CONTINUE;
}
ULONG* pjoynum = reinterpret_cast<ULONG*>(pref);
if (IsXInputDevice(&pdevinst->guidProduct)) {
// loop through your known devices and see if this GUI already exists
// we are looking for one which we don't know about yet.
if (!found) {
// store GUI / Name / ... in some global controllers-array
return DIENUM_STOP; // done
}
}
DEBUG_INFO(Debug::XDF_General, "continue");
return DIENUM_CONTINUE;
}
Note that if you have multiple xbox-controllers, you'll get a callback for each one separately.
Implementation of IsXInputDevice can be found in the MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ee417014(v=vs.85).aspx

Address COM in OS X via FireMonkey/C++

The COM-based Blackmagic DeckLink API is available for both Windows and OS X. I wish to address it in OS X but using FireMonkey (FMX) in C++. The problem is that their sample code* is written for Cocoa and I have no idea how to rewrite it for FireMonkey. Does anyone have any experience with this, is it even possible.
Or, is there a generic way in which libraries with a COM interface can be addressed in FireMonkey/OS X?
Here's part of the code for Cocoa, per request.
void InitDeckLinkAPI (void)
{
CFURLRef bundleURL;
bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR(kDeckLinkAPI_BundlePath), kCFURLPOSIXPathStyle, true);
if (bundleURL != NULL)
{
gDeckLinkAPIBundleRef = CFBundleCreate(kCFAllocatorDefault, bundleURL);
if (gDeckLinkAPIBundleRef != NULL)
{
gCreateIteratorFunc = (CreateIteratorFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkIteratorInstance_0002"));
gCreateAPIInformationFunc = (CreateAPIInformationFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateDeckLinkAPIInformationInstance_0001"));
gCreateOpenGLPreviewFunc = (CreateOpenGLScreenPreviewHelperFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateOpenGLScreenPreviewHelper_0001"));
gCreateCocoaPreviewFunc = (CreateCocoaScreenPreviewFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateCocoaScreenPreview_0001"));
gCreateVideoConversionFunc = (CreateVideoConversionInstanceFunc)CFBundleGetFunctionPointerForName(gDeckLinkAPIBundleRef, CFSTR("CreateVideoConversionInstance_0001"));
}
CFRelease(bundleURL);
}
}
bool IsDeckLinkAPIPresent (void)
{
// If the DeckLink API bundle was successfully loaded, return this knowledge to the caller
if (gDeckLinkAPIBundleRef != NULL)
return true;
return false;
}
IDeckLinkIterator* CreateDeckLinkIteratorInstance (void)
{
pthread_once(&gDeckLinkOnceControl, InitDeckLinkAPI);
if (gCreateIteratorFunc == NULL)
return NULL;
return gCreateIteratorFunc();
}
*Too long to include here but you can download it here.
On platforms without native COM support (such as OS X) a C entry point should be provided to access an Interface and in DeckLink API there is such factory methods :
IDeckLinkIterator *deckLinkIterator = CreateDeckLinkIteratorInstance();
Thus you can simply use DeckLink API in C++Builder. But there is a problem, C++Builder has defined some COM types such as IUnknown in sysmac.h (is included by System.hpp) which conflicts with same types have been defined in CFPluginCOM.h, If your project includes System.hpp such as all firemonkey projects the compiler displays an error:
[bccosx Error] sysmac.h(287): E2238 Multiple declaration for 'IUnknown'
There is a sample named DeckControl in samples directory of DeckLink API which is a console program and you can compile it by C++Builder:
Create a console project and specify main.cpp as project source.
Select "None" as Target Framework
Add "OSX" Platform
The project is compiled successfuly.
And what about Fmx project (uses System.hpp) ?
Create a wrapper unit (For example bcb_deck) put needed APIs in it. Note that do not include "DeckLinkAPI.h" in unit header, this causes same problems described above, but put it in cpp (bcb_deck.cpp), for example:
bcb_deck.cpp:
void* createDeckLinkIteratorInstance() // use camel case to prevent conflict
{
return (void*) CreateDeckLinkIteratorInstance();
}
bool deckLinkIteratorNext(void *hDeckLinkIterator, void *hDeckLink)
{
IDeckLinkIterator *deckLinkIterator = (IDeckLinkIterator*) hDeckLinkIterator;
IDeckLink *deckLink = (IDeckLink*) hDeckLink;
return deckLinkIterator->Next(&deckLink) == S_OK;
}
Usage:
#include "bcb_deck.h"
void *hDeckLinkIterator, *hDeckLink;
hDeckLinkIterator = createDeckLinkIteratorInstance();
if (hDeckLinkIterator)
{
// Enumerate all cards in this system
while (deckLinkIteratorNext(hDeckLinkIterator, hDeckLink))
{
// ...
}
}

How to get current directory of a specific drive in Visual Studio Express 2013(C++)?

I am porting a program from Borland C++ Builder to Visual Studio 2013 (C++). The program uses getcurdir to get the current directory of a drive. This function has a parameter drive, but the Microsoft equivalent function getcwd don't have such parameter. How can I do it?
As you tagged visual studio, I assume you'r using windows. Beside that current directory is just one, (i.e. where the executable is located or other if you've moved to) current directory won't be different depending on current drive, I think. Then, in windows you may use the function GetCurrentDirectory from winapi. The prototype is:
DWORD WINAPI GetCurrentDirectory(
_In_ DWORD nBufferLength,
_Out_ LPTSTR lpBuffer
);
You may get details here.
Example:
TCHAR cwd[100];
GetCurrentDirectory(100,cwd);
// now cwd will contain absolute path to current directory
(Yes I know this is an old entry, just for the records if somebody stumbles over the same issue...)
As deeiip already said correctly, in Windows there is only 1 current directory, but cmd.exe fakes DOS behavior when there was 1 current directory per drive.
If you need to access cmd's current directory per drive, use the according hidden environment variables, e.g. "%=C:%".
Here an example application (in C#):
using System;
static class Module1 {
public static void Main(String[] args) {
var myFolder = GetCurrentFolderPerDrive(args[0]); //e.g. "C:"
Console.WriteLine(myFolder);
}
private static string GetCurrentFolderPerDrive(string driveLetter) {
driveLetter = NormalizeDriveLetter(driveLetter);
string myEnvironmentVariable = $"%={driveLetter}%";
string myResult = Environment.ExpandEnvironmentVariables(myEnvironmentVariable);
if (myResult == myEnvironmentVariable) return $"{driveLetter.ToUpperInvariant()}\\"; //No current folder set, return root
return myResult;
}
private static String NormalizeDriveLetter(String driveLetter) {
if (String.IsNullOrWhiteSpace(driveLetter)) throw new ArgumentNullException(nameof(driveLetter), "The drive letter is null, empty or white-space.");
Boolean throwException = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".IndexOf(driveLetter[0]) < 0);
if (!throwException) {
if (driveLetter.Length == 1) {
driveLetter += ':';
} else if (driveLetter.Length != 2) {
throwException = true;
}
}
if (throwException) throw new ArgumentException($"A well-formed drive letter expected, e.g. \"C:\"!\r\nGiven value: \"{driveLetter}\".", nameof(driveLetter));
return driveLetter;
}
}