Question

How do I run a dll in a seperate process?

Asked by: Thrasymachus

I have two VB6 projects. the first one is a exe (deviceManager.exe) and the second one is a dll (ersptdev.dll). Currently the EXE calls the dll like this:

---------------------------------------------------
Public Function PrinterStatus(ByVal msgCurrent As MSMQMessage, ByVal strDevice As String) As String
On Error GoTo Err_HandlePrintJob

Dim q As MSMQQueue
Dim qi As MSMQQueueInfo
Dim msgPrinter As MSMQMessage

'to locate the existing queues
Dim objqiSet As MSMQQueue
Dim objQuery As MSMQQuery

Dim PlotControl As Object
Dim lngPCValue As Long
Set PlotControl = CreateObject("ERSPtDev.c" & strDevice)

'Send Job to ERSPtDev to do the work
Call PlotControl.iPrintDevice_PrintJob(msgCurrent.Body, msgCurrent.Id)
ExitRoutine:
On Error Resume Next
    Set PlotControl = Nothing
Exit Function

Err_HandlePrintJob:
   
   [handling stuff removed]
       
    Resume ExitRoutine
End Function
-------------------------------------------------------------------

What I want to do is make this line:
Call PlotControl.iPrintDevice_PrintJob(msgCurrent.Body, msgCurrent.Id)
run in an independent process. I am not conserned with any return values, and it may take a long time for it to finish. I just want to let ersptdev.dll do it's thing and let DeviceManager.exe continue doing its thing.

Also there is a possibliity that if Devicemanager.exe is able to continue doing it's thing it may call ersptdev again, in which case I would expect both 'threads' to run Simultaneously and independently. Can this be done and if so how?

Thanks for you help. I can provide further details if needed and I have the source code for both projects.

This Question has been solved and asker verified All Experts Exchange premium technology solutions are available to subscription members.

Subscribe now for full access to Experts Exchange and get

Instant Access to this Solution

  • Plus...
  • 30 Day FREE access, no risk, no obligation
  • Collaborate with the world's top tech experts
  • Unlimited access to our exclusive solution database
  • Never be left without tech help again

Subscribe Now

Asked On
2005-01-06 at 11:46:07ID21264494
Tags

dll

Topic

Visual Basic Programming

Participating Experts
2
Points
400
Comments
54

Trusted by hundreds of thousands everyday for fast, accurate and reliable tech support.

  • "The time we save is the biggest benefit of Experts Exchange to Warner Bros. What could take multiple guys 2 hours or more each to find is accessed in around 15 minutes on Experts Exchange." Mike Kapnisakis, Warner Bros.
  • "Our team likes having a resource that is more secure than just using Google and most experts using this service really know their stuff. It's nice to look here first versus using Google." Dayna Sellner, Lockheed Martin
  • "Anytime that I've been stumped with a problem, 9 out of 10 times Experts Exchange has either the accepted solution or an open discussion of the potential solution to the problem." Kenny Red, eBay Inc.

See what Experts Exchange can do for you.

Got a question?

We've got the answer.

Experts Exchange has been collecting answers to technology questions since 1996…3 million and counting! If you have a question, chances are we already have your answer.

Screenshot of Experts Exchange Knowledgebase

Need individual assistance?

Our experts are ready to help.

If you can't find the exact answer you're looking for, ask our exclusive community of 50,000 experts. You’ll get a personalized answer from a trusted professional.

Screenshot of Experts Exchange Knowledgebase

Want to learn from the best?

Read articles from industry experts.

Thousands of free tech tips, tricks, how-to’s and tutorials are available in our peer reviewed articles section. See for yourself how smart our experts are, no login required.

Screenshot of an Article

Working on a long term project?

Store your work and research.

Save solutions to your questions, answers you’ve discovered through searching plus helpful articles in your personal knowledgebase for easy future access.

Screenshot of Experts Exchange Knowledgebase

Access the answers to your technology questions today.

Subscribe Now

30-day free trial. Register in 60 seconds.

What Makes Experts Exchange Unique?

Members of the expert community talk about why the experience at Experts Exchange is different than what you will find anywhere else.

Trusted by the world's most respected brands.

image of each brand's logo

Faithfully serving IT professionals since 1996.

Experts Exchange Logo

Try it out and discover for yourself.

Subscribe Now

30-day free trial. Register in 60 seconds.

