Stephen Smith's Blog

Musings on Machine Learning…

Launching Non-SDK Programs From the Desktop

with 23 comments


launch

Introduction

When SDK applications are launched from the Desktop, they are passed an object handle that they can use to create a session that exactly matches the session of the desktop that launched them. Further the desktop can manage these programs and for instance put up an error message if you try to close the Desktop while they are still running.

Quite a few people create programs that aren’t written using our SDK, but still are tightly integrated to Sage 300 ERP via one of our APIs such as the COM API, .Net API or Java API. You can add arbitrary EXE programs to the Desktop as icons and launch them just like any other screen.

When we designed the current UI framework, we had the intention that our UIs could be run from many places, such as VBA macros or hosted inside Internet Explorer. We also envisioned them being strung together in workflow type applications. Towards this we created the Session Manager and the Signon Manager to help tie together programs running inside the desktop with programs running outside the desktop. For information on using the Session Manager, check out this blog posting.

Generally this has worked quite well. Especially if you only signon to one company, then all the various things running will share the same session and you won’t have to signon to everything separately. However there are a few limitations to our current approach. If there are two desktops running (usually signed on to different companies) then when the external program is run, it has to present a dialog to choose which company’s session to use. This makes sense if you say start an EXE program from the start menu, since how would it know which desktop session to use? But when you run from the desktop you would expect it to just use that desktop’s session, rather than being treated like it wasn’t run from the desktop. Similarly if you started an EXE program from the desktop you would expect the Desktop to prevent closing until this program is closed, right now this check is only for SDK programs run from the desktop and doesn’t apply to anything else.

A lot of people are creating standalone EXE’s that use our APIs and they are using the Session Manager, which generally works well, but would like to get the other cases handled as nicely. So for the upcoming Sage 300 ERP 2014 release we have added some support to the Desktop to help with this. To basically allow non-SDK programs to start with the same protocol as the SDK programs so they can behave in the same manner. The alpha (developer) early release of Sage 300 ERP 2014 is available to ISVs so you can try this out now.

New Macro Substitution

If you add a program to a groupfile or if you add a program via file new in the desktop, if you specify $objecthandle$ as an argument, then this will be translated into an object handle when you are run that you can use in other API calls to get a session that matches your desktop. For instance in a group file:

[ PROGRAM ]
ID = "XX0500"
PARENT = "XX0000"
DESCRIPTION = "test ojbect key"
CMDLINE = "c:\\accpac6\\sm\\testobj\\testobj.exe $objecthandle$"
RSC = "XXSDK"

 

This is then the token you can use to create your session exactly from the Desktop that ran you.

If you aren’t an SDK program, you probably don’t have your own group file. However you can use a couple of SDK tools to add your item to an existing group file. The program unccgrp.exe (usually installed in c:\pluswdev\bin) will de-compile a group file (these are usually grp.dat files in a programs language folder, like ar62a\eng\grp.dat). Then you can add your own entries to this and then use ccgrp.exe to re-compile the group file. This is a bit of a kludge because it may overwritten by Product Updates and two people trying to do this at once may collide and interfere with each other. But it can be an effective and useful technique.

How to Use the Object Handle

You can then pass the object handle into a session init call and your session will be configured to match the desktop you are run from. Additionally if you want to register your windows handle, you can use the roto api to do so given the object handle. You should also clear this when you terminate.

Below is a VB program which does these things. It uses the session it gets to display the company name in a label. This way it will be connected to the right Desktop and the Desktop knows when it’s running.

Private Declare Sub rotoSetObjectWindow Lib "a4wroto.dll" (ByVal objectHandle As Long, ByVal hWnd As Long)
Dim strObjectHandle As String

Private Sub Command1_Click()
    Unload Me
    '
    ' clear our window handle when closing so we don't block the desktop closing
    '
    rotoSetObjectWindow Val(strObjectHandle), 0
End Sub

Private Sub Form_Load()
    Dim mSession As New AccpacCOMAPI.AccpacSession

    '
    ' Get the object handle from the command line
    '
    strObjectHandle = Command$

    MsgBox "Object Handle = " + strObjectHandle

    '
    ' Use the object handle to intialize the session. With this you will inherit
    ' an open session matching the desktop that launched you.
    '
    mSession.Init strObjectHandle, "XY", "XY1000", "62A"

    '
    ' Set the window handle so the desktop can track whether you have closed
    ' effectively doing a roto openok.
    '
    rotoSetObjectWindow Val(strObjectHandle), Me.hWnd

    Dim mDBLinkCmpRW As AccpacCOMAPI.AccpacDBLink
    Set mDBLinkCmpRW = mSession.OpenDBLink(DBLINK_COMPANY, DBLINK_FLG_READWRITE)

    Dim CSCOM As AccpacCOMAPI.AccpacView
    Dim CSCOMFields As AccpacCOMAPI.AccpacViewFields
    mDBLinkCmpRW.OpenView "CS0001", CSCOM
    Set CSCOMFields = CSCOM.Fields

    CSCOM.Fetch
    Label1.Caption = CSCOMFields("CONAME").Value

