Discussion on multi-process parameters

xiaoxiao2021-03-06  43

Discuss multi-process parameters transfer technology

The so-called multi-process parameter transmission process, in fact, when the program is repeated, to ensure that the uniqueness of the process is unique in the memory and the command line parameters passed during launch, and passed this command line parameter to the running process the process of. And this time you may activate a handling event or thread in the running process.

This mainly involves repeated operation detection, inter-process communication, remote process activation, and possible multi-threaded technology.

The occasion that initially needs this technology is to start like Netants' IE right-click menu. Using this technology can be communicated with process through COM when adding a download task every time. Because this obviously increases the complexity of the COM component and the degree of coupling of the process. Just use the downloaded URL to start the program as the command line parameter, then the process started is responsible for transmitting the URL to the previously running process and inspire the addition of download events in the target process.

The basic techniques involved in the following are discussed below.

First, the program is repeatedly running detection

It can achieve this function of this function, but the most commonly used and robust approach is to use mutex kernel objects Mutex. Mutex ensures that the thread has a mutual exclusive access to a single resource. When these threads are in different processes, it is to ensure that the process can have mutually exclusive access to a single resource. Here, the operating conditions can be used as a mutually exclusive resource, i.e., only a single process can be operated, and the processes that are started after running, the process of starting is not available because of the mutual exclusion of the operating conditions. This also ensures that the uniqueness of the running of a process is ensured.

To use a mutex, you must have a process first created a mutex, which is naturally the work that needs to be generated for the first time. This can create a mutually exclusive object by calling the API function CREATEMUTEX.

Handle Createmutex (Psecurity_Attributes PSA, Bool Fireital, PCTSTR PSZNAME);

Parameter PSA: A pointer to the Structure_Attributes structure indicates a security descriptor. The security descriptor is used to describe who created the object, who can access or use the object, who is not right to access it. Security descriptors are typically used when writing server applications, and this feature can be ignored if it is written to a client. Windows98 system has no such security feature, but from Windows2000, this feature is available. But most applications are to deliver a NULL for this parameter, which can create kernel objects with default security.

Parameter Finitialowner: The initial state used to control the mutex. If False is transmitted, it means that the mutual exclusive object is not owned by any thread, so it is necessary to issue its comrade signals. If TRUE is passed, then the object's creating thread has, and does not issue a notification signal at the beginning.

Parameter pszname: A string pointer with a maximum length of 0 ends, indicating the name of this mutex. Since all kernel objects in the system share a single name space, you cannot rename the name already existing.

Release a mutex. Note that only threads that successfully wait for this object can release it.

Bool ReleaseMuteX (Handle Hmutex);

Parameters hmutex: The object handle that wants to release.

The judgment function implemented by this method should look like this:

Bool isotherstarted () // Judging another process backup of this program has been run

{

Handle Hmutex = Createmutex (NULL, TRUE, "MY_MUTEX_NAME");

DWORD lasterr = getLastError ();

Return (lasterr == error_already_exists)? True: false;

}

Where my_mutex_name is the name of the mutually exclusive object, the only thing is taken as much as possible. The general use of the program name plus the version number or date is the best. Of course, if you are willing, using guid seems to be better.

Second, inter-process communication

The topic regarding inter-process communication can write a book, and the aspects involved are too broad. Here is only a variety of possible options and explains one of the following instead of another.

It is well known that Windows is a multitasking operating system. The various processes running within the system cannot interact, and the crash of a process cannot affect other processes. Thus, in order to achieve this security mechanism, each process can only access its own private address space, and cannot access the address space of other processes. This undoubtedly brings difficulties to communication between processes.

Inter-process communication recommended by Microsoft is file mapping technology. But establishing a mapping file is not easy. First, two processes are required to specify the same disk file to open it, then create a file mapping object, followed by address mapping, after using it, release, revoke, and close resources. If you add this to the fault tolerance to cope, you can make your code at all. In fact, there is a way to change the file mapping technology - WM_CopyData message. The principle, the WM_CopyData message is also file mapping technology, but only the process of establishing and revoking file mapping is helped by the system. Because this is only necessary to communicate only the command line parameters such as the URL, there is no complex structure, so the WM_COPYDATA message is completely competent.

WM_COPYDATA message is used.

CopyDataStruct CDS;

SendMessage (HWndReceiver, WM_CopyData, (WPARAM) HWNDSENDER, (LPARAM) & CDS);

CopyDataStruct is a structure, as follows:

Typedef struct tagcopydatatruct {

Ulong_ptr dwdata;

DWORD CBDATA;

PVOID LPDATA;

} CopyDataStruct;

This structure must be initialized when sending data. DWDATA is an alternate data item that can be stored. Generally used to illustrate information about the sending data, which is defined by yourself. The CBDATA data member specifies the number of bytes sent to another process, and the LPDATA data member points to the first byte to be sent. The address referred to by LPDATA is of course in the process of sending messages.

