Porting a few lines from C# to ABL.NET

Posted by Stefan Marquardt on 11-Feb-2010 04:34

Hello,

i created a class that checks the dis/connect of a usb device with using WMI very quickly with C#:

using

System;

using

System.Collections.Generic;

using

System.Text;

using

System.Management;

using

System.Runtime.InteropServices;

namespace

ConsoleApplication2

{

class Program

{

static void Main(string[] args)

{

StartDetection();

while (true)

{

System.Threading.

Thread.Sleep(100);

}

}

static ManagementEventWatcher mew;

static void StartDetection()

{

WqlEventQuery queryWql = new WqlEventQuery("SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_DiskDrive'");

mew =

new ManagementEventWatcher(queryWql);

mew.EventArrived += Arrived;

mew.Start();

}

static void Arrived(object sender, EventArrivedEventArgs e)

{

ManagementBaseObject mboE, mboTarget;

mboE = (

ManagementBaseObject)e.NewEvent;

mboTarget = (

ManagementBaseObject)mboE["TargetInstance"];

if (mboTarget["InterfaceType"].Equals("USB"))

{

switch (mboE.ClassPath.ClassName)

{

case "__InstanceCreationEvent":

{

Console.WriteLine("Gert angelegt");

Console.WriteLine(mboTarget["Caption"]);

Console.WriteLine(GetDriveLetterFromDisk(mboTarget["Name"].ToString()));

break;

}

case "__InstanceDeletionEvent":

{

Console.WriteLine("Gert gelscht");

break;

}

}

}

}

static string GetDriveLetterFromDisk(string strName)

{

List<String> strResponse = new List<string>();

ObjectQuery oqPart = new ObjectQuery(string.Format("ASSOCIATORS OF {{Win32_DiskDrive.DeviceID=\"{0}\"}} WHERE AssocClass = Win32_DiskDriveToDiskPartition", strName.Replace("\\", "\\\\")));

ManagementObjectSearcher mosPart = new ManagementObjectSearcher(oqPart);

foreach (ManagementObject mobPart in mosPart.Get())

{

ObjectQuery oqDisk;

oqDisk =

new ObjectQuery(string.Format("ASSOCIATORS OF {{Win32_DiskPartition.DeviceID=\"{0}\"}} WHERE AssocClass = Win32_LogicalDiskToPartition", mobPart["DeviceID"].ToString()));

ManagementObjectSearcher mosDisk;

mosDisk =

new ManagementObjectSearcher(oqDisk);

foreach (ManagementObject mobDisk in mosDisk.Get())

{

strResponse.Add(mobDisk[

"Name"].ToString());

}

}

return string.Join(",", strResponse.ToArray());

}

}

}

Every time i hit the problem that foreach isn't supported in ABL.NET.

I started to convert this but foreach in GetDriveFromLetterDisk seems to be a problem.

This is my actual version:

method

private character GetDriveLetterFromDisk( cDisk as char ):

define variable result as character no-undo.

define variable oqPart as ObjectQuery no-undo.

define variable mosPart as ManagementObjectSearcher no-undo.

define variable mobPartsEnum as ManagementObjectCollection+ManagementObjectEnumerator no-undo.

define variable mobPart as ManagementObject no-undo.

define variable oqDisk as ObjectQuery no-undo.

define variable i as integer no-undo.

assign

  oqPart =

