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
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.
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
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.
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
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.
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
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