This way, just handle this WM_COPYDATA message in the accepted process. This method is simple and convenient, and the disadvantage requires nothing to be picky in addition to the system to establish a mapping file and copy data. However, if you think about it, you will find the following questions:

1 Because it is a send message, the acceptance thread that is required to accept the process must have a message queue, that is, there must be a message loop and window procedure.

2 Must get a window handle that accepted the process before use to send a message.

The application here is merely transmitting command line parameters to the target process, and does not require a window. This is the problem.

The following is an unconventional method, which is very good to implement the task that only passing the command line parameters to the target process without requiring the target process, must have a window.

Since the process of both communication is actually the two running processes of the same program, their address space distribution is exactly the same. From a certain point of view, a certain data pointer passed to another process is a pointer to the corresponding data of another process. What we need to do is to copy data to the same address of another process. Note that the data here should be all in order to ensure the constantness and existence of the address. This can be implemented by the API function WriteProcessMemory. The code is about the following:

// GlobalBuffer serves as a data buffer for the global variable, the size is max_path.

LSTRCPY (GlobalBuffer, LPCMDLINE);

Handle Ph = OpenProcess (Process_All_Access, false, otherid);

IF (WriteProcessMemory (PH, (LPVOID) GlobalBuffer, (LPVOID) GlobalBuffal, Max_Path, NULL) == 0) CloseHandle (pH);

// The above code write parameter lpcmdline to another backup process global buffer

Third, remote process activation

The processes can be notified to accept the process in order to accept the process. So how do I notify the process? The message naturally cannot be used, and the solution can be solved is to use the event kernel object (Event) to notify accept the acceptance process. That is, the activation of the remote process.

An event kernel object can notify one operation has been completed. There are two different types of event objects. One is a manual reset event, and the other is an automatic reset event. When the automatic reset event is notified, only one of the threads waiting for the event will be activated.

Below is the CreateEvent function used to create an event kernel object:

Handle CreateEvent (Psecuity_Attributes PSA, Bool FmanualReset, Bool FinitialState, PCTSTR PSZNAME);

The first and last parameters have been introduced, and the meaning of the middle two parameters will be described here.

Parameter fmanualReset: Point out that it is created a manual reset (TRUE) event or creates an automatic reset event (FALSE).

Parameter FinitialState: Terminal to indicate that the event is to be initialized to a notified state (TRUR) or not notified status (FALSE).

Of course, this also needs to accept the process waiting for this event kernel object. The method is to use OpenEvent to get the handle of this event kernel object with OpenEvent. Then use the API function waitforsingleObject to wait.

The following is an explanation of the OpenEvent and WaitForsingleObject functions:

Handle OpenEvent (DWORD FDWACCESS, BOOL FINHERIT, PCTSTSTSTSZNAME);

Parameters FDWACCESS: Specify access rights required for event objects. For systems that support object security, if the security descriptor of the specified object does not allow calls to be called with the specified permissions, then the function will fail.

Any combination of the following values:

Event_all_access: Specify all possible access signs.

Event_MODITY_STATE: Allows the modification of the event status (via the API function setevent or resetEvent).

Parameter Finherit: Specifies whether the end of the returned can be inherited. There is no need here, pass NULL.

DWORD WAITFORSINGLEOBJECT (Handle Hobject, DWORD DWMILLISECONDS);

Parameters hobject: Waiting for the handle of the object.

Parameter dwmilliseconds: Take the timeout value in milliseconds, exceeding this event interval, whether or not the success is returned. It can be specified as INFINITE to represent never timeout, that is, forever waiting.

So far, the wait process can write the code like the following.

Handle HEVENT = CREATEEVENT (NULL, FALSE, FALSE, Event_Name);

WaitforsingleObject (HEVENT, INFINITE);

How to send a process notify the event? Use the API function setEvent.

Bool setEvent (Handle HEVENT);

Parameter HEVENT: The event handle to notify.

The sending process will call setEvent as long as the data replication is completed. code show as below:

Handle HEVENT = OPENEVENT (Event_all_Access | Event_modify_state, true, event_name); setEvent (hevent);

CloseHandle (HEVENT);

Once the process successfully waited for the data, the data is already valid, and the data can be read and the operation is performed. However, it is worth noting because the acceptance process is suspended when waiting for kernel objects, but it is clear that there is still something else to do, and it cannot be just hanging only for this event. How to solve this contradiction? One of the ways is to use a thread to wait for an event. Once the event is awaiting, this wait thread will be activated, then notify the main thread or simply handle it.

The above technique is well solved, but some complexity is introduced because of the introduction of multi-threads. Since the wafer is very simple, the complexity is not very large.

Fourth, multi-thread

