Sending argument to an .exe via SHELLEXECUTEINFO - c++

I have an .exe which takes 2 arguments. I need to run the .exe file between my program in runtime. I have used ShellExecuteEx to run the .exe file but it is not taking the arguments. Please help where did i went wrong. I am posting my code below.
void StartProgram() {
SHELLEXECUTEINFO lpExecInfo;
lpExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
lpExecInfo.lpFile = L"F:\\EXEFolder\\RunMe.exe"; // Path to the .exe
lpExecInfo.fMask = SEE_MASK_DOENVSUBST | SEE_MASK_NOCLOSEPROCESS;
lpExecInfo.hwnd = NULL;
lpExecInfo.lpVerb = L"open"; // to open program
lpExecInfo.lpParameters = L"D:/input.csv" "F:/output.csv;"; //Arguments to be passed to .exe
lpExecInfo.lpDirectory = NULL;
lpExecInfo.nShow = SW_SHOW;
lpExecInfo.hInstApp = (HINSTANCE)SE_ERR_DDEFAIL;
ShellExecuteEx(&lpExecInfo);
//wait until a file is finished printing
if (lpExecInfo.hProcess != NULL)
{
::WaitForSingleObject(lpExecInfo.hProcess, INFINITE);
::CloseHandle(lpExecInfo.hProcess);
}
}

The lpParameters member is a pointer to a space-delimited string of arguments, just like you would pass them in a command-line environment.
If you want to pass two arguments to the program, you do it like
lpExecInfo.lpParameters = L"argument1 argument2";
For your case it should be like
lpExecInfo.lpParameters = L"D:/input.csv F:/output.csv";
To explain what you do wrong, the C++ compiler have a phase where it concatenate adjacent string literals into a single string. That means L"D:/input.csv" "F:/output.csv;" will be concatenated into L"D:/input.csvF:/output.csv;" which is a single argument.

Related

Keep ShellExecuteExW's command prompt opened

I'm trying to use ShellExecuteExW to run a bat file.
I want the command prompt to be kept opened after the bat file has been executed.
Here's what I tried,
SHELLEXECUTEINFOW TempInfo = { 0 };
TempInfo.cbSize = sizeof(SHELLEXECUTEINFOA);
TempInfo.fMask = 0;
TempInfo.hwnd = NULL;
TempInfo.lpVerb = L"runas";
TempInfo.lpFile = L"cmd.exe";
TempInfo.lpParameters = L"\\k runMe.bat StartTool";
TempInfo.lpDirectory = NULL;
TempInfo.nShow = SW_NORMAL;
::ShellExecuteExW(&TempInfo);
But what I see is my cmd.exe stays opened but it didn't actually ran my runMe.bat file.
#dewaffled answered it... /k instead of \\k

TaskDialogIndirect is returning an unusual error code

I'm using TaskDialogIndirect to display prompts to the user. Normally this works just fine, but sometimes, after the program has been running for a while, it begins returning an error code that the MSDN entry does not list as being one of the error codes this function can return.
0x80040001 OLE_E_ADVF "Invalid advise flags"
I have checked all the inputs to the function against previous successful calls in the same run. Aside from differences in the string to be displayed, they are identical. (the strings are even the same length.)
// create task dialog struct
TASKDIALOGCONFIG tdc;
ZeroMemory(&tdc, sizeof(TASKDIALOGCONFIG));
tdc.cbSize = sizeof(tdc);
tdc.dwFlags = (((dwMessageBoxFlags & MB_OKCANCEL) == MB_OKCANCEL) ? TDF_ALLOW_DIALOG_CANCELLATION : 0) | TDF_POSITION_RELATIVE_TO_WINDOW;
tdc.hwndParent = hwndOwner;
tdc.hInstance = LGetHInstance();
tdc.pszContent = usrText.wsz;
tdc.pButtons = _pButtons;
tdc.cButtons = nButtons;
tdc.pszMainIcon = pszTaskDialogIcon;
tdc.pszWindowTitle = usrCaption.wsz;
tdc.nDefaultButton = nDefaultButton;
// display it now
int iButton = 0;
BOOL b = 0;
HRESULT hResult = TaskDialogIndirect(&tdc, &iButton, NULL, &b);
NEW INFORMATION
At the same time that TaskDialogIndirect stops behaving correctly, ShellExecute also stops working, as does CreateFile.
This was actually caused by an event handle leak elsewhere. When the available handles ran out, no more API calls which needed to create a handle could succeed. They did return a rather odd set of error codes though, none of which was "out of handles".

