mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
SDA-3148 Stable implementation + tests
This commit is contained in:
parent
b4e3641c8c
commit
f4ba00dbe9
@ -8,6 +8,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
|
||||||
|
#define PIPE_NAME "symphony_sda_auto_update_ipc"
|
||||||
|
|
||||||
int main( int argc, char** argv ) {
|
int main( int argc, char** argv ) {
|
||||||
if( argc < 3 ) {
|
if( argc < 3 ) {
|
||||||
@ -16,7 +17,7 @@ int main( int argc, char** argv ) {
|
|||||||
}
|
}
|
||||||
char const* installer_filename = argv[ 1 ];
|
char const* installer_filename = argv[ 1 ];
|
||||||
char const* application_filename = argv[ 2 ];
|
char const* application_filename = argv[ 2 ];
|
||||||
ipc_client_t* client = ipc_client_connect();
|
ipc_client_t* client = ipc_client_connect( PIPE_NAME );
|
||||||
ipc_client_send( client, installer_filename );
|
ipc_client_send( client, installer_filename );
|
||||||
char response[ 256 ];
|
char response[ 256 ];
|
||||||
int size = 0;
|
int size = 0;
|
||||||
@ -29,7 +30,7 @@ int main( int argc, char** argv ) {
|
|||||||
}
|
}
|
||||||
response[ size ] = '\0';
|
response[ size ] = '\0';
|
||||||
printf( "%s\n", response );
|
printf( "%s\n", response );
|
||||||
ipc_client_connect();
|
ipc_client_connect( PIPE_NAME );
|
||||||
|
|
||||||
int result = (int)(uintptr_t) ShellExecute( NULL, NULL, application_filename,
|
int result = (int)(uintptr_t) ShellExecute( NULL, NULL, application_filename,
|
||||||
NULL, NULL, SW_SHOWNORMAL );
|
NULL, NULL, SW_SHOWNORMAL );
|
||||||
@ -45,3 +46,7 @@ int main( int argc, char** argv ) {
|
|||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define IPC_IMPLEMENTATION
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
@ -70,7 +70,18 @@
|
|||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup />
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_helper\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_helper\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_helper\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_helper\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
#pragma comment(lib, "advapi32.lib")
|
#pragma comment(lib, "advapi32.lib")
|
||||||
#pragma comment(lib, "Shell32.lib")
|
#pragma comment(lib, "Shell32.lib")
|
||||||
|
|
||||||
|
#define PIPE_NAME "symphony_sda_auto_update_ipc"
|
||||||
|
|
||||||
#define SVCNAME TEXT("SDA Auto Update")
|
#define SVCNAME TEXT("SDA Auto Update")
|
||||||
|
|
||||||
SERVICE_STATUS gSvcStatus;
|
SERVICE_STATUS gSvcStatus;
|
||||||
@ -168,6 +170,35 @@ VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// This routine is a simple function to print the client request to the console
|
||||||
|
// and populate the reply buffer with a default data string. This is where you
|
||||||
|
// would put the actual client request processing code that runs in the context
|
||||||
|
// of an instance thread. Keep in mind the main thread will continue to wait for
|
||||||
|
// and receive other client connections while the instance thread is working.
|
||||||
|
void run_installer( char const* request, void* user_data, char* response, size_t capacity ) {
|
||||||
|
printf( "Client Request String:\"%s\"\n", request );
|
||||||
|
|
||||||
|
char command[ 512 ];
|
||||||
|
sprintf( command, "/i %s /q", request );
|
||||||
|
|
||||||
|
SHELLEXECUTEINFO ShExecInfo = { 0 };
|
||||||
|
ShExecInfo.cbSize = sizeof( SHELLEXECUTEINFO );
|
||||||
|
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||||
|
ShExecInfo.hwnd = NULL;
|
||||||
|
ShExecInfo.lpVerb = "open";
|
||||||
|
ShExecInfo.lpFile = "msiexec";
|
||||||
|
ShExecInfo.lpParameters = command;
|
||||||
|
ShExecInfo.lpDirectory = NULL;
|
||||||
|
ShExecInfo.nShow = SW_SHOW;
|
||||||
|
ShExecInfo.hInstApp = NULL;
|
||||||
|
ShellExecuteEx( &ShExecInfo );
|
||||||
|
WaitForSingleObject( ShExecInfo.hProcess, INFINITE );
|
||||||
|
CloseHandle( ShExecInfo.hProcess );
|
||||||
|
|
||||||
|
strcpy( response, "OK" );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Purpose:
|
// Purpose:
|
||||||
// The service code
|
// The service code
|
||||||
@ -183,7 +214,7 @@ VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
|
|||||||
//
|
//
|
||||||
VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
|
VOID SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
|
||||||
{
|
{
|
||||||
ipc_server_t* server = ipc_server_start();
|
ipc_server_t* server = ipc_server_start( PIPE_NAME, run_installer, NULL );
|
||||||
|
|
||||||
// TO_DO: Declare and set any required variables.
|
// TO_DO: Declare and set any required variables.
|
||||||
// Be sure to periodically call ReportSvcStatus() with
|
// Be sure to periodically call ReportSvcStatus() with
|
||||||
@ -296,3 +327,6 @@ VOID WINAPI SvcCtrlHandler( DWORD dwCtrl )
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define IPC_IMPLEMENTATION
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
@ -69,7 +69,18 @@
|
|||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup />
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_service\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_service\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_service\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\auto_update_service\</IntDir>
|
||||||
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
@ -125,6 +136,9 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="auto_update_service.cpp" />
|
<ClCompile Include="auto_update_service.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClInclude Include="ipc.h" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
@ -1,7 +1,60 @@
|
|||||||
|
#ifndef ipc_h
|
||||||
|
#define ipc_h
|
||||||
|
|
||||||
|
#define IPC_MESSAGE_MAX_LENGTH 512
|
||||||
|
|
||||||
|
// client
|
||||||
|
|
||||||
|
typedef struct ipc_client_t ipc_client_t;
|
||||||
|
|
||||||
|
ipc_client_t* ipc_client_connect( char const* pipe_name );
|
||||||
|
|
||||||
|
void ipc_client_disconnect( ipc_client_t* connection );
|
||||||
|
|
||||||
|
typedef enum ipc_receive_status_t {
|
||||||
|
IPC_RECEIVE_STATUS_DONE,
|
||||||
|
IPC_RECEIVE_STATUS_MORE_DATA,
|
||||||
|
IPC_RECEIVE_STATUS_ERROR,
|
||||||
|
} ipc_receive_status_t;
|
||||||
|
|
||||||
|
ipc_receive_status_t ipc_client_receive( ipc_client_t* connection, char* output, int output_size, int* received_size );
|
||||||
|
|
||||||
|
bool ipc_client_send( ipc_client_t* connection, char const* message );
|
||||||
|
|
||||||
|
|
||||||
|
// server
|
||||||
|
|
||||||
|
typedef struct ipc_server_t ipc_server_t;
|
||||||
|
|
||||||
|
typedef void (*ipc_request_handler_t)( char const* request, void* user_data, char* response, size_t capacity );
|
||||||
|
|
||||||
|
ipc_server_t* ipc_server_start( char const* pipe_name, ipc_request_handler_t request_handler, void* user_data );
|
||||||
|
|
||||||
|
void ipc_server_stop( ipc_server_t* server );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ipc_h */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef IPC_IMPLEMENTATION
|
||||||
|
#undef IPC_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <aclapi.h>
|
||||||
|
|
||||||
|
#pragma comment(lib, "advapi32.lib")
|
||||||
|
|
||||||
|
bool expand_pipe_name( char const* pipe_name, char* buffer, size_t capacity ) {
|
||||||
|
int result = snprintf( buffer, capacity, "\\\\.\\pipe\\%s", pipe_name );
|
||||||
|
return result >= 0 && result < (int) capacity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool pipe_exists( const char* pipe_name ) {
|
bool pipe_exists( const char* pipe_name ) {
|
||||||
WIN32_FIND_DATA data;
|
WIN32_FIND_DATAA data;
|
||||||
memset( &data, 0, sizeof( data ) );
|
memset( &data, 0, sizeof( data ) );
|
||||||
|
|
||||||
HANDLE hfind = FindFirstFileA( "\\\\.\\pipe\\*", &data );
|
HANDLE hfind = FindFirstFileA( "\\\\.\\pipe\\*", &data );
|
||||||
@ -19,37 +72,65 @@ bool pipe_exists( const char* pipe_name ) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define PIPE_NAME "\\\\.\\pipe\\symphony_sda_auto_update_ipc"
|
|
||||||
#define BUFSIZE 512
|
|
||||||
|
|
||||||
struct ipc_client_t {
|
struct ipc_client_t {
|
||||||
HANDLE pipe;
|
HANDLE pipe;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ipc_client_t* ipc_client_connect() {
|
ipc_client_t* ipc_client_connect( char const* pipe_name ) {
|
||||||
if( !pipe_exists( PIPE_NAME ) ) {
|
if( !pipe_exists( pipe_name ) ) {
|
||||||
printf( "Named pipe does not exits\n" );
|
// Retry once if pipe was not found
|
||||||
|
Sleep( 1000 );
|
||||||
|
if( !pipe_exists( pipe_name ) ) {
|
||||||
|
printf( "Named pipe does not exist\n" );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char expanded_pipe_name[ MAX_PATH ];
|
||||||
|
if( !expand_pipe_name( pipe_name, expanded_pipe_name, sizeof( expanded_pipe_name ) ) ) {
|
||||||
|
printf( "Pipe name too long\n" );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
HANDLE pipe = NULL;
|
HANDLE pipe = NULL;
|
||||||
for( ; ; ) { // Keep trying to connect while pipe is busy
|
for( ; ; ) { // Keep trying to connect while pipe is busy
|
||||||
pipe = CreateFile(
|
pipe = CreateFileA(
|
||||||
PIPE_NAME, // pipe name
|
expanded_pipe_name, // pipe name
|
||||||
GENERIC_READ | // read and write access
|
GENERIC_READ | // read and write access
|
||||||
GENERIC_WRITE,
|
GENERIC_WRITE,
|
||||||
0, // no sharing
|
0, // no sharing
|
||||||
NULL, // default security attributes
|
NULL, // default security attributes
|
||||||
OPEN_EXISTING, // opens existing pipe
|
OPEN_EXISTING, // opens existing pipe
|
||||||
0, // default attributes
|
0, // default attributes
|
||||||
NULL ); // no template file
|
NULL ); // no template file
|
||||||
|
|
||||||
// Break if the pipe handle is valid.
|
// Break if the pipe handle is valid.
|
||||||
if( pipe != INVALID_HANDLE_VALUE ) {
|
if( pipe != INVALID_HANDLE_VALUE ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retry once if pipe was not found
|
||||||
|
if( GetLastError() == ERROR_FILE_NOT_FOUND ) {
|
||||||
|
Sleep( 1000 );
|
||||||
|
printf( "retry\n" );
|
||||||
|
pipe = CreateFileA(
|
||||||
|
expanded_pipe_name, // pipe name
|
||||||
|
GENERIC_READ | // read and write access
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0, // no sharing
|
||||||
|
NULL, // default security attributes
|
||||||
|
OPEN_EXISTING, // opens existing pipe
|
||||||
|
0, // default attributes
|
||||||
|
NULL ); // no template file
|
||||||
|
// Break if the pipe handle is valid.
|
||||||
|
if( pipe != INVALID_HANDLE_VALUE ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
// Exit if an error other than ERROR_PIPE_BUSY occurs.
|
||||||
if( GetLastError() != ERROR_PIPE_BUSY ) {
|
if( GetLastError() != ERROR_PIPE_BUSY ) {
|
||||||
printf( "Could not open pipe. LastError=%d\n", GetLastError() );
|
printf( "Could not open pipe. LastError=%d\n", GetLastError() );
|
||||||
@ -57,21 +138,57 @@ ipc_client_t* ipc_client_connect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All pipe instances are busy, so wait for 20 seconds.
|
// All pipe instances are busy, so wait for 20 seconds.
|
||||||
if( !WaitNamedPipe( PIPE_NAME, 20000 ) ) {
|
if( !WaitNamedPipeA( expanded_pipe_name, 20000 ) ) {
|
||||||
printf( "Could not open pipe: 20 second wait timed out." );
|
if( GetLastError() == ERROR_FILE_NOT_FOUND ) {
|
||||||
return NULL;
|
// retry once just in case pipe was not created yet
|
||||||
|
Sleep(1000);
|
||||||
|
if( !WaitNamedPipeA( expanded_pipe_name, 20000 ) ) {
|
||||||
|
printf( "Could not open pipe on second attempt: 20 second wait timed out. LastError=%d\n", GetLastError() );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf( "Could not open pipe: 20 second wait timed out. LastError=%d\n", GetLastError() );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ipc_client_t* connection = (ipc_client_t*) malloc( sizeof( ipc_client_t ) );
|
||||||
ipc_client_t* connection = new ipc_client_t();
|
|
||||||
connection->pipe = pipe;
|
connection->pipe = pipe;
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ipc_client_disconnect( ipc_client_t* connection ) {
|
void ipc_client_disconnect( ipc_client_t* connection ) {
|
||||||
|
FlushFileBuffers( connection->pipe );
|
||||||
|
DisconnectNamedPipe( connection->pipe );
|
||||||
CloseHandle( connection->pipe );
|
CloseHandle( connection->pipe );
|
||||||
delete connection;
|
free( connection );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ipc_receive_status_t ipc_client_receive( ipc_client_t* connection, char* output, int output_size, int* received_size ) {
|
||||||
|
DWORD size_read = 0;
|
||||||
|
BOOL success = ReadFile(
|
||||||
|
connection->pipe, // pipe handle
|
||||||
|
output, // buffer to receive reply
|
||||||
|
output_size, // size of buffer
|
||||||
|
&size_read, // number of bytes read
|
||||||
|
NULL ); // not overlapped
|
||||||
|
|
||||||
|
if( !success && GetLastError() != ERROR_MORE_DATA ) {
|
||||||
|
printf( "ReadFile from pipe failed. LastError=%d\n", GetLastError() );
|
||||||
|
return IPC_RECEIVE_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( received_size ) {
|
||||||
|
*received_size = size_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( success ) {
|
||||||
|
return IPC_RECEIVE_STATUS_DONE;
|
||||||
|
} else {
|
||||||
|
return IPC_RECEIVE_STATUS_MORE_DATA;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -104,71 +221,33 @@ bool ipc_client_send( ipc_client_t* connection, char const* message ) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ipc_receive_status_t {
|
|
||||||
IPC_RECEIVE_STATUS_DONE,
|
|
||||||
IPC_RECEIVE_STATUS_MORE_DATA,
|
|
||||||
IPC_RECEIVE_STATUS_ERROR,
|
|
||||||
};
|
|
||||||
|
|
||||||
ipc_receive_status_t ipc_client_receive( ipc_client_t* connection, char* output, int output_size, int* received_size ) {
|
|
||||||
DWORD size_read = 0;
|
|
||||||
BOOL success = ReadFile(
|
|
||||||
connection->pipe, // pipe handle
|
|
||||||
output, // buffer to receive reply
|
|
||||||
output_size, // size of buffer
|
|
||||||
&size_read, // number of bytes read
|
|
||||||
NULL ); // not overlapped
|
|
||||||
|
|
||||||
if( !success && GetLastError() != ERROR_MORE_DATA ) {
|
|
||||||
printf( "ReadFile from pipe failed. LastError=%d\n", GetLastError() );
|
|
||||||
return IPC_RECEIVE_STATUS_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( received_size ) {
|
|
||||||
*received_size = size_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( success ) {
|
|
||||||
return IPC_RECEIVE_STATUS_DONE;
|
|
||||||
} else {
|
|
||||||
return IPC_RECEIVE_STATUS_MORE_DATA;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// This routine is a simple function to print the client request to the console
|
|
||||||
// and populate the reply buffer with a default data string. This is where you
|
|
||||||
// would put the actual client request processing code that runs in the context
|
|
||||||
// of an instance thread. Keep in mind the main thread will continue to wait for
|
|
||||||
// and receive other client connections while the instance thread is working.
|
|
||||||
VOID run_installer( LPSTR pchRequest, LPSTR pchReply, LPDWORD pchBytes ) {
|
|
||||||
printf( "Client Request String:\"%s\"\n", pchRequest );
|
|
||||||
|
|
||||||
char command[ 512 ];
|
|
||||||
sprintf( command, "/i %s /q", pchRequest );
|
|
||||||
|
|
||||||
SHELLEXECUTEINFO ShExecInfo = { 0 };
|
|
||||||
ShExecInfo.cbSize = sizeof( SHELLEXECUTEINFO );
|
|
||||||
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
|
|
||||||
ShExecInfo.hwnd = NULL;
|
|
||||||
ShExecInfo.lpVerb = "open";
|
|
||||||
ShExecInfo.lpFile = "msiexec";
|
|
||||||
ShExecInfo.lpParameters = command;
|
|
||||||
ShExecInfo.lpDirectory = NULL;
|
|
||||||
ShExecInfo.nShow = SW_SHOW;
|
|
||||||
ShExecInfo.hInstApp = NULL;
|
|
||||||
ShellExecuteEx( &ShExecInfo );
|
|
||||||
WaitForSingleObject( ShExecInfo.hProcess, INFINITE );
|
|
||||||
CloseHandle( ShExecInfo.hProcess );
|
|
||||||
|
|
||||||
strcpy( pchReply, "OK" );
|
|
||||||
*pchBytes = (DWORD)( ( strlen( pchReply ) + 1 ) * sizeof( CHAR ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
typedef struct ipc_client_thread_t {
|
||||||
|
BOOL recycle;
|
||||||
|
ipc_server_t* server;
|
||||||
|
HANDLE thread;
|
||||||
|
HANDLE thread_started_event;
|
||||||
|
HANDLE pipe;
|
||||||
|
OVERLAPPED io;
|
||||||
|
int exit_flag;
|
||||||
|
} ipc_client_thread_t;
|
||||||
|
|
||||||
|
#define MAX_CLIENT_CONNECTIONS 32
|
||||||
|
|
||||||
struct ipc_server_t {
|
struct ipc_server_t {
|
||||||
|
char expanded_pipe_name[ MAX_PATH ];
|
||||||
|
PSID sid;
|
||||||
|
PACL acl;
|
||||||
|
PSECURITY_DESCRIPTOR sd;
|
||||||
HANDLE thread;
|
HANDLE thread;
|
||||||
|
HANDLE thread_started_event;
|
||||||
|
HANDLE pipe;
|
||||||
|
OVERLAPPED io;
|
||||||
|
int exit_flag;
|
||||||
|
ipc_request_handler_t request_handler;
|
||||||
|
void* user_data;
|
||||||
|
ipc_client_thread_t client_threads[ MAX_CLIENT_CONNECTIONS ];
|
||||||
|
int client_threads_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -178,106 +257,127 @@ struct ipc_server_t {
|
|||||||
// this procedure to run concurrently, depending on the number of incoming
|
// this procedure to run concurrently, depending on the number of incoming
|
||||||
// client connections.
|
// client connections.
|
||||||
DWORD WINAPI ipc_client_thread( LPVOID param ) {
|
DWORD WINAPI ipc_client_thread( LPVOID param ) {
|
||||||
HANDLE hHeap = GetProcessHeap();
|
|
||||||
CHAR* pchRequest = (CHAR*) HeapAlloc( hHeap, 0, BUFSIZE * sizeof( CHAR ) );
|
|
||||||
CHAR* pchReply = (CHAR*) HeapAlloc( hHeap, 0, BUFSIZE * sizeof( CHAR ) );
|
|
||||||
|
|
||||||
DWORD cbBytesRead = 0, cbReplyBytes = 0, cbWritten = 0;
|
|
||||||
|
|
||||||
// Do some extra error checking since the app will keep running even if this
|
|
||||||
// thread fails.
|
|
||||||
|
|
||||||
if( param == NULL ) {
|
|
||||||
printf( "\nERROR - Pipe Server Failure:\n" );
|
|
||||||
printf( " ipc_client_thread got an unexpected NULL value in lpvParam.\n" );
|
|
||||||
printf( " ipc_client_thread exitting.\n" );
|
|
||||||
if( pchReply != NULL ) {
|
|
||||||
HeapFree( hHeap, 0, pchReply );
|
|
||||||
}
|
|
||||||
if( pchRequest != NULL ) {
|
|
||||||
HeapFree( hHeap, 0, pchRequest );
|
|
||||||
}
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pchRequest == NULL ) {
|
|
||||||
printf( "\nERROR - Pipe Server Failure:\n" );
|
|
||||||
printf( " ipc_client_thread got an unexpected NULL heap allocation.\n" );
|
|
||||||
printf( " ipc_client_thread exitting.\n" );
|
|
||||||
if( pchReply != NULL ) {
|
|
||||||
HeapFree( hHeap, 0, pchReply );
|
|
||||||
}
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if( pchReply == NULL ) {
|
|
||||||
printf( "\nERROR - Pipe Server Failure:\n");
|
|
||||||
printf( " ipc_client_thread got an unexpected NULL heap allocation.\n" );
|
|
||||||
printf( " ipc_client_thread exitting.\n");
|
|
||||||
if( pchRequest != NULL ) {
|
|
||||||
HeapFree( hHeap, 0, pchRequest );
|
|
||||||
}
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print verbose messages. In production code, this should be for debugging only.
|
// Print verbose messages. In production code, this should be for debugging only.
|
||||||
printf( "ipc_client_thread created, receiving and processing messages.\n" );
|
//printf( "ipc_client_thread created, receiving and processing messages.\n" );
|
||||||
|
|
||||||
// The thread's parameter is a handle to a pipe object instance.
|
// The thread's parameter is a handle to a pipe object instance.
|
||||||
HANDLE hPipe = (HANDLE) param;
|
ipc_client_thread_t* context= (ipc_client_thread_t*) param;
|
||||||
|
ipc_server_t* server = context->server;
|
||||||
|
HANDLE hPipe = context->pipe;
|
||||||
|
|
||||||
|
HANDLE hEvent = CreateEvent(
|
||||||
|
NULL, // default security attributes
|
||||||
|
TRUE, // manual-reset event
|
||||||
|
FALSE, // initial state is nonsignaled
|
||||||
|
NULL // object name
|
||||||
|
);
|
||||||
|
//printf( "Thread started\n" );
|
||||||
|
SetEvent( context->thread_started_event );
|
||||||
// Loop until done reading
|
// Loop until done reading
|
||||||
for( ; ; ) {
|
for( ; ; ) {
|
||||||
// Read client requests from the pipe. This simplistic code only allows messages
|
CHAR pchRequest[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
// up to BUFSIZE characters in length.
|
for( ; ; ) {
|
||||||
BOOL fSuccess = ReadFile(
|
// Read client requests from the pipe. This simplistic code only allows messages
|
||||||
hPipe, // handle to pipe
|
// up to IPC_MESSAGE_MAX_LENGTH characters in length.
|
||||||
pchRequest, // buffer to receive data
|
//printf( "ipc_client_thread: reading.\n" );
|
||||||
BUFSIZE * sizeof( CHAR ), // size of buffer
|
DWORD cbBytesRead = 0;
|
||||||
&cbBytesRead, // number of bytes read
|
memset( &context->io, 0, sizeof( context->io ) );
|
||||||
NULL ); // not overlapped I/O
|
ResetEvent( hEvent );
|
||||||
|
context->io.hEvent = hEvent;
|
||||||
|
BOOL fSuccess = ReadFile(
|
||||||
|
hPipe, // handle to pipe
|
||||||
|
pchRequest, // buffer to receive data
|
||||||
|
IPC_MESSAGE_MAX_LENGTH * sizeof( CHAR ), // size of buffer
|
||||||
|
&cbBytesRead, // number of bytes read
|
||||||
|
&context->io ); // overlapped I/O
|
||||||
|
//printf( "ipc_client_thread: read.\n" );
|
||||||
|
|
||||||
if( !fSuccess || cbBytesRead == 0 ) {
|
if( !fSuccess && GetLastError() == ERROR_IO_PENDING ) {
|
||||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
if( WaitForSingleObject( hEvent, 500 ) == WAIT_TIMEOUT ) {
|
||||||
printf( "ipc_client_thread: client disconnected.\n" );
|
CancelIoEx( context->pipe, &context->io );
|
||||||
} else {
|
printf( "\n\nRETRY\n\n");
|
||||||
printf( "ipc_client_thread ReadFile failed, LastError=%d.\n", GetLastError() );
|
continue;
|
||||||
|
}
|
||||||
|
fSuccess = GetOverlappedResult(
|
||||||
|
hPipe, // handle to pipe
|
||||||
|
&context->io, // OVERLAPPED structure
|
||||||
|
&cbBytesRead, // bytes transferred
|
||||||
|
FALSE ); // wait
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if( !fSuccess || cbBytesRead == 0 ) {
|
||||||
|
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||||
|
//printf( "ipc_client_thread: client disconnected.\n" );
|
||||||
|
} else {
|
||||||
|
printf( "ipc_client_thread ReadFile failed, LastError=%d.\n", GetLastError() );
|
||||||
|
}
|
||||||
|
goto exit_client_thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( context->exit_flag ) {
|
||||||
|
goto exit_client_thread;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the incoming message.
|
// Process the incoming message.
|
||||||
run_installer( pchRequest, pchReply, &cbReplyBytes );
|
char pchReply[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
memset( pchReply, 0, sizeof( pchReply ) );
|
||||||
|
server->request_handler( pchRequest, server->user_data, pchReply, sizeof( pchReply ) );
|
||||||
|
pchReply[ IPC_MESSAGE_MAX_LENGTH - 1 ] = '\0'; // Force zero termination (truncate string)
|
||||||
|
DWORD cbReplyBytes = (DWORD)strlen(pchReply) + 1;
|
||||||
|
|
||||||
// Write the reply to the pipe.
|
// Write the reply to the pipe.
|
||||||
fSuccess = WriteFile(
|
//printf( "ipc_client_thread: writing.\n" );
|
||||||
|
DWORD cbWritten = 0;
|
||||||
|
BOOL fSuccess = WriteFile(
|
||||||
hPipe, // handle to pipe
|
hPipe, // handle to pipe
|
||||||
pchReply, // buffer to write from
|
pchReply, // buffer to write from
|
||||||
cbReplyBytes, // number of bytes to write
|
cbReplyBytes, // number of bytes to write
|
||||||
&cbWritten, // number of bytes written
|
&cbWritten, // number of bytes written
|
||||||
NULL ); // not overlapped I/O
|
&context->io ); // overlapped I/O
|
||||||
|
|
||||||
|
//printf( "ipc_client_thread: writ.\n" );
|
||||||
|
if( fSuccess || GetLastError() == ERROR_IO_PENDING ) {
|
||||||
|
fSuccess = GetOverlappedResult(
|
||||||
|
hPipe, // handle to pipe
|
||||||
|
&context->io, // OVERLAPPED structure
|
||||||
|
&cbWritten, // bytes transferred
|
||||||
|
FALSE); // wait
|
||||||
|
}
|
||||||
|
|
||||||
if( !fSuccess || cbReplyBytes != cbWritten ) {
|
if( !fSuccess || cbReplyBytes != cbWritten ) {
|
||||||
printf( ("ipc_client_thread WriteFile failed, LastError=%d.\n"), GetLastError());
|
printf( ("ipc_client_thread WriteFile failed, LastError=%d.\n"), GetLastError());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if( context->exit_flag ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exit_client_thread:
|
||||||
|
//printf( "ipc_client_thread cleanup.\n" );
|
||||||
|
|
||||||
// Flush the pipe to allow the client to read the pipe's contents
|
// Flush the pipe to allow the client to read the pipe's contents
|
||||||
// before disconnecting. Then disconnect the pipe, and close the
|
// before disconnecting. Then disconnect the pipe, and close the
|
||||||
// handle to this pipe instance.
|
// handle to this pipe instance.
|
||||||
|
CloseHandle( hEvent );
|
||||||
FlushFileBuffers( hPipe );
|
FlushFileBuffers( hPipe );
|
||||||
DisconnectNamedPipe( hPipe );
|
DisconnectNamedPipe( hPipe );
|
||||||
CloseHandle( hPipe );
|
CloseHandle( hPipe );
|
||||||
|
context->pipe = INVALID_HANDLE_VALUE;
|
||||||
HeapFree( hHeap, 0, pchRequest );
|
context->recycle = TRUE;
|
||||||
HeapFree( hHeap, 0, pchReply );
|
//printf( "ipc_client_thread exiting.\n" );
|
||||||
|
|
||||||
printf( "ipc_client_thread exiting.\n" );
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ipc_stop_client_thread( ipc_client_thread_t* thread_context ) {
|
||||||
|
thread_context->exit_flag = 1;
|
||||||
|
CancelIoEx( thread_context->pipe, &thread_context->io );
|
||||||
|
WaitForSingleObject( thread_context->thread, INFINITE );
|
||||||
|
}
|
||||||
|
|
||||||
DWORD WINAPI ipc_server_thread( LPVOID param ) {
|
DWORD WINAPI ipc_server_thread( LPVOID param ) {
|
||||||
ipc_server_t* server = (ipc_server_t*) param;
|
ipc_server_t* server = (ipc_server_t*) param;
|
||||||
@ -286,11 +386,147 @@ DWORD WINAPI ipc_server_thread( LPVOID param ) {
|
|||||||
//fp = fopen( "C:\\auto_update_poc\\log.txt", "w" );
|
//fp = fopen( "C:\\auto_update_poc\\log.txt", "w" );
|
||||||
//setvbuf(fp, NULL, _IONBF, 0);
|
//setvbuf(fp, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES attribs;
|
||||||
|
attribs.nLength = sizeof( SECURITY_ATTRIBUTES );
|
||||||
|
attribs.lpSecurityDescriptor = server->sd;
|
||||||
|
attribs.bInheritHandle = -1;
|
||||||
|
|
||||||
|
// The main loop creates an instance of the named pipe and
|
||||||
|
// then waits for a client to connect to it. When the client
|
||||||
|
// connects, a thread is created to handle communications
|
||||||
|
// with that client, and this loop is free to wait for the
|
||||||
|
// next client connect request. It is an infinite loop.
|
||||||
|
bool event_raised = false;
|
||||||
|
while( !server->exit_flag ) {
|
||||||
|
//printf( "\nPipe Server: Main thread awaiting client connection on %s\n", server->expanded_pipe_name );
|
||||||
|
server->pipe = CreateNamedPipeA(
|
||||||
|
server->expanded_pipe_name,// pipe name
|
||||||
|
PIPE_ACCESS_DUPLEX | // read/write access
|
||||||
|
FILE_FLAG_OVERLAPPED,
|
||||||
|
PIPE_TYPE_MESSAGE | // message type pipe
|
||||||
|
PIPE_READMODE_MESSAGE | // message-read mode
|
||||||
|
PIPE_WAIT, // blocking mode
|
||||||
|
MAX_CLIENT_CONNECTIONS, // max. instances
|
||||||
|
IPC_MESSAGE_MAX_LENGTH, // output buffer size
|
||||||
|
IPC_MESSAGE_MAX_LENGTH, // input buffer size
|
||||||
|
0, // client time-out
|
||||||
|
&attribs ); // default security attribute
|
||||||
|
|
||||||
|
if( server->pipe == INVALID_HANDLE_VALUE ) {
|
||||||
|
printf( "CreateNamedPipe failed, LastError=%d.\n", GetLastError() );
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !event_raised ) {
|
||||||
|
SetEvent( server->thread_started_event );
|
||||||
|
event_raised = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the client to connect; if it succeeds,
|
||||||
|
// the function returns a nonzero value. If the function
|
||||||
|
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
|
||||||
|
memset( &server->io, 0, sizeof( server->io ) );
|
||||||
|
server->io.hEvent = CreateEvent(
|
||||||
|
NULL, // default security attributes
|
||||||
|
TRUE, // manual-reset event
|
||||||
|
FALSE, // initial state is nonsignaled
|
||||||
|
NULL // object name
|
||||||
|
);
|
||||||
|
ConnectNamedPipe( server->pipe, &server->io );
|
||||||
|
if( GetLastError() == ERROR_IO_PENDING ) {
|
||||||
|
for( ; ; ) {
|
||||||
|
if( WaitForSingleObject( server->io.hEvent, 100 ) == WAIT_OBJECT_0 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( server->exit_flag ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if( GetLastError() != ERROR_PIPE_CONNECTED ) {
|
||||||
|
if( GetLastError() != ERROR_OPERATION_ABORTED || server->exit_flag == 0 ) {
|
||||||
|
// The client could not connect, so close the pipe.
|
||||||
|
printf( "Connection failed. LastError=%d\n",GetLastError() );
|
||||||
|
CloseHandle( server->io.hEvent );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle( server->io.hEvent );
|
||||||
|
if( server->exit_flag ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf( "Client connected, creating a processing thread.\n" );
|
||||||
|
// Create a thread for this client.
|
||||||
|
ipc_client_thread_t* context = NULL;
|
||||||
|
for( int i = 0; i < server->client_threads_count; ++i ) {
|
||||||
|
if( server->client_threads[ i ].recycle ) {
|
||||||
|
context = &server->client_threads[ i ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( !context ) {
|
||||||
|
if( server->client_threads_count == MAX_CLIENT_CONNECTIONS ) {
|
||||||
|
printf( "Too many connections\n" );
|
||||||
|
if( server->pipe != INVALID_HANDLE_VALUE ) {
|
||||||
|
CloseHandle( server->pipe );
|
||||||
|
server->pipe = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
context = &server->client_threads[ server->client_threads_count++ ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memset( context, 0, sizeof( *context ) );
|
||||||
|
context->server = server;
|
||||||
|
context->pipe = server->pipe;
|
||||||
|
server->pipe = INVALID_HANDLE_VALUE;
|
||||||
|
context->thread_started_event = CreateEvent(
|
||||||
|
NULL, // default security attributes
|
||||||
|
TRUE, // manual-reset event
|
||||||
|
FALSE, // initial state is nonsignaled
|
||||||
|
NULL // object name
|
||||||
|
);
|
||||||
|
|
||||||
|
context->thread = CreateThread(
|
||||||
|
NULL, // no security attribute
|
||||||
|
0, // default stack size
|
||||||
|
ipc_client_thread, // thread proc
|
||||||
|
(LPVOID) context, // thread parameter
|
||||||
|
0, // not suspended
|
||||||
|
NULL ); // returns thread ID
|
||||||
|
|
||||||
|
if( context->thread == NULL ) {
|
||||||
|
printf( "CreateThread failed, LastError=%d.\n", GetLastError() );
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
//printf( "Waiting for thread\n" );
|
||||||
|
WaitForSingleObject( context->thread_started_event, INFINITE );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( server->pipe != INVALID_HANDLE_VALUE ) {
|
||||||
|
CloseHandle( server->pipe );
|
||||||
|
server->pipe = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ipc_server_t* ipc_server_start( char const* pipe_name, ipc_request_handler_t request_handler, void* user_data ) {
|
||||||
|
ipc_server_t* server = (ipc_server_t*) malloc( sizeof( ipc_server_t ) );
|
||||||
|
memset( server, 0, sizeof( ipc_server_t ) );
|
||||||
|
server->pipe = INVALID_HANDLE_VALUE;
|
||||||
|
if( !expand_pipe_name( pipe_name, server->expanded_pipe_name, sizeof( server->expanded_pipe_name ) ) ) {
|
||||||
|
printf( "Pipe name too long\n" );
|
||||||
|
free( server );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Create security attribs
|
// Create security attribs
|
||||||
SID_IDENTIFIER_AUTHORITY auth = { SECURITY_WORLD_SID_AUTHORITY };
|
SID_IDENTIFIER_AUTHORITY auth = { SECURITY_WORLD_SID_AUTHORITY };
|
||||||
PSID sid;
|
if( !AllocateAndInitializeSid( &auth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &server->sid ) ) {
|
||||||
if( !AllocateAndInitializeSid( &auth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &sid ) ) {
|
free( server );
|
||||||
return EXIT_FAILURE;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPLICIT_ACCESS access = { 0 };
|
EXPLICIT_ACCESS access = { 0 };
|
||||||
@ -299,127 +535,87 @@ DWORD WINAPI ipc_server_thread( LPVOID param ) {
|
|||||||
access.grfInheritance = NO_INHERITANCE;
|
access.grfInheritance = NO_INHERITANCE;
|
||||||
access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
access.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
||||||
access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
access.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
|
||||||
access.Trustee.ptstrName = (LPTSTR)sid;
|
access.Trustee.ptstrName = (LPTSTR)server->sid;
|
||||||
|
|
||||||
PACL acl;
|
if( SetEntriesInAcl(1, &access, NULL, &server->acl) != ERROR_SUCCESS ) {
|
||||||
if( SetEntriesInAcl(1, &access, NULL, &acl) != ERROR_SUCCESS ) {
|
FreeSid( server->sid );
|
||||||
FreeSid(sid);
|
free( server );
|
||||||
return EXIT_FAILURE;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
PSECURITY_DESCRIPTOR sd = (PSECURITY_DESCRIPTOR)LocalAlloc( LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH );
|
server->sd = (PSECURITY_DESCRIPTOR)LocalAlloc( LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH );
|
||||||
if( !sd ) {
|
if( !server->sd ) {
|
||||||
FreeSid( sid );
|
FreeSid( server->sid );
|
||||||
return EXIT_FAILURE;
|
free( server );
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !InitializeSecurityDescriptor( sd, SECURITY_DESCRIPTOR_REVISION ) ) {
|
if( !InitializeSecurityDescriptor( server->sd, SECURITY_DESCRIPTOR_REVISION ) ) {
|
||||||
LocalFree(sd);
|
LocalFree(server->sd);
|
||||||
FreeSid(sid);
|
FreeSid( server->sid );
|
||||||
return EXIT_FAILURE;
|
free( server );
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( !SetSecurityDescriptorDacl( sd, TRUE, acl, FALSE ) ) {
|
if( !SetSecurityDescriptorDacl( server->sd, TRUE, server->acl, FALSE ) ) {
|
||||||
LocalFree( sd );
|
LocalFree( server->sd );
|
||||||
FreeSid( sid );
|
FreeSid( server->sid );
|
||||||
return EXIT_FAILURE;
|
free( server );
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SECURITY_ATTRIBUTES attribs;
|
server->request_handler = request_handler;
|
||||||
attribs.nLength = sizeof( SECURITY_ATTRIBUTES );
|
server->user_data = user_data;
|
||||||
attribs.lpSecurityDescriptor = sd;
|
server->thread_started_event = CreateEvent(
|
||||||
attribs.bInheritHandle = -1;
|
NULL, // default security attributes
|
||||||
|
TRUE, // manual-reset event
|
||||||
|
FALSE, // initial state is nonsignaled
|
||||||
// The main loop creates an instance of the named pipe and
|
NULL // object name
|
||||||
// then waits for a client to connect to it. When the client
|
);
|
||||||
// connects, a thread is created to handle communications
|
DWORD threadId = 0;
|
||||||
// with that client, and this loop is free to wait for the
|
server->thread = CreateThread(
|
||||||
// next client connect request. It is an infinite loop.
|
|
||||||
|
|
||||||
for( ; ; ) {
|
|
||||||
printf( "\nPipe Server: Main thread awaiting client connection on %s\n", PIPE_NAME );
|
|
||||||
HANDLE hPipe = CreateNamedPipe(
|
|
||||||
PIPE_NAME, // pipe name
|
|
||||||
PIPE_ACCESS_DUPLEX, // read/write access
|
|
||||||
PIPE_TYPE_MESSAGE | // message type pipe
|
|
||||||
PIPE_READMODE_MESSAGE | // message-read mode
|
|
||||||
PIPE_WAIT, // blocking mode
|
|
||||||
PIPE_UNLIMITED_INSTANCES, // max. instances
|
|
||||||
BUFSIZE, // output buffer size
|
|
||||||
BUFSIZE, // input buffer size
|
|
||||||
0, // client time-out
|
|
||||||
&attribs ); // default security attribute
|
|
||||||
|
|
||||||
if( hPipe == INVALID_HANDLE_VALUE ) {
|
|
||||||
printf( "CreateNamedPipe failed, LastError=%d.\n", GetLastError() );
|
|
||||||
LocalFree( acl );
|
|
||||||
LocalFree( sd );
|
|
||||||
FreeSid( sid );
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for the client to connect; if it succeeds,
|
|
||||||
// the function returns a nonzero value. If the function
|
|
||||||
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.
|
|
||||||
|
|
||||||
BOOL fConnected = ConnectNamedPipe( hPipe, NULL ) ?
|
|
||||||
TRUE : ( GetLastError() == ERROR_PIPE_CONNECTED );
|
|
||||||
|
|
||||||
if( fConnected ) {
|
|
||||||
printf( "Client connected, creating a processing thread.\n" );
|
|
||||||
|
|
||||||
// Create a thread for this client.
|
|
||||||
DWORD dwThreadId = 0;
|
|
||||||
HANDLE hThread = CreateThread(
|
|
||||||
NULL, // no security attribute
|
|
||||||
0, // default stack size
|
|
||||||
ipc_client_thread, // thread proc
|
|
||||||
(LPVOID) hPipe, // thread parameter
|
|
||||||
0, // not suspended
|
|
||||||
&dwThreadId ); // returns thread ID
|
|
||||||
|
|
||||||
if( hThread == NULL ) {
|
|
||||||
printf( "CreateThread failed, LastError=%d.\n", GetLastError() );
|
|
||||||
LocalFree( acl );
|
|
||||||
LocalFree( sd );
|
|
||||||
FreeSid( sid );
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
} else {
|
|
||||||
CloseHandle( hThread );
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// The client could not connect, so close the pipe.
|
|
||||||
CloseHandle( hPipe );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
LocalFree( acl );
|
|
||||||
LocalFree( sd );
|
|
||||||
FreeSid( sid );
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ipc_server_t* ipc_server_start() {
|
|
||||||
DWORD threadId = 0;
|
|
||||||
HANDLE thread = CreateThread(
|
|
||||||
NULL, // default security attributes
|
NULL, // default security attributes
|
||||||
0, // use default stack size
|
0, // use default stack size
|
||||||
ipc_server_thread, // thread function name
|
ipc_server_thread, // thread function name
|
||||||
NULL, // argument to thread function
|
server, // argument to thread function
|
||||||
0, // use default creation flags
|
0, // use default creation flags
|
||||||
&threadId ); // returns the thread identifier
|
&threadId ); // returns the thread identifier
|
||||||
|
if( server->thread == NULL ) {
|
||||||
|
printf( "Failed to create server thread\n" );
|
||||||
|
LocalFree( server->acl );
|
||||||
|
LocalFree( server->sd );
|
||||||
|
FreeSid( server->sid );
|
||||||
|
free( server );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
ipc_server_t* server = new ipc_server_t;
|
if( WaitForSingleObject( server->thread_started_event, 10000 ) != WAIT_OBJECT_0 ) {
|
||||||
server->thread = thread;
|
printf( "Timeout waiting for client thread to start\n" );
|
||||||
|
LocalFree( server->acl );
|
||||||
|
LocalFree( server->sd );
|
||||||
|
FreeSid( server->sid );
|
||||||
|
free( server );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void ipc_server_stop( ipc_server_t* server ) {
|
void ipc_server_stop( ipc_server_t* server ) {
|
||||||
TerminateThread( server->thread, EXIT_SUCCESS );
|
server->exit_flag = 1;
|
||||||
|
if( server->pipe != INVALID_HANDLE_VALUE ) {
|
||||||
|
CancelIoEx( server->pipe, &server->io );
|
||||||
|
}
|
||||||
WaitForSingleObject( server->thread, INFINITE );
|
WaitForSingleObject( server->thread, INFINITE );
|
||||||
delete server;
|
LocalFree( server->acl );
|
||||||
|
LocalFree( server->sd );
|
||||||
|
FreeSid( server->sid );
|
||||||
|
for( int i = 0; i < server->client_threads_count; ++i ) {
|
||||||
|
if( !server->client_threads[ i ].recycle ) {
|
||||||
|
ipc_stop_client_thread( &server->client_threads[ i ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free( server );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif /* IPC_IMPLEMENTATION */
|
||||||
|
8
auto_update/run_loop.bat
Normal file
8
auto_update/run_loop.bat
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
REM runs the tests in an infinite loop
|
||||||
|
REM intended to be run manually and left running for a long time,
|
||||||
|
REM as a final sanity check to make sure the tests are stable
|
||||||
|
REM will exit if any tests fail
|
||||||
|
:loop
|
||||||
|
call npm run test
|
||||||
|
if %ERRORLEVEL% NEQ 0 goto :eof
|
||||||
|
goto :loop
|
@ -5,7 +5,13 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "testfw.h"
|
#include "testfw.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
|
bool pipe_exists( const char* pipe_name );
|
||||||
|
|
||||||
void test_fw_ok() {
|
void test_fw_ok() {
|
||||||
TESTFW_TEST_BEGIN( "Checking that test framework is ok" );
|
TESTFW_TEST_BEGIN( "Checking that test framework is ok" );
|
||||||
@ -14,15 +20,214 @@ void test_fw_ok() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void ipc_tests() {
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Check that IPC server is not already running" );
|
||||||
|
TESTFW_EXPECTED( !pipe_exists( "test_pipe" ) );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can start IPC server" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const*, void*, char*, size_t ) { }, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can stop IPC server" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const*, void*, char*, size_t ) { }, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_EXPECTED( !pipe_exists( "test_pipe" ) );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can connect multiple IPC clients" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const*, void*, char*, size_t ) { }, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_client_t* clients[ 32 ];
|
||||||
|
for( int i = 0; i < sizeof( clients ) / sizeof( *clients ); ++i ) {
|
||||||
|
clients[ i ] = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( clients[ i ] );
|
||||||
|
}
|
||||||
|
for( int i = 0; i < sizeof( clients ) / sizeof( *clients ); ++i ) {
|
||||||
|
ipc_client_disconnect( clients[ i ] );
|
||||||
|
}
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can connect multiple IPC clients multiple times" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const*, void*, char*, size_t ) { }, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
for( int j = 0; j < 10; ++j ) {
|
||||||
|
ipc_client_t* clients[ 32 ];
|
||||||
|
for( int i = 0; i < sizeof( clients ) / sizeof( *clients ); ++i ) {
|
||||||
|
clients[ i ] = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( clients[ i ] );
|
||||||
|
}
|
||||||
|
for( int i = 0; i < sizeof( clients ) / sizeof( *clients ); ++i ) {
|
||||||
|
ipc_client_disconnect( clients[ i ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can connect IPC client" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const*, void*, char*, size_t ) { }, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_client_t* client = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( client != NULL );
|
||||||
|
ipc_client_disconnect( client );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can send IPC message from client to server" );
|
||||||
|
bool message_received = false;
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const* message, void* user_data, char*, size_t ) {
|
||||||
|
bool* message_received = (bool*) user_data;
|
||||||
|
*message_received = true;
|
||||||
|
TESTFW_EXPECTED( strcmp( message, "Test message" ) == 0 );
|
||||||
|
}, &message_received );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_client_t* client = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( client != NULL );
|
||||||
|
TESTFW_EXPECTED( ipc_client_send( client, "Test message" ) == true );
|
||||||
|
char temp[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
int size = 0;
|
||||||
|
ipc_client_receive( client, temp, sizeof( temp ), &size );
|
||||||
|
TESTFW_EXPECTED( message_received == true );
|
||||||
|
|
||||||
|
ipc_client_disconnect( client );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can receive IPC response from server" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const* message, void* user_data, char* response, size_t ) {
|
||||||
|
strcpy( response, "Test response" );
|
||||||
|
}, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_client_t* client = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( client != NULL );
|
||||||
|
TESTFW_EXPECTED( ipc_client_send( client, "Test message" ) == true );
|
||||||
|
char response[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
int size = 0;
|
||||||
|
TESTFW_EXPECTED( ipc_client_receive( client, response, sizeof( response ), &size ) == IPC_RECEIVE_STATUS_DONE );
|
||||||
|
TESTFW_EXPECTED( size == strlen( "Test response" ) + 1 );
|
||||||
|
TESTFW_EXPECTED( strcmp( response, "Test response" ) == 0 );
|
||||||
|
ipc_client_disconnect( client );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can send and receive long IPC messages" );
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const* message, void* user_data, char* response, size_t capacity ) {
|
||||||
|
char expected_message[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
for( int i = 0; i < IPC_MESSAGE_MAX_LENGTH - 1; ++i ) {
|
||||||
|
expected_message[ i ] = 'A' + ( i % ( 'Z' - 'A' + 1 ) );
|
||||||
|
}
|
||||||
|
expected_message[ IPC_MESSAGE_MAX_LENGTH - 1 ] = '\0';
|
||||||
|
TESTFW_EXPECTED( strcmp( message, expected_message ) == 0 );
|
||||||
|
for( int i = 0; i < (int) capacity - 1; ++i ) {
|
||||||
|
response[ i ] = 'a' + ( i % ( 'z' - 'a' + 1 ) );
|
||||||
|
}
|
||||||
|
response[ capacity - 1 ] = '\0';
|
||||||
|
}, NULL );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_client_t* client = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( client != NULL );
|
||||||
|
char message[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
for( int i = 0; i < IPC_MESSAGE_MAX_LENGTH - 1; ++i ) {
|
||||||
|
message[ i ] = 'A' + ( i % ( 'Z' - 'A' + 1 ) );
|
||||||
|
}
|
||||||
|
message[ IPC_MESSAGE_MAX_LENGTH - 1 ] = '\0';
|
||||||
|
TESTFW_EXPECTED( ipc_client_send( client, message ) == true );
|
||||||
|
char response[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
int size = 0;
|
||||||
|
TESTFW_EXPECTED( ipc_client_receive( client, response, sizeof( response ), &size ) == IPC_RECEIVE_STATUS_DONE );
|
||||||
|
char expected_response[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
for( int i = 0; i < IPC_MESSAGE_MAX_LENGTH - 1; ++i ) {
|
||||||
|
expected_response[ i ] = 'a' + ( i % ( 'z' - 'a' + 1 ) );
|
||||||
|
}
|
||||||
|
expected_response[ IPC_MESSAGE_MAX_LENGTH - 1 ] = '\0';
|
||||||
|
TESTFW_EXPECTED( size == IPC_MESSAGE_MAX_LENGTH );
|
||||||
|
TESTFW_EXPECTED( strcmp( response, expected_response ) == 0 );
|
||||||
|
|
||||||
|
ipc_client_disconnect( client );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TESTFW_TEST_BEGIN( "Can send and receive multiple IPC messages" );
|
||||||
|
int received_count = 0;
|
||||||
|
ipc_server_t* server = ipc_server_start( "test_pipe",
|
||||||
|
[]( char const* message, void* user_data, char* response, size_t ) {
|
||||||
|
int* received_count = (int*) user_data;
|
||||||
|
char expected_message[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
sprintf( expected_message, "Test message %d", *received_count );
|
||||||
|
TESTFW_EXPECTED( strcmp( message, expected_message ) == 0 );
|
||||||
|
sprintf( response, "Test response %d", *received_count );
|
||||||
|
*received_count = *received_count + 1;
|
||||||
|
}, &received_count );
|
||||||
|
TESTFW_EXPECTED( server != NULL );
|
||||||
|
ipc_client_t* client = ipc_client_connect( "test_pipe" );
|
||||||
|
TESTFW_EXPECTED( client != NULL );
|
||||||
|
for( int i = 0; i < 64; ++i ) {
|
||||||
|
char message[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
sprintf( message, "Test message %d", i );
|
||||||
|
TESTFW_EXPECTED( ipc_client_send( client, message ) == true );
|
||||||
|
char response[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
int size = 0;
|
||||||
|
TESTFW_EXPECTED( ipc_client_receive( client, response, sizeof( response ), &size ) == IPC_RECEIVE_STATUS_DONE );
|
||||||
|
char expected_response[ IPC_MESSAGE_MAX_LENGTH ];
|
||||||
|
sprintf( expected_response, "Test response %d", i );
|
||||||
|
TESTFW_EXPECTED( size == strlen( expected_response ) + 1 );
|
||||||
|
TESTFW_EXPECTED( strcmp( response, expected_response ) == 0 );
|
||||||
|
}
|
||||||
|
TESTFW_EXPECTED( received_count == 64 );
|
||||||
|
|
||||||
|
ipc_client_disconnect( client );
|
||||||
|
ipc_server_stop( server );
|
||||||
|
TESTFW_TEST_END();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main( int argc, char** argv ) {
|
int main( int argc, char** argv ) {
|
||||||
TESTFW_INIT();
|
TESTFW_INIT();
|
||||||
|
|
||||||
test_fw_ok();
|
test_fw_ok();
|
||||||
|
ipc_tests();
|
||||||
|
|
||||||
int result = TESTFW_SUMMARY();
|
int result = TESTFW_SUMMARY();
|
||||||
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
return result == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define IPC_IMPLEMENTATION
|
||||||
|
#include "ipc.h"
|
||||||
|
|
||||||
#define TESTFW_IMPLEMENTATION
|
#define TESTFW_IMPLEMENTATION
|
||||||
#define TESTFW_NO_ANSI
|
#define TESTFW_NO_ANSI
|
||||||
#include "testfw.h"
|
#include "testfw.h"
|
||||||
|
|
||||||
|
@ -72,21 +72,25 @@
|
|||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\tests\</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\tests\</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<LinkIncremental>true</LinkIncremental>
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\tests\</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<LinkIncremental>false</LinkIncremental>
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
<IntDir>$(Platform)\$(Configuration)\tests\</IntDir>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -100,7 +104,7 @@
|
|||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -114,7 +118,7 @@
|
|||||||
<ClCompile>
|
<ClCompile>
|
||||||
<WarningLevel>Level3</WarningLevel>
|
<WarningLevel>Level3</WarningLevel>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -128,7 +132,7 @@
|
|||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
<SDLCheck>true</SDLCheck>
|
<SDLCheck>true</SDLCheck>
|
||||||
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
<PreprocessorDefinitions>_CRTDBG_MAP_ALLOC;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
<ConformanceMode>true</ConformanceMode>
|
<ConformanceMode>true</ConformanceMode>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<Link>
|
<Link>
|
||||||
@ -139,6 +143,7 @@
|
|||||||
</Link>
|
</Link>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="ipc.h" />
|
||||||
<ClInclude Include="testfw.h" />
|
<ClInclude Include="testfw.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user