PDA

Ver la Versión Completa : Problema con ShellExecute


aguml
16-11-2017, 07:50:42
Hola amigos tengo una aplicación de consola que usa parámetros de entrada para hacer ciertas cosas. Si hago así por consola:
pfdtool.exe -g BLUS30270 -d "C:\PS3\SAVEDATA\BLUS3027010AE" DATA0.DAT
Funciona perfectamente pero si hago eso con ShellExecute en C++Builder asi:
AnsiString directorio = ExtractFilePath(OpenDialog1->FileName);
directorio=directorio.SubString(1,directorio.Length()-1);
AnsiString parametros = " -g BLUS30270 -d \"" + directorio + "\" DATA0.DAT";
ShellExecute(NULL,
NULL,
"pfdtool.exe",
parametros.c_str(),
NULL,
SW_HIDE);
No hace nada aunque el parámetro es idéntico.
¿Que hago mal con ShellExecute?

Casimiro Notevi
16-11-2017, 12:25:05
Seguramente, al ser C, tendrá que escribir "\\" en las barras

aguml
16-11-2017, 18:37:05
Solucionado. El problema estaba en que tenia que indicarle el directorio de trabajo. El resto estaba bien.
El problema ahora es que esto no me sirve porque tengo que hacer varias operaciones encadenadas y para eso tengo que esperar que el programa termine una tarea para ejecutarlo de nuevo con otra tarea y asi. La solucion que he encontrado es con ShellExecuteEx el cual me permite esperar a que el proceso termine y luego seguir la ejecucion de mi proceso pero esto me congela mi proceso con lo que decidí meter esa parte en un hilo para que espere el hilo y no mi proceso pero ahora me encuentro con que al esperar que el hilo termine tambien me congela el proceso. Esto es lo que tengo:

Lo que se ejecuta al crearse el hilo:
void __fastcall ThreadPfdtool::Execute()
{
//---- Place thread code here ----
SHELLEXECUTEINFO execInfo;

ZeroMemory(&execInfo, sizeof(SHELLEXECUTEINFO));

// Llenamos la estructura
execInfo.cbSize = sizeof(SHELLEXECUTEINFO);
execInfo.fMask = SEE_MASK_FLAG_DDEWAIT + SEE_MASK_FLAG_NO_UI + SEE_MASK_NOCLOSEPROCESS;
execInfo.lpVerb = "open";
execInfo.lpFile = AnsiString(PathPfdtool + Nombre).c_str();
execInfo.lpParameters = Parametros.c_str();
execInfo.lpDirectory = PathPfdtool.c_str();
execInfo.nShow = SW_HIDE;
execInfo.hInstApp = 0;

// Lanzamos el archivo
ShellExecuteEx(&execInfo);

// Verificamos que no hayan errores
if ((int)execInfo.hInstApp <= 32)
{
if ((int)execInfo.hInstApp == SE_ERR_NOASSOC)
MessageBox(NULL,
"No existe una aplicación asociada \na la extensión del archivo",
"Error",
MB_ICONERROR);

MessageBox(NULL,
"No se ha podido ejecutar el archivo",
"Error",
MB_ICONERROR);
}

// Esperamos a que termine el proceso
WaitForSingleObject(execInfo.hProcess, INFINITE);
}

Una de las llamadas desde una funcion del formulario:
void TForm1::Encriptar()
{
//Para encriptar
NombreArchivoData = ExtractFileName(OpenDialog1->FileName);
PathData = OpenDialog1->FileName.SubString(1,ExtractFilePath(OpenDialog1->FileName).Length()-1);
Parametros = " -g " + GameSettingSet + " -e \"" + PathData + "\" " + NombreArchivoData;

//Ejecuto el programa pfdtool con los parametros
ThreadPfdtool *pfdtool= new ThreadPfdtool(false,PathHerramienta,Parametros,NombreHerramienta);
pfdtool->WaitFor();
ListBoxLog->Items->Add("Se ha encriptado el archivo");
ButtonParchear->Enabled=false;
}

El problema como dije es que primero desencripto, luego modifico, y por ultimo encripto. Para desencriptar creo un hilo y espero que termine y para encriptar lo mismo y tengo que esperar a que uno termine para poder crear el segundo ya que trabajan con el mismo archivo que ademas entre medio modifico.

ecfisa
16-11-2017, 19:44:15
Hola.

¿ Y no te serviría usar la función CreateProcess (https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx) ?

Un ejemplo:

void createProcesswaiting( char* fullExefilename )
{
PROCESS_INFORMATION PI;
STARTUPINFO SI;

ZeroMemory( &SI, sizeof( SI ) );
SI.cb = sizeof( SI );

if( CreateProcess( NULL, fullExefilename, NULL, NULL, false,
NORMAL_PRIORITY_CLASS, NULL, NULL, &SI, &PI ) )
WaitForSingleObject( PI.hThread, INFINITE );

CloseHandle( PI.hProcess );
CloseHandle( PI.hThread );
}


Llamada ejemplo:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
createProcesswaiting( "C:\\WINDOWS\\SYSTEM32\\CALC.EXE" );
createProcesswaiting( "C:\\WINDOWS\\NOTEPAD.EXE" );
createProcesswaiting( "C:\\WINDOWS\\REGEDIT.EXE" );
//...
}


Saludos :)

aguml
16-11-2017, 22:11:24
Tengo dos dudas sobre lo que me dices.
1.Esta linea:
WaitForSingleObject( PI.hThread, INFINITE );
¿No haria como WaitFor y congelaria el hilo desde el que lo llame? en este caso seria el del form.

2.¿Como le paso con CreateProcess los parametros para que se ejecute con parametros?

ecfisa
16-11-2017, 23:03:13
Hola.

1. Eso dependerá de como estes implementando los hilos, pero quitando esa línea no se esperará a que termine el proceso creado para continuar.

2. Sería: "exefile.exe arg1 arg2 ... argN", por ejemplo:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
createProcesswaiting( "C:\\WINDOWS\\NOTEPAD.EXE C:\\WINDOWS\\WIN.INI" );
...
}


Saludos :)

aguml
17-11-2017, 09:32:48
Supongo que para el caso lo mismo da usar ShellExecuteEx que CreateProcess ¿no? Lo unico que ShellExecuteEx permite ejecutar cosas diferentes como un archivo de texto y cosas asi y CreateProcess solo crea procesos. Esto es todo lo que tengo:
El Main.h del Form:
//---------------------------------------------------------------------------

#ifndef MainH
#define MainH

#include <Classes.hpp>
#include <Controls.hpp>
#include <Dialogs.hpp>
#include <StdCtrls.hpp>
#include <ExtCtrls.hpp>
#include <jpeg.hpp>
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TButton *ButtonParchear;
TButton *ButtonElegir;
TOpenDialog *OpenDialog1;
TButton *ButtonCerrar;
TCheckBox *CheckBox1;
TCheckBox *CheckBox2;
TCheckBox *CheckBox3;
TGroupBox *GroupBox1;
TListBox *ListBoxLog;
TLabel *Label1;
TImage *Image1;
void __fastcall ButtonParchearClick(TObject *Sender);
void __fastcall ButtonElegirClick(TObject *Sender);
void __fastcall ButtonCerrarClick(TObject *Sender);
void __fastcall FormCreate(TObject *Sender);
private: // User declarations
AnsiString NombreArchivoData;
AnsiString NombreHerramienta;
AnsiString PathHerramienta;
AnsiString GameSettingSet;
AnsiString Parametros;
AnsiString PathData;

void Encriptar();
void Desencriptar();
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif


El Main.cpp del Form:
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Main.h"
#include "ThreadPfdtool.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------

//Este evento se produce al presionar el boton de Abrir
void __fastcall TForm1::ButtonElegirClick(TObject *Sender)
{
//Elijo el fichero a modificar
if(OpenDialog1->Execute()){
ListBoxLog->Clear();
ButtonParchear->Enabled=true;
}else{
ButtonParchear->Enabled=false;
}
}
//---------------------------------------------------------------------------

