How to Traverse ABB’s Native Tag Name Properties

December 6, 2011

In the article about mapping the tag names to the GUID, I’ve provide some partial solution about how to get the properties of each tag name.

The drawback of this solution is that it requires working on PPA explorer and hex editor application. The explorer is used to activates certain faceplate and the editor will be used to view the resulting *.sub file.

Accessing the tag names properties proves to be rather elusive, because at first I thought I can first accessed it using the IAfwStructureServer interface described earlier. After utilizing some of the methods inside this interface, I realized that it is only acted as directory or structure browsing, just as DIR command on DOS command prompt.

The only differences between DIR command and ABB’s structure browsing is that in the later, it is not providing the leaf traversing, i.e. the objects below the directory structure.

Take T13UE059 tag name for example, by using opcguid.exe, it will retrieve the path of Root/Network 41/T10PS04/Extended Process Objects/MB300 Motor/T13UE059. But there are no way to retrieve leaf node below T13UE059.

It turns out that to retrieve leaf nodes below the end of directory object is by using the Object – Aspect concept in the ABB terminology, which means that T13UE059 is some kind of object and to access the leaf nodes, i.e. the properties, I have to gets the “Aspect” of this object.

But before I can access the aspect of the object, I have to get instance pointer to the object. This is done by calling GetObject method of IAfwObjectManager interface described earlier in my previous post. This method accepts the GUID of the object and gives interface pointer to IAfwObject. As you can see, the GUID is retrieved via OPCGUID.EXE utility.

Next, to traverse available aspects to the object, I can use enumeration class by calling EnumerateAspects method. It is by using this enumeration class that I can get access to aspect object through IAfwAspect.

Successful access to IAfwAspect is not enough, because there is still one more step to retrieve the properties. If you pay close attention to my post about tag name to GUID mapping, you can see that to access OPC value of certain tag names requires formatting the tag name in the form of A:B:C. (separated by colons).

The A part is object, B part is aspect, and the last, i.e. C part is the property.

To retrieve the C part, I can use QueryAspectInterface method inside retrieved aspect object (i.e. instance of IAfwAspect object), by passing the IAfwSubscribeInfo. This method will gives IUnknown instance on one of its parameter, so I have to perform QueryInterface of unknown class to get a hand of actual instance IAfwSubscribeInfo class.

To get the number of available properties, I can use SubPropCount of this class (i.e. IAfwSubscribeInfo), and for each loop of the element count, I can use either GetNameOfId or GetPropertyInfo method to list out each complete properties for the object.

I’ve create the utility called OPCPATH.EXE to provide working prototype using the above concept, so that now I can view properties of any tag names without resorting to PPA explorer and clumsy hex editor :)

How to Traverse ABB’s Aspect Directory Structure

November 25, 2011

The utility that I’ve explained in the previous article (i.e. the OPCGUID.EXE) has some limitations because it just provides indication whether the supplied query returns the appropriate objects. The prerequisite is that I have to know at least partial information about the objects I sought for.

So here, I will explain some methods to perform basic function to traverse (i.e. to list out) available objects inside the Aspect Directory Structure.

The basic architecture of this kind of task is accomplished by server and client model, i.e. the client perform the request and the servers provides the feedback or response to the client.

When I activates the opcguid.exe utility to get the object ids of *t13* for example, my utility actually send the request to the available servers on ABB PPA’s framework. You can refer to the previous articles about some of these servers.

To handle the task of finding the GUID of the names, or vice versa, is carried out by AfwSNSrv.Exe server that gets activated when the ABB’s services is running. In this case, the client will send request command to this server using socket API and receive the response and show the result to the screen.

OPCGUID utility relies on methods provided in IAfwTranslate to perform its task, but there are no methods to perform traversing or browsing of the tree structure.

This task is actually resides in IAfwStructureServer inteface. You can peform QueryInterface method to IAfwObjectManager object to obtain pointer to instance of this interface.

There are some basic function to perform some traversal such as ListDescendants, but one method that will provide comprehensive information of the structure is obtained via GetBrowser method.

The GetBrowser methods will give the IUnknown interface of the object. The actual browser object is obtained by calling QueryInterface to this unknown interface object to get IAfwBrowsingSupport.

