[.net] Thread start and passing ref variables

Started by
9 comments, last by Numsgil 16 years ago
I'm trying to do some interop between a Visual Basic 6 game and a C# library. Specifically moving some functions from VB6 that were slow to C# to make them asynchronous (and benefit from some other features of .NET too). My plan is for a VB6 to call the library with some ref variables, and a ref bool to equal true when the spawned library's thread operation is done. So my VB6 app would call void DoStuff(ref string Results, ref bool DoneFlag), and then in its main loop periodically check DoneFlag for true. Once DoneFlag is equal to true, it knows that the Results string is ready for use. My problem is figuring out how to start the thread. I tried this (where DoSlowStuff is my C# library's slow function that used to be in VB6):

void DoStuff(ref string Results, ref bool DoneFlag)
{
    new Thread(delegate() { DoSlowStuff(ref Results, ref DoneFlag); } ).Start();
}
The problem is that it says that "error CS1628: Cannot use ref or out parameter 'Results' inside an anonymous method block" So what's the best way to start my thread and keep the ref arguments "ref"fing correctly?
[size=2]Darwinbots - [size=2]Artificial life simulation
Advertisement
Why not port the VB6 game to VB.NET? That would save having two dependancies.

Anyways I'm not 100% sure but take a look at "ParameterizedThreadStart". I'm not sure if it will suit your needs but give it a try.

BTW Why do you need to pass as ref? Did you know that only value types like structs, int's, bools etc can be passed as ref. Class objects are already passed as ref without need for the keyword, so you may not actually need to pass as ref. If you using a struct, just change it to a class. ParameterizedThreadStart takes Object's as it's paramters. Objects are reference types.
I'm presently rewriting the whole thing in C#, from the ground up, but there are still users who use the VB6 version and I'm trying to do some stopgap improvements on it. An actual port from VB6 to VB.NET is a huge undertaking. The automatic upgrader had something like 1000s of individual issues to address.

I need to pass by ref because I think you have to to pass array handles. I tried it without ref and it didn't work. Likewise the bool flag is a value type and thus needs a pass by ref. I could probably get away with the response string not being ref, but I haven't played with that much.

ParameterizedThreadStart would work except I have more than one argument. Unless there's a way to pass multiple arguments as a single object?
[size=2]Darwinbots - [size=2]Artificial life simulation
Quote:Original post by Numsgil
I'm presently rewriting the whole thing in C#, from the ground up, but there are still users who use the VB6 version and I'm trying to do some stopgap improvements on it. An actual port from VB6 to VB.NET is a huge undertaking. The automatic upgrader had something like 1000s of individual issues to address.

I need to pass by ref because I think you have to to pass array handles. I tried it without ref and it didn't work. Likewise the bool flag is a value type and thus needs a pass by ref. I could probably get away with the response string not being ref, but I haven't played with that much.

ParameterizedThreadStart would work except I have more than one argument. Unless there's a way to pass multiple arguments as a single object?


It takes an object as it's parameter so just send over a class with as many values as you want.

Eg.
class MyParameters{    public int Value1;    public string Value2;    public bool Value3;}MyParameters myParams = new MyParamters();myParams.Value1 = 1;myParams.Value2 = "Test1";myParams.Value3 = true;Thread t = new Thread(new ParameterizedThreadStart(MyThread));t.Start(myParams);static void MyThread(object params){    MyParameters myParams = (MyParameters) params;    System.Diagnostics.Debug.WriteLine(String.Format("{0},{1},{2}", myParams.Value1, myParams.Value2, myParams.Value3));}


But how do I send those value types as ref if they're in a class? ie: In C++ you could do something like this:

class Thing{    bool &b    Thing(bool &B) : b(B) { }};


But I don't think anything similar exists in C#.
[size=2]Darwinbots - [size=2]Artificial life simulation
you don't "send things as ref" things >are< refs , a class is a reference type , hence you pass the class

class theclass
{
public bool thisismybool;
}

main()
{
theclass myclass=new theclass();
myclass.thisismybool=true;
function(myclass);
}

void function(theclass c)
{
use c.thisismybool here since your class that contains it was passed by reference
}

edit : in case i was unclear , what was suggested above was sticking value type in a class (reference type) & then passing the whole class not individual values

btw i don't see why you couldn't simply pass value types by reference without adding ref , if it expects a reference type & receives a value type iirc C# boxes it & unboxes it. could you describe what you're trying to pass? in most case you're prolly doing an error because the whole point of value types is that they're mostly used in cases where you don't need to pass by ref
Assuming he wants changes to his two variables, both required a pass by reference. The boolean type is a value type, and hence if he wants changes to the flag to show up outside the function he needs to pass by reference.

Although string is a reference type itself, it is immutable, so it effectively is a value type when it comes to making modifications. Therefore, it too needs the ref keyword.

While the class method mentioned above should work, you could also avoid the use of explicit threads and instead make use of C# delegates to get your task done, depending on how much control you need.

delegate void DoStuffDelegate(ref string results, ref bool doneFlag);DoStuffDelegate myDelegate = new DoStuffDelegate(DoSlowStuff);IAsyncResult result = myDelegate.BeginInvoke(ref results, ref doneFlag, null, null);


One you have called BeginInvoke the method begins running on a separate thread. If you need to wait for it to finish or mess with other results options, take a look at the IAsyncResult interface. For example, you may do something like this:

// Display periods every second to indicate that the program is running and not frozen.while(!result.AsyncWaitHandle.WaitOne(1000, false)){    Console.Write('.');}// at this point the method has completed, so finish upmyDelegate.EndInvoke(result);
Mike Popoloski | Journal | SlimDX
Quote:Original post by Mike.Popoloski
Assuming he wants changes to his two variables, both required a pass by reference. The boolean type is a value type, and hence if he wants changes to the flag to show up outside the function he needs to pass by reference.

Although string is a reference type itself, it is immutable, so it effectively is a value type when it comes to making modifications. Therefore, it too needs the ref keyword.

While the class method mentioned above should work, you could also avoid the use of explicit threads and instead make use of C# delegates to get your task done, depending on how much control you need.

*** Source Snippet Removed ***

One you have called BeginInvoke the method begins running on a separate thread. If you need to wait for it to finish or mess with other results options, take a look at the IAsyncResult interface. For example, you may do something like this:

*** Source Snippet Removed ***


Ah, that looks like what I need. Since the VB app is just going to test to see if the bool flag is true, I don't need to control the thread at all once it's started.

I'll play around with it tonight. Thanks.
[size=2]Darwinbots - [size=2]Artificial life simulation
Okay, I think I'm making progress, but it's still not quite working.

This is an example of an actual call in the code:

public void ASyncDirectoryListing(string Directory, out string Response, out string[] Listing, ref bool DoneFlag){    RefFTP ftp = new RefFTP(Site, User, Password);    DirectoryListingDelegate myDelegate = new DirectoryListingDelegate(ftp.DirectoryListing);                myDelegate.BeginInvoke(Directory, out Response, out Listing, ref DoneFlag, null, null);}


It calls the function correctly, but nothing changed inside ftp.DirectoryListing seems to propagate out. If I change the above from BeginInvoke to Invoke, and remove the two null arguments, things run like they should. Is it a language restriction that you can't send ref arguments like this across threads?

On the idea of passing a class with the arguments in it, again, how would I make sure that the bool I get from VB is the actual argument being passed, and not a copy of the bool. If I understand boxing correctly (which I might not), a copy of the value is created, and the underlying value isn't changed.
[size=2]Darwinbots - [size=2]Artificial life simulation
Oh, I guess I wasn't entirely clear there. If you want return values or ref/out parameter results, you need to call EndInvoke, which will have your ref parameters for you.

This may seem like a pain, but you can supply a method to BeginInvoke (the second to last parameter) that gets called whenever the method completes. When this event fires, you can use the provided IAsyncResult to extract the delegate, call EndInvoke, and retrieve your parameters.

If you need some source code on this, I can whip some up when I get home later today.
Mike Popoloski | Journal | SlimDX

This topic is closed to new replies.

Advertisement