xsharp.eu • Executable started from the IDE
Page 1 of 1

Executable started from the IDE

Posted: Tue Mar 07, 2017 1:58 pm
by wriedmann
For me one of the important functions in Visual Objects is

Code: Select all

IsStartedFromIDE()
I'm using this for special debugging output or other specific behavior.
Unfortunately this is not so easy in X# as there is no special executable when started from XIDE or Visual Studio.

The solution is to check whether the executable was started from XIDE or devenv (Visual Studio's executable).

This is the code (and you'll find a complete XIDE export attached to this message):

Code: Select all

// Source (C#):
// http://stackoverflow.com/questions/2531837/how-can-i-get-the-pid-of-the-parent-process-of-my-application
// translated to X# and adapted by Wolfgang Riedmann wolfgang@riedmann.it 

using System
using System.Diagnostics
using System.Management

function Start( ) as void          
  local oProcess as Process
  local oParent  as Process
  local nId      as int
	
  oProcess := Process.GetCurrentProcess()
  nId := oProcess:Id
  oParent := ProcessUtil.GetParentProcess( nId )
  if oParent != null                            
    if oParent:ProcessName:Tolower() == "cmd"
      oParent := ProcessUtil.GetParentProcess( oParent:Id )
    endif
    if oParent:ProcessName:ToLower() == "xide"
      System.Console.WriteLine( String.Format( "Executable was launched from development environment {0}", oParent:ProcessName ) )
    else
      System.Console.WriteLine( String.Format( "Executable was launched by {0}", oParent:ProcessName ) )
    endif
  else
    System.Console.WriteLine( "No parent process!" ) 
  endif 
	
  return
	
static class ProcessUtil
	
static method GetParentProcess( nProcessId as int ) as Process
  local oParent as Process
  local nParentId as dword
  local cQuery as string
  local oSearch as ManagementObjectSearcher 
  local oResults as ManagementObjectCollection
  local oEnumerator as ManagementObjectCollection.ManagementObjectEnumerator 
  local oQueryObj as ManagementBaseObject

  oParent := null
	
  try

  cQuery := String.Format( "SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", nProcessId )
  oSearch := ManagementObjectSearcher{ "rootCIMV2", cQuery }
  oResults := oSearch:Get()
  oEnumerator := oResults:GetEnumerator()
  oEnumerator:MoveNext()       
  oQueryObj := oEnumerator:Current
  nParentId := ( dword ) oQueryObj["ParentProcessId"]
  if nParentId > 0
    oParent				:= Process.GetProcessById( int( nParentId ) )
  endif 
	
  catch oEx as Exception
		
  Debug.WriteLine( String.Format( e"Error occurred: {0} n{1}", oEx:Message, oEx:StackTrace ) )
	
  end try
	
  return oParent
	
end class
IsStartedFromIDE.zip
(1.69 KiB) Downloaded 49 times

Executable started from the IDE

Posted: Tue Mar 07, 2017 2:01 pm
by robert
Wolfgang,

What about:

System.Diagnostics.Debugger:IsAttached

Robert

Executable started from the IDE

Posted: Tue Mar 07, 2017 2:08 pm
by wriedmann
Hi Robert,

at least in XIDE this works only when I'm debugging and not when I'm running normally from the IDE. I haven't tested it in Visual Studio.

Wolfgang

Executable started from the IDE

Posted: Tue Mar 07, 2017 6:01 pm
by Chris
Hi Wolfgang,

Very interesting piece of code...I had no idea about this functionality.
Thanks for sharing!

Chris

Executable started from the IDE

Posted: Wed Aug 30, 2017 3:02 am
by wriedmann
Hi,

sometimes in the last weeks my code to detect if my executables were started from the IDE stopped working - I suspect Microsoft has changed something in the WMI interface to fix a security issue.

So I had to change the implementation of the GetParentProcess() function.

The new code is the following:

Code: Select all

using System.Diagnostics
using System.Management

static class Utility
	
static method GetParentProcess( nId as dword ) as Process
  local nParentId as dword
  local oHandle as IntPtr
  local oProcInfo as WindowsAPI.PROCESSENTRY32
  local oReturn as Process

  nParentId := 0	
  oReturn := null
  oHandle := WindowsAPI.CreateToolhelp32Snapshot( WindowsAPI.TH32CS_SNAPPROCESS, nId )
  if oHandle != IntPtr.Zero
    oProcInfo := WindowsAPI.PROCESSENTRY32{}
    oProcInfo.dwSize := dword( System.Runtime.InteropServices.Marshal.SizeOf( typeof( WindowsAPI.PROCESSENTRY32 ) ) )
    if WindowsAPI.Process32First( oHandle, oProcInfo ) 
      while nId != oProcInfo.th32ProcessID .and. WindowsAPI.Process32Next( oHandle, oProcInfo )
      end
      if nId == oProcInfo.th32ProcessID
        nParentId := oProcInfo.th32ParentProcessID 
      endif
    endif
  endif 
  if nParentId != 0
    oReturn := Process.GetProcessById( nParentId )  
  endif

   return oReturn

Code: Select all

using System.Runtime.InteropServices

static class WindowsAPI
	
[DllImport("kernel32.dll",SetLastError:= true,EntryPoint:="CreateToolhelp32Snapshot")];
static method CreateToolhelp32Snapshot( dwFlags as dword, th32ProcessID as dword ) as IntPtr
 
[DllImport("kernel32.dll",EntryPoint:="Process32First")];
static method Process32First( hSnapshot as IntPtr, oEntry ref PROCESSENTRY32 ) as logic
 
[DllImport("kernel32.dll",EntryPoint:="Process32Next")];
static method Process32Next( hSnapshot as IntPtr, oEntry ref PROCESSENTRY32 ) as logic

[StructLayout(LayoutKind.Sequential)];
structure PROCESSENTRY32 
  dwSize as dword
  cntUsage as dword
  th32ProcessID as dword
  th32DefaultHeapID as IntPtr
  th32ModuleID as dword
  cntThreads as dword
  th32ParentProcessID as dword
  pcPriClassBase as int
  dwFlags as dword
  [MarshalAs(UnmanagedType.ByValTStr, SizeConst:=260)] szExeFile as string       
  // requires /unsafe compiler switch to compile
  // unsafe FIXED dim szExeFile[260] as byte 
end structure

static property TH32CS_SNAPPROCESS as dword get winTH32CS_SNAPPROCESS

end class

static define winTH32CS_SNAPPROCESS := 2 as dword
You can find a complete (working) sample together with the old WMI based function here:

https://riedmann.it/download/StartedFromIDE.viaef

and attached to this message.

Wolfgang