End Sub
Advertisement

23 Responses

Subscribe to comments with RSS.

  1. […] Introduction When SDK applications are launched from the Desktop, they are passed an object handle that they can use to create a session that exactly matches the session of the desktop that launche…  […]

    • Should Desktop pass ObjectHandle to EXE program automatically when launching EXE as Program Short-cut on Desktop?

      This is not really an improvement, as it require access to ccgrp.exe and unccgrp.exe (SDK). However, if already using SDK (or as it said, SDK tools) you can simply add exe entry into ROTO instead of GROUP file and it already pass ObjectHandle to the program.

      Khanh Trinh

      July 7, 2013 at 4:44 am

      • Perhaps I shouldn’t have included the paragraph on unccgrp/ccgrp since the intent here is to do it without the SDK. Ie use File New to add and EXE file to the desktop.

        smist08

        July 7, 2013 at 3:04 pm

  2. Yes. The Macro substitution section make me understand I need to alter grp.dat to make it work. I hope desktop will add ObjectHandle to the last end of command line arguments, since almost current program that has arguments won’t need to be changed if they don’t want to take this advantage.

    Khanh Trinh

    July 8, 2013 at 2:13 pm

  3. […] Launching Non-SDK Programs From the Desktop […]

  4. Hi Stephen, I used unccgrp for grp.dat under CP70A\ENG and without changing anything. I used ccgrp and produced the grp.dat file. Copied the new grp.dat file to the CP70A\ENG folder. After doing this, the ‘Generate EFT File’ icon is giving an error: ‘Program file problem. Object is not defined in the Roto event file.’. Is this a known issue ? Is there a work-around? Thanks

    Emy

    March 5, 2014 at 8:04 pm

    • All I can think is that you inadvertently edited the entry for CP2700 somehow. Perhaps repair CP70A and try again?

      smist08

      March 5, 2014 at 8:10 pm

      • Thanks! That’s it. For some reasons, unccgrp changed CP2700 to CP8332. But, when I changed it to CP2700, it worked.

        Emy

        March 5, 2014 at 10:07 pm

  5. Hi Stephen, I tried to use the $objecthandle$ following your instructions above, but, it’s not working for me. What is supposed to be the value expected for $objecthandle$ ? I get the value “Object Handle = $ObjectHandle$” from the message box, which seemed to be just the string value “$ObjectHandle$”. After that, I can’t inititate the Accpac session with “Runtime error ‘-217467259 (80004005)’: 1.2071.18”. Any ideas?
    Thanks

    Emy

    April 17, 2014 at 4:18 pm

    • Try all lower case for objecthandle, also you need to be running Sage 300 ERP 2014 and the program in the group file must be an EXE.

      smist08

      April 21, 2014 at 7:15 pm

      • Hi Stephen,
        I’m facing the same problem, tried all lower case for objecthandle but it still not worked. Do u have any ideas?
        Thanks

        LukeLe

        October 21, 2014 at 11:22 am

  6. Hi Stephen. Great article. I’m writing a .net app that integrates with Sage 300 ERP so this was very helpful in getting a session that I could use. One question. Is there any way to load a form (if sage 300 is open) from a non-sdk application? In other words, my application is creating a payment batch and I want the user to be able to click on a link or button inside of my app that will open up the payment batch that was created so they can save a few steps. Is that possible? I couldn’t find anything while searching but I may not be searching for the right keywords 🙂 Thanks Stephen.

    Brad Edwards

    May 22, 2014 at 2:20 pm

    • There is a hidden property ObjectHandle for each OCX that if you set the object handle talked about in the article, will hopefully do the job for you.

      smist08

      July 24, 2014 at 3:58 am

  7. So sorry to post to an old thread, but can’t find any answers after hours of searching. When two or more companies have open desktops, rotoSetObjectWindow seems to always present a signon manager dialog to select which company, even though the I’ve verified that the $objecthandle$ value is unique to the desktop launching the custom non-SDK program. Any ideas what I’m doing wrong or leaving out? Sage300-2014 calling a VB6 program.

    Tim Brodie

    June 10, 2015 at 12:45 am

    • It sounds like you are using the session manager or signon manager. Perhaps try opening the session directly using this object handle.

      smist08

      June 10, 2015 at 3:58 am

      • Thanks for answering so quickly! I have the AccpacAP2100Ctrl control inside a VB6 form, as I am trying to add some additional field validation logic on the Vendor PO number that is specific to the business. On the form initialize event, I’ve coded the following:

        Private Sub Form_Initialize()
        Dim mSession As New AccpacCOMAPI.AccpacSession

        ‘ Get the object handle from the command line

        strObjectHandle = Command$

        MsgBox “Object Handle = ” + strObjectHandle

        ‘ Set the control object handle to use this handle

        JLAP2100frm.AccpacAP2100UICtrl1.objectHandle = strObjectHandle

        End Sub

        I think this is what you’ve suggested to do, but I still get a Signon Manager dialog listing the two open companies. Or have I misunderstood you?

        Thanks… Tim

        Tim Brodie

        June 10, 2015 at 9:04 pm

      • Hey Tim I believe I have a solution to your issue. Did you find a solution already?

        Jason

        July 31, 2015 at 5:29 pm

  8. Jason, no we took another approach given time constraints — yet I really would like to see what the solution would have been! Thanks

    Tim Brodie

    July 31, 2015 at 5:50 pm

    • Here’s generally what I did….

      ————————-
      ‘declare control to fire events
      Dim WithEvents oeRet As AccpacOE1600UICtrl

      Private Sub Form_Load()
      Dim myObjectHandle As String

      ‘add to the forms controls collection
      Me.Controls.Add “AccpacOE1600.AccpacOE1600UICtrl”, “mycontrol”
      ‘assign a reference to it so we can manipulate it
      Set oeRet = Me.Controls.Item(“mycontrol”)

      ‘get an object handle from the current session and set to the control
      myObjectHandle = session.CreateObjectHandle2(“OE1600”, “”)
      oeRet.objectHandle = myObjectHandle

      ‘Position the control on the screen
      With oeRet
      .Top = 0
      .Left = 0
      .Width = Me.Width
      .Height = Me.Height
      End With

      oeRet.Visible = True

      End Sub

      Jason

      August 5, 2015 at 6:41 pm

      • Hi Jason,

        I was trying your suggestion however, I get runtime error when oeRet.objectHandle line is executed. I get the below error message

        “The object doesn’t support this property or method”

        Sage 300 ERP 2014
        MS SQL Server 2008
        Desktop version
        Sample Company

        Let me know if you need further info.

        Thanks,

        spkrish

        October 20, 2017 at 3:18 am

  9. Hi Steve,

    Greetings for you

    I am not getting this fixed when multiple companies are open and I run this code I still get a window asking user to choose the company database and not the signon window though. how to overcome this issue?

    please give me a sample code as i am using VBControlExtender to add the AR Invoice UI

    Looking forward to your suggestion.

    Thanks,
    Krish

    here is my code…

    ‘ Code Starts ============================================

    Option Explicit

    Private WithEvents mAR2100 As VBControlExtender

    Private mSession As AccpacSession
    Private mDBLink As AccpacDBLink

    Private Declare Sub rotoSetObjectWindow Lib “a4wroto.dll” (ByVal objectHandle As Long, ByVal hWnd As Long)
    Private mstrObjectHandle As String

    Private Sub Form_Load()

    Debug.Print “Private Sub Form_Load()”

    mstrObjectHandle = Command$
    Set mSession = New AccpacSession
    mSession.Init mstrObjectHandle, “RT”, “RT1000”, “62A”

    rotoSetObjectWindow Val(mstrObjectHandle), Me.hWnd

    Set mAR2100 = Me.Controls.Add(“ACCPACAR2100.ACCPACAR2100UICtrl”, “mAR2100”)
    Me.Caption = “Customized A/R Invoice Entry” ‘mAR2100.object.uiname
    mAR2100.Visible = True

    End Sub

    Private Sub Form_Unload(Cancel As Integer)
    ‘ clear our window handle when closing so we don’t block the desktop closing

    rotoSetObjectWindow Val(mstrObjectHandle), 0
    End Sub

    Private Sub mAR2100_ObjectEvent(Info As EventInfo)

    Debug.Print “Private Sub mAR2100_ObjectEvent(Info As EventInfo)”

    Select Case Info.Name

    Case “OnUIAppClosed”

    Case “OnUIAppOpened”

    Me.Caption = mAR2100.object.CompanyID & ” – ” & “Customized A/R Invoice Entry” ‘mAR2100.object.uiname
    Me.Icon = mAR2100.object.uiicon
    Me.WindowState = vbNormal

    Me.Move (Screen.Width – Me.Width) / 2, (Screen.Height – Me.Height) / 2, mAR2100.Width + 120, mAR2100.Height + 820
    Me.Move (Screen.Width – Me.Width) / 2, (Screen.Height – Me.Height) / 2

    With mAR2100.object
    Set mSession = .UISession
    Set mDBLink = .UIDBLinks(1)
    End With ‘mAR2100.object

    Case “OnPopupOpened”

    End Select

    End Sub

    ‘ Code Ends ============================================

    spkrish

    October 20, 2017 at 3:14 am

  10. Hi,
    I have created seperate .net C# app and pass $ObjectHandle$ as argument.It is working but when i close the Sage external app is not logging out.Kindly help me on this

    Karthika

    February 22, 2020 at 8:19 am

    • It should logout when the session object is destroyed. You might try setting the variable to null or calling logout explicitly to force it to happen. Also check your process is really fully terminated (and doesn’t show up in the task list).

      smist08

      February 22, 2020 at 4:41 pm


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: