COM

Everything else that doesn't fall into one of the other PB categories.
gamecoders
User
User
Posts: 13
Joined: Wed May 28, 2003 2:41 pm
Location: Italy

COM

Post by gamecoders »

Hello,
I am interested to create an estension for an application and this application require a com dll is possible to create in purebasic a com dll?
hm
User
User
Posts: 30
Joined: Mon Oct 27, 2003 12:15 pm
Location: Germany
Contact:

Post by hm »

i think this hasn't been done yet. i gave up implementing a COM object in purebasic due to lack of COM and assembler knowledge.
perhaps it would help to port the MASM examples of COM objects from www.japheth.de to FASM and use inline asm in purebasic. or send your FASM ports to Fred and ask him to implement them into purebasic.
Amiga5k
Enthusiast
Enthusiast
Posts: 329
Joined: Fri Apr 25, 2003 8:57 pm

Post by Amiga5k »

The documentation for Interface\Extends\EndInterface for talking to com objects seems to hint that this can be done.

Any experienced programmers (Fred?) willing to show us a quick example of how this would be done in PB? I'm sure more than a few of us would be very interested to know :)

Russell
*** Diapers and politicians need to be changed...for the same reason! ***
*** Make every vote equal: Abolish the Electoral College ***
*** www.au.org ***
dell_jockey
Enthusiast
Enthusiast
Posts: 767
Joined: Sat Jan 24, 2004 6:56 pm

Post by dell_jockey »

Timo Harter (Fr34k) wrote something about creating COM objects with PB. You might want to check out http://www.purearea.net/pb/download/tut ... wnload.zip
cheers,
dell_jockey
________
http://blog.forex-trading-ideas.com
gamecoders
User
User
Posts: 13
Joined: Wed May 28, 2003 2:41 pm
Location: Italy

Post by gamecoders »

thx to all for reply
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

You can also take a look in the "Examples\Source - Advanced\DirectX 7 - Interfaces" drawer, it's an example of use of a COM object (here a DX one).
ricardo
Addict
Addict
Posts: 2438
Joined: Fri Apr 25, 2003 7:06 pm
Location: Argentina

Post by ricardo »

From Microsoft website (its not about hot to write COM objects, but an good explanation of why its important to do it, sorry if its very large but i think is important):

---------------------------------------

If you've used Visual Basic much, you're very familiar with using components to do your programming: you use both visual ActiveX controls (such as spin buttons) and nonvisual ActiveX components (such as database access objects). It's hard to find a significant Visual Basic program that doesn't make heavy use of premade reusable components. But although you reuse plenty of components, most folks don't yet write a whole lot of reusable components of their own.

If you're programming in C++, you likely have a different experience of reuse. C++ and object-oriented programming claim to make reuse easy, but what has your experience been? Have you been able to create a library of reusable objects? A few of you no doubt have, but most of us have not. And if we have such a library, do we routinely make good use of it? It's not just a lack of discipline that keeps us from reusing our code: the fact is that it's hard to reuse code (it never seems to do quite what we need) and it's even harder to write reusable code (it's very hard to be general enough yet useful enough).

On top of that, what C++ makes easy is not creation of reusable binary components; rather, C++ makes it relatively easy to reuse source code. Note that most major C++ libraries are shipped in source form, not compiled form. It's all too often necessary to look at that source in order to inherit correctly from an object?and it's all too easy (and often necessary) to rely on implementation details of the original library when you reuse it. As if that isn't bad enough, it's often tempting (or necessary) to modify the original source and do a private build of the library. (How many private builds of MFC are there? The world will never know . . .)
So let's reuse binary objects, not source code

So how can you reuse binary objects? Well, the answer that first comes to a Windows programmer's mind is simple: use dynamic-link libraries (DLLs). Using DLLs does work?Windows is itself, after all, primarily a set of DLLs. But there are some problems.

First, DLLs are not necessarily programming-language independent. Even for DLLs written in C, it's all too easy to change the calling convention (which parameters are pushed in which order?) such that the DLL is only usable from C programs. Granted, the calling convention used by Windows is pretty well established as the standard for Windows systems, but the good doctor has seen DLLs fail because of mismatched calling conventions.

Writing a C-style interface for your DLL has some major limitations. First off, it restricts you from doing object-oriented programming because the object-oriented features of C++ require name decoration of function names. There is no standard for name decoration; in some cases, different versions of the same compiler decorate names differently. Second, implementing polymorphism is difficult. You can work around these problems by creating wrapper classes for both sides, but doing so is painful. And Dr. GUI's not into pain. (Not too much, anyway.)

Even if you did resolve the name decoration problems so you could link successfully to the DLL, other problems arise when it comes time to update your objects.

First off, you're hosed (that's a medical term) if you add any virtual functions to your object when you update it. You might think you're okay if you add the new functions to the end, but you're not: you've just shifted the vtable entries for all objects that inherit from you. And since a virtual function call needs a fixed offset into the vtable in order to call the correct function, you can't make any changes to the vtable?at least not without recompiling every program that uses your object or any object derived from it. Clearly, recompiling the world every time you update your object is not an option.

