First off, are you on-premise or using the Cloud version of DocuWare?

Next, I assume by REST you mean using the Platform SDK?

If you are talking about the Platform Services, then yes, you can manipulate DocuWare in any tool that can automate the "MSXML2.ServerXMLHTTP" request object built into Windows (at least from Windows 7 onward, I think). Not sure if Delphi has a wrapper around that or if you'll just need to use COM automation. I have it working in Visual Foxpro, and my Delphi experience tells me that it should be at least as capable.

This was my first question on these forums about using non-.NET tools to integrate with the Platform SDK:

I didn't get much help. And when I put in a support request, I was told we had not paid for Platform SDK support (it is a whole other layer of support, apparently), so they could not help me.

The biggest issue was with authenticating. I could perform requests and get responses, but it always told me I was not authenticated, and authentication did not stay persistent. So I started messing around with POSTing credentials, just shots in the dark, really (but bnased on experience with using URL Integration). As you can see in the final sample further down, you need to pass in the Organization, Username, and Password credentials as part of the POST data when you run the "Logon" Platform resource. A report of my initial success can be found here:

I have refined the SDK request object code into something more streamlined and better commented. I also use a global instance for the connection so that subsequent calls to Platform Service operations do not burn a new connection/license. Here is what it looks like, and we have been using this for several months now in Visual Foxpro:

    * This routine returns an authenticated XMLHTTP request/response object to interact with
    * DocuWare's Platform Service (SDK). It also has output parameters to house error and connected
    * user information. RETURNS: An object if successful, otherwise NULL.
    * NOTES:
    * -- Windows authentication only appears to work on Windows 10. It does not work on Windows 7
    *     or on Windows Server 2012 boxes such as used for Terminal Services.
    * -- While the initial logon call provides for running a GET immediately (via a "returnUrl"
    *     parameter), that also does not work reliably on Windows 7 or Server 2012. So, this routine
    *     should be called to get an authenticated request object and then subsequent GETs and POSTs
    *     need to be run from the calling program.
    * -- This routine to directly create a Platform Service connection object should rarely be used.
    *    That's because every instance of a request object (even if using the same username from the 
    *    same workstation) uses up a license when created fron non-.NET based applications. What should
    *    be used instead is the DWInitPlatformService() procedure that establishes a Platform Service
    *    connection via a global variable, _DWPlatformService.
    LPARAMETERS plUseWindowsAuth, pcError, pcDBName, pcNetworkID
    * Our output variables should start out empty.
    pcError = ""
    pcDBName = ""
    pcNetworkID = ""
    LOCAL loRequest, lcLogonVerb, lcLoginURL, lcLoginData, loErr, lcResponse, lcUserInfoResource
    lcLoginData = "Organization=" + DW_ORG
    IF plUseWindowsAuth
        lcLogonVerb = "LogonNTLM"
        lcLogonVerb = "Logon"
        lcLoginData = lcLoginData + "&UserName=" + DW_USERNAME + "&Password=" + DW_PASSWORD
    lcLoginURL = DW_SDK_ENTRY_POINT + "/Account/" + lcLogonVerb
        loRequest = CREATEOBJECT("MSXML2.ServerXMLHTTP")
        IF plUseWindowsAuth
            loRequest.Open("POST", lcLoginURL)
            loRequest.Open("POST", lcLoginURL)
            loRequest.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
        lcResponse = loRequest.ResponseText
        IF ((LEFT(lcResponse, 6) == "<html>") AND (OCCURS("YOU ARE REDIRECTED", UPPER(lcResponse)) > 0))
            * Response is HTML, not XML, and there is a string inside that makes it look like we are not connected.
            pcError = "Authentication failed. Full response was:" + CHR(10) + CHR(10) + lcResponse
            loRequest = .NULL.
            * It looks like we are authenticated. Now gather connected user information to be the default
            * content of the connected SDK object.
            lcUserInfoResource = "/DocuWare/Platform/Organizations/" + DW_ORG_ID + "/UserInfo"
            loRequest.Open("GET", DW_PROTOCOL + "://" + DW_SERVER + lcUserInfoResource)
            SET PROCEDURE TO GlobalProc ADDITIVE
            IF ParseXML(loRequest.ResponseText, "_tempDWGetSDKRequestObject")
                pcDBName = DWGetAttrValueFromSDKResponseCursor("_tempDWGetSDKRequestObject", "/UserInfo/", "User", "DBName")
                pcNetworkID = DWGetAttrValueFromSDKResponseCursor("_tempDWGetSDKRequestObject", "/UserInfo/", "User", "NetworkID")
                pcError = "Connection made, but user information response is not valid. Response text is:" + CHR(10) + CHR(10) + loRequest.ResponseText
                loRequest = .NULL.
            USE IN SELECT("_tempDWGetSDKRequestObject")            
    CATCH TO loErr
        pcError = loErr.Message
        loRequest = .NULL.
    RETURN loRequest