Multi-thread is not a fresh technology, and Windows is an operating system that supports multi-threaded. There are many technical details about multithreading, not this article can tell. You can refer to a lot of books specializing in multithreaded programming, which is no longer subject to it.

Five, instance code

The following instance code is written in C Builder6 and has a detailed comment. There is no longer an explanation here.

1.csysprotab.h file

/ / -------------------------------------------------------------------------------------------- ---------------------------

#ifndef csysprotabh

#define csysprotabh

/ / -------------------------------------------------------------------------------------------- ---------------------------

#include

#include

#include

// This class is used to get all process information in the system

// Xu Jinpeng

//2003.4.10

//

Class Csysprocesstable

{

Vector

m_processtable;

PUBLIC:

Csysprocesstable ();

INT getCount (); // Get total number of processes in the system

/ / Get the total number of processes in the specified process name (ie multiple run backups of the same program)

INT getCount (const onistring & procname);

// Start searching for the process ID of the specified process name from OFFER

DWORD GETPROCESSID (Const Ansistring & Procname, INT & IOFFER);

INT GetThreadCount (DWORD Procid); // Get the total number of threads in the specified process

Void SaveTerstrings (TSTRINGLIST * SL)

{

FOR (Vector

:: Iterator i = m_processtable.begin (); i! = m_processtable.end (); i )

SL-> Add (i-> szexefile);

}

}

#ENDIF

/ / -------------------------------------------------------------------------------------------- ---------------------------

2. CSysProtab.cpp file

/ / -------------------------------------------------------------------------------------------- ---------------------------

#pragma HDRSTOP

#include "csysprotab.h"

/ / -------------------------------------------------------------------------------------------- ---------------------------

#pragma package (smart_init)

/ / -------------------------------------------------------------------------------------------- --------------------------- csysprocesstable :: csysprocesstable ()