Second, if you're using new in your client to allocate objects, you won't be able to change the size of the object (that is, add any data) without recompiling the world.

Lastly (and most importantly), updating DLLs is a nightmare because you're stuck between a rock and a hard place with two unappetizing options: either try to update the DLL "in place" by overwriting it or rename the new version. Updating the DLL in place is very bad: the chances that even if you keep the interface consistent you'll break some user of your DLL are very high. Dr. GUI doesn't need to tell you of all the problems the industry, including Microsoft, has run into because of this issue.

The alternative?using a new DLL name?will at least keep your working systems working. But there's a cost in disk space (perhaps not a big deal when typical hard disks are 3 gigabytes or so), and a second cost: increased memory usage. If your user is using both versions of the DLL, there will be two copies of very similar code in the user's working set. It's not unusual when you examine a user's memory usage to find two or three versions of the Visual Basic runtime or the Microsoft Foundation Class (MFC) DLL, for instance. Given that almost all Windows systems typically use more virtual memory than they have physical memory, increasing the working set size has serious performance implications in the form of increased virtual memory swapping to disk. (That's why, to paraphrase a counter-example to Brook's law, adding more memory to a slow system makes it faster.)

Ideally, you'd like to allow your user (or application) to be able to choose what version to use. This is VERY hard with statically linked DLLs, but very easy if you dynamically load your DLL.

To be fair to C++, we should note that it was never intended to solve this set of problems. C++ was intended to allow you to reuse code in programs that reside in one file, so all of the objects are compiled at the same time. C++ was not intended to provide a way to build reusable binary components that can be mixed and matched over versions and years. By the way, the creators of Java noticed these problems?these deficiencies were a major motivation for developing Oak, which later became Java.
So what about Java?

Java does solve some of these problems, but it also adds some of its own. The biggest problem is that Java components (JavaBeans, usually) are only intended to be used by programs written in Java. Now it's true that the Microsoft virtual machine (VM) allows you to use JavaBeans as COM objects. You can therefore use them from any language. And it's true that Sun has a Java/ActiveX bridge. But in general, unless you're running on Windows, Java is a single-language system: Java components can only be used in Java programs. And, for the most part, you have to rewrite your systems from scratch to use Java. (Yes, you can make native calls?but it's a big hassle to do using Java Native Interface (JNI)?and your programs will no longer be at all portable.) Dr. GUI finds this pretty unacceptable, so he's glad that the Microsoft virtual machine (VM) is more flexible?for Windows, at least. No language, not even C++, Visual Basic, or Java, is right for every programmer and every problem.

Java also makes you decide when you write your program whether the component you're using is local (on your machine) or remote (on another machine)?and the methods for using local and remote components are quite different.

Java has a couple of other issues that make it a less-than-ideal answer for all component needs. First, it has no really solid way to deal with versioning. (The package manager in the Microsoft VM helps a lot with this issue.) Second, Java will be to one degree or another slower than C++. Dr. GUI notes that recent "it's as fast as C++" benchmarks published in an online Java magazine omitted tests where Java would do poorly. Two examples that come to mind are string and array manipulation (Java has to bounds-check each access), and initial method calls (Java has to look up the method by signature in a table in the class for the first call, although subsequent calls, which the magazine did test, can be fast). Finally, Java's "one-class-at-a-time" loading scheme can be considerably slower than loading all the code at once (even if there's less code!) because it requires so many more file or HTTP transactions, both of which have a high overhead.

Even if you're using Java in a way that gives good performance, your performance will suffer when you use Java components from another language because of the translation layer that needs to be there to link the dissimilar languages and object models.