Now before this object can be useful, you have to provides some initialization information to AfwSNSrv.exe so that the server acknowledges and can carry out the browsing task.

To perform the initialization, you have to call SetEnvironment by providing the relevant information in the parameters to the server. The functional prototype of this method is :

SetEnvironment([in] long countNameCats, [in] AfwAspectCategoryId* nameCategoryIds, [in] long useWeakBinding, [in] IAfwBrowsingCallback* callback);

The first parameter is the number of name category id that get passed to the method, the second parameter is the actual id, the third one is some unknown internal flag, the fourth is used when you require a callback of what happens to the server. The third and fourth parameter can be ignored by setting them to null value.

If you forget to call the above method, you will get 0x8ABB0A25 error code, which is an E_AFW_SNS_NO_BROWSER each time you call one of the traversal methods.

The name category is actually the file in the format of {GUID}.dat that contains all the names for the object id. Usually the GUID of this file is {9879EB4B-87FA-11D1-B1E0-0060B017F346}. Depending on the setup information, this file resides in Categories folder. In my case, it is precisely in C:\OperateITData1\AspDir\Categories folder, with {9879EB4B-87FA-11D1-B1E0-0060B017F346}.dat as its file name.

So for the second parameter, you can pass this GUID to let this method returns appropriate name of each object. Actually you can pass any other of the GUID inside the directory, but of course when the server can’t find the name by looking up into this file, it just returns null value for name pointer string. As it is implies in the countNameCats, you can pass an array of this id for name look up.

After this, you are free to call any relevant method in this interface, for example GetRoots or GetSubtree.

Oh, and don’t forget to call the ClearEnvironment method after you’ve finish the browsing session to let the server to free some allocated resources :)

Low Level View of ABB PPA’s Object Structure

November 18, 2011

This articles serves as the sequel to my previous one about opcguid utility. In essence, the purpose of this utility is to show the GUID of arbitrary names. This GUID can be used for various purposes such as finding the internal names of certain tag id on OPC server.

The utility will return 0×80004005 when performing the exact search and it can find none on existing ABB PPA’s objects directory :

In the above case, there is no exact match for “t13″ name in the directory.

This utility also accepts wildcard search such as :

In this case, the utility will just show the GUID of the first element that matches the wildcard criteria (i.e. *T13*).

But here comes the question as to what’s the object name that correlates to the above GUID of {8B44BCD6-8D6A-4B4D-8F78-3BDD652EECA8}.

There are two kinds of approach to answer the above question, i.e. by utilitizing some ABB internal methods, if any, or perform direct search to the existing object structure. I will now explain the second approach.

The GUID can easily be located using some file searching utilities such as winhex and the above GUID actually resides in many of the object structure files located in C:\OperateITData1\AspDir\Categories, depending on the ABB PPA’ s data directory setup.

Inside the “Categories” directory is the collection of files that contains the object description about each category in the form of {GUID}.dat.

For example, the file {9879EB4B-87FA-11D1-B1E0-0060B017F346}.dat contains collection of names that fulfill the wildcard criteria for *T13*.

The mapping between *.dat file inside Categories directory and its description is located inside ObjMgr.dat. This file resides in C:\OperateITData1\Temp\AspectDirectory. For the above *.dat file the description is “Name” :

You can find the description of any guid of *.dat inside category directory inside ObjMgr.Dat.

Now let’s view the {9879EB4B-87FA-11D1-B1E0-0060B017F346}.dat for the corresponding of the above GUID :

You can see that the answer of the above question should be “T13 Sample” :)

Identifying ABB PPA’s Service ID Modules

October 24, 2011

In this occasion, I will explain certain aspects about ABB PPA’s (Process Portal A) services that is routinely used by many of its client. Especially the mapping of the service id to their consecutive executable files.

This information is important for troubleshooting the connection problem between ABB client and its server. Usually this problem manifested in the form of the client application appears to be hang, i.e. not responding for indefinite of time.

I’ve came across this symptom when I try to execute my prototype small command prompt application that utilizes the OPC features of ABB PPA. In this case, my application, when activated, seems to be waiting for something in-definitely, and after perform some debugging, it appears to be waiting for some connection object, using the socket modules.

By performing the checking of network status using netstat -a -n -b, it reveals that the application waits to receive some data from port 4224 and 2870 :

The AFWDSO~1.EXE (AFWDsOPCSurrogate.exe) is actually a server stub that gets activated when the client is using the OPC data access methods.

Then, realizing that I haven’t yet activates the server services using ABB Service Manager SCM, I started it and re-run my application. But it is still hangs. Re-checking it using the above netstat command reveals the same port that the client waits for receiving data.

This raises my question as to what kind of server services that the client actually waits, and before long, I’ve found out that the port information is obtain via a call to GetNetworkIdAddresses of AfwSMAddrHdlr.exe module.

This method, in turn perform access of port information through the registry, precisely in HKLMEY_LOCAL_MACHINE\SOFTWARE\ABB\Sigma\Systems\ key path.

Below the Systems node, there are lists of GUID of installed system (called the context) and the current active system is recorded in the WPDefaultContext sub key.

Inside each context guid (i.e. the guids of each system) there are lists of available services and its related parameter. So, in my case, the port 4224 belongs to services with id of {2089E12E-10BC-11D2-8635-0060B0CEAB60}, whereas the port information is located as the substring of port* sub keys, which the asteriks denotes port index. The port 4224 belongs to port index 0 (zero).

But, what kind of the executable module that links to this service id ? To answer this question, I have to verify each of the server services that gets activated when I starts the ABB Service Manager.

When the each of the server services initializes, it calls the ServiceDoInit of IAfwServiceOperations to register the identification data of the service, including the service id, and in the above case, the above id happens to be the property of AfwAppLogSrv.exe server service.

By examining each call to ServiceDoInit method for each service, I can now built the list that correlates the service id and its associated executable module as follows :

AfwAppLogSrv.exe
{2089E12E-10BC-11D2-8635-0060B0CEAB60}

AfwAspDirSrv.exe
{DCC91B1A-BDBB-11D1-B723-0000F878B945}

AfwSNSrv.exe
{E2596CE0-A233-11D1-8979-006097052D4A}

AdvAELogger.exe
{CA00D8A1-6B42-4CEB-96DD-D5CA55770B3C}

AdvAeSoftAlarms.exe
{DAA630D5-A3A3-47EC-BC6D-137B5FC50BE2}

ADVAESRV.EXE
{DFC44E50-B38B-11D1-9717-0060B05C5608}

AdvEAEngine.exe
{77BB3626-E13D-4463-8224-6BE517B45FAF}

AfwFsdSrv.exe
{D7851870-820B-11D2-8E8C-000000000000}

AfwSysMsgSrv.exe
{581ECD70-FC4B-11D1-BEA0-0060085C8452}

AfwTimeSrv.exe
{2F3A558C-E681-11D2-857D-0060B0CEAB60}

AfwXRefSrv.exe
{D15AEC48-2579-11D3-9672-0008C73F2527}

I can now use this list as an important cross referencing next time when connection problem occurs again and also to solve other related problems :)

Parameter Passing Between VB and VC++’s ActiveX DLL

October 6, 2011

In one of the tasks of my project, I have to create an ActiveX DLL using VC++ MFC Framework to provide some data to VB application. More specifically, one of the methods inside the ActiveX DLL should returned an array of VB’ s double type.

At first, I try using some rudimentary samples I got from either the MSDN documentation using the SAFEARRAY data type. After I already performing some necessary coding requirements, to test the sanity of the passed parameter from VB, I just returned one of the acquired properties of the supposed SAFEARRAY data type, such as the dimension :

long myDataCtrl::myArrayTest(SAFEARRAY *saTest)
{
return saTest->cDims;
}

And inside the VB test application on the click event, I just created some small codings just to show the returned value :

Dim aVar() As Double
Dim nResult As Long
Dim oTest As Object
Set oTest = CreateObject(“myControl.1″)
nResult = oTest.myArrayTest(aVar)
MsgBox nResult

If everything is according to scenario, the returned value should be of valid dimension such as 1. But in this case, the returned value is 24581 or hex 0×6005. This value is actually a variant type enumeration that consists of VT_BYREF | VT_ARRAY | VT_R8, which reads “parameter passed by reference, that’s consists of array of variable of double numeric type”.

Certainly this is not a SAFEARRAY type as expected. I think because at my current development environment the DISP_FUNCTION_ID dispatch mapping do not supports the array type for their argument list.

So, that leaves me only the VARIANT type as the only choice for parameter passing. So, again, I perform some parameter type change to the variant type :

long myDataCtrl::myArrayTest(VARIANT *saTest)
{
return saTest->vt;
}

And indeed, the returned value is still 0×6005. I can now continue to access the passed safe array from VB by resizing the dimension and assigned the necessary values to the caller (i.e. VB application). But first, let’s test it whether this conjecture is true, by checking the passed dimension information :

pMySafeArray = saTest->parray;
return pMySafeArray->cDims;

It returns the zero, since I haven’t decide the dimension yet in the VB declaration for the variable, because it will be decided inside the called method. So far so good, now the next step is try to resize the array dimension :

hr = SafeArrayAllocDescriptor(1, &saTest->parray);
return saTest->parray->cDims;

This time, Inside called method (ActiveX DLL) the dimension can be changed, but it is not reflected in the VB application. Scratching my head, I think this is because I haven’t yet revise the argument type list inside DISP_FUNCTION_ID from VTS_VARIANT which is passed by value, to VTS_PVARIANT, to be passed by reference.

But by changing it to VTS_PVARIANT causes the VB application breaks down by showing the “Type Mismatch” error.

What happened here ? Well, I supposed because at the time when VTS_PVARIANT is declared, the ActiveX DLL tries to return the VARIANT type, whereas VB application expects an array type of the data, hence the “Type Mismatch” error.

So, I have a deadlock situation here, the parameter can be passed in, but it is trapped, can’t go out. In order to resolve this situation, how about I just declared the Variant type in VB application :

Dim aVar As Variant

And, leaving the VTS_PVARIANT, I activates the application, calls the method, which just returned the variable type. I am happy because this time it works !

Next, I perform the re-run of the above procedure, and this time, the modifications inside the method is now reflected in the calling VB application :)

How to Fix DCOM Access Denied Error

October 4, 2011

I’ve recently facing some persistent problem regarding the implementation of the DCOM architecture of one of my applications.

I already have the prototype that runs successfully when placed on the same machine, and I think it is only a matter of time before I can successfully placed the same application so that it can activates certain method remotely by using DCOM methodology.

For doing this, I’ve performed necessary modifications to the existing prototype and adding the authentication information so that it can be passed to CoCreateInstanceEx COM function and execute the method remotely from client machine.

I never thought that by only moving it to remote machine will involved vastly different aspect and different settings in order that this configuration can be worked out.

I’ve solving each of the symptom one by one, leaving just one symptom that persistently defies any configuration methods I’ve found either from surfing the web, or from official documentation.

This persistent symptom, is that, I can successfully create the remote DCOM object but I got 0×80070005 or Access Denied Error when try to call any one of the methods for the created object.

After exhausting any of the conventional methods, it is time to exert more focus on this one.

Upon performing more detailed examination on the server side, it is found that the client error is caused by the returned error code of 0x8009030c or SEC_E_LOGON_DENIED from AcceptSecurityContext API function of secur32.dll.

This happens inside the AcceptThirdLeg method rpcrt4.dll’s security context class. I remembered that this scenario involved the NTLM authentication process that requires three times passing through of communication between the client and server, hence the term of Third Leg implies.

In this scenario, the client is supposed to provide the authentication information using InitializeSecurityContext API call. This is indeed happens again inside the client’s rpcrt4.dll’s third leg failed initialization.

As you can see from the official documentation of the API, the client is supposed to send the credential data to the server for identity verification.

This raises the question of how the RPC infrastructure knows the credential since I haven’t changed one, so I supposed that because the credential information is not supplied, the RPC infrastructure obtained it from existing active or logged-on ID on the client (which is my ID) to be sent to the server.

This would certainly causes the Access Denied Error because my server has different ID assigned to activate the DCOM objects.

So, the solutions proves to be rather simple, i.e. how to assigned different credential to the created interface proxy from the CoCreateInstanceEx that is accepted on server side.

And it is only a matter of time before I found that there is one function that is neatly fit to the picture, i.e. the CoSetProxyBlanket API function. This function can assigned the correct credential for each interface proxy so that each method call that’s related to this object can be executed successfully without generating 0×80070005 error.