{

Handle hsnapshot = CreateToolHelp32Snapshot (TH32CS_SNAPPROCESS, 0);

PROCESSENTRY32 T_PROCENT;

IF (hsnapshot! = (void *) - 1)

{

IF (Process32First (HSnapshot, & T_Procent) == True

{DO

{

m_processtable.push_back (t_procent);

}

While (process32next (hsnapshot, & t_procent) == true);

}

CloseHandle (HSnapshot);

}

Else

{

ShowMessage ("CreateToolhelp32Snapshot Error!");

}

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

INT CSYSPROCESSTABLE :: getCount () // Get the total number of processes in the system

{

Return m_processtable.size ();

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

INT Csysprocesstable :: getCount (const ANSISTRING & Procname)

/ / Get the total number of processes in the specified process name (ie multiple run backups of the same program)

{

INT T_RET = 0;

FOR (Vector

:: Iterator i = m_processtable.begin (); i! = m_processtable.end (); i )

{

IF (procname.lowercase () == Ansistring (i-> szexefile) .LowerCase ()) t_ret ;

}

Return T_Ret;

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

DWORD CSYSPROCESSTABLE :: GetProcessId (Const Ansistring & Procname, INT & IOFFER)

// Start searching for the process ID of the specified process name from OFFER

{

INT j = ioffer;

FOR (Vector

:: Iterator i = m_processtable.begin () ioffer; i! = m_processtable.end (); i )

{

IF (procname.lowercase () == Ansistring (i-> szexefile) .llowercase ())

{

IOffer = J;

Return I-> TH32ProcessId;

}

J ;

}

Return 0; // Nothing to find back 0

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

INT CSYSPROCESSTABLE :: GetThreadCount (DWORD ProID) // Get the total number of threads in the specified process {

FOR (Vector

:: Iterator i = m_processtable.begin (); i! = m_processtable.end (); i )

{

IF (I-> TH32Processid == Procid) Return I-> CNTTHREADS;

}

Return 0;

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

3.Project.cpp file

/ / -------------------------------------------------------------------------------------------- ---------------------------

#include

#pragma HDRSTOP

/ / -------------------------------------------------------------------------------------------- ---------------------------

#include "csysprotab.h"

#define Event_name "Goldroc_ NT_Event"

#define mutex_name "Goldroc_n_mutex"

#define process_Already_Start 1

#define process_Param_Start 2

#define process_normal_start 0

#DEfine ErrbackBox (Errno) ShowMessage ("Application error! / n Please feed back the error number, the operating system version is given to the author in order to improve! Thank you! / n / n error Number:" Ansistring (errno))

/ / -------------------------------------------------------------------------------------------- ---------------------------

Useform ("ufrmmain.cpp", formmain;

/ / -------------------------------------------------------------------------------------------- ---------------------------

Typedef int starttag;

Char GlobalBuffer [260]; // Global buffer, used to write other process backups to URL and Title

Handle threadEvent;

Bool isotherstarted () // Judging another process backup of this program has been run

{

Handle Hmutex = CreateMutex (Null, True, Mutex_Name);

DWORD lasterr = getLastError ();

IF (LasterR == Error_Already_exists) Return True;

IF (hmutex == null)

{

ErrBackBox (lasterr);

Application-> Terminate ();

}

Return False;

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

WinAPI Winmain (Hinstance, Hinstance, LPSTR LPCMDLINE, INT)

{

Try

{

StartTag starttag;

IF (ANSISTRING (LPCMDLINE) .Isempty ())

STARTTAG = process_normal_start; Else

StartTag = process_Param_Start;

IF (isotherstarted ()) StartTag = starttag | process_already_start;

Csysprocesstable * t_pt = new csysprocesstable;

INT j = 0;

DWORD OtherID;

Ansistring ExefileName = extractFileName (Application-> Exename);

Handle Ph; Handle HEVENT;

Int OtherProccount;

Switch (STATTAG)

{

Case process_param_start: // Started

LSTRCPY (GlobalBuffer, LPCMDLINE);

ThreadEvent = CreateEvent (NULL, FALSE, TRUE, Event_Name);

Case process_normal_start: // Start normally

ThreadEvent = CreateEvent (NULL, FALSE, FALSE, Event_Name);

Break;

Case process_param_start | process_already_start: // Started and has backup operation

// Task: Send the parameters of the belt to another backup process to set an event object

/ / For the signal, let the thread in another backup process start running.

// Another backup process is a thread of this event to send a GM_ADDURL message to the main window.

The // The main window gets the URL, Title, responds from the global buffer.

// With this method, the process is implemented. Event notification message arrives; GlobalBuffer puts the message content

LSTRCPY (GlobalBuffer, LPCMDLINE);

OtherProccount = T_PT-> getCount (ExefileName);

For (int i = 0; i

{

Otherid = t_pt-> getProcessId (ExefileName, J);

IF (OtherID! = getCurrentProcessID ())

Break;

Else

J ;

}

// The above code gets another backup process ID: (OtherID)

PH = OpenProcess (Process_all_access, false, otherid;

IF (pH == null)

{

ErrBackBox ("0x0001 / T Open Remote Process Error");

Return 0;

}

IF (WriteProcessMemory "GlobalBuffer, (LPVOID) GlobalBuffer, Max_Path, NULL) == 0)

{

ErrBackBox ("0x1000 / T Write Remote Process Error");

Return 0;

}

CloseHandle (pH);

// The above code write parameter lpcmdline to another backup process global buffer

HEVENT = OPENEVENT (Event_all_access | Event_Modify_State, True, Event_NAME);

IF (HEVENT == NULL)

{

ErrBackBox (getLastError ());

Return 0;

}

STEVENT (HEVENT);

CloseHandle (HEVENT);

// The above code sets the event object to the notification status so that the thread of this event in another backup process is started.

Delete T_PT;

t_pt = null;

Case process_normal_start | process_already_start: // No argument starts and has backup operation

Return 0;

// Because there is already another process backup, this process exits

}

Application-> Initialize ();

Application-> Createform (__ classid (tformmain), & formmain;

Application-> Run ();

}

Catch (Exception & Exception)

{

Application-> Showexception (& Exception);

}

Return 0;

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

4. UthreadAdd.h file

/ / -------------------------------------------------------------------------------------------- ---------------------------

#ifndef uthreadaddh

#define uthreadaddh

/ / -------------------------------------------------------------------------------------------- ---------------------------

#include

/ / -------------------------------------------------------------------------------------------- ---------------------------

Class TTHREADDURL: PUBLIC TTHREAD

{

Private:

protected:

Void __fastcall execute ();

PUBLIC:

__fastcall tthreadaddurl (Bool createSuspend);

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

#ENDIF

/ / -------------------------------------------------------------------------------------------- ---------------------------

5. uthreadadd.cpp file

/ / -------------------------------------------------------------------------------------------- ---------------------------

#include

#pragma HDRSTOP

#include "uthreadadd.h"

#include "ufrmmain.h"

#pragma package (smart_init)

/ / -------------------------------------------------------------------------------------------- ---------------------------

Extern Handle ThreadEvent;

__fastcall tthreadaddurl :: TTHREADDURL (BOOL CREATESUSPENDED)

: Tthread (createSuspend)

{

}

/ / -------------------------------------------------------------------------------------------- ---------------------------

// Wait for an event object notified from another process backup, then send a message to the main window

void __fastcall tthreadaddurl :: execute ()

{

For (;;)

{

WaitforsingleObject (ThreadEvent, Infinite);

IF (Terminated) Return;

// TO DO successfully waited for some processing.

}

}

转载请注明原文地址:https://www.9cbs.com/read-119372.html

New Post(0)