Here is the DWInitPlatformService() routine, including some good comments on all I have learned about connecting in non-.NET environments:

PROCEDURE DWInitPlatformService
    * A few notes about connecting to the DocuWare Platform Service (SDK):
    *    -- When connecting to the DocuWare Platform Service from .NET applications using the same username
    *       on the same workstation, an existing connection/license will be used, if found.
    *    -- When connecting to the DocuWare Platform Service from NON-.NET applications using the same username
    *       on the same workstation, an NEW connection/license is created every time an SDK connection object
    *       is created. So, every Platform SDK operation (document download, indexing, etc.) uses up a license.
    *    -- The "Logoff" operation can be performed on a connection via the following code:
    *            _DWPlatformService.Open("POST", DW_LOGOFF_URL)
    *            _DWPlatformService.Send()
    *       The problem with this methodology is that even after successfully logging off, a license remains in
    *       use for two minutes from the time of log-off. It has been confirmed with DocuWare support that
    *       stuck with it.
    *    -- When making a standard connection to DocuWare, a license is allotted for 30 minutes from the time of login.
    *    -- Due to the two-minute period after a log-off where a license is still in use, if we do many Platform 
    *       Service operations within a two-minute span, we will still run out of licenses (even if we log off after
    *       each operation). That is why this global scheme has been developed for connecting to the DocuWare Platform Service.
    *    -- Through testing, it has been determined that an idle connection/license will log itself off after 10 minutes.
    *       Since a log-off has an additional period of two minutes built in, that means that an idle license will 
    *       expire after roughly 12 minutes.
    *    -- By calling this procedure, a global Platform Service connection (_DWPlatformService) is created that can
    *       then be used to consume Platform Service resources. The global instance will be created if it doesn't already
    *       exist or if it fails at running a simple Platform Service operation. If the existing instance works OK,
    *       then it can be used without re-creating it (which would consume an additional license).
    *    -- This global connection will release itself in approximately 12 minutes after last use.
    *    -- What we would ideally want is for a log-off operation to immediately release the connection (and license),
    *       but it does not sound like that is something that is going to happen.
    * What this all means is that this initialization routine should be used to create a global Platform Service connection
    * whenever possible, as opposed to creating a new SDK request object directly with DWGetSDKRequestObject().
    * *** NOTE ***: If you need to change a connection from being a Windows logon to the non-Windows login (or vice versa),
    * you will need to release the global instance (_DWPlatformService), otherwise this routine will try to use the existing
    * connection which may or may not be the logon you want to be using.

    LPARAMETERS plUseWindowsAuth, pcError, pcDBName, pcNetworkID
    LOCAL llCreate, lcUserInfoResource, lcDBName
    llCreate = .F.
    IF (VARTYPE(_DWPlatformService) == "O")
        * Global object exists, but we need to see if the connection is working.
        lcUserInfoResource = "/DocuWare/Platform/Organizations/" + DW_ORG_ID + "/UserInfo"
            _DWPlatformService.Open("GET", DW_PROTOCOL + "://" + DW_SERVER + lcUserInfoResource)
            IF ParseXML(_DWPlatformService.ResponseText, "_tempDWInitPlatformService")
                lcDBName = DWGetAttrValueFromSDKResponseCursor("_tempDWInitPlatformService", "/UserInfo/", "User", "DBName")
                llCreate = EMPTY(lcDBName)
                llCreate = .T.
            * An error occurred while trying to manipulate DW connection object. So we will need to re-create it.
            llCreate = .T.
        USE IN SELECT("_tempDWInitPlatformService")                    
        * Global connection object does not exist, so it will need to be created.
        llCreate = .T.
    IF llCreate
        RELEASE _DWPlatformService
        PUBLIC _DWPlatformService
        _DWPlatformService = DWGetSDKRequestObject(plUseWindowsAuth, @pcError, pcDBName, pcNetworkID)
    RETURN (NOT ISNULL(_DWPlatformService))