Related Solutions

  1. Help! When use byVal or ByRef in declare DLL function
    I have wrote a DLL in C. It has only one function. Long APIENTRY EncodeData(unsigned char *buffer, char *result, int iActualLen, int EncodeLen) int I; for(I = 0; I < EncodeLen; I++){ If (I < iActualLen){ // do something } else{ // do another thing }...
  2. Implements and CreateObject
    I have written an ActiveX EXE that exports a IExtender class so I can write DLLs that extend my app later. This class declares a Run method (with empty body) that extensions will need to implement. During initialization, this app looks in the Registry for a specific key indic...
  3. CreateObject remote
    Hi, how come I cant use Server.CreateObject("some.object", "somemachine") like this? I can call CreateObject (without going through the server object) instead, and this works. Why is this? Thanks in advance!
  4. CreateObject("MSComDlg.CommonDialog")???
    Dim ImportTbl Set ImportTbl= CreateObject("MSComDlg.CommonDialog") on the "Set ImportTbl= CreateObject("MSComDlg.CommonDialog")" line it gives an error stating: Run-time error '429': ActiveX component can't create object This code wo...

Free Tech Articles

  1. WARNING: 5 Reasons why you should NEVER fix a computer for free.
    It is in our nature to love the puzzle. We are obsessed. The lot of us. We love puzzles. We love the challenge. We thrive on finding the answer. We hate disarray. It bothers us deep in our soul. W...
  2. SCCM OSD Basic troubleshooting
    SCCM 2007 OSD is a fantastic way to deploy operating systems, however, like most things SCCM issues can sometimes be difficult to resolve due to the sheer volume of logs to sift through and the dispe...
  3. Migrate Small Business Server 2003 to Exchange 2010 and Windows 2008 R2
    This guide is intended to provide step by step instructions on how to migrate from Small Business Server 2003 to Windows 2008 R2 with Exchange 2010. For this migration to work you will need the fo...
  4. Create a Win7 Gadget
    This article shows you how to create a simple "Gadget" -- a sort of mini-application supported by Windows 7 and Vista. Gadgets can be dropped anywhere on the desktop to provide instant information, ...
  5. Outlook continually prompting for username and password
    There have been a lot of questions recently regarding Outlook prompting for a username and password whilst using Exchange 2007. There are a few reasons why this would happen and I will try to cover t...
  6. Backup Exchange 2010 Information Store using Windows Backup
    There seems to be quite a lot of confusion around the ability to backup Exchange 2010 using the built in Windows Backup feature. This stems from the omission of this feature prior to Exchange 2007 s...

Cloud Class Webinars

  1. Avoiding Bugs in Microsoft Access
    Alison Balter takes and in-depth look at avoiding bugs in Access. In this webinar you will learn about using the immediate window to debug your applications, invoking the debugger, using breakpoints to troubleshoot, stepping through code, setting the next statement to execute, ...
  2. Top 10 Best New Features in Visio 2010
    Scott Helmers gives live demonstrations of the top 10 new features in Visio 2010. This webinar will teach you how to create compelling diagrams by adding shapes to the page with a single click, linking the shapes in a diagram to data in Excel (or SQL Server, or SharePoint), ...
  3. IT Consultant Business Secrets Revealed
    Michael Munger, Experts Exchange tech pro and IT consultant, pulls back the curtain on his very successful businesses and answers question on every IT consultant and business owner should know about. He shares secrets on what he did to solve the 5 most common problems in IT, ...
  4. Disaster Recovery and Business Continuity
    Quest CTO, Mike Billon, gives an overview of the steps involved in building a dunamic disaster recovery plan. Through case studies and an examination of software/hardware tooles for monitoring and testing, you'll gain a better understandin of where you are, where you want ...
  5. Organize Your Visio Diagrams with Containers and Lists
    Scott Helmers uses cross functional flowcharts, wireframe diagrams, data graphic legends and seating charts to teach you: how to ustilize all three new structured diagram components in Visio 2010, the best practices for organizeing shapes in previous version of Visio, how to organize ...
  6. How to Us Objects, Properties, Events and Methods in Microsoft Access
    Alison Dalter gives an in-depbth look at objects, properties, events and methods in Microsoft Access. In this webinar you will learn about using the object browser, referring to objects, working with properties and methods, working with object variables, understanding the ...

Join the Community

Give a Little. Get a Lot.

Join the community of experts here and help other tech pros by answering question in your area of expertise. You can earn FREE access to all Experts Exchange's premium features and resources.

Join the Community

Answers

 

by: JohnBPricePosted on 2005-01-06 at 11:51:22ID: 12975955

VB6 doesn't like to do multi-threaded, but you can trick it.  This only works, I believe, as long as the original thread is running.  Basically, you put the request to process in a queue, trigger a new event to yourself, and return.  Upon receiving the event, you process the queue (calling DoEvents frequently enough to avoid hosing up your main app).  I answered a question a while ago that describes this, give me a minute and I'll look it up.

BTW, the clean way would be to create a service or write it in a language which supports threads, e.g. VB.Net.

 

by: JohnBPricePosted on 2005-01-06 at 11:55:23ID: 12976000

 

by: ThrasymachusPosted on 2005-01-06 at 12:23:35ID: 12976274

JohnBPrice,
Thanks for your input. Unfortunately I do not believe your example applies to my problem.  your example has a do..While loop that would hit the DoEvents command. My program does not. Once I hit the line that calls the ersptdev.dll there is no chance to run a DoEvents or PostMessage Function until it is completely finished, at which point it is no longer necessary anyway.

 

by: ThrasymachusPosted on 2005-01-06 at 12:28:11ID: 12976322

Oh -- and DeviceManager IS running as an NT service.

 

by: JohnBPricePosted on 2005-01-06 at 12:40:00ID: 12976435

Are you saying that you do not have the source/ability to change the code that implements PlotControl.iPrintDevice_PrintJob and that there are no DoEvents (or an insufficient amount) in iPrintDevice_PrintJob?  That is where the DoEvents are critical (in order to allow your "main" app to be responsive to new events, which is the whole point).  In this case it would be difficult to do this in one VB6 process.

Perhaps the easiest way would be to wrap PlotControl.iPrintDevice_PrintJob in a simple VB EXE (or convert it into an EXE), and have DeviceManager.exe shell a new copy of of your wrapper EXE whenever a request comes in.  Then it will run in it's own process.

 

by: ThrasymachusPosted on 2005-01-06 at 12:54:27ID: 12976583

I have not actually tried this, but are you saying that if I put a DoEvents in the .dll the process would continue in the main app? This does not make sense to me. What if I was expecting a return value from the dll? I do not have any events in the dll to raise and I do not know how to declare it WithEvents when I am using a late binding to create the object.

The problem with converting it to an exe is that there are several different classes in the dll that need to be called (thus the late binding) and I do not know of a way to do the same thing with an exe. However creating a wrapper exe with some command line arguments may be a thought. Definitely not as nice but it may be a solution if multithreading is not an option.

I'll look into it, but would still like to see if there is a actually a way to call the dll directly in a seperate process.

 

by: EnladePosted on 2005-01-06 at 13:13:38ID: 12976789


Rewrite your DLL in the following way.  Create a VB EXE that contains a single class (clsTest).  Add the following function to the class.

Public Sub Start()
  EnableOneShot 100
End Sub

Then add a module (modTest) and add this code.

Private Declare Function SetTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long, _
                ByVal uElapse As Long, ByVal lpTimerFunc As Long) As Long
Private Declare Function KillTimer Lib "user32" (ByVal hWnd As Long, ByVal nIDEvent As Long) As Long

Public Sub EnableOneShot(ByVal ulTime As Long)
  glob_TimerID = SetTimer(0, 0, ulTime, AddressOf TimerCallback)
End Sub

Public Sub TimerCallback(ByVal hWnd As Long, ByVal uMsg As Long, ByVal idEvent As Long, ByVal dwTime As Long)

  'Kill the timer - we do not need it anymore
  KillTimer 0, glob_TimerID
 
  'Call your printing function here!!!!!!
  Print_Job    
End Sub

Private Sub Print_Job()
  'Do your thing!!!!
End Sub

Now, in your main program you simple add the object as a component and open it in the normal way.

Set objTest As New clsTest    'or whatever you called things

clsTest.Start

Of course, you probably want to add some additional functions to the CLASS so that you can initialize the print job and also some method to check the status of the current print job and to prevent multiple calls to Start from crashing things.  But you get the idea.

 

by: JohnBPricePosted on 2005-01-06 at 13:14:45ID: 12976799

Yes, putting a DoEvents in the .dll (as long as you use one of the tricks to call it in a separate event) will allow the main process to continue, sort of.  The VB6 runtime translates windows messages into VB Events, and will wait until one event is processed & returned before it raises the next event.  A DoEvents in an event processing loop essentially "pauses" the loop and VB will go ahead and raise any other events waiting to be processed.  When these events finish & return, execution returns to the line after the DoEvents.  Thus your main app could still process new events, but note they must be new events, an polling loop in the main app will not work, even with the separate event trick.

Technically, if you convert the DLL to an ActiveX EXE, you can call the objects in the exact same way that you would a DLL and it WILL run in a separate process, but the VB runtime won't return control to the calling process until the object is done, so it's about the same situation as using a DLL.

 

by: EnladePosted on 2005-01-06 at 13:28:26ID: 12976921


Oh, and you probably need to set the Class Instancing property to SingleUse and Presistable property to NotPresistable.

And also under the Project Properties of the Class object you should check the Unattended Execution and check the Upgrade ActiveX Controls.  And also select the "Thread Per Object" option.

Then compile it before you try to add it as a component into your main application.  I recommend that you set the compile location to the same directory as your main applications compile location so that you don't have to always reassign the component every time you compile your code.  But you will discover those quarks as you work with this method of multi-threading in VB6.

 

by: JohnBPricePosted on 2005-01-06 at 13:29:27ID: 12976930

Enlade's code is one of the tricks to call your print process in a separate event, the other is to post yourself a message as in my link above.  It actually doesn't matter if it is a DLL or EXE.  Notwithstanding, the key to having your main app be responsive to new events is to have the lengthy print process call DoEvents frequently, and have your main app process events & return without doing any lengthy process itself.

 

by: EnladePosted on 2005-01-06 at 13:31:49ID: 12976948


JohnBPrice:  If you set it up like an ActiveX EXE it isn't the same as just calling a DLL.  That is if you use the timer callback method that I mentioned above.  I didn't want to call it an ActiveX though since it is a little strange what I am suggesting.  But it is really the only way you can create a seperete thread in VB6 (that I know of).  Try it and see.

 

by: EnladePosted on 2005-01-06 at 13:35:15ID: 12977008


JohnBPrice: Why would you need to call DoEvents at all?  Its a seperate thread and thus DoEvents won't have any effect on the main application which is running in its own thread.  At least, I don't think it should matter.

 

by: JohnBPricePosted on 2005-01-06 at 13:36:12ID: 12977024

Yes, it is not the same from a windows perspective, but you see the same behavior from within a VB6 app.  In fact it doesn't even need to be in a separate executable at all, it can be in your main app, because it is not true Windows mutlit-hreading, it is the VB runtime controlling execution.  I've done it hundreds of times

 

by: EnladePosted on 2005-01-06 at 13:42:32ID: 12977078


Well, if you think it makes no difference then you should try what I have suggested.  You will find that it will add a new weapon to your coding arsinal.  It does make a difference.  The EXE that I created above will run on a seperete thread then your main application and you will not need any DoEvents.  Give it a try.

 

by: EnladePosted on 2005-01-06 at 13:54:55ID: 12977204


Maybe I just made a mess of explaining myself.  I'll go ahead and write you all up a little test code so you can take a peak at it.  Let me get that to you so I can be more clear on what I am saying.  Hold up a bit...

Actually, I have to head home now so I'll post it when I get home...hold up a bit.

 

by: EnladePosted on 2005-01-06 at 14:16:35ID: 12977379


Oh you will need DoEvents to get the ActiveX component to respond to any requests for status or to respond to a request to cancel.  But not if you just want it to go off and print unattendend.  In any case, that would be the same restriction for any true multithread solution.  And since I want to allow my example to give you some indication of status I will add a DoEvents (though I could leave it out and still give you an example).

If your application is particularly tricky you could employ a third process to control the communications between the main application and your printing ActiveX EXE.  You would then simply send requests to the thrid process and it would pass it on to the printing process whenever that printing process got around to checking for any messages (so to speak).  Then you would not need to use any DoEvents and you still would be able to get the status and/or control the printing process from your main application.  But that is an overkill.  I'll get you the example shortly.

 

by: ThrasymachusPosted on 2005-01-06 at 14:33:46ID: 12977547

Enable,
     Actually I already have a method to know if the print job was sucessful or not. I am using MSMQ and I am going to have ERSptDev.dll just send a message indicating success of failure of the job and a completely seperate service picks up the status, so it is not an issue. I just want my app (DeviceManager) to start the jobs and not get hung on the larger print jobs when a smaller one comes down the pike.  I will experiment with your example tomorrow.

 

by: EnladePosted on 2005-01-06 at 18:15:41ID: 12979080


Sorry about that, I got tied up with dinner and forgot to get back to you.  Anyway, I threw together some code that might help you.

The code consists of two processes Test.EXE and axTest.EXE.  axTest.EXE is a process that simply counts to 20 seconds and then stops.  It could be doing anything, but I just thought of whatever was easiest.  axTest.EXE is a sub process of Test.EXE (its really just an ActiveX EXE with a little twist due to the timer callback).  This means that there is some dependence that axTest.EXE has on Test.EXE.  Which means that in this case you probably want to have your main process (Test.EXE in my case) be able to measure the status of its axTest.EXE sub process (I'm being very liberal with my use of words here and do apologies).

Still, maybe you could get away with getting rid of any status related communications between the main process and the axTest.EXE process.  I'll let you consider that in the context of your environment.

You can find the code here:

http://www.enlade.com/Samples/axTest.zip

You will need to start by compiling axTest.EXE.  After which you then load Test.EXE and go to the References.  Uncheck the axTest reference and close the References, and then reopen the References and find axTest again and check it (I think that’s a bug/quark in VB that Microsoft should fix).  Then compile Test.EXE and run.  Hope it helps or at least points you in the right direction.

Keep in mind that your solution might be to create a third process (as I said earlier) that will control the communication between Test.EXE and axTest.EXE.  Having a third process makes things run very smoothly from the users perspective and allows you to eliminate all the DoEvents as well.  Keep that in mind as you work through my code.

 

by: EnladePosted on 2005-01-06 at 18:33:15ID: 12979176


Actually, this code is a little better.  I just took out a button and added a timer to show the status of axTest.EXE's count.

http://www.enlade.com/Samples/axTest2.zip

Basically, the first button starts axTest.EXE counting.  While the second button shows you that you can do other things without axTest.EXE interfering.  The third button was suppose to get the current status of axTest.EXE, but I just made it a Timer that updates the label with the status.

Now, axTest.EXE needed the DoEvents in the program, but not because it interfered with Test.EXE's exicution (because it doesn't since it is running on a seperate thread).  However, it does need it in order to respond to any status requests from Test.EXE.  If you don't include it then you when Test.EXE trys to talk to axTest.EXE then Test.EXE will lock up waiting for a reply.  Keep in mind that you don't need to have a loop in your printing program.  You simple need to throw in DoEvents every so often to give your main process a chance to get a word in.

If you don't want to use any DoEvents then you need to create that thrid process that I was talking about earlier.  Hope that helps.

 

by: JohnBPricePosted on 2005-01-07 at 06:13:23ID: 12983132

mmm, it seems cool enough, but what you have done is hijack the Apartment-Thread for the COM object using a windows callback under the nose of the VB runtime for the COM component.  This is fine for many things, and it probably mostly works as long as you use apartment threading and thread per object so you get a new thread to hijack for each instance and avoid certain high risk activity, but there are constraints and situations that could cause it to intermittently fail, hang, or GPF out.

First, as you said, you can't touch anything in the object unless you put in DoEvents.  Indeed, you can't even let the object go out of scope.  If the hijack hasn't started, windows will terminate the thread and the hijack will never start.  If the thread is running, VB will hang, eventually getting a COM timeout.  So you either constantly add to an array of new objects forever to get new threads, eventually having lots of finished threads that will never terminate, or you check if it is done and to do so you must put DoEvents in the loop, which is what you proposed to avoid.  (I suppose you could keep track of some ID, perhaps the thread ID, associated with each object and have the object post back a message to yourself (with MSMQ or windows), but that brings it's own complications).