Where Java shines is in the possibility that you can use your compiled components on dissimilar machines without needing to recompile for each machine's processor and operating system. But this often doesn't "just work"?you'll need to test and debug on every platform you intend to support.
So what's the alternative?

As it turns out, it is possible to use C++ to build DLLs and other binary components that are reusable. Both Dale Rogerson's book, Inside COM, and Don Box's book, Essential COM, start with a C++ class they want to reuse and solve each of the problems I've listed above (and a few more) with some slick tricks. And both of them end up with, not surprisingly, COM. In other words, each of the solutions to the problem of binary reuse is an important feature of COM. (If you'd like to check this progression out now, check out Markus Horstmann's article, From CPP to COM.)

While COM's "native language" is C++, it's relatively easy to use COM from C?the headers even support this. And, with a few language tweaks, it's possible to have two-way COM support from any language?Visual Basic, Java, Delphi, and so on. (By two-way COM support, I mean that it's possible both to use COM objects from that language, and to write COM objects in that language.) Implementing COM compatibility in your language run time isn't trivial, but the benefits are great: once you do, you open up a whole world of already written and debugged COM objects for your use. And there's a wide market for the COM components you write?the Giga Information Group estimates the current market at $400 million a year and expects it to be $3 billion in three years. (The COM-component market is growing faster than Microsoft!) Note that these market estimates are for third-party COM objects: they exclude COM components provided by Microsoft.

Another key feature of COM is that three types of objects are supported: in process (DLL), local (EXE in separate process on the same machine), and remote (DLL or EXE on a different machine via Distributed COM, or DCOM). You write code that uses COM components without worrying (or even knowing) what type of COM object you'll end up using, so you use the exact same code to hook up to an in-process, local, or remote object. How does COM hook you up to the right object? Well, it looks for the object's Class ID in the registry?and the registry entries tell COM which type or types of objects are available. COM does the rest, including starting processes and communicating over the network. (Note: there are performance differences between the different types of COM objects that you'll need to think about?but at least the code for connecting to and using the objects is exactly the same no matter what type of object you eventually use.)

COM doesn't solve all of the world's problems, however. For instance, it's still possible to break programs that use your component when you update a component. (Perhaps the fact that COM enforces a "black box" view of the component where it's not possible to find implementation details makes such breakage less common, but they still occur.) So you still have to choose between updating components in place and risking breakage, and using new Class IDs for new components. But COM does make it somewhat easier to write code that allows the user (or application) to choose what version it will use without recompilation.

Recall that COM components can be written in and used from most any language, and they can reside on any machine. That's nice. But what about cross-platform support?

Well, the cross-platform story is a good-news/bad-news story. The bad news is that there isn't very much COM on any platform besides Win32 right now. There are a couple of ports of COM to some non-Windows platforms, but not many. But that's not all the news.

