mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-27 14:40:28 +00:00
Merge remote-tracking branch 'origin/GP-3895_d-millar_dbgeng_exe_name'
(#5817)
This commit is contained in:
commit
19e32acd90
@ -110,5 +110,7 @@ public interface DebugSystemObjects {
|
|||||||
void setImplicitThreadDataOffset(long systemOffset);
|
void setImplicitThreadDataOffset(long systemOffset);
|
||||||
|
|
||||||
void setImplicitProcessDataOffset(long systemOffset);
|
void setImplicitProcessDataOffset(long systemOffset);
|
||||||
|
|
||||||
|
String getCurrentProcessExecutableName();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import com.sun.jna.platform.win32.WinDef.ULONGByReference;
|
|||||||
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
|
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
|
||||||
import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference;
|
import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference;
|
||||||
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
||||||
|
import com.sun.jna.Native;
|
||||||
import com.sun.jna.platform.win32.COM.COMUtils;
|
import com.sun.jna.platform.win32.COM.COMUtils;
|
||||||
|
|
||||||
import agent.dbgeng.dbgeng.COMUtilsExtra;
|
import agent.dbgeng.dbgeng.COMUtilsExtra;
|
||||||
@ -261,6 +262,18 @@ public class DebugSystemObjectsImpl1 implements DebugSystemObjectsInternal {
|
|||||||
return pulSysOffset.getValue().longValue();
|
return pulSysOffset.getValue().longValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentProcessExecutableName() {
|
||||||
|
ULONGByReference pulPathLength = new ULONGByReference();
|
||||||
|
COMUtils.checkRC(jnaSysobj.GetCurrentProcessExecutableName(null, new ULONG(0), pulPathLength));
|
||||||
|
byte[] aBuffer = new byte[pulPathLength.getValue().intValue()];
|
||||||
|
HRESULT hr = jnaSysobj.GetCurrentProcessExecutableName(aBuffer, pulPathLength.getValue(), null);
|
||||||
|
if (hr.equals(COMUtilsExtra.E_UNEXPECTED) || hr.equals(COMUtilsExtra.E_NOTIMPLEMENTED)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return Native.toString(aBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DebugSessionId getEventSystem() {
|
public DebugSessionId getEventSystem() {
|
||||||
throw new UnsupportedOperationException("Not supported by this interface");
|
throw new UnsupportedOperationException("Not supported by this interface");
|
||||||
|
@ -103,4 +103,6 @@ public interface IDebugSystemObjects extends IUnknown {
|
|||||||
|
|
||||||
HRESULT GetCurrentProcessDataOffset(ULONGLONGByReference SysOffset);
|
HRESULT GetCurrentProcessDataOffset(ULONGLONGByReference SysOffset);
|
||||||
|
|
||||||
|
HRESULT GetCurrentProcessExecutableName(byte[] Buffer, ULONG BufferSize, ULONGByReference ExeSize);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,13 @@ package agent.dbgeng.jna.dbgeng.sysobj;
|
|||||||
|
|
||||||
import com.sun.jna.Pointer;
|
import com.sun.jna.Pointer;
|
||||||
import com.sun.jna.Structure;
|
import com.sun.jna.Structure;
|
||||||
import com.sun.jna.platform.win32.WinDef.*;
|
import com.sun.jna.platform.win32.WinDef.ULONG;
|
||||||
|
import com.sun.jna.platform.win32.WinDef.ULONGByReference;
|
||||||
|
import com.sun.jna.platform.win32.WinDef.ULONGLONG;
|
||||||
|
import com.sun.jna.platform.win32.WinDef.ULONGLONGByReference;
|
||||||
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
import com.sun.jna.platform.win32.WinNT.HRESULT;
|
||||||
|
|
||||||
import agent.dbgeng.jna.dbgeng.UnknownWithUtils;
|
import agent.dbgeng.jna.dbgeng.UnknownWithUtils;
|
||||||
import agent.dbgeng.jna.dbgeng.sysobj.IDebugSystemObjects2.VTIndices2;
|
|
||||||
|
|
||||||
public class WrapIDebugSystemObjects extends UnknownWithUtils implements IDebugSystemObjects {
|
public class WrapIDebugSystemObjects extends UnknownWithUtils implements IDebugSystemObjects {
|
||||||
public static class ByReference extends WrapIDebugSystemObjects
|
public static class ByReference extends WrapIDebugSystemObjects
|
||||||
@ -132,4 +134,9 @@ public class WrapIDebugSystemObjects extends UnknownWithUtils implements IDebugS
|
|||||||
return _invokeHR(VTIndices.GET_CURRENT_PROCESS_DATA_OFFSET, getPointer(), SysOffset);
|
return _invokeHR(VTIndices.GET_CURRENT_PROCESS_DATA_OFFSET, getPointer(), SysOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HRESULT GetCurrentProcessExecutableName(byte[] Buffer, ULONG BufferSize, ULONGByReference ExeSize) {
|
||||||
|
return _invokeHR(VTIndices.GET_CURRENT_PROCESS_EXECUTABLE_NAME, getPointer(), Buffer, BufferSize, ExeSize);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -241,4 +241,14 @@ public interface DbgProcess extends DbgMemoryOperations {
|
|||||||
*/
|
*/
|
||||||
Long getOffset();
|
Long getOffset();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the executable's name
|
||||||
|
*/
|
||||||
|
String getExecutableName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the executable's name
|
||||||
|
*/
|
||||||
|
void setExecutableName(String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public class DbgListOSProcessesCommand extends AbstractDbgCommand<Map<DebugProce
|
|||||||
String[] fields = line.trim().split("\\s+");
|
String[] fields = line.trim().split("\\s+");
|
||||||
if (fields.length > 3 && fields[2].equals("Cid:")) {
|
if (fields.length > 3 && fields[2].equals("Cid:")) {
|
||||||
Long pid = Long.parseLong(fields[3], 16);
|
Long pid = Long.parseLong(fields[3], 16);
|
||||||
DbgProcessImpl mirror = manager.getProcessComputeIfAbsent(new DebugProcessRecord(pid), pid, false);
|
DbgProcessImpl mirror = manager.getProcessComputeIfAbsent(new DebugProcessRecord(pid), pid, null, false);
|
||||||
if (offset != null) {
|
if (offset != null) {
|
||||||
mirror.setOffset(offset);
|
mirror.setOffset(offset);
|
||||||
updatedProcessIds.add(mirror.getId());
|
updatedProcessIds.add(mirror.getId());
|
||||||
|
@ -51,16 +51,19 @@ public class DbgListProcessesCommand extends AbstractDbgCommand<Map<DebugProcess
|
|||||||
// Need to create the inferior as if we received =thread-group-created
|
// Need to create the inferior as if we received =thread-group-created
|
||||||
DebugSystemObjects so = manager.getSystemObjects();
|
DebugSystemObjects so = manager.getSystemObjects();
|
||||||
long pid;
|
long pid;
|
||||||
|
String name;
|
||||||
if (!manager.isKernelMode()) {
|
if (!manager.isKernelMode()) {
|
||||||
Msg.warn(this, "Resync: Was missing group: i" + id);
|
Msg.warn(this, "Resync: Was missing group: i" + id);
|
||||||
so.setCurrentProcessId(id);
|
so.setCurrentProcessId(id);
|
||||||
pid = so.getCurrentProcessSystemId();
|
pid = so.getCurrentProcessSystemId();
|
||||||
|
name = so.getCurrentProcessExecutableName();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
id = new DebugSystemProcessRecord(id.value());
|
id = new DebugSystemProcessRecord(id.value());
|
||||||
pid = -1;
|
pid = -1;
|
||||||
|
name = so.getCurrentProcessExecutableName();;
|
||||||
}
|
}
|
||||||
DbgProcessImpl proc = manager.getProcessComputeIfAbsent(id, pid, true);
|
DbgProcessImpl proc = manager.getProcessComputeIfAbsent(id, pid, name, true);
|
||||||
Long offset = so.getCurrentProcessDataOffset();
|
Long offset = so.getCurrentProcessDataOffset();
|
||||||
proc.setOffset(offset);
|
proc.setOffset(offset);
|
||||||
}
|
}
|
||||||
|
@ -317,13 +317,13 @@ public class DbgManagerImpl implements DbgManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DbgProcessImpl getProcessComputeIfAbsent(DebugProcessId id, long pid, boolean fire) {
|
public DbgProcessImpl getProcessComputeIfAbsent(DebugProcessId id, long pid, String name, boolean fire) {
|
||||||
synchronized (processes) {
|
synchronized (processes) {
|
||||||
if (processes.containsKey(id)) {
|
if (processes.containsKey(id)) {
|
||||||
DbgProcessImpl existingProc = processes.get(id);
|
DbgProcessImpl existingProc = processes.get(id);
|
||||||
return existingProc;
|
return existingProc;
|
||||||
}
|
}
|
||||||
DbgProcessImpl process = new DbgProcessImpl(this, id, pid);
|
DbgProcessImpl process = new DbgProcessImpl(this, id, pid, name);
|
||||||
process.add();
|
process.add();
|
||||||
if (fire) {
|
if (fire) {
|
||||||
getEventListeners().fire.processAdded(process, DbgCause.Causes.UNCLAIMED);
|
getEventListeners().fire.processAdded(process, DbgCause.Causes.UNCLAIMED);
|
||||||
@ -1738,7 +1738,8 @@ public class DbgManagerImpl implements DbgManager {
|
|||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
int pid = so.getCurrentProcessSystemId();
|
int pid = so.getCurrentProcessSystemId();
|
||||||
return getProcessComputeIfAbsent(id, pid, true);
|
String name = so.getCurrentProcessExecutableName();
|
||||||
|
return getProcessComputeIfAbsent(id, pid, name, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1758,14 +1759,15 @@ public class DbgManagerImpl implements DbgManager {
|
|||||||
DebugSystemObjects so = getSystemObjects();
|
DebugSystemObjects so = getSystemObjects();
|
||||||
currentSession = eventSession = getSessionComputeIfAbsent(esid, true);
|
currentSession = eventSession = getSessionComputeIfAbsent(esid, true);
|
||||||
if (kernelMode) {
|
if (kernelMode) {
|
||||||
DbgProcessImpl cp = getProcessComputeIfAbsent(new DebugSystemProcessRecord(epid.value()), -1, true);
|
DbgProcessImpl cp = getProcessComputeIfAbsent(new DebugSystemProcessRecord(epid.value()), -1, null, true);
|
||||||
cp.setOffset(so.getCurrentProcessDataOffset());
|
cp.setOffset(so.getCurrentProcessDataOffset());
|
||||||
|
cp.setExecutableName(so.getCurrentProcessExecutableName());
|
||||||
currentProcess = eventProcess = cp;
|
currentProcess = eventProcess = cp;
|
||||||
if (currentProcess.getId().isSystem()) {
|
if (currentProcess.getId().isSystem()) {
|
||||||
execute(new DbgResolveProcessCommand(this, currentProcess)).thenAccept(proc -> {
|
execute(new DbgResolveProcessCommand(this, currentProcess)).thenAccept(proc -> {
|
||||||
currentProcess = eventProcess = proc;
|
currentProcess = eventProcess = proc;
|
||||||
// As you now have both pid & offset, update the id==pid version
|
// As you now have both pid & offset, update the id==pid version
|
||||||
DbgProcessImpl mirror = getProcessComputeIfAbsent(new DebugProcessRecord(proc.getPid()), proc.getPid(), true);
|
DbgProcessImpl mirror = getProcessComputeIfAbsent(new DebugProcessRecord(proc.getPid()), proc.getPid(), null, true);
|
||||||
if (mirror != null) {
|
if (mirror != null) {
|
||||||
mirror.setOffset(currentProcess.getOffset());
|
mirror.setOffset(currentProcess.getOffset());
|
||||||
currentProcess = eventProcess = mirror;
|
currentProcess = eventProcess = mirror;
|
||||||
@ -1790,7 +1792,7 @@ public class DbgManagerImpl implements DbgManager {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
currentProcess =
|
currentProcess =
|
||||||
eventProcess = getProcessComputeIfAbsent(epid, so.getCurrentProcessSystemId(), true);
|
eventProcess = getProcessComputeIfAbsent(epid, so.getCurrentProcessSystemId(), so.getCurrentProcessExecutableName(), true);
|
||||||
currentThread = eventThread = getThreadComputeIfAbsent(etid, (DbgProcessImpl) eventProcess,
|
currentThread = eventThread = getThreadComputeIfAbsent(etid, (DbgProcessImpl) eventProcess,
|
||||||
so.getCurrentThreadSystemId(), false);
|
so.getCurrentThreadSystemId(), false);
|
||||||
getEventListeners().fire.threadSelected(eventThread, null, Causes.UNCLAIMED);
|
getEventListeners().fire.threadSelected(eventThread, null, Causes.UNCLAIMED);
|
||||||
|
@ -52,6 +52,7 @@ public class DbgProcessImpl implements DbgProcess {
|
|||||||
private Long pid;
|
private Long pid;
|
||||||
private Long exitCode;
|
private Long exitCode;
|
||||||
private Long offset;
|
private Long offset;
|
||||||
|
private String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new inferior
|
* Construct a new inferior
|
||||||
@ -59,6 +60,13 @@ public class DbgProcessImpl implements DbgProcess {
|
|||||||
* @param manager the manager creating the process
|
* @param manager the manager creating the process
|
||||||
* @param id the dbgeng-assigned process ID
|
* @param id the dbgeng-assigned process ID
|
||||||
*/
|
*/
|
||||||
|
public DbgProcessImpl(DbgManagerImpl manager, DebugProcessId id, long pid, String name) {
|
||||||
|
this.manager = manager;
|
||||||
|
this.id = id;
|
||||||
|
this.pid = pid;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public DbgProcessImpl(DbgManagerImpl manager, DebugProcessId id, long pid) {
|
public DbgProcessImpl(DbgManagerImpl manager, DebugProcessId id, long pid) {
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@ -392,4 +400,14 @@ public class DbgProcessImpl implements DbgProcess {
|
|||||||
this.pid = pid;
|
this.pid = pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getExecutableName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExecutableName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ public interface DbgModelTargetProcess extends //
|
|||||||
String index = PathUtils.parseIndex(getName());
|
String index = PathUtils.parseIndex(getName());
|
||||||
Long pid = Long.decode(index);
|
Long pid = Long.decode(index);
|
||||||
DebugProcessId id = new DebugProcessRecord(pid);
|
DebugProcessId id = new DebugProcessRecord(pid);
|
||||||
return manager.getProcessComputeIfAbsent(id, pid, fire);
|
return manager.getProcessComputeIfAbsent(id, pid, null, fire);
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
return manager.getCurrentProcess();
|
return manager.getCurrentProcess();
|
||||||
|
@ -49,26 +49,32 @@ import ghidra.dbg.util.PathUtils;
|
|||||||
name = "Debug",
|
name = "Debug",
|
||||||
type = DbgModelTargetDebugContainerImpl.class,
|
type = DbgModelTargetDebugContainerImpl.class,
|
||||||
required = true,
|
required = true,
|
||||||
fixed = true),
|
fixed = true
|
||||||
|
),
|
||||||
@TargetAttributeType(
|
@TargetAttributeType(
|
||||||
name = "Memory",
|
name = "Memory",
|
||||||
type = DbgModelTargetMemoryContainerImpl.class,
|
type = DbgModelTargetMemoryContainerImpl.class,
|
||||||
required = true,
|
required = true,
|
||||||
fixed = true),
|
fixed = true
|
||||||
|
),
|
||||||
@TargetAttributeType(
|
@TargetAttributeType(
|
||||||
name = "Modules",
|
name = "Modules",
|
||||||
type = DbgModelTargetModuleContainerImpl.class,
|
type = DbgModelTargetModuleContainerImpl.class,
|
||||||
required = true,
|
required = true,
|
||||||
fixed = true),
|
fixed = true
|
||||||
|
),
|
||||||
@TargetAttributeType(
|
@TargetAttributeType(
|
||||||
name = "Threads",
|
name = "Threads",
|
||||||
type = DbgModelTargetThreadContainerImpl.class,
|
type = DbgModelTargetThreadContainerImpl.class,
|
||||||
required = true,
|
required = true,
|
||||||
fixed = true),
|
fixed = true
|
||||||
|
),
|
||||||
@TargetAttributeType(
|
@TargetAttributeType(
|
||||||
name = DbgModelTargetProcessImpl.EXIT_CODE_ATTRIBUTE_NAME,
|
name = DbgModelTargetProcessImpl.EXIT_CODE_ATTRIBUTE_NAME,
|
||||||
type = Long.class),
|
type = Long.class
|
||||||
@TargetAttributeType(type = Void.class) })
|
),
|
||||||
|
@TargetAttributeType(type = Void.class) }
|
||||||
|
)
|
||||||
public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
||||||
implements DbgModelTargetProcess {
|
implements DbgModelTargetProcess {
|
||||||
|
|
||||||
@ -122,8 +128,8 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
|||||||
SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, DbgModelTargetThreadImpl.SUPPORTED_KINDS //
|
SUPPORTED_STEP_KINDS_ATTRIBUTE_NAME, DbgModelTargetThreadImpl.SUPPORTED_KINDS //
|
||||||
), "Initialized");
|
), "Initialized");
|
||||||
if (getManager().isKernelMode()) {
|
if (getManager().isKernelMode()) {
|
||||||
TargetExecutionState state = process.getPid() > 0 ?
|
TargetExecutionState state =
|
||||||
TargetExecutionState.INACTIVE : TargetExecutionState.ALIVE;
|
process.getPid() > 0 ? TargetExecutionState.INACTIVE : TargetExecutionState.ALIVE;
|
||||||
setExecutionState(state, "Initialized");
|
setExecutionState(state, "Initialized");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -139,14 +145,15 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
|||||||
Long pid = process.getPid();
|
Long pid = process.getPid();
|
||||||
if (getManager().isKernelMode()) {
|
if (getManager().isKernelMode()) {
|
||||||
if (id.isSystem()) {
|
if (id.isSystem()) {
|
||||||
return "["+id.id()+"]";
|
return "[" + id.id() + "]";
|
||||||
}
|
}
|
||||||
String pidstr = Long.toString(pid, base);
|
String pidstr = Long.toString(pid, base);
|
||||||
if (base == 16) {
|
if (base == 16) {
|
||||||
pidstr = "0x" + pidstr;
|
pidstr = "0x" + pidstr;
|
||||||
}
|
}
|
||||||
Long offset = process.getOffset();
|
Long offset = process.getOffset();
|
||||||
return offset == null ? "[" + pidstr + "]" : "[" + pidstr + " : " + Long.toHexString(offset) + "]";
|
return offset == null ? "[" + pidstr + "]"
|
||||||
|
: "[" + pidstr + " : " + Long.toHexString(offset) + "]";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
@ -156,7 +163,9 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
|||||||
if (base == 16) {
|
if (base == 16) {
|
||||||
pidstr = "0x" + pidstr;
|
pidstr = "0x" + pidstr;
|
||||||
}
|
}
|
||||||
return "[" + id.id() + ":" + pidstr + "]";
|
String name = process.getExecutableName();
|
||||||
|
return name == null ? "[" + id.id() + ":" + pidstr + "]"
|
||||||
|
: "[" + id.id() + ":" + pidstr + "]" + " : " + name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +293,8 @@ public class DbgModelTargetProcessImpl extends DbgModelTargetObjectImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CompletableFuture<Void> resync(RefreshBehavior refreshAttributes, RefreshBehavior refreshElements) {
|
public CompletableFuture<Void> resync(RefreshBehavior refreshAttributes,
|
||||||
|
RefreshBehavior refreshElements) {
|
||||||
if (memory != null) {
|
if (memory != null) {
|
||||||
memory.requestElements(RefreshBehavior.REFRESH_ALWAYS);
|
memory.requestElements(RefreshBehavior.REFRESH_ALWAYS);
|
||||||
}
|
}
|
||||||
|
@ -1058,4 +1058,9 @@ public class WrappedDbgModel
|
|||||||
client.getSymbols().setCurrentScopeFrameIndex(index);
|
client.getSymbols().setCurrentScopeFrameIndex(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentProcessExecutableName() {
|
||||||
|
return client.getSystemObjects().getCurrentProcessExecutableName();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -578,7 +578,7 @@ public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot
|
|||||||
activate((DbgModelTargetExecutionStateful) obj);
|
activate((DbgModelTargetExecutionStateful) obj);
|
||||||
// OK, this sucks, but not all threads are parented to activated objects
|
// OK, this sucks, but not all threads are parented to activated objects
|
||||||
DbgModelTargetProcess parentProcess = ((DbgModelTargetObject) obj).getParentProcess();
|
DbgModelTargetProcess parentProcess = ((DbgModelTargetObject) obj).getParentProcess();
|
||||||
if (obj instanceof DbgModelTargetExecutionStateful) {
|
if (parentProcess != null && obj instanceof DbgModelTargetExecutionStateful) {
|
||||||
activate(parentProcess);
|
activate(parentProcess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user