new ObjectQuery(substitute("ASSOCIATORS OF ~{Win32_DiskDrive.DeviceID='&1'~} WHERE AssocClass = Win32_DiskDriveToDiskPartition", replace(cDisk,"\","\\")))

  mosPart =

new ManagementObjectSearcher(oqPart)

.

mobPartsEnum = mosPart:

Get():GetEnumerator().

repeat:

  EventArrived:

Publish(this-object,"Schleife1" + string(i)).

  if not mobPartsEnum:MoveNext() then

   leave.

  mobPart =

cast(mobPartsEnum:current,ManagementObject).

end.

/* missing code */

return result.

end method.

If i use this class i get the event for the device but at mobPartsEnum:MoveNext() the ABL hungs.

Other problems:

Debugger isn't working with this class.

MessageBoxes are not working within this class

Very strange thing. ( in know that the Threading class is one of the unsupported things, i used this in C# to simulate a wait-for)

Stefan Marquardt

hagebau dd

All Replies

Posted by Matt Baker on 11-Feb-2010 08:45

What is happening is that you are publishing your events on a background thread.  The AVM can only function if everything runs on the proper thread.  "Sometimes" things will work if you execute the ABL on the background thread, but usually it will just hang, especially if the AVM needs to do anything with the UI.  So your first couple of lines might work, but after this things go wrong.  Don't try to rely on this "sometimes" behavior because future versions may behave different.

Not having all your code available I'm making some guesses here....

You need to execute your events on the proper thread.  .NET programs (actually all Windows programs) are only allowed to have a single "UI" thread which owns all the UI objects.  This "UI" thread is the "main" thread of the AVM.  Normally you could fix this by doing something like to ensure the event is published on the UI thread.

myform:Invoke(new (), null).

Where "myform" is an instanceof System.Windows.Forms.Form or some other instanceof control that supports Invoke () method.

mattB

p.s. OpenEdge is not "ABL.NET".  The proper term is "OpenEdge GUI for .NET".  The ABL is not a "true" .NET language, since the AVM bridges the calls to the CLR and does not compile ABL into IL.

Posted by Stefan Marquardt on 11-Feb-2010 09:10

Matthew,

i thought that "OpenEdge GUI for .NET" isn't multithreaded.

That's the full code:

using

Progress.Lang.*.

using

System.Management.*.

using

System.* .

using

System.Collections.Generic.*.

using

System.Text.*.

 

routine-level

on error undo, throw.

class

prohibisCash.Controller.USBController final:

define public event EventArrived signature void (sender as Progress.Lang.Object, e as char).

define variable queryWql as WqlEventQuery.

define variable mew as ManagementEventWatcher.

define variable goController as prohibisCash.Controller.CashController.

constructor public USBController (pioController as prohibisCash.Controller.CashController ):

super ().

goController = pioController.

StartDetection().

end constructor.

 

method private character GetDriveLetterFromDisk( cDisk as char ):

define variable result as character no-undo.

define variable oqPart as ObjectQuery no-undo.

define variable mosPart as ManagementObjectSearcher no-undo.

define variable mobPartsEnum as ManagementObjectCollection+ManagementObjectEnumerator no-undo.

define variable mobPart as ManagementObject no-undo.

define variable oqDisk as ObjectQuery no-undo.

define variable i as integer no-undo.

assign

oqPart =

new ObjectQuery(substitute("ASSOCIATORS OF ~{Win32_DiskDrive.DeviceID='&1'~} WHERE AssocClass = Win32_DiskDriveToDiskPartition", replace(cDisk,"\","\\")))

mosPart =

new ManagementObjectSearcher(oqPart)

.

mobPartsEnum = mosPart:

Get():GetEnumerator().

repeat:

if not mobPartsEnum:MoveNext() then

  leave.

  mobPart =

cast(mobPartsEnum:current,ManagementObject).

  /* missing code */

end.

return result.

end method.

 

method protected void OnEventArrived(sender as Object, e as EventArrivedEventArgs ):

define variable mboE as ManagementBaseObject.

define variable mboTarget as ManagementBaseObject.

mboE =

cast(e:NewEvent,ManagementBaseObject).

mboTarget =

cast(mboE:item["TargetInstance"],ManagementBaseObject).

if mboTarget:Item["InterfaceType"]:Equals("USB") then

do:

  case mboE:ClassPath:ClassName:

   when "__InstanceCreationEvent" then

   do:

    EventArrived:

Publish(this-object,"USB inserted A").

    this-object:GetDriveLetterFromDisk(mboTarget:Item["Name"]:ToString()).

    EventArrived:

Publish(this-object,"USB inserted B").

   end.

   when "__InstanceDeletionEvent" then

   do:

    EventArrived:

Publish(this-object,"USB removed").

   end.

  end.

end.

end method.

method private void StartDetection( ):

define variable queryWql as WqlEventQuery.

define variable mew as ManagementEventWatcher.

queryWql =

new WqlEventQuery("SELECT * FROM __InstanceOperationEvent WITHIN 1 WHERE TargetInstance ISA 'Win32_DiskDrive'").

mew =

new ManagementEventWatcher(queryWql).

mew:EventArrived:

Subscribe(OnEventArrived).

mew:

Start().

end method.

 

destructor public USBController ( ):

if valid-object(mew) then

mew:

Stop().

end destructor.

end

class.


The Controller, a standard cls, define, new this class and subscribe the events.

I only get "USB inserted A".

Stefan

Posted by Matt Baker on 11-Feb-2010 13:55

You are using multiple threads.  This method call:

StartDetection(). 

Calls a method which starts a new thread.  Even though the method is still int he C# code you still have interactions with the AVM.

When the OnEventArrived method which is ABL code is fired, it gets run on the background thread that you started because it is called from the C# code which is running on a background thread (by background thread I mean "not the thread the AVM is using for the UI").

Yes, the AVM is not mult-threaded, and that is exactly why you have this hang.  Because you subscribed to an event and the event is fired from a separate thread, the AVM is trying to run some ABL code on a thread that isn't the AVM's "main" thread so it ends up hanging.

Posted by Stefan Marquardt on 12-Feb-2010 05:48

Matthew,

that is the cause! I proved this with C#.

There i get an exception that the call is from another thread.

So there is no chance to use WMI with OpenEdge .NET ...

Stefan

Posted by Matt Baker on 12-Feb-2010 08:51


So there is no chance to use WMI with OpenEdge
.NET .

Yes, you can.  As i made note of in my first example you need to make sure you run the code in the correct thread.  Normally you would use the Invoke() method on a UI control.  This of course requires that you have an object available that has already been created on the "UI/main" thread.

Posted by Stefan Marquardt on 12-Feb-2010 08:59

Matthew,

i know about Invoke with a Delgate but didn't know that this is allowed.

I used it sometimes with VB.net in the past in multithreaded applications but have to look again in my old code how to do this.

Is using multithreading offical supported in "OpenEdge GUI for .NET" (i like ABL.NET)  since 10.2B ?

Stefan

Posted by khowell on 15-Feb-2010 12:44

Stefan,

Unfortunately multi-threaded processes are not supported. As noted in the EULA, OpenEdge 10.2A and above includes the “OpenEdge AVM to Microsoft .NET CLR intra-process communication” (“AVM to CLR Bridge” or the “Bridge”). At this time, the allowable uses of the Bridge are restricted to developing or deploying Microsoft .NET Winforms User Interfaces for business applications using OpenEdge products, and for one-way usage of AVM calling the CLR (and not vice versa).

Any use of the Bridge other than as set forth in the previous paragraph is prohibited. Additionally, the Bridge may not be used for multi-threaded processes.

Regards

Kristen Howell

OpenEdge Product Manager

This thread is closed