Second, the VB runtime has finished it's marshalling from the initial call, and has finished all the COM cleanup such as CoGetInterfaceAndReleaseStream, yet the hijack thread still runs.  On the surface, this means you can't touch any parameters passed to the object (you can copy them to global memory before you hijack the thread), but there are a number of other nasty issues that can crop up.  While you may not notice any reproducible problems, many things can cause sporadic issues, such as file I/O, error handling , arrays, other COM method calls such as other VB objects, non-stack memory allocation, using the App object or other builtin global objects, etc.  I had a client who did this trick, and for months couldn't figure out why once a week or so their service would fail for varying reasons until they cleaned it up.

Third, the hijacked execution is no longer under VB runtime control, so if your main app ends it will not end your threads (windows will keep the exe in memory and running), you have to incorporate a way to shut them down or wait for all the objects to finish.  If you app finishes unexpectedly, the object threads will continue to run until they are killed, crash, or finish.  Furthermore, you have to be responsible for you own cleanup for some things because the VB runtime doesn't know the hijack is running and possibly allocating things, but you don't really know which ones are safe and which ones are not.  Thus, an invitation for slow memory leaks.

Lastly, you probably won't be able to use the VB interactive debugger to debug the hijacked portions.  Not that you may care,  you also won't get any tech support from MS because they do not support unmarshalled execution of a VB 6 COM object.