//Este evento se produce al presionar el boton de Parchear
void __fastcall TForm1::ButtonParchearClick(TObject *Sender)
{
//unsigned int Bytes_Copy = 0x39883050; //Los bytes a copiar en el fichero
//En hexadecimal la memoria se pone al reves de como se escribe
//Tambien se puede usar cadenas de caracteres en lugar de enteros:
unsigned char Check1[] = "1234";
unsigned char Check2[] = "5678";
unsigned char Check3[] = "9ABC";
int hFile;

ListBoxLog->Items->Add("Iniciando el proceso...");

//Aqui ya habria que desencriptarlo
Desencriptar();

ListBoxLog->Items->Add("Aplicando las modificaciones...");

//Abro el fichero con permisos de escritura
hFile= FileOpen(OpenDialog1->FileName.c_str(),fmOpenWrite);

if(CheckBox1->Checked==true)
{
//Coloco el puntero en el primer byte a modificar
FileSeek (hFile,0,0);

//Escribo en el fichero el numero de bytes indicado
FileWrite(hFile, &Check1,4);
}

if(CheckBox2->Checked==true)
{
//Coloco el puntero en el primer byte a modificar
FileSeek (hFile,4,0);

//Escribo en el fichero el numero de bytes indicado
FileWrite(hFile, &Check2,4);
}

if(CheckBox3->Checked==true)
{
//Coloco el puntero en el primer byte a modificar
FileSeek (hFile,8,0);

//Escribo en el fichero el numero de bytes indicado
FileWrite(hFile, &Check3,4);
}

//Cierro el fichero
FileClose(hFile);

ListBoxLog->Items->Add("Modificaciones aplicadas");

//Aqui habria que firmarlo y encriptarlo
Encriptar();

//Muestro un mensaje para avisar de que terminó
Application->MessageBoxA("Archivo parcheado con éxito.","Prueba de parche PS3",MB_ICONINFORMATION | MB_APPLMODAL);
}
//---------------------------------------------------------------------------

//Este evento se produce al presionar el boton de Cerrar
void __fastcall TForm1::ButtonCerrarClick(TObject *Sender)
{
//Cierro la ventana
Close();
}
//---------------------------------------------------------------------------

//Este evento se produce al crearse la ventana
void __fastcall TForm1::FormCreate(TObject *Sender)
{
PathHerramienta=ExtractFilePath(Application->ExeName);
NombreHerramienta= "pfdtool.exe";
GameSettingSet = "BLUS30270"; //Hay que cambiarlo por el que corresponda a cada DATA

//Muestro un mensaje
Application->MessageBoxA("Creado por Agustin","Prueba de parche PS3",MB_ICONINFORMATION);
}
//-------------------------------------------------------------------------

void TForm1::Desencriptar(void)
{
AnsiString PathBackup=OpenDialog1->FileName + ".BACKUP";

//Compruebo si ya existe un archivo de copia de seguridad del DATA con
//el mismo nombre que el que se va a crear
if (FileExists(PathBackup)) //Si ya existe un archivo con el nombre que se le pondrá al backup
//Muestro un mensaje
if(Application->MessageBoxA("Ya existe un archivo de copia de seguridad con ese nombre. Si sigue adelante se sobreescribirá.\n¿Desea continuar igualmente?","Prueba de parche PS3",MB_ICONWARNING | MB_YESNO)==IDNO)
return; //Si se pulsa el boton NO se termina el proceso de desencriptado
//Creo la copia del archivo DATA con la extension .BACKUP
if (CopyFile(OpenDialog1->FileName.c_str(),PathBackup.c_str(),false)==0)
{
//Si falla muestro un mensaje
if(Application->MessageBoxA("No se pudo crear la copia de seguridad del archivo de datos original.\n¿Desea seguir con el proceso de todos modos?","Prueba de parche PS3",MB_ICONWARNING | MB_YESNO)==IDNO)
return; //Si se contesta NO se termina el proceso de desencriptado
}else{
ListBoxLog->Items->Add("Creada copia de seguridad del SaveData");
}

//Para desencriptar
NombreArchivoData = ExtractFileName(OpenDialog1->FileName);
PathData = OpenDialog1->FileName.SubString(1,ExtractFilePath(OpenDialog1->FileName).Length()-1);
Parametros = " -g " + GameSettingSet + " -d \"" + PathData + "\" " + NombreArchivoData;

//Ejecuto el programa pfdtool con los parametros
ThreadPfdtool *pfdtool= new ThreadPfdtool(false,PathHerramienta,Parametros,NombreHerramienta);
pfdtool->WaitFor();
delete pfdtool;
ListBoxLog->Items->Add("Se ha desencriptado el archivo");
ButtonParchear->Enabled=true;
}
//---------------------------------------------------------------------------

void TForm1::Encriptar()
{
//Para encriptar
NombreArchivoData = ExtractFileName(OpenDialog1->FileName);
PathData = OpenDialog1->FileName.SubString(1,ExtractFilePath(OpenDialog1->FileName).Length()-1);
Parametros = " -g " + GameSettingSet + " -e \"" + PathData + "\" " + NombreArchivoData;

//Ejecuto el programa pfdtool con los parametros
ThreadPfdtool *pfdtool= new ThreadPfdtool(false,PathHerramienta,Parametros,NombreHerramienta);
pfdtool->WaitFor();
delete pfdtool;
ListBoxLog->Items->Add("Se ha encriptado el archivo");
ButtonParchear->Enabled=false;
}
//---------------------------------------------------------------------------


El .h del hilo:
//---------------------------------------------------------------------------

#ifndef Unit2H
#define Unit2H
//---------------------------------------------------------------------------
#include <Classes.hpp>
//---------------------------------------------------------------------------
class ThreadPfdtool : public TThread
{
private:
AnsiString PathPfdtool;
AnsiString Parametros;
AnsiString Nombre;
protected:
void __fastcall Execute();
public:
__fastcall ThreadPfdtool(bool CreateSuspended,AnsiString pathPfdtool, AnsiString parametros, AnsiString nombre);
};
//---------------------------------------------------------------------------
#endif


El .cpp del hilo:
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "ThreadPfdtool.h"
#pragma package(smart_init)
//---------------------------------------------------------------------------

// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall ThreadPfdtool::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
//---------------------------------------------------------------------------

__fastcall ThreadPfdtool::ThreadPfdtool(bool CreateSuspended,AnsiString pathPfdtool, AnsiString parametros, AnsiString nombre)
: TThread(CreateSuspended)
{
PathPfdtool=pathPfdtool;
Parametros=parametros;
Nombre=nombre;
}
//---------------------------------------------------------------------------
void __fastcall ThreadPfdtool::Execute()
{
//---- Place thread code here ----
SHELLEXECUTEINFO execInfo;

ZeroMemory(&execInfo, sizeof(SHELLEXECUTEINFO));

// Llenamos la estructura
execInfo.cbSize = sizeof(SHELLEXECUTEINFO);
execInfo.fMask = SEE_MASK_FLAG_DDEWAIT + SEE_MASK_FLAG_NO_UI + SEE_MASK_NOCLOSEPROCESS;
execInfo.lpVerb = "open";
execInfo.lpFile = AnsiString(PathPfdtool + Nombre).c_str();
execInfo.lpParameters = Parametros.c_str();
execInfo.lpDirectory = PathPfdtool.c_str();
execInfo.nShow = SW_HIDE;
execInfo.hInstApp = 0;

// Lanzamos el archivo
ShellExecuteEx(&execInfo);

// Verificamos que no hayan errores
if ((int)execInfo.hInstApp <= 32)
{
if ((int)execInfo.hInstApp == SE_ERR_NOASSOC)
MessageBox(NULL,
"No existe una aplicación asociada \na la extensión del archivo",
"Error",
MB_ICONERROR);

MessageBox(NULL,
"No se ha podido ejecutar el archivo",
"Error",
MB_ICONERROR);
}

// Esperamos a que termine el proceso
WaitForSingleObject(execInfo.hProcess, INFINITE);
}
//---------------------------------------------------------------------------


Supongo que se congela porque mi estructura no es la mejor y querría ver si pueden ayudarme.
PD:El proceso inicia al hacer clic en el botón Parchear.