By adding this function, I can move on to another task that’s related to this project :)

Tag Name to ABB’s GUID Mapping Utility

September 22, 2011

I’ve recently came across the task that’s related to DCS (Distributed Control System). Specifically, the task is to develop a web application that directly perform access to DCS’s parameter values.

The existing DCS connects to the machine that still runs on Windows 2000 – until now :) , and with the interface applications from ABB called the PPA System (Process Portal A).

There is already a standard interfaces that perform this kind of task called the OPC DA. So, after performing necessary studies of the related documents of the OPC DA, I am ready to create at first the prototype to test the possibility of accomplishing the given task.

This prototype is necessary it is to ascertain whether existing PPA in that machines supports OPC DA. If it isn’t then I can move on to another more promising tasks :)

At first, I’ve successfully created a the tag name browser using IOPCBrowseServerAddressSpace interface. This is a standard OPC DA interface that’s should be supported by any DCS interface applications.

So far so good, I’ve now have the complete lists of the tag names of the existing system.

It is time to move on to the next step, i.e. to determine the properties of each of the tag name.

Again, I created another small application. This time, after creation’s of ABB’s DA Object, I will first utilize the IOPCItemProperties that supposed to get the all available properties of the given tag name.

This is done by calling the QueryInterface of the existing IOPCBrowseServerAddressSpace to get IOPCItemProperties and after that, calls the QueryAvailableProperties method to list out the available properties.

Later on this properties subsequently used to access the value of DCS parameters, or so I thought :)

I was thinking of another steps of the task when activating the above application, and it returned error code 0×80004001, which means that this method is not implemented.

So, I perform QueryInterface using another object, but with the same result. It will successfully returns the object pointer, but when I try to call it’s QueryAvailableProperties method, it returns the above show stopper error. This continues on until I’ve exhausted all conceivable easier ways.

This is strange, I thought that every OPC DA compliance application should supports all the required interfaces in the OPC DA framework. So, I now have to realize that I have to perform a more difficult and time consuming endeavour regarding this task, i.e. life is never meant to be easier :)

By performing more detailed runtime code checks using the windbg, I’ve known that the ABB PPA System’s implementation of OPC DA is accomplished using AfwDsOPCSurrogate.exe by utilizing Microsoft’s proxy stub mechanism.

And I’ve realized, why I exhausted all options, because, after stepping through the supposed QueryAvailableProperties method, the applications just happily returns 0×80004001 to my application. Just on line of moving 0×80004001 to eax register and execute ret opcode.

There must be some other methods to access the properties to get the value, precisely the ABB’s specific way.

After spending several days of poking around of existing PPA System, I’ve found that the properties for tag name of interest resides in the cache directory when I activates related windows called a “Faceplate” in PPA’s Workplace viewer.

This faceplate will creates the subscription file for a group of objects in the *.sub extention file. The object group is called an Aspect in ABB terminology.

The *.sub files contains all of the properties required to access each DCS value. This is arranged in the form of OLE Structured Storage, so it can be viewed using standard OLE document viewer, but each properties is arranged in the form of streams that has an internal format recognizes only by ABB’s PPA System.

Although each streams of unknown format, but each properties is in the form of clear wide char text which is fully readable by human :)

By viewing this file, I can see that to access certain values of DCS System, I have to pass the Item Names in the form of [ItemName]:X:Y to AddItem Method of IOPCServer.

The [ItemName] is the Tag Name, and X and Y is the properties in question, for example T13UE059:Control Connection:Real_Res.

In ABB’s PPA System, the tag name is transformed to the GUID for example AD11B3EE-FE78-4E75-86AF-BFAA936A46B1.

Later on, this ID is used to construct the *.sub name in the form of _{AD11B3EE-FE78-4E75-86AF-BFAA936A46B1}.sub in ABB’s cache directory.

After this fact is known, one of the task is to perform the mapping of the tag name to the internal ID of ABB’s Object Structure. The idea is, after the ID is obtained, this can be used to search for existing *.sub file for this ID, and I can get the properties of interest in a couple of seconds.

By examining the mechanism of how ABB PPA System handles the AddItem method, I’ve found that the method to perform this task (i.e. of mapping the tag name to its ID) is inside GetStructureNodeArray method of AfwObjMgr.DLL. This methods is part of IAfwTranslate and has the functional prototype as below :

GetStructureNodeArray([in] long count, [in] BSTR* pQuerypaths, [out] HRESULT** pResults, [out] AfwStructureNode** pStructureNodes)

Two of the parameter of interest is pQuerypaths, this is the tag name to be mapped to ID and the output parameter is pStructureNodes in the form of GUID.

So, by passing for example the T13UE059, this method will returns for example AD11B3EE-FE78-4E75-86AF-BFAA936A46B1. This ID can be used as the name of *.sub file to find for properties of interest.

By utilizing this method, I can now create command line utility that accepts tag name as it’s parameter and returns the ID of tag names of interest. From this ID, I can now lookup into the generated *.sub file to find the property of interest to get DCS parameter values.

Here is the screen-shot of the utility in action :

How to View SAP’s ABAP Source Code

September 17, 2011

When starting to learn the SAP ABAP programming, I’ve found that there are extremely lacks of real life example of ABAP program that’s can be used as a perfect starting point of how the ABAP application works.

At the start of any session, when the SAP GUI client tries to connect to the R/3 System, the server always presented the very familiar login screen to get and validates user’s authentication information.

So, I think the most perfect sample ABAP program to be learned should be this authenticaton or login screen, because every elements of the programming aspects should be represented by this program.

The importance of understanding this program is more emphasized because it will link to another modules that constructs the building blocks to accomplish many tasks after the login phase is successfully carried out, i.e. what kind of modules that gets activated after each successful login, how to perform compilation and loading customized programs etc.

By understanding the inner working of this program, I believe I will have the robust foundation to move on to another aspects of ABAP programming.

By examining the DIAG stream and the extensive trace log information at R/3 side of the application, I came to know, that when the user connects to R/3 server for the first time, the server will load and submits the SAPMSYST ABAP program to the client.

It is by the virtue of this program that the user will be represented by all too familiar SAP Logon Screen :

After knowing the program name, it’s time to log-on to the ABAP Editor and view this module. But when I try to display the source of ‘SAPMSYST’ :

The editor fails to show it, instead, it shows “Operation terminated” :

Whereas I can view any other elements such as the screen, etc, except the program that is the starting point information. Without the program, I will only get an incomplete picture of how the login mechanism works.

So, I am sifting through the trace log files and eventually found that the ABAP source code repository is located in D010S table and I can view for any program except SAPMSYST.

So, it would be nice if I can replace the view-able program, for example the existing S_AUTHORITY_CHECK with the SAPMSYST program. Then I should be able to view the SAPMSYST programs using S_AUTHORITY_CHECK as its surrogate.

After performing the tedious task of replacing the source code in the BLOCK binary data field of D010S as described above, I am ready to start again.

The plot thickens because, the editor still shows the “Operation terminated” message.

So, I decided to examine the R/3′s disp_work.exe that performs the source code access. And by the help of trace file, before long, I found that after accessing the BLOCK field, the server calls CsDecompr routine to perform decompression of the source code.

By performing the break point just after decompression is done, I’ve noticed the curious string of decompressed SAPMSYST program :

Whereas any other program do not have one. So, using the memory write, after the decompression process, I try to replace the this string with series of spaces (0×20) and this time no “Operation terminated” and I can now view the SAPMSYST program :

GUI Components of SAP Front End

August 5, 2011

If you examine the standard installation of SAP Front End, you will notice that there are three types of themes that govern the appearance of GUI of the application. These are Enjoy, StreamLine and High Contrast.

While there should be a menu in the SAP GUI itself to change theme type, the actual information about the loaded theme is located in the registry. Its located in the registry key path HKEY_CURRENT_USER\Software\SAP\General\Appearance\Themes\SystemDefault.

Access to the application resources such as icons and bitmap is accomplished also using the registry. By examining the registry key path of each theme sub key, you can see that SAP GUI utilized the file called theme.xml. This file exists in each of the theme folder below SAP GUI application.

SAP Front End application accessed theme.xml file by reading the HKEY_CURRENT_USER\Software\SAP\General\Appearance\Themes\Enjoy\path sub key in the registry.

Inside the file is where most of GUI components gets drawed to the window canvas. Most of the drawing is carried out by using Windows’ GDI BitBlt function to perform the block transfer of selected areas in the master bitmap file that contains all the GUI resources to client’s screen.

I will use the Enjoy theme as an example to describe how SAP GUI perform the GUI drawing. Let’s start by the top left location in SAP Front End :

This icon is located in the sapfront.bmp of the Enjoy theme folder. The coordinate information for this icon is obtained using BtnIconE node below Caption node in theme.xml as follows :

The top and left information corresponds to the x and y coordinate location inside the sapfront.bmp file. The top and left information is for starting corner, bottom and right is for ending corner. Using this information, the SAP GUI will mark the start and end corner of the loaded bitmap and perform BitBlt to appropriate screen location.

Next, for the background color for the above caption bar (i.e. the color below the BtnIconE icon), is obtained using “Bgnd” node, also below the Caption node :

The top right logo :

Is accessed using “AnimationLo” node :

The main menu caption font color :

Is accessed using “PalIndex” sub node of “CoNormalFont” node. This node is part of “Color” node, which eventually below “MainMenu” node :

The PalIndex denotes the pallete index (starting from zero) and the actual RGB color is obtained from the pallete information inside sapfront.bmp file in each theme folder. In the above sample, the index value of 59 will corresponds to RGB 255,204,51 for Enjoy theme. If this node is not specified, the color of main menu text will become RGB 156,165,198 which corresponds to PalIndex 229.

The font type of client area labels :

Is determine using “Var” node below “Fonts” node. This node is located just below the “Root” node :

You can also see that the content of entry field :

Can also be set using “Fix” node inside the “Fonts” node above. For all of the theme, its usually using the “Arial Monospaced for SAP” for entry fields font.

What about the color of the font ? The color of the entry field font and the label is configured using the palette index 0 inside the sapfront.bmp file. To edit the palette information of sapfront.bmp, you can use the graphic manipulation such as Paintshop pro or other application that has palette editing capabilities.

The leading line, border and background color of the edit box :

or combo box :

can be customized using the “edit” node :

The PalIndex of “CoNormalFill” node governs the background color of editbox and combobox . The default value for this color is at pallete index 255 or white color.

The “CoBorderLine” is used to determine the color of leading lines or border for the editbox.

The color and appearance of application bar area (i.e. the one that has buttons on it) :

is determined using “Applbar” node. For example, to set the background color of this area, you can change the pallete index of “CoBackgroundFill” node below “Color” node :

The background of client area, i.e. the color inside red rectangle marker :

Unfortunately, there is no way to change the background color of this area using the xml node. But this value is actually resides in the registry. I’ve already describe the method to change the active theme at the start of this article.

Each active theme, for example the Enjoy theme, has several sub themes, and it is inside this sub themes that the SAP GUI application obtained the color code for the above background color.

Let’s take Enjoy theme as an example. This theme has several sub themes such as Blooming Desert, Bright Complementary, etc :

The information about active or selected sub theme is recorded in ActiveColorSetting value of the theme node :

In order to get the background color, based on the value of active color setting, the SAP GUI perform access to strPaletteMain value. For example, when the active color setting is Blooming Desert, the application will get the strPaletteMain value using this keypath :

HKCU\Software\SAP\General\Appearance\Themes\Enjoy\Blooming Desert

The color code for background color is located at the last element of strPaletteMain string :

So, for the Blooming Desert sub theme, the colorref rgb color code for background will be 0x0098cbcb, or RGB value of 203,203,152.

The highlight color (i.e. inside red rectangle marker) for field that currently activated / on focus :

Again, there is no xml information that can be used to customize this color, but you can do it by changing palette index 97 of sapfront.bmp file.

Well, I think that’s all for now. Happy customizing :)

Dissecting SAP DIAG Protocol – Server Side Processing

May 26, 2011

In the previous post, I’ve already explained that the DIAG packet consists of three parts called dp, th and data.

Let’s examine again the the first DIAG packet that is sent by SAP GUI client to SAP R/3 server by respective parts :

dp part (length = 200 dec = 0xC8 hex) :

th part (length = 8 dec = 0×8 hex) :

data part (length = 54 dec = 0×36 hex) – the top row is offset information :

Here I will describe how this DIAG packet get processed on the server.

If you perform the list out of active tasks in the SAP R/3 server, you will find more than one active disp+work.exe. One of the disp+work.exe acts as the dispatcher, and the other is the worker processes, depending on the request type in DIAG packet.

The communication or synchronization solution between the dispatcher and worker is using the event object.

The dp part is first received at DpRGetLogin, but the task of this routine is just to initialize the th and data packet, then it is moved to shared memory to be processed by corresponding worker process.

The first dp part from SAP GUI is actually not used, but transformed using DpRTmPrepReq into a proper login request :

Here is the dump of the above dp part (taken from dev_disp log file) :

-IN– sender_id REMOTE_TERMINAL tid 1 wp_ca_blk 0 wp_id -1
-IN– action SEND_TO_WP uid 2 appc_ca_blk -1 type DIA
-IN– new_stat NO_CHANGE mode 0 len 62 rq_id 11
-IN– req_info LOGIN GRAPHIC_TM

You can refer to my previous post to locate the exact offset positions for the above dump data.

Next, the request is pushed into the queue by DpRqPutIntoQueue.

The routine DpRqServiceQueue that servicing the queue, perform checks to the integrity of request before passed to DpRqNormalHandle. Here the request is used to initialize _wp_adm shared structure. For request data (i.e. dp part), the dispatcher and worker process communicates using the _wp_adm structure.

As for the payload which consists of th and data part remains the same. After the queue information is done, the dispatcher then signals one of the event object to activate the assigned worker process.

On worker process side, after being signaled by dispatcher, it enters the ThRqAccept routine, which calls ThCheckReqInfo. The dispatched request (dp part) is accessed via _wp_adm shared structure using wp_id as its index, and the th and data part is accessed via a call to DpCaGetWpPtr.

At this point, the worker process has the dp part information in the wp_adm structure, and returned pointer from DpCaGetWpPtr for th and data part.

Returned pointer (i.e. pointer to th and data stream) of is moved to fields of ztta structure (via zttaptr variable). Because there is no documentation regarding this fields information, I can only stated that memory address of th and data stream is recorded at offset 0×4 and for information of its length is moved to offset 0x0A.

Using the two undocumented fields of ztta structure, the data part (excluding the th part) is then moved to MsgInputBuffer and MsgInputBufferLength respectively. It is by using this two variables that the first incoming data is processed by DiagSetGuiConnectData for processing the data part.

But first, let’s see how the server process the th part.

Offset 0×1 and 0×4 of th part is the login overhead data. This fields is dependent on request info from wp_adm structure. For example, when req_info is 0×09 (a combination of LOGIN and GRAPHIC_TM), overhead is set to INI state. this is evident from the log information generated by ThCheckReqInfo routine :

LOGIN: set INI in Overhead

The LOGIN request info is obtained from the field inside wp_tm structure.

Here is several important values for offset 0×1 and 0×4 for th part together with it’s definition for overhead :

0×10 and 0×00 INI for LOGIN request
0×02 and 0×00 EOC for LOGOFF request
0×01 and 0×00 EOS for CANCELMODE Request

Let’s move on to the data part.

When the trace flag has sufficient level, the dump of data part is executed by DiagTraceStream with typical log string at dev_w* trace file as follows (the asterisk denotes the wp_id, starting from zero) :

D APPL ST_USER CONNECT len 12
D
D APPL ST_USER SUPPORTDATA len 32

From this routine, we know that the first byte of data (0×10) is the record type called APPL, and the first DIAG packet from SAP GUI contains connect and support bit data records. So it has two records.

For first record, byte at offset 0×01 (value = 0×04) is interpreted as ST_USER, byte at offset 0×02 (value = 0×02) is CONNECT. Offset 0×03 apparently unused. The length of data record is at offset location 0×04 (value = 0x0C). The length count is not including the first five bytes, it’s just for the data record only.

For second record, byte at offset 0×01 (value = 0×04) is interpreted as ST_USER, byte at offset 0×02 (value = 0x0B) is SUPPORTDATA. The length of this record is 0×20 (32 dec).

We can see that the data part structure is relatively simple and each detail data communication session can be monitored using trace flags and the dev_w* log file in SAP R/3 server.

Using this design, the data part can have as many records as required, and also can process any data with variable lengths.


Follow

Get every new post delivered to your Inbox.