The good news is that many more ports?to most common UNIX versions and to MVS?are coming, and soon. Further, Microsoft is doing some of the porting work itself. So it won't be long until COM and DCOM will be available on your favorite mainframes and UNIX boxes?availability for UNIX is scheduled to be announced in February. And think of how cool it'll be to have remote COM objects written in any language running on some fast mainframe that you can access from any language (Visual Basic, Java, Delphi, C++) on your machine. Check out the latest information on the Microsoft COM Web site (http://www.microsoft.com/com/).

So if you're developing for Windows, you'll certainly want to consider writing COM objects, no matter whether you develop in Visual Basic, Java, C++, Delphi, or some other COM-compatible language. The objects you write will be usable on the local machine or remotely without rebuilding your component or the component's clients, thanks to the magic of COM and DCOM. And if you need your solutions to run on platforms other than Windows, COM is looking better and better, so it's still certainly worth looking into and seriously considering.

http://msdn.microsoft.com/library/defau ... 020298.asp
ARGENTINA WORLD CHAMPION
User avatar
Maxus
User
User
Posts: 71
Joined: Thu Feb 16, 2006 9:35 am
Location: Russia
Contact:

Post by Maxus »

Code: Select all

; ---------------------------------------------------------
; Backrgound downloading with UrlDownloadToFile_()
; Tutorial for creating a COM object with PB
;
; 10/09/2003 by Timo 'Fr34k' Harter
; http://freak.purearea.net
; ---------------------------------------------------------
;
; First, I want to say, that not everything here came from
; my mind, i had some input from a C++ program found here:
; http://www.codeproject.com/internet/urlfile.asp
;
; This tutorial is a complete PB executable, that can be executed
; as it is, with hopefully enough comments for you to understand.
;
; Intro:
; Ok, much people know the UrlDownloadToFile_() Api, which is
; a very comfortable way to download a file, because you don't
; have to worry about the protocol you use, and reading headers
; and stuff with raw network commands.
; Now the problem with that command was, no easy way display the
; status of the download operation. This is possible by creating
; an own IBindStatusCallback Interface to handle this. Now actually
; you don't need any of PB's new Interface stuff to do this, as you
; can see in this code. Only till now i didn't have the knowledge how
; to do this.
; I will show here, how to create an object with a IBindStatusCallback
; Interface, and how to do a nice background downloaading with that.
;
; But that is unfortunately not all. UrlDownloadToFile_() stops the
; program flow, till the download is done, and we need a way around
; that. To do this, we put the function in a seperate thread. The
; problem then is, that the methods of our IBindStatusCallback
; Interface are then also called in this thread's conext, and so we
; can't update our user interface from there, as it is in a different
; thread. So, in order to communicate between the threads, we use
; SendMessage_() and send 2 userdefined messages.
;
; To get more info on UrlDownLoadToFile_(), go here:
; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wceurlmn/html/cerefurldownloadtofile.asp
;
; Read more about the IBindStatusCallback Interface here:
; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/guide_ppc/htm/urlmon1_rgqn.asp
;
; So much for the general way this program functions, let's get started...
;
; ---------------------------------------------------------


; These Global variables are used by the thread to start the download, and
; to post the messages, so they may only be manipulated BEFORE a download is
; started, changing them while a download is running may have unexpected 
; results (most likely a crash)
Global Url.s, SaveTo.s, MainWindow.l
; Url is the Source address. May be something like "www.purebasic.com" or a direct file.
; SaveTo is the target filename on disk
; MainWindow is a WindowID() of an open Window, where the messages will be sent to

; This value is Global, but not used from the thread. We use it to indicate, that
; the download should be aborted (if it is #TRUE)
Global Abort.l

; This structure is used to communicate between the thread and the WindowCallback
Structure ProgressData
  Progress.l    ; bytes downloaded
  ProgressMax.l ; bytes total (this value might change during the download)
  StatusCode.l  ; A code indicating what is happening
EndStructure

; Now these are the 2 messages we send. One to indicate a progress status
; and one to inbdicate the download end. Values above #WM_USER are free for use
; inside programs.
#WM_DOWNLOADPROGRESS = #WM_USER + 1
#WM_DOWNLOADEND      = #WM_USER + 2

Enumeration 1
  #BINDSTATUS_FINDINGRESOURCE
  #BINDSTATUS_CONNECTING
  #BINDSTATUS_REDIRECTING
  #BINDSTATUS_BEGINDOWNLOADDATA
  #BINDSTATUS_DOWNLOADINGDATA
  #BINDSTATUS_ENDDOWNLOADDATA
  #BINDSTATUS_BEGINDOWNLOADCOMPONENTS
  #BINDSTATUS_INSTALLINGCOMPONENTS
  #BINDSTATUS_ENDDOWNLOADCOMPONENTS
  #BINDSTATUS_USINGCACHEDCOPY
  #BINDSTATUS_SENDINGREQUEST
  #BINDSTATUS_CLASSIDAVAILABLE
  #BINDSTATUS_MIMETYPEAVAILABLE
  #BINDSTATUS_CACHEFILENAMEAVAILABLE
  #BINDSTATUS_BEGINSYNCOPERATION
  #BINDSTATUS_ENDSYNCOPERATION
  #BINDSTATUS_BEGINUPLOADDATA
  #BINDSTATUS_UPLOADINGDATA
  #BINDSTATUS_ENDUPLOADINGDATA
  #BINDSTATUS_PROTOCOLCLASSID
  #BINDSTATUS_ENCODING
  #BINDSTATUS_VERFIEDMIMETYPEAVAILABLE
  #BINDSTATUS_CLASSINSTALLLOCATION
  #BINDSTATUS_DECODING
  #BINDSTATUS_LOADINGMIMEHANDLER
  #BINDSTATUS_CONTENTDISPOSITIONATTACH
  #BINDSTATUS_FILTERREPORTMIMETYPE
  #BINDSTATUS_CLSIDCANINSTANTIATE
  #BINDSTATUS_IUNKNOWNAVAILABLE
  #BINDSTATUS_DIRECTBIND
  #BINDSTATUS_RAWMIMETYPE
  #BINDSTATUS_PROXYDETECTING
  #BINDSTATUS_ACCEPTRANGES
EndEnumeration

Class StatusObject

  Procedure StatusObject()
  EndProcedure
  
  Procedure.l QueryInterface(*iid.IID, *Object.LONG)
  
    ; compare the IID to the IID's in our DataSection
    If CompareMemory(*iid, ?IID_IUnknown, SizeOf(IID)) Or CompareMemory(*iid, ?IID_IBindStatusCallback, SizeOf(IID))
    
      ; return the object itself. See this is why this *THIS pointer is usefull
      *Object\l = *THIS    
      ProcedureReturn #S_OK
    Else    
      
      ; Ok, the caller requests an interface we don't have, so let's tell him:
      *Object\l = 0
      ProcedureReturn #E_NOINTERFACE
    EndIf
  EndProcedure
  
  
  ; In AddRef we just have to increase a counter, of how much references exist to
  ; our object, and return that number:
  Procedure.l AddRef()
    Shared Object_Count.l
    Object_Count + 1
    ProcedureReturn Object_Count
  EndProcedure
  
  ; Release is the same the other way around:
  Procedure.l Release()
    Shared Object_Count.l
    Object_Count - 1
    ProcedureReturn Object_Count
  EndProcedure
  
  ; ---------------------------------------------------------
  ; Ok, now for the IBindStatusCallback specific methods:
  ; We basically only need the OnProgress method, so we just return
  ; #S_OK everywhere we don't need to take any action, and #E_NOTIMPL, where
  ; we would need to do something (to tell that we didn't implement the method)
  
  Procedure.l OnStartBinding(Reserved.l, *IB.IBinding)
    ProcedureReturn #S_OK
  EndProcedure
  
  Procedure.l GetPriority(*Priority.LONG) 
    ProcedureReturn #E_NOTIMPL
  EndProcedure
  
  Procedure.l OnLowResource()
    ProcedureReturn #E_NOTIMPL
  EndProcedure
  
  ; Now we come to the interresting part: OnProgress
  ; Remember: this is called from inside the second thread, so we can't use
  ; any Strings in there for example. We basically just pass on every information
  ; to the main thread via SendMessage, and do nothing else here:
  Procedure.l OnProgress(Progress.l, ProgressMax.l, StatusCode.l, szStatusText.l)
  
    ; Make a ProgressData structure, fill it with the information we have:
    ProgressData.ProgressData
    ProgressData\Progress = Progress
    ProgressData\ProgressMax = ProgressMax
    ProgressData\StatusCode = StatusCode
    
    ; szStatusText contains additional information, unfortunately, in UNICODE format.
    ; So we have to convert it. For more information on that, search the forum, there
    ; are several examples for UNICODE strings.
    
    ; get length of string
    Length = WideCharToMultiByte_(#CP_ACP, 0, szStatusText, -1, 0, 0, 0, 0)
    
    ; now we allocate some memory for that string, we can't use AllocateMemory(), because
    ; it requeres a fixed number, we don't want to use.
    *String = HeapAlloc_(GetProcessHeap_(), 0, Length) 
    
    ; convert string   
    WideCharToMultiByte_(#CP_ACP, 0, szStatusText, -1, *String, Length, 0, 0)
    
    ; we use SendMessage to send the information, the address of the ProgressData 
    ; structure as wParam, and the address of the string as lParam.
    ; SendMessage waits until the WindowCallback of the main thread has processed
    ; the message, so the threads are syncronized like that, and we can destroy our
    ; string afterwards.
    Result =  SendMessage_(MainWindow, #WM_DOWNLOADPROGRESS, @ProgressData, *String)
    
    ; free the string
    HeapFree_(GetProcessHeap_(), 0, *String)
    
    ; From the Windowcallback, we return the value of the Global 'Abort' variable. If it 
    ; is #TRUE, we return #E_ABORT here, to stop the download:
    If Result = #True
      ProcedureReturn #E_ABORT
    Else
      ProcedureReturn #S_OK
    EndIf
  EndProcedure
  
  Procedure.l OnStopBinding(Result.l, szError.l)
    ProcedureReturn #S_OK
  EndProcedure
  
  Procedure.l GetBindInfo(BINDF.l, *bindinfo)
    ProcedureReturn #S_OK
  EndProcedure
  
  Procedure.l OnDataAvailable(BSCF.l, Size.l, *formatec, *stgmed)
    ProcedureReturn #S_OK
  EndProcedure
  
  Procedure.l OnObjectAvailable(*iid.IID, *UNK.IUnknown)
    ProcedureReturn #S_OK
  EndProcedure

EndClass

Global *StatusObject.StatusObject=New StatusObject()
; Here's the DataSection with the IID's for IUnknown and IBindStatusCallback
; I put them here, because they belong to the Interface stuff, not to the GUI part.
DataSection
  IID_IUnknown:  ; {00000000-0000-0000-C000-000000000046}
    Data.l $00000000
    Data.w $0000, $0000
    Data.b $C0, $00, $00, $00, $00, $00, $00, $46
    
  IID_IBindStatusCallback:  ; {79eac9c1-baf9-11ce-8c82-00aa004ba90b}
    Data.l $79eac9c1
    Data.w $baf9, $11ce
    Data.b $8c, $82, $00, $aa, $00, $4b, $a9, $0b    
EndDataSection

; That was actually all that was there to do to implement a IBindStatusCallback
; Interface in our program. We now have a 'StatusObject' object structure containing
; our Interface. That's all we need.

; GUI part comes next. Let's first create a nice GUI with the Visual Designer:

; ---------------------------------------------------------

; PureBasic Visual Designer v3.80 build 1249


; Window Constants
;
Enumeration
  #DownloadWindow
EndEnumeration

; Gadget Constants
;
Enumeration
  #Gadget_1
  #Gadget_2
  #Gadget_Url
  #Gadget_SaveTo
  #Gadget_ChooseFile
  #Gadget_Status
  #Gadget_Progress
  #Gadget_Start
  #Gadget_Stop
  #Gadget_Close
  #Gadget_StatusText
EndEnumeration


Procedure Open_DownloadWindow()
  If OpenWindow(#DownloadWindow, 414, 385, 447, 230, "", #PB_Window_SystemMenu | #PB_Window_TitleBar | #PB_Window_ScreenCentered)
    If CreateGadgetList(WindowID(#DownloadWindow))
      TextGadget(#Gadget_1, 5, 10, 60, 20, "Url:", #PB_Text_Right)
      TextGadget(#Gadget_2, 5, 35, 60, 20, "SaveTo:", #PB_Text_Right)
      StringGadget(#Gadget_Url, 70, 5, 320, 20, "")
      StringGadget(#Gadget_SaveTo, 70, 30, 320, 20, "")
      ButtonGadget(#Gadget_ChooseFile, 395, 30, 50, 20, "...")
      ListViewGadget(#Gadget_Status, 5, 55, 385, 120)
      ProgressBarGadget(#Gadget_Progress, 5, 180, 385, 20, 0, 100)
      ButtonGadget(#Gadget_Start, 395, 80, 50, 20, "Start")
      ButtonGadget(#Gadget_Stop, 395, 105, 50, 20, "Abort")
      ButtonGadget(#Gadget_Close, 395, 205, 50, 20, "Close")
      TextGadget(#Gadget_StatusText, 5, 205, 385, 20, "", #PB_Text_Center | #PB_Text_Border)
      
    EndIf
  EndIf
EndProcedure

Procedure BackgroundDownload(Dummy.l)
  Result.l = URLDownloadToFile_(0, @Url, @SaveTo, 0, *StatusObject)
  SendMessage_(MainWindow, #WM_DOWNLOADEND, 0, Result)
EndProcedure

Procedure WindowCallback(Window.l, Message.l, wParam.l, lParam.l)
  Result.l = #PB_ProcessPureBasicEvents
  
  ; download is in progress...
  If Message = #WM_DOWNLOADPROGRESS
   
    ; in wParam, we habe a pointer to the infor structure:
    *Progress.ProgressData = wParam
    
    ; let's update the ProgressBar:
    ; Progress may be always equal to ProgressMax, for example if the real size
    ; is unknown.
    If *Progress\Progress = *Progress\ProgressMax Or *Progress\ProgressMax = 0
      SetGadgetState(#Gadget_Progress, 0)
    Else
      SetGadgetState(#Gadget_Progress, (*Progress\Progress*100)/*Progress\ProgressMax)
    EndIf
    
    ; a pointer to the extra text is in lParam:
    StatusText.s = PeekS(lParam)
    
    ; now we check those StatusCodes, that are used for downloads, and set up a nice
    ; message:
    Select *Progress\StatusCode
      Case #BINDSTATUS_FINDINGRESOURCE:   Text.s = "Finding "+StatusText
      Case #BINDSTATUS_CONNECTING:        Text.s = "Connecting to "+StatusText        
      Case #BINDSTATUS_REDIRECTING:       Text.s = "Resolved to "+StatusText
      Case #BINDSTATUS_BEGINDOWNLOADDATA: Text.s = "Downloading "+StatusText
      Case #BINDSTATUS_ENDDOWNLOADDATA:   Text.s = "Finished downloading "+StatusText
      Case #BINDSTATUS_USINGCACHEDCOPY:   Text.s = "Receiving file from cache."
      Case #BINDSTATUS_MIMETYPEAVAILABLE: Text.s = "MIME Type is "+StatusText
      Case #BINDSTATUS_PROXYDETECTING:    Text.s = "A Proxy Server was detected"
      Default: Text.s = ""
    EndSelect
    If Text <> ""
      AddGadgetItem(#Gadget_Status, -1, Text)
    EndIf
    
    ; scroll down to the end:
    SetGadgetState(#Gadget_Status, CountGadgetItems(#GAdget_Status)-1)
    
    ; Set the sizes also in our TextGadget
    SetGadgetText(#Gadget_StatusText, Str(*Progress\Progress) + " of " + Str(*Progress\ProgressMax) + " Bytes complete")
    
    ProcedureReturn Abort
    
  ; download finished:
  ; Note: there is also a StatusCode for finished, but it is not sent on errors, so
  ; we also need this one:
  ElseIf Message = #WM_DOWNLOADEND
  
    ; lParam contains the result of the UrlDownLoadToFile_() Api:
    If lParam = #S_OK        
      ;jippeeeee :)
      AddGadgetItem(#Gadget_Status, -1, "Download complete.")
      SetGadgetState(#Gadget_Progress, 100)
    Else
      ; damn :(
      AddGadgetItem(#Gadget_Status, -1, "Download failed!!")
      SetGadgetState(#Gadget_Progress, 0)
    EndIf
    SetGadgetState(#Gadget_Status, CountGadgetItems(#GAdget_Status)-1)    
    
    ; switch Start/Stop button:
    DisableGadget(#Gadget_Start, #False)
    DisableGadget(#Gadget_Stop, #True)                
    
  EndIf
  
  ProcedureReturn Result
EndProcedure

Open_DownloadWindow()
SetWindowCallback(@WindowCallback())
DisableGadget(#Gadget_Stop, #True)
#SHACF_URLALL = 2|4
#SHACF_FILESYSTEM = 1
CoInitialize_(0)
If OpenLibrary(0, "shlwapi.dll")  
  CallFunction(0, "SHAutoComplete", GadgetID(#Gadget_Url), #SHACF_URLALL)
  CallFunction(0, "SHAutoComplete", GadgetID(#Gadget_SaveTo), #SHACF_FILESYSTEM)  
  CloseLibrary(0)
EndIf

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow: End
    Case #PB_Event_Gadget
      Select EventGadget()
        Case #Gadget_Close: End
        
        Case #Gadget_ChooseFile
          File.s = SaveFileRequester("Save File to...", GetGadgetText(#Gadget_SaveTo), "All Files|*.*", 0)
          If File <> "": SetGadgetText(#Gadget_SaveTo, File): EndIf
        Case #Gadget_Start
          Abort = #False
          DisableGadget(#Gadget_Start, #True)
          DisableGadget(#Gadget_Stop, #False)
          SetGadgetState(#Gadget_Progress, 0)
          ClearGadgetItemList(#Gadget_Status)
          Url = GetGadgetText(#Gadget_Url)
          SaveTo = GetGadgetText(#Gadget_SaveTo)
          MainWindow = WindowID(#DownloadWindow)
          CreateThread(@BackgroundDownload(), 0)
        Case #Gadget_Stop
          Abort = #True
      EndSelect
  EndSelect
ForEver
This code in my class preprocessor nice working THX.
Sorry my English, I'm Russian
AMT Laboratory
Post Reply