The constants in play (capitalized stuff) are:

#DEFINE DW_ORG                      <your org name>
#DEFINE DW_ORG_ID                <your org ID, probably "1" if you are on-premise>
#DEFINE DW_SERVER                <server name, either cloud or on-premise name>
#DEFINE DW_USERNAME                <a global username to use for Platform access>
#DEFINE DW_PASSWORD                <password for global username>
#DEFINE DW_USERNAME_URL_INTEGRATION        <we use a different username for URL Integration>
#DEFINE DW_PASSWORD_URL_INTEGRATION        <password for URL integration>
#DEFINE DW_DBUSERNAME                <we also access SQL Server directly at times>
#DEFINE DW_DBPASSWORD                <SQL Server password>
#DEFINE DW_SYSTEMDB                "dwsystem"
#DEFINE DW_DATADB                      "dwdata"
#DEFINE DW_LOGGINGDB              "dwlogging"
#DEFINE DW_PROTOCOL                "http"    <use "https" if you are using SSL>
#DEFINE DW_INTEGRATION_ENTRY_POINT        DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform/WebClient/" + DW_ORG_ID + "/Integration"
#DEFINE DW_INTEGRATION_ENTRY_POINT_NTLM        DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform/WebClient/NTLM/" + DW_ORG_ID + "/Integration"
#DEFINE DW_SDK_ENTRY_POINT            DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform"
#DEFINE DW_LOGOFF_URL                DW_PROTOCOL + "://" + DW_SERVER + "/DocuWare/Platform/Account/Logoff"

As you can tell from a couple of the above constants, we access the on-premise database server (MSSQL) quite a bit to gather data that either cannot be accessed by the Platform, or for quicker, easier access. I am sure Delphi can speak to SQL Server easily. We primarily do only readonly operations in this direct fashion because writing data to SQL Server without going through the Platform is not a good idea.

This scheme also uses global variables, something Visual Foxpro is pretty "fast and loose" with in terms of instantiatiojn and release. Not sure how Delphi handles such things, so your mileage may vary on how well you can determine whether to keep using an existing connection or create one anew. Just know that licenses can get eaten up really quickly if you create a lot of instances, and they don't release themself (even when idle) for at last 12 minutes. Even if you explicitly invoke a "Logoff" resource, the connection will remain in place (and license used up) for 2 minutes.

Between SQL Server queries and Platform SDK requests, we have all the following working in both .NET (C#) and non-.NET (Visual Foxpro) environments, currently:

  • Uploading documents, with indexes.
  • Downloading documents.
  • Changing indexes on an existing document.
  • Creating a data-only document (just the header information).
  • Querying a file cabinet, diretly (SQL Server), via the Platform SDK, and via URL Integration.
  • Manipulating "keyword" indices.

Note that before I got stuff working from Visual Foxpro, I wrote routines in C# (.NET) and made them externally callable via an executable. That works, too, though is obviously more of a kludge. I just use command-line parameters and create files that denote success or failure (I did not know how to create a COM object in C#, or just didn't have the time to figure it out). The .NET libraries are quite nice, so if you have someone who knows C#, that is always a fine route to go to perhaps move beyond Delphi (says the guy who runs a department still coding rigorously in Visual Foxpro).

Also be aware that manipulating date/time fields can be tricky, as all date/time values stored in as DocuWare system fields or indexes are normalized to Universal Time (UTC). So, you will want to be aware that you may need to massage timestamps to and from your local time when pulling and pushing such values. You must be especially aware of this if you are pullling date/time values directly from SQL Server, as in some (most?) cases the Platform will convert values back to local time for you. It is just something to always be aware of.

Hope this helps, and please do not hesitate to ask more questions. I have all kinds of sample code that I struggled mightily to get working.

Joe Kaufman