Controlling Program Flow Between VB and C++

I have a program that runs from VB/Excel and executes a C++ program in the middle of it. I have two (I think related) questions:
I capture the return value from the C++ program when it executes, but the number I get isn't zero (it's a 4-digit integer value instead, sample values I've received are 8156, 5844, 6100, 5528). I am certain the program exits normally with code 0, but I think VB is getting its value before the C++ program has completed its execution - would that explain why I am not getting a value of zero, and how I can get the final, correct return value from my C++ program?
[Probably as a solution to #1] How can I make the VB program "pause" until the C++ model has completed its execution? I need to do some additional VB work (output configuration based on the C++ model run) once the model is complete
Here is my VB code for how the model call. I am running a full-compiled C++ program through the windows shell.
'---------------------------------------------------------
' SECTION III - RUN THE MODEL AS C++ EXECUTABLE
'---------------------------------------------------------
Dim ModelDirectoryPath As String
Dim ModelExecutableName As String
Dim ModelFullString As String
Dim ret As Long
ModelDirectoryPath = Range("ModelFilePath").value
ModelExecutableName = Range("ModelFileName").value
ModelFullString = ModelDirectoryPath & ModelExecutableName
' Call the model to run
Application.StatusBar = "Running C Model..."
ModelFullString = ModelFullString & " " & ScenarioCounter & " " & NumDeals _
& " " & ModelRunTimeStamp
ret = Shell(ModelFullString)
' Add error checking based on return value
' This is where I want to do some checks on the return value and then start more VB code
1) You are capturing the Task ID of the program (this is what Shell() returns) not any return from the opened programme - that is why it is a 4 digit number
2) Shell() runs all programs asychronously.
To run a program synchronously or to run it and wait for the return, either:
Use a Windows API function (I refer you to https://stackoverflow.com/a/5686052/1101846 for a list of options / API calls you could use)
Much more easily, use the WshShell object provided by Windows Scripting Host (see https://stackoverflow.com/a/8906912/1101846 for more examples than what I give below). See Microsoft documentation of the Run method at http://msdn.microsoft.com/en-us/library/d5fk67ky
Essentially, do something like:
Set o = CreateObject("WScript.Shell")
valueReturnedFromYourProgram = o.Run( _
strCommand:="notepad", _
intWindowStyle:=1,
bWaitOnReturn:=true)
Debug.Print valueReturnedFromYourProgram
I'm not sure what the VBA code would be but have you checked out ShellExecuteEx? Below is the C/C++ code:
SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = "c:\\MyProgram.exe";
ShExecInfo.lpParameters = "";
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess,INFINITE);

SymEnumSourceFiles get incomplete file names

I was tring to use SymEnumSourceFiles to get the file name within the debugging module.But the file name as a parameter in the callback function seems incomplete. e.g. A file named"c:\program files\test\test.cpp" only showed "c:\program fi" in the FileName part of the PSOURCEFILE type parameter, and that's very wired.
Here is my code:
struct foo
{
static BOOL CALLBACK run( PSOURCEFILE pSourceFile, PVOID UserContext)
{
static TCHAR szFileName[MAX_PATH] = _T("");
if (_tcscmp(szFileName, pSourceFile->FileName))
{
_tcscpy(szFileName, pSourceFile->FileName);
}
return TRUE;
}
};
HANDLE hCurrentProcess = GetCurrentProcess();
SymInitialize(hCurrentProcess, NULL, FALSE);
DWORD64 BaseOfDll = SymLoadModule64(hCurrentProcess,
NULL,
(LPCSTR)_bstr_t(lpszFile),
NULL,0,0);
if(!SymEnumSourceFiles(hCurrentProcess, BaseOfDll, NULL, foo::run, (PVOID)pCallBack))
{
ATLTRACE(_T(__FUNCTION__) _T(" error:0x%x\n"), GetLastError());
}
SymUnloadModule64(hCurrentProcess, BaseOfDll);
SymCleanup(hCurrentProcess);
Can anyone tell me where I go wrong please?
PS. When I simply replace SymEnumSourceFiles with SymEnumLines and change the callback function, the file name I got is correct.
There used to be a bug with SymEnumSymbols where symbol name was not zero terminated, maybe this one is related. Try zeroing out the filename after copying it (pSourceFile->FileName[0] = 0;).
Try ANSI version of SymEnumSourceFiles. There seems to be a bug in SymEnumSourceFilesW function.

Unable to set Reporting Services Parameters

I'm generating a reporting services report from an ASP.NET (MVC) based application but am having problems setting the parameters for the report.
I believe the issue has only occurred since we upgraded SQL Server from 2005 to 2008 R2 (and Reporting Services along with it).
The original error encountered was from calling rsExec.Render:
Procedure or function 'pCommunication_ReturnRegistrationLetterDetails'
expects parameter '#guid', which was not supplied.
Debugging the code I noticed that rsExec.SetExecutionParameters is returning the following response:
Cannot call 'NameOfApp.SQLRSExec.ReportExecutionService.SetExecutionParameters(NameOfApp.SQLRSExec.ParameterValue[],
string)' because it is a web method.
Here is the function in it's entirety:
public static bool ProduceReportToFile(string reportname, string filename, string[,] reportparams,
string fileformat)
{
bool successful = false;
SQLRS.ReportingService2005 rs = new SQLRS.ReportingService2005();
SQLRSExec.ReportExecutionService rsExec = new NameOfApp.SQLRSExec.ReportExecutionService();
rs.Credentials = System.Net.CredentialCache.DefaultCredentials;
rsExec.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Prepare Render arguments
string historyID = null;
string deviceInfo = null;
// Prepare format - available options are "PDF","Word","CSV","TIFF","XML","EXCEL"
string format = fileformat;
Byte[] results;
string encoding = String.Empty;
string mimeType = String.Empty;
string extension = String.Empty;
SQLRSExec.Warning[] warnings = null;
string[] streamIDs = null;
// Define variables needed for GetParameters() method
// Get the report name
string _reportName = reportname;
string _historyID = null;
bool _forRendering = false;
SQLRS.ParameterValue[] _values = null;
SQLRS.DataSourceCredentials[] _credentials = null;
SQLRS.ReportParameter[] _parameters = null;
// Get if any parameters needed.
_parameters = rs.GetReportParameters(_reportName, _historyID,
_forRendering, _values, _credentials);
// Load the selected report.
SQLRSExec.ExecutionInfo ei =
rsExec.LoadReport(_reportName, historyID);
// Prepare report parameter.
// Set the parameters for the report needed.
SQLRSExec.ParameterValue[] parameters =
new SQLRSExec.ParameterValue[1];
// Place to include the parameter.
if (_parameters.Length > 0)
{
for (int i = 0; i < _parameters.Length; i++)
{
parameters[i] = new SQLRSExec.ParameterValue();
parameters[i].Label = reportparams[i,0];
parameters[i].Name = reportparams[i, 0];
parameters[i].Value = reportparams[i, 1];
}
}
rsExec.SetExecutionParameters(parameters, "en-us");
results = rsExec.Render(format, deviceInfo,
out extension, out encoding,
out mimeType, out warnings, out streamIDs);
// Create a file stream and write the report to it
using (FileStream stream = System.IO.File.OpenWrite(filename))
{
stream.Write(results, 0, results.Length);
}
successful = true;
return successful;
}
Any ideas why I'm now unable to set parameters? The report generation works without issue if parameters aren't required.
Looks like it may have been an issue with how reporting services passes parameters through to the stored procedure providing the data. A string guid was being passed through to the report and the stored procedure expected a varchar guid. I suspect reporting services may have been noticing the string followed the guid format pattern and so passed it through as a uniqueidentifier to the stored procedure.
I changed the data source for the report from "stored procedure" to "text" and set the SQL as "EXEC pMyStoredOProcName #guid".
Please note the guid being passed in as a string to the stored procedure is probably not best practice... I was simply debugging an issue with another developers code.
Parameter _reportName cannot be null or empty. The [CLASSNAME].[METHODNAME]() reflection API could not create and return the SrsReportNameAttribute object
In this specific case it looks like an earlier full compile did not finish.
If you encounter this problem I would suggest that you first compile the class mentioned in the error message and see if this solves the problem.
go to AOT (get Ctrl+D)
in classes find CLASSNAME
3.compile it (F7)