For a quick hack to achieve a specific goal, fine.  To build a Windows Service in VB 6 (which isn't recommended anyway) hijacking an unmarshalled "stealth" COM thread is pretty risky.  If it works for your need, great, if you see weird unexplained issues, try taking it out and go with a clean approach (they are basically the same code, it only differs in how you trigger the second execution).  At the end of the day, rather than Hijacking a thread, I would just whip up a safe multi-threaded app in VB.net.

 

by: ThrasymachusPosted on 2005-01-07 at 07:36:37ID: 12984189

Enlade,
     Thank you for your code examples. they seem to work as intended, but as JohnBPrice mentioned it does not work for my purpose. Specifically I need to be able to run the object multipule times in seperate threads at the same time, which your code does not allow. At least not without creating an array of objects.  Also What John said about this line of coding being unstable is worrysome to me, this service NEEDS to be robust, I cannot have a solution that gives me errors and I spend weeks trying to troubbleshoot the problem.

JohnBPrice,
     You seem to be saying that multiThreading in VB6 is not available and that I am going to have to convert the Service Application to .NET, assuming I can do this (I do have some .NET experience, but never wrote a service, used multithreading or actually converted a project from VB6) can I leave the .dll in VB6? or do I have to convert both of them? Does .NET multithreading allow for objects to run out of context? some advice would be appreciated. The other option discussed was to write a wrapper .exe to call it and let it run in it's own thread. since I am Already using MSMQ to get the callback that I need, do you think that this would be an easier solution or should I be conserned with these exe's running independently and not being able to shut them down.

Again thanks to both of you for your help so far...

 

by: JohnBPricePosted on 2005-01-07 at 08:04:44ID: 12984509

Technically, Enlades code will allow multiple objects on multiple threads at the same time, you just need to create a new object for each instance, but since your example method is "iPrintDevice_PrintJob", is it a print job?  If so, why would you care if multiple instances run at the same time, since the print spooler will queue them up anyway?  Just queue them up in your main app.

Well, not that it is not available, just that it is easier & safe to do it in VB.Net.  You don't have to rewrite it in VB.Net, you could just make a wrapper for your class, and make the wrapper multi-threaded.  Then everything would be kosher, since all the COM init will be handled normally.  If you are short on time, the exe wrapper would be faster than learning VB.Net, but if you have the time, then VB.Net is cleaner.

BTW, Building a service in VB.Net is a LOT easier than you'd think, since the .Net framework includes service, service installer, and service controller classes.

 

by: ThrasymachusPosted on 2005-01-07 at 08:22:04ID: 12984734

Yes it is a print job, but it does not just print to one printer. this application is the hub for a print server that controlls all the printers at a particular location and there are times when a user sends a very large print job to a large format plotter (I have seen examples of over 2 GB) and it ties up the server for an hour or more and anyone who is trying to print a one page word doc to their local (local meaning proximity) laserjet printer has to wait until the job is completely done spooling before ersptdev.dll lets go of it.

How would I make a multi-threaded wrapper for the class in VB.net? Let me make sure I understand what you are saying. I create a .NET exe that uses multithreading to call the .dll and then call the .NET exe from the main VB6 app.
The .NET exe calls out the .dll in a seperate thread and returns the original thread back to the VB6 app so it can do it all over again.
Do I  have it essentially right?

I may just look into converting the application to .NET. it is really not all that big, and if creating a service is as easy as you say that may be the way to go.

 

by: JohnBPricePosted on 2005-01-07 at 10:05:35ID: 12986056

Well, here is a crack at a VB.Net wrapper for a DLL, but I haven't really tried it yet.  You'd have to wrap every function you wanted to spin in a thread.
This is a VB.Net DLL from the wizard, in the project build properties for the app you need to set "Register for COM Interop"


Imports System.Threading
Public Interface IClass1
    Function StartOne() As Boolean

End Interface
Public Class Class1
    Implements IClass1
    Implements IDisposable
    Dim x As Thread


    Function StartOne() As Boolean Implements IClass1.StartOne
        'Start a new thread
        x = New Thread(AddressOf handler)
        x.Start() 'now the thread is running, we can exit
        StartOne = True 'whatever
    End Function

    Private Sub handler()
        'this guy gets called on his own thread
        Dim x As New Project1.Class1 'this is your real worker object
        x.Start() 'This is the real start to your worker object
    End Sub

    Public Sub Dispose() Implements System.IDisposable.Dispose
        'I haven't tried this, but I think it might work, essentially kill the thread if we die
        If x.ThreadState <> ThreadState.Unstarted Then
            x.Abort()
        End If
    End Sub
End Class

 

by: ThrasymachusPosted on 2005-01-07 at 13:01:47ID: 12987951

JohnBPrice,

I had to add some aruments that I pass to ERSPtDev but Here is what I have:
Function StartOne(ByVal strDevice As String, ByVal msgCurrent As MSMQ.MSMQMessage) As Boolean Implements IThreading.StartOne
        Dim PlotControl As Object = CreateObject("ERSPtDev.c" & strDevice)
        'Start thread
        x = New Thread(AddressOf PlotControl.iPrintDevice_PrintJob(msgCurrent.Body, msgCurrent.Id))
        x.Start()
        StartOne = True
End Function

on the line where the new thread is created I get this: "'AddressOf' operand must be the name of a method; no parentheses are needed."

How do I pass these arguments to the thread? Am I doing this right?

 

by: JohnBPricePosted on 2005-01-07 at 13:16:56ID: 12988095

You can't spin your COM control directly as a thread, use AddressOf a method in your class, and have that method call PlotControl.iPrintDevice_PrintJob(msgCurrent.Body, msgCurrent.Id).  See the handler() function in my little test.  It's actually AddressOf Handler that I launch in a separate VB thread.

I'm not sure that you could pass args to the start, you might have to copy them to class storage.  Might want to check the docs, but I believe VB or CLR will ensure that references to class storage thread safe.


BTW, I'm thinking that if you have a lot of methods, you could make the VB.Net class as "Implements ERSPtDev.cDevice", in which case you could interchange your VB.Net calls (which spin off threads) with direct calls to ERSptDev.cDevice, thus making converting your code pretty easy, just changing the library name, with all the calls being exactly the same.  Haven't had time to try it though.

 

by: ThrasymachusPosted on 2005-01-07 at 13:36:36ID: 12988342

Oh yeah I missed the handeler sub - that makes more sense

>>BTW, I'm thinking that if you have a lot of methods, you could make the VB.Net class as "Implements ERSPtDev.cDevice", in which case you >>could interchange your VB.Net calls (which spin off threads) with direct calls to ERSptDev.cDevice, thus making converting your code pretty >>easy, just changing the library name, with all the calls being exactly the same.  Haven't had time to try it though.

Good Point I will give this a try.

I will spend some more time hashing things out and let you know how it works, it may be next week though with how things are going today.
Thanks again for your help so far...

 

by: EnladePosted on 2005-01-07 at 13:37:33ID: 12988351


JohnBPrice: True enough, but keep in mind that my intention was not to avoid the DoEvents altogether, but simply to avoid its use to keep the main process exicuting.  This is to say that in the first instance the DoEvents is being used to give the main process a change to continue exicution, but in the second case it is only being used to allow the second process to communicate with the main process.  To illistarate the difference I suggested that a thrid process be created that simply stores the state of each of the other processes.  Then there is no need for any DoEvents though there would be a need to add code so that each process checks with that third process for instruction on the state and/or desires of the other process (whew).

Most of the other issues that you mentioned are simply a byproduct of using multithreading to begin with.  They are all very important issues that any multithreading environment will need to consider when writting code.  So, it would do well for Thras to consider them all when he works out his solution.  My point was not that VB6 is the best language to do Multithreading, because I would lose that arguement very quickly.  What I am proposing is a bit strange and clearly not a method that was intended by Microsoft.  However, its still the only way that I can see to do anything close to what we would consider a true multithreaded application in VB6.  Worth investigating to say the least.

 

by: EnladePosted on 2005-01-07 at 13:40:33ID: 12988390


If you application simply prints jobs then why would you create more then one instance.  You simply create a queue function that you call to queue jobs on the same object.  Right?

Maybe I don't understand your application, but it seemed like you just wanted some thread that printed jobs in the background.  If thats the case then you just need to write the code so that it queues each job before it prints and that way you would only need one object.  Just a thought.

 

by: EnladePosted on 2005-01-07 at 13:52:29ID: 12988554


I didn't know that moving to VB.Net is an option for you.  If you can move to .Net then you will have some other things you can try.  However, VB6 limits what you can do by way of MultiThreading.  If VB.Net is an option then you probably should move to .Net for this type of a problem.

In any case, it sounds like your problem is not really a matter of writting a multiThreaded application.  It sounds like you simply want to write a stand alone application that other application use to print jobs.  So really you are looking for examples of ways that you can communicate between applications (right?).  The DLL that you would link into your application would simple be an API for your stand alone printing application.  So you really need three sets of code.  You need to write your main application, you need to write a stand alone printing application, and you need to write a DLL API for you stand alone printing application.  Right?

I'm just trying to understand the problem better.  But MultiThreading doesn't seem to be what you are interested in (at least in the sense of a "MultiThreaded application").

 

by: JohnBPricePosted on 2005-01-07 at 14:08:13ID: 12988700

OK, check this out, It is not complete.  Dispose does not work, it never gets called, I'll have to look up the correct destructor name.  It has the nice attribute that you can debug into the threads using the VB.net debugger.

Here is my DLL to emulate your ERS, I named the project ERSPtDev to match yours, it has one class, cPlotter

Option Explicit
Private gabort As Boolean

Public Sub PrintDevice_PrintJob(ParamOne As String, ParamTwo As Integer)
    'This is just a really long loop to run for a long time
    Dim I As Long
    Dim j As Long
    Dim L As Long
    Dim x As Long
    gabort = False
    For L = 1 To 10000
        For I = 1 To 10000
            For j = 1 To 10000
                x = I * j
                DoEvents
                If gabort Then Exit Sub
            Next j
        Next I
    Next L
End Sub
Public Sub PrintDevice_GracefullTerminate()
    gabort = True
End Sub


Here is the VB.Net wrapper,

Imports System.Threading
Public Class cPlotter
    Implements ERSPtDev.cPlotter 'Note this implements the original DLL class!
    Implements IDisposable
    Private t As Thread
    Private RealWorker As New ERSPtDev.cPlotter 'This is the real object that does the work
    Private MyParamOne As String
    Private MyParamTwo As Short

    Public Sub PrintDevice_GracefullTerminate() Implements ERSPtDev._cPlotter.PrintDevice_GracefullTerminate
       'I threw this in, didn't try to see if it worked.  I'm presumng your class might have some "Cancel Print" kind of call
        t.Suspend() 'waits until the thread reaches a safe point
        RealWorker.PrintDevice_GracefullTerminate()
        t.Resume()
    End Sub

    Public Sub PrintDevice_PrintJob(ByRef ParamOne As String, ByRef ParamTwo As Short) Implements ERSPtDev._cPlotter.PrintDevice_PrintJob
        'This implements the PrintDevice_PrintJob call, but in a separate thread
        MyParamOne = ParamOne
        MyParamTwo = ParamTwo
        t = New Thread(AddressOf MyPrintDevice_PrintJob)
        t.Start()

    End Sub
    Private Sub MyPrintDevice_PrintJob()
        'This is the new thread, which just does the call
        RealWorker.PrintDevice_PrintJob(MyParamOne, MyParamTwo)
    End Sub

    Public Sub Dispose() Implements System.IDisposable.Dispose
        t.Abort() 'just kill it
    End Sub
End Class


And finally, here is the main app
Private Sub cmdStartOne_Click()
    Dim c As New ERSPtDevAsynch.cPlotter
    Call c.PrintDevice_PrintJob("Blah", 5) 'And away it goes...
End Sub

 

by: JohnBPricePosted on 2005-01-07 at 14:18:00ID: 12988771

Enlade, I guess my point could be summed up as "true multi-threading in VB6 scares me".  Your trick is very cool, and I know lots of people do that, and some even attempt to do CreateThread which comes with a lot more details to fret over.  I'd rather either simulate it with straight VB6 style DoEvents, or switch to .net where I can delude myself that MS has taken care of or at least has documented the threading issues.

 

by: JohnBPricePosted on 2005-01-07 at 14:18:27ID: 12988774

Have a good weekend folks....

 

by: EnladePosted on 2005-01-07 at 14:59:44ID: 12989169


True enough.  If he can move to .Net that would be a better approuch.  Have a good weekend.

 

by: ThrasymachusPosted on 2005-01-10 at 07:54:14ID: 13003922

OK I think I have the beginning to something that will work, but I have never actually created a dll in .NET before. Is there a way to debug the dll? I have been able to debug dll's in VB6 and it just waits until it is called by an external app and it runs in debug great. When I try to debug the dll in .NET I get: "A Project with an Output Type of Class Library cannot be started directly....."
Any ideas?

I know this may be outside the original scope of the question, but I will add points if I can get this all worked out.
Thanks again

 

by: JohnBPricePosted on 2005-01-10 at 07:56:52ID: 13003957

One way is to set a startup executable for your VB.Net project, e.g. set the startup to your main program.

You can also use "Attach to process" to attach to a running process, this works when your main process is a service.

 

by: ThrasymachusPosted on 2005-01-10 at 10:22:45ID: 13005739

I created PTDevAsync.dll that should do what want, but I cannot either add a refrence to it in VB6 ("Cannot add refrence to specified File") nor can I register it using regsvr32 ("Entry point cannot be found"). I perfer to add a refrence so that I can use early binding in my main VB6 app since there is only one class in the .NET async dll
I assume that there is something I am missing about the .NET stuff to work with unmanaged (vb6) code, but I am at a loss to figureout what it is.

Here is the Entire code that I have:

Imports System.Threading
Public Interface IThreading
    Function StartOne(ByVal strDevice As String, ByVal msgCurrent As MSMQ.MSMQMessage) As Boolean
End Interface
Public Class Threading
    Implements IThreading
    Implements IDisposable
    Dim x As Thread
    Dim DevCls As String
    Dim msgCur As MSMQ.MSMQMessage

    Public Function StartOne(ByVal strDevice As String, ByVal msgCurrent As MSMQ.MSMQMessage) As Boolean Implements IThreading.StartOne
        DevCls = strDevice
        msgCur = msgCurrent
        'Start thread
        x = New Thread(AddressOf Handler)
        x.Start()
        StartOne = True
    End Function
    Public Sub dispose() Implements IDisposable.Dispose

    End Sub
    Private Sub Handler()
        Dim PlotControl As Object = CreateObject("ERSPtDev.c" & DevCls)
        PlotControl.iPrintDevice_PrintJob(msgCur.Body, msgCur.Id)
    End Sub
End Class

I did not Implelemt ERSPtDev.cXXX becasue there are alot of classes and potential for more down the road, so it is better to use latebinding in the this new .NET thredding util that way we will not need to modify it in the future.

Also I have one question that is a little worrysome to me and that is the two class level variables I need to make this thing work. This is probably unlikely, but isn't it possible to have the variables set by one thread and used by another, so that you actually passed the wrong 'set' of variables to the wrong thread? There isn't a whole lot going on between the setting of the variables and using them, so I suppose I could just ingore it.

Thanks again.

 

by: JohnBPricePosted on 2005-01-10 at 10:45:25ID: 13005958

In my test project, I could not reference the .DLL by browsing, I got the same message you did, but my project name automatically showed up on my VB6 list of available references, so I didn't to browse for it.  I did notice that the VB6 list showed the .TLB file from the bin directory as the reference, not the .DLL, so give that a try.  There is probably a switch in VB.NEt to include the TLD info in the DLL as VB6 does, but I didn't look for it since it showed up.

If you create a threading object for each thread you start, each threading object will have it's own variables, and this also gives you the option to abort a thread or otherwise communicate with it.  

If you used just one threading object to create multiple threads, you still shouldn't have a problem, because the only thing calling the threading object is your VB6 app, which isn't multi-threaded so it will never be able to call StartOne while an instance of StartOne is already running.

 

by: JohnBPricePosted on 2005-01-10 at 10:55:06ID: 13006073

I thought of one possible glitch.  The second parameter is a MSMQMessage, is MSMQMessage an object?  I'm not sure how COM marshall's object references, but I suspect it doesn't do a deep copy, even though you pass it ByVal.  It might just add a new reference onto the underlying object.  Everything would be fine unless your main app modifies the object underneath (it can go out of scope, the new reference will keep the object alive).  If, for example, you change the text of the message object parameter after you have set it printing, that might foul things up.

 

by: ThrasymachusPosted on 2005-01-10 at 12:38:13ID: 13007160

JohnBPrice
You are right it was already in my list, I just assumed that it would not be.

OK I got it working for the most part. What I mean is that it appears that the multi threading is working, except that my main app really hangs while ERSPtDev is doing it's stuff.  I send a very large job through to see if I could get smaller jobs to go through at the same time, and it did work, but it was very slow. Don't get me wrong I KNOW it is going to be slower, even alot slower, but it appears that the main app is still somehow linked to those threads because I was running othere apps that were all still very responsive. Also when I ran the main code in debug I got the thread back right away, but the debugger was 'Not responding' until the job got through.

I'm still gving you the points (adding a few as I said I would too) 'cause I think we have accomplised what I asked for, but I would be intrested in your take on why I am seeing this.

Thanks again for all your help!

 

by: ThrasymachusPosted on 2005-01-10 at 12:40:04ID: 13007188

Also ERSPtdev only reads the MSMQMessage object so we should be in the clear there.

 

by: JohnBPricePosted on 2005-01-10 at 13:16:54ID: 13007564

I'm guessing you were using the VB6 debugger?  The VB6 debugger is not thread safe, so you can't use it to debug multi-threaded apps.  If you looked at your running processes, even if you were sitting on the line after the call, you'd see VBRun.EXE churning up CPU cycles.  That's because the VB6 debugger runs apps in the VB6 process, not in a separate process like the VB.net debugger.  You can to use the VB.net debugger.  You might have to revert to the old fashined method of debugging ERSPtDev (e.g. use msgbox or application events (since it is a service)).  You might be able to debug ERSPtDev with the VB.Net debugger if you build with symbols.  Essentially you are debugging the dissassembly, but the symbols show you comments with what the original VB 6 code was.

 

by: ThrasymachusPosted on 2005-01-10 at 14:03:18ID: 13008021

Once the VB6 debugger locked up on me I complied it and I am now just running it as a service. Infact I am not actually debugging anything at this point as all components are currently compiled and 'working'.  I am not too conserned about debugging ERSPtDev at the moment - I can always write up a wrapper app if I need to.
What I think is happining is that unless I hit a DoEvents in ERSPtDev then the main app (the VB6 Service) Hangs. When I hit a DoEvents then the main app can start the next job. Since I am not debugging I cannot gurantee this, but I have a good understanding of what is happining even if I am not debugging.

I still find this strange. the Service is not all that large so I am considering just rewrighting the whole service in .NET at this point. It would get rid of my extra Async.dll Do you think that it would have any effect on this hanging that I am seeing? I would REALLY rather not try to redo ERSPtDev as it is not small.

 

by: JohnBPricePosted on 2005-01-11 at 04:59:51ID: 13013016

What does ERSPtDev do after starting a thread?  It should exit out of any subs/functions it is in so that the VB runtime event loop (it's WinProc)  can respond to a new print job event can be handled.  It's been a while since I wrote a service in VB6, I forget what the correct way is to keep your application from exiting altogether.  In a non-service, you would just open a form.  The existance of the form would keep the whole EXE in memory.

If ERSPtDev is a service and it itself has long loops (other than the print threads you now spin off), I'm thinking that not having DoEvents might have some effects.  Windows might be sending the service events, such as external application start stops or whatever events services get.  

 

by: ThrasymachusPosted on 2005-01-11 at 07:42:57ID: 13014721

Lets just make sure were are clear as to what is going on.
DeviceManager.exe is the Windows Service currently a VB6 exe. I only changed 2 lines of code so that it would call PtDevAsync instead of ERSPtDev
DeviceManager calls PtDevAsync.dll (our new .NET .dll) which creates a new thread and runs ERSPtDev.dll (a VB6 dll) - I have made no changes to ERSPtDev.dll

Essentially what ERSPtDev.dll does is concatinates several files together into one larger print job along with some header information that is baised on how the user requested it to be printed (this is why we need the MSMQmessage - it has all this information). Once the File that contains the whole print job has been completely created it calls a C++ .dll file that sends it off to a windows printer. Then the original function exits.

I can get jobs to go through while the large print job is concatinated by putting DoEvents in the loop and it seems to work well, (or at least well enough) but there is only one line that sends the file to the print Queue and while this happens everything just 'waits' until it is completely finished spooling to the queue.

Does that help you understand what the process is?

 

by: JohnBPricePosted on 2005-01-11 at 08:02:55ID: 13014951

Sorry, I got the names mixed up.  I should have asked "What does DeviceManager do after the call to PtDevAsync?"

What loop do you put the DoEvents in to make it work?  Can you log an application event immediately before & after the call to PtDevAsync? (using App.LogEvent)

 

by: ThrasymachusPosted on 2005-01-11 at 08:20:43ID: 13015158

not too much - It deletes the message from the MSMQ and then waits until the next timer event to check the MSMQ for new jobs.

I can put some events in to see where it seems to be stalling

 

by: JohnBPricePosted on 2005-01-11 at 08:40:15ID: 13015436

Here is some code that seems to work, I get all three events & I can see the threads with Spy++.
Sub Main()
    Dim x As ERSPtDev.cPlotter 'this is the interface
    Set x = New ERSPtDevAsynch.cPlotter 'this is the asynch starter
    App.LogEvent "Starting first thread", 4
    x.PrintDevice_PrintJob "Blah", 5
    Set x = Nothing
    Set x = New ERSPtDevAsynch.cPlotter
    App.LogEvent "Starting second thread", 4
   
    x.PrintDevice_PrintJob "Blah2", 5
    Set x = Nothing
    Set x = New ERSPtDevAsynch.cPlotter
    App.LogEvent "Starting Third thread", 4
   
    x.PrintDevice_PrintJob "Blah2", 5
    'sit and do nothing
    While True
        Sleep 500
        DoEvents
    Wend
   
End Sub

 

by: ThrasymachusPosted on 2005-01-11 at 09:34:16ID: 13016098

Still not 100% sure what is going on, but It appears that the timer event is not fireing.
the Service has a timer that checks for new jobs every 1 second. I put logging events in to show me as soon as the event is fired and at the very end of the event (which 'should' then fire again in 1 second). I also am logging events just after handing it off to PtDevAsyc

Every time I get a new job - regardless of how big it is now - all events (in timer event, after thread is handed back to Service, out of timer) occur witin one second.

The delay occurs between the end of the Timer event and the fireing of the next one. One other interesting thing to note is that the delay seems to coincide with a delay in ERSPtDev. I noticed that other small jobs were sent though but were delayed until the NEXT timer event fired, even though they were picked up on and sent off on the LAST timer event.

one other thing to note is that the C++ dll I mentioned that submits the file is also called several times (in a loop) to concatinate the files into one job. could it have anything to do with this?  It appears that things are not Exactly running completely independently.

 

by: JohnBPricePosted on 2005-01-11 at 09:55:01ID: 13016341

So, a timer (a VB6 timer on a form?) fires, collects the MSMQ message, sends it to ERSPtDevAsync, which starts ERSPtDev in a thread and returns, ERSPtDevAsync returns, control returns to DeviceManager, which exits the timer sub, but the next timer event doesn't occur until the thread started by ERSPtDevAsync finishes?

 

by: JohnBPricePosted on 2005-01-11 at 10:05:14ID: 13016435

Did you check that ERSPtDev was built with apartment threading?  how about the C++ DLL?  If either is single threaded, it could block ERSPtDev, though I think it would block on the second call to ERSPtDev and not the timer event.

 

by: ThrasymachusPosted on 2005-01-11 at 10:19:15ID: 13016594

ERSPtDev is Single Threaded, I don't know aout the C++ .dll I have the code, but I don't know C++
Do you know how to check?

 

by: ThrasymachusPosted on 2005-01-11 at 10:25:29ID: 13016661

OK very interesting - I changed ERSPtDev to Apartment Threading and I now get all the Timer events to fire, but I am still getting the same delays in printing. it has to be the C++ dll. I think if we can do the same thing to it, the problem would be solved.

 

by: JohnBPricePosted on 2005-01-11 at 10:35:05ID: 13016779

Um, hopefully there is not a reason for the C++ DLL to be single threaded?

20120131-EE-VQP-002

3 Ways to Join

30-Day Free Trial

The Experts

98% positive feedback on 31,087 answers since March 2000. angeliii is a Microsoft Most Valuable Professional for his work with MS SQL Server & Develoment.

He has also proven his knowledge of Visual Basic Programming, PHP Scripting and Oracle Databases.

The Experts

97% positive feedback on 10,752 answers since July 2000. lrmoore has more than 18 years experience in the networking industry.

The six-time Mircosoft MVPs specialties include firewalls, virtual private networking, and network management.

Testimonials

"...and excellent source for support... Kind of like having your very own IT dept." Electriciansnet

Testimonials

"I was apprehensive at signing up at first. However... it has already made my life as an IT administrator much easier." JaCrews

Testimonials

"WOW! You guys have great, active, and knowledgeable people on here." moore50

Business Clients

Business Clients

In the Press

"If you’ve got a question... Experts Exchange can supply an answer.”

In the Press

"...an invaluable aid for both IT professionals and those who require tech support."

In the Press

"where IT professionals provide quick answers on just about any topic"

Business Account Plans

Loading Advertisement...