No announcement yet.

Can PowerBasic 9 Create ActiveX Controls (.OCX)?

  • Filter
  • Time
  • Show
Clear All
new posts

  • Dominic Mitchell
    The DllGetClassObject() ... is (I believe???) only called one time where as the
    class factories are called for every instantiated object, I suppose.
    No. To see why this statement is not right, compare the documentation for CoCreateInstance and
    CoGetClassObject. As you read the docs, keep in mind that CoCreateInstance calls CoGetClassObject.

    You can use either CoCreateInstance or CoGetClassObject to create an instance of a control.
    CoCreateInstance involves less work by the programmer but comes at a price.

                                                                                                    pointer to
    CoCreateInstance -> CoGetClassObject -> DllGetClassObject -> IClassFactory:::CreateInstance ->  interface 
                      /|\                                                                       /|\ on object
                                             Hidden from programmer
    This is fine if you are creating only one control but not very efficient when creating many controls with the
    same CLSID at the same time. Let's say we want to create ten controls with the same CLSID. If CoCreateInstance
    is used, DllGetClassObject will be called ten times. If CoGetClassObject is used, DllGetClassObject will be
    called only once. That is because once we have a pointer to the IClassFactory interface, the CreateInstance
    method can be called as many times as we like to instantiate a control of the same class.

    Leave a comment:

  • Fred Harris

    Routed through? What do you mean by that? This is trivial to implement because... On second thought, I will let you figure it out.
    Dominic isn't being mean! He knows I like to solve these things like puzzels. Anyway, to answer Dominic's question all I meant was that the purported PB ActiveX control would need to fully and satisfactorily implement ...


    ...because PowerBASIC fully implements the COM standards which will expect these interfaces to be present and working if PowerBASIC's 'With Events'
    functionality is to receive events from the control. With my limited knowledge at least I see no way of getting around this. This is the required infrastructure/internal plumbing through which both objects (ActiveX control/client app) exchange pointers to each other's interfaces.

    Thanks for setting me straight on the RegisterClass() thing Dominic. I hadn't 'mused' hard enough on it. The DllGetClassObject() would be the place to do it since that is (I believe???) only called one time where as the class factories are called for every instantiated object, I suppose.

    The whole issue of windowed controls vs. windowless controls has me a bit confused. Where I ran into the issue is when I created the simple Atl tutorial project I mentioned above. If anyone is interested in persuing this topic as I am, and you have Visual Studio 6 and the MSDN docs that came with it, that tutorial is called Polygon and is at...

    MSDN Library Visual Studio 6.0\Visual C++ Documentation\Reference\Microsoft Foundation Class Library And Templates\Active Template Library\Atl Tutorial
    If you follow that tutorial you can create a functioning visual ActiveX control in about an hour. I wouldn't even say you need to know C++ to do it - lots of screenshots and wizards. The control is a geometric colored shape within a circle. There are properties to change the number of sides of the shape from 3 (a triangle) to a hundred or something. You can also set the color. When you click within the polygon the control fires an event to the client's sink. If you click outside the shape but within the enclosing circle, the control fires an event to that effect. When you build the project the control is automatically registered for you.

    I had no trouble instantiating this control in Visual Basic or PowerBASIC. Below is the PowerBASIC source for a program that will instantiate the control.

    #Compile            Exe
    #Include            ""
    #Include Once       ""
    Global pPolyCtl     As IPolyCtl
    Global pPolyEvents  As IPolyCtlEventsImpl
    Type WndEventArgs
      wParam            As Long
      lParam            As Long
      hWnd              As Dword
      hInst             As Dword
    End Type
    Interface IPolyCtl Guid$("{873F0176-C037-4613-9A8F-A0BCD2EF5B2E}") : Inherit IDispatch
      Property Set FillColor <-510> (Byval Dword)  ' IID = {873F0176-C037-4613-9A8F-A0BCD2EF5B2E}
      Property Get FillColor <-510> () As Dword    ' IPolyCtl Interface
      Property Get Sides <1> () As Integer         ' Attributes = 4160 [&H1040] [Dual] [Dispatchable]
      Property Set Sides <1> (Byval Integer)       ' Inherited interface = IDispatch
    End Interface
    Class CIPolyCtlEvents Guid$("{C22266B9-EFB0-45D6-98D8-066734F7BA0B}") As Event
      Interface IPolyCtlEventsImpl Guid$("{FF786299-5C0A-4D47-B753-44C8892838DC}") As Event
        Inherit IDispatch                                    ' Class CIPolyCtlEvents
        Method ClickIn<1>(Byval x As Long, Byval y As Long)  ' Interface name = _IPolyCtlEvents
          MsgBox("You Clicked In The Colored Triangle")      ' IID = {FF786299-5C0A-4D47-B753-44C8892838DC}
        End Method                                           ' _IPolyCtlEvents Interface
        Method ClickOut<2>(Byval x As Long, Byval y As Long) ' Attributes = 4096 [&H1000] [Dispatchable]
          MsgBox("You Clicked Outside The Colored Triangle") ' Code generated by the TypeLib Browser 4.0.11 (c) 2008 by José Roca
        End Method                                           ' Date: 09 Nov 2008   Time: 18:02:43
      End Interface
    End Class
    Function fnWndProc_OnCreate(Wea As WndEventArgs) As Long
      Local pStream,ppUnkContainer As IUnknown
      Local oPolyCtl As Dispatch
      Local strCtrl As String
      Local vVnt As Variant
      Local hCtrl As Long
      Call AtlAxWinInit()
      strCtrl=UCode$("Polygon.PolyCtl")   'Program ID
      pPolyEvents=Class "CIPolyCtlEvents"
      If IsObject(pPolyCtl) Then
         oPolyCtl=pPolyCtl         'Probably QueryInterfaces() For IID_IDISPATCH
         vVnt=5                    :  Object Let oPolyCtl.Sides=vVnt
         vVnt=RGB(&H255,&H0,&H0)   :  Object Let oPolyCtl.FillColor=vVnt
         Events From pPolyCtl         Call pPolyEvents
         Set oPolyCtl=Nothing
      End If
    End Function
    Function fnWndProc_OnDestroy(Wea As WndEventArgs) As Long
      If IsObject(pPolyCtl) And IsObject(pPolyEvents) Then
         Events End pPolyEvents
         Set pPolyEvents=Nothing : Set pPolyCtl=Nothing
      End If
      Call PostQuitMessage(0)
    End Function
    Function fnWndProc(ByVal hWnd As Long,ByVal wMsg As Long,ByVal wParam As Long,ByVal lParam As Long) As Long
      Local Wea As WndEventArgs
      Select Case As Long wMsg
        Case %WM_CREATE
          Wea.hWnd=hWnd : Wea.wParam=wParam : Wea.lParam=lParam
          Exit Function
        Case %WM_DESTROY
          Call PostQuitMessage(0)
          Exit Function
      End Select
    End Function
    Function WinMain(ByVal hIns As Long,ByVal hPrev As Long,ByVal lpCL As Asciiz Ptr,ByVal iShow As Long) As Long
      Local winclass As WndClassEx
      Local szAppName As Asciiz*16
      Local Msg As tagMsg
      Local hWnd As Dword
      szAppName="PolyCtrl"                                    : winclass.cbSize=SizeOf(winclass) Or %CS_VREDRAW               : winclass.lpfnWndProc=CodePtr(fnWndProc)
      winclass.cbClsExtra=0                                   : winclass.cbWndExtra=0
      winclass.hInstance=hIns                                 : winclass.hIcon=LoadIcon(%NULL, ByVal %IDI_APPLICATION)
      winclass.hCursor=LoadCursor(%NULL, ByVal %IDC_ARROW)    : winclass.hbrBackground=%COLOR_BTNFACE+1
      winclass.lpszMenuName=%NULL                             : winclass.lpszClassName=VarPtr(szAppName)
      Call RegisterClassEx(winclass)
      hWnd=CreateWindow(szAppName,"PolyCtrl",%WS_OVERLAPPEDWINDOW Xor %WS_MAXIMIZEBOX,350,300,425,325,0,0,hIns,ByVal 0)
      Call ShowWindow(hWnd,iShow)
      While GetMessage(Msg,%NULL,0,0)
        TranslateMessage Msg
        DispatchMessage Msg
    End Function
    What's a bit interesting about the above code and relates I think to your comment about windowed vs. windowless controls is in the WM_CREATE message handler where I instantiate the control with...


    The 2nd parameter is the handle of the window which the control will be attached to. I couldn't get the control to work if for a container I used a static control. I had to use the main form as the parent. If my WM_CREATE looked like this it would create the control within a window on the main window, but I couldn't get the events to work...

    Function fnWndProc_OnCreate(Wea As WndEventArgs) As Long
      Local pStream,ppUnkContainer,ppUnkControl As IUnknown
      Local lpCreateStruct As CREATESTRUCT Ptr
      Local hCtl,hContainer As Dword
      Local hCtrl,iNull As Long
      Local dwPtr As DWord Ptr
      Local blnReturn As Long
      Local strCtrl As String
      Local fp As Integer
      Open "Output.txt" For Output As #fp
      lpCreateStruct=Wea.lParam   'Can use GetModuleHandle() here instead, or, use Global (Hate Globals!)
      [email protected]
      Call AtlAxWinInit()
      hContainer= _
      CreateWindowEx _
      ( _
        10,10,400,250,Wea.hWnd,%ID_CONTAINER,Wea.hInst,Byval %NULL _
      Print #fp,"@dwPtr = "@dwPtr
      pPolyEvents=Class "CIPolyCtlEvents"
      'Call AtlAxGetControl(hContainer,pPoly)    'neither works    'Global pPoly As IPolyCtl
      pPolyCtl=AtlAxGetDispatch(hContainer)      '-------------    'Global pPolyEvents As IPolyCtlEventsImpl
      If IsObject(pPolyCtl) Then
         MsgBox("pPolyCtl Is An Object")
         Events From pPolyCtl Call pPolyEvents
         MsgBox("pPolyCtl Ain't Nothin")
      End If
      Print #fp, "hContainer = " hContainer
      Print #fp,"@dwPtr = "@dwPtr
      Close #fp
    End Function
    So to make a long story short, the thing refused to work if a control on the main form was set as the host of the control. I believe the C++ ATL wizard code created a 'windowless' control. At this point, its just a guess for me.

    I'll include some additional information if anyone is interested. The above described ActiveX control is very simple. Its just a tutorial. But the wizard generated code clearly shows the interfaces that make up the guts of an ActiveX visual COM control. Although the project created many files (as MFC/ATL/C++ is famous for), the guts of the thing are in PolyCtl.h, and I'll post that code here shortly bellow. All the interfaces are listed in the class pretty much entertwined within pretty ugly macros and templates the workings of which I have not as yet figured out. But one can easily see there is some complexity involved. That being said, I fully expect for this particular turorial/object most of the implementation is probably nothing much more than 'stub' code. In that sense I wouldn't be to surprised if implementing it all in PowerBASIC wouldn't be that hard - as Dominic is saying. Anyway, here's the Atl C++ code. Here and there I made a few comments.

    // PolyCtl.h : Declaration of the CPolyCtl
    #ifndef __POLYCTL_H_
    #define __POLYCTL_H_
    #include <math.h>
    #include "resource.h"       // main symbols
    #include <atlctl.h>
    #include "PolygonCP.h"
    // CPolyCtl
    class ATL_NO_VTABLE CPolyCtl :                       //these 19 objects below are publically inherited template classes
     public CComObjectRootEx<CComSingleThreadModel>,
     public CStockPropImpl<CPolyCtl, IPolyCtl, &IID_IPolyCtl, &LIBID_POLYGONLib>,
     public CComControl<CPolyCtl>,
     public IPersistStreamInitImpl<CPolyCtl>,
     public IOleControlImpl<CPolyCtl>,
     public IOleObjectImpl<CPolyCtl>,
     public IOleInPlaceActiveObjectImpl<CPolyCtl>,
     public IViewObjectExImpl<CPolyCtl>,
     public IOleInPlaceObjectWindowlessImpl<CPolyCtl>,
     public ISupportErrorInfo,
     public IConnectionPointContainerImpl<CPolyCtl>,
     public IPersistStorageImpl<CPolyCtl>,
     public ISpecifyPropertyPagesImpl<CPolyCtl>,
     public IQuickActivateImpl<CPolyCtl>,
     public IDataObjectImpl<CPolyCtl>,
     public IProvideClassInfo2Impl<&CLSID_PolyCtl, &DIID__IPolyCtlEvents, &LIBID_POLYGONLib>,
     public IPropertyNotifySinkCP<CPolyCtl>,
     public CComCoClass<CPolyCtl, &CLSID_PolyCtl>,
     public CProxy_IPolyCtlEvents< CPolyCtl >
     CPolyCtl()  //Constructor
      m_nSides = 3;
      m_clrFillColor = RGB(0, 0xFF, 0);
     DECLARE_REGISTRY_RESOURCEID(IDR_POLYCTL)             //these are all the interfaces ActiveX
     DECLARE_PROTECT_FINAL_CONSTRUCT()                    //controls typically implement.  I expect
     BEGIN_COM_MAP(CPolyCtl)                              //for this trivial control many of them
       COM_INTERFACE_ENTRY(IPolyCtl)                      //amount to little more than stubs
       COM_INTERFACE_ENTRY2(IOleWindow, IOleInPlaceObjectWindowless)
       COM_INTERFACE_ENTRY2(IPersist, IPersistStreamInit)
       PROP_DATA_ENTRY("_cx",, VT_UI4)
       PROP_DATA_ENTRY("_cy",, VT_UI4)
       PROP_ENTRY("FillColor", DISPID_FILLCOLOR, CLSID_StockColorPage)
       // Example entries
       // PROP_ENTRY("Property Description", dispid, clsid)
       // PROP_PAGE(CLSID_StockColorPage)
     // Handler prototypes:
     //  LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);
     //  LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);
     //  LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
     // ISupportsErrorInfo
     STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid)
      static const IID* arr[] = { &IID_IPolyCtl,};
      for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
          if(InlineIsEqualGUID(*arr[i], riid))
             return S_OK;
      return S_FALSE;
     // IViewObjectEx
     // IPolyCtl
     STDMETHOD(get_Sides)(/*[out, retval]*/ short *pVal);
     STDMETHOD(put_Sides)(/*[in]*/ short newVal);
     void CalcPoints(const RECT& rc);
      RECT& rc = *(RECT*)di.prcBounds;
      HDC hdc  = di.hdcDraw;
      COLORREF    colFore;
      HBRUSH      hOldBrush, hBrush;
      HPEN        hOldPen, hPen;
      // Translate m_colFore into a COLORREF type
      OleTranslateColor(m_clrFillColor, NULL, &colFore);
      // Create and select the colors to draw the circle
      hPen = (HPEN)GetStockObject(BLACK_PEN);
      hOldPen = (HPEN)SelectObject(hdc, hPen);
      hBrush = (HBRUSH)GetStockObject(WHITE_BRUSH);
      hOldBrush = (HBRUSH)SelectObject(hdc, hBrush);
      Ellipse(hdc, rc.left,, rc.right, rc.bottom);
      // Create and select the brush that will be used to fill the polygon
      hBrush    = CreateSolidBrush(colFore);
      SelectObject(hdc, hBrush);
      Polygon(hdc, &m_arrPoint[0], m_nSides);
      // Select back the old pen and brush and delete the brush we created
      SelectObject(hdc, hOldPen);
      SelectObject(hdc, hOldBrush);
      return S_OK;
     OLE_COLOR m_clrFillColor;
     short m_nSides;
     POINT m_arrPoint[100];
     LRESULT OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
      HRGN hRgn;
      WORD xPos = LOWORD(lParam);  // horizontal position of cursor
      WORD yPos = HIWORD(lParam);  // vertical position of cursor
      // Create a region from our list of points
      hRgn = CreatePolygonRgn(&m_arrPoint[0], m_nSides, WINDING);
      // If the clicked point is in our polygon then fire the ClickIn
      //  event otherwise we fire the ClickOut event
      if(PtInRegion(hRgn, xPos, yPos))
         Fire_ClickIn(xPos, yPos);
         Fire_ClickOut(xPos, yPos);
      // Delete the region that we created
      return 0;
    #endif //__POLYCTL_H_
    Towards the bottom can be seen an OnDraw() and OnLButtonDown() message handler that contain nothing more than good ol' sdk api code.

    Just occurred to me that PowerBASIC has already implemented the Connection Point interfaces, so somehow or other one would just need to tie into them. All that leaves then are those other interfaces!

    Finally, it wouldn't bother me if PowerBASIC never implemented the ability to create ActiveX control. What they did do is make the ability to use already existing ones quite easy. That satisfies me.
    Last edited by Fred Harris; 17 Dec 2008, 01:19 PM. Reason: add line

    Leave a comment:

  • Steven Pringels 3
    The short answer: No

    The long answer, with some tricks one may create some kind of ActiveX control
    however one should then be able to access the events fired by the ActiveX which at this point I don't see how that could be accomplished.

    Now, I may just overlook a lot of stuff and therefore don't take my word for it but I would think if this great compiler would be able to do this, there would be
    some kind of example code in the samples directory which I didn't find.

    To be 100% certain, just drop an email at the support department of PowerBasic. They will be able to answer all of your questions regarding this


    Leave a comment:

  • Dominic Mitchell
    In musing about this, if one were to wish to create a visual control, then the control would
    need a class registered somewhere, and I would think one of the class factory interfaces would
    be the place to do this.
    No, you don't want to put it there, that is not the purpose of a class factory.

    There are two types of visual ActiveX controls, windowed and windowless. The windowed form is
    your old fashioned control wrapped in COM.

    An ActiveX control exports the following functions:

    The window class for a windowed control is registered in DllGetClassObject.
    For example,
    FUNCTION DllGetClassObject ALIAS "DllGetClassObject" _
      ( _
      rclsid  AS GUID, _
      riid    AS GUID, _
      ppv     AS DWORD _
      LOCAL hr      AS LONG
      ppv = %NULL
      SELECT CASE rclsid
          IF g_pClassFactoryTree = %NULL THEN
            g_pClassFactoryTree = treeIClassFactory_Create()
          END IF
          IF g_pClassFactoryTree = %NULL THEN
            EXIT FUNCTION
          END IF
          hr = treeIClassFactory_QueryInterface(g_pClassFactoryTree, riid, ppv)
          treeIClassFactory_Release g_pClassFactoryTree
          InitTree2D 0
        CASE ELSE
      FUNCTION = hr
    The InitTree2D in the code above registers the window class.
    The COM function, CoGetClassObject, calls DllGetClassObject when an ActiveX control is instantiated.

    In terms of messages from a window procedure, they would need to be routed through the Connection
    Point mechanism to be picked up in the client. Lots of issues, to be sure. Not a trivial undertaking.
    Routed through? What do you mean by that? This is trivial to implement because... On second thought,
    I will let you figure it out.
    Last edited by Dominic Mitchell; 16 Dec 2008, 11:19 PM.

    Leave a comment:

  • Wayne Worthington
    Fred nailed my intent almost correctly when he stated "market them to other developers", but slightly incorrect for the part "using scripting languages for web development". I am creating an SDK for developers to manage a hardware device, in which one implementation was an ActiveX control that adds design persistence, and manages and removes callback complexities across different platforms and languages that support COM.

    I will probably go ahead and code an ActiveX DLL that accomplishes this purpose, but I was hoping to be able to show up the C++ developer [who is about to get a new assignment!] -- and reuse the COM DLL code if Fred's endeavor to reverse-engineer proves successful!

    Leave a comment:

  • Fred Harris
    Yes, you are right Kev. I was interpreting Wayne's question in terms of whether its humanly possible to do it - not whether its a practical possibility. And in my mind anyway I don't see any use for creating ActiveX controls unless one would be doing it to market them to other developers using scripting languages for web development. If all you want is a visual control in a dll, then an ordinary custom control seems to be about a thousand times more practical - and generally faster than an IDispatch implementation in an ActiveX control.

    Leave a comment:

  • Kev Peel
    I was talking about native support - you can't just compile an ActiveX DLL, rename to OCX and add it to VB's control palette. There are rules that need to be followed. My answer to the OP is that PowerBASIC does not currently have native support for property sheets or design-time functions, therefor does not create ActiveX controls. A comparison can be made with PB7, before v7.00 was released you could call ActiveX libraries but it was a lot of work without shortcuts.

    Sure, you might be able to add it using native code, but then again you may not
    Last edited by Kev Peel; 16 Dec 2008, 01:08 PM.

    Leave a comment:

  • Fred Harris
    Hi Kev,

    Nonetheless, I tend to think it can be done. PowerBASIC is certainly internally implementing a number of base COM interfaces such as IUnknown, IClassFactory, etc. Pointers to all these should be obtainable, I would think. The rest of the interfaces usually associated with ActiveX controls would have to be hand wired into the already existing PowerBASIC infrastructure.

    In musing about this, if one were to wish to create a visual control, then the control would need a class registered somewhere, and I would think one of the class factory interfaces would be the place to do this. However, this is internally implemented by PB, so it would have to be handled elsewhere.

    In terms of messages from a window procedure, they would need to be routed through the Connection Point mechanism to be picked up in the client. Lots of issues, to be sure. Not a trivial undertaking. That's why I'm taking the approach I am, that is, start with a C++ created ActiveX control for which I have the code, and reverse engineer the thing so to speak to see what makes it tick. Then implement it in PB.

    Leave a comment:

  • Kev Peel
    You won't be able to create a visual control (aka OCX) as PB has no native support for property sheets or containers to be used with the target design environment. Whilst it's true ActiveX objects can be under any extension (DLL or OCX is most common), OCX usually denotes the library is an ActiveX control.

    You can still create an ActiveX DLL and code the object calls by hand in VB, VC++, etc.

    Leave a comment:

  • Fred Harris
    Perhaps the forum administrator will want to move it there.

    Another thought Wayne. At...

    is a very simple C++ tutorial on creating an ActiveX control with Microsoft's Active Template Library ( ATL ). I built the control myself (its mostly wizards) as it only takes an hour or so to go through it. I was able to very easily get the control working in both PowerBASIC 9 and VB6. My present intention is to reverse engineer the wizard generated code to figure out what is going on. Its a major undertaking for me and will take some time that I imagine very few folks would want to do themselves. But I like figuring things out.

    Leave a comment:

  • Wayne Worthington
    Apology for wrong Forum

    I apologize in advance for placing this question on the wrong forum. I didn't notice the Programming with Objects forum until after my post...

    Leave a comment:

  • Fred Harris
    far as I know

    Yes, as far as I know it can. I've been studying up on it for the past six months, and give me another three months or so and I think I'll have it! There are several folks here who know how to do it actually (Jose, Dominic, etc). There are about fifteen or so interfaces involved that most ActiveX controls implement, and you can go to the MS documentation to learn about them (good luck). Perhaps someone more knowlegable than I can correct me on this, but I don't believe the file extension, i.e., ocx, dll, exe, matters. What matters are the implementations of the interfaces.

    In terms of ActiveX exe servers, I believe there is a problem there. You have the situation of having to handle the out of process remote procedure call RPC situation. In C/C++ midl takes care of that, but I don't believe that was implemented in PB9.
    Last edited by Fred Harris; 16 Dec 2008, 11:18 AM.

    Leave a comment:

  • Can PowerBasic 9 Create ActiveX Controls (.OCX)?

    I understand that Powerbasic 9 can create ActiveX (COM) .DLLs, but does anybody know if PowerBasic 9 is capable of creating an ActiveX (.OCX) Control? I have seen several posts on how PowerBasic can use ActiveX Controls, but no references to creating them.

    If so, could sombody post sample source code that demonstrates the concept?

    Thanks in advance.