This page provides answers to basic support question for our products. There are answers to many general questions about DDE on the DDE FAQ page. Queries are answered as soon as possible, but we are a small company and there may not be anyone in the office. The questions below cover everything we have been asked...
In addition, a support group has been created on Yahoo Groups for customers using our products. Please visit it here.
Ordering and Licensing
DDClient and DDServer
How do I order and pay for the controls?
DDClient, DDServer and DataSend can still be purchased at reduced prices on the grounds that no technical support is available. Please e-mail us for further information.
How many licences do I need?
You need one per developer or web server. What you pay for is the use of the design time licence. This is required to incorporate the control into an application, or have a web server deliver the licence to a web browser.
There is no restriction whatsoever on distributing the controls themselves and registering them on as many machines as you wish.
I have clicked on the registry key files, but I still see the about box when the program starts.
You are still using the evaluation version of the control. You need to remove the evaluation control from your project and replace it with the full version, keeping the same name. The interface is identical.
Can I distribute DDClient and DDServer with the applications I have written?
Yes, the controls are freely distributable. What you must not distribute is the licence key, which is supplied for one developer to use or on one web server. Warning ! Always put the control on a form, even if you have to create an extra invisible one to contain it. For guidance on how to write your code with the control on a form click to Why does CreateObject fail when I run my program on another machine?.
How do I get licences for updated versions of the controls?
DDClient and DDServer implement all the capabilities of the DDEML interface. Any upgrades will be due to an incomplete implementation or bugs. It is our policy that you should not have to pay for such upgrades. The password protected licence files can be downloaded from http://www.rhaminisys.com/ddclic.html and http://www.rhaminisys.com/ddslic.html. The password was provided when you purchased the licence, or in an email sent to all registered customers.
How do I get the browser to run the controls from a web page?
We are not experts in this area so please don't ask us for further details. Web browsers can use the control through the OBJECT tag. It is downloaded (if necessary) and the browser asks the web server for the design time licence key. Without this key the browser is unable to create an instance of the control. It does not help to have the design time licence on the client machine.
Start by reading the Microsoft Knowledgebase article Q159923, HOWTO: Use Licensed ActiveX Controls in Internet Explorer. The lpk_tool.exe can be downloaded from the Microsoft site, but it is hard to find. Go to http://msdn.microsoft.com and in the panel on the left click on Downloads->Developer. It is in the section Component Development->ActiveX Controls. The tool is also supplied with the Professional and Enterprise versions of Visual Basic. It is not installed by setup, you must find it in a folder called Lpk_tool on the CDROM. This folder contains the executable and help file, simply copy them to your hard drive. The same version is provided with VB5 and VB6. The tool is simple to use, the help file and Knowledgebase article between them tell you everything you need to know.
The controls are licenced for a single developer. That developer may deploy the licence package generated by lpk_tool.exe on one web server.
How can my web server use the controls in constructing active pages?
We are not experts in this area so please don't ask us for further details. You can use the controls in VBScript, or any other scripting system which can use ActiveX controls. The web server must have a design time licence or an instance of the control cannot be created.
The controls are licenced for a single developer. That developer may make the necessary licence registry entry on one web server.
Do the full controls have limits like the evaluation versions?
No, none at all except total memory. All objects are held in Collections which can grow indefinitely. Arrays are not used anywhere in the controls.
Why does the second instance of DDClient or DDServer fail to work?
Each instance of DDEML requires a callback routine at a different address, because it has no parameter identifying the instance. But each instance of a control in a VB application shares the same code and therefore has the same callback address. It follows that an application may have only a single instance of DDClient or DDServer. This is not a programming limitation, because there is a simple way of doing everything you need with one instance. Put the controls on an invisible form and keep a global reference to it.
DDClient single instance management
There is no limit to the number of conversations an instance of DDClient can handle. Instead of having separate control instances, keep separate references to conversations created by the single instance of the DDClient control.
DDServer single instance management
There is no limit to the number of Services an instance of DDServer can handle. Each Service can have any number of Topics. Instead of using separate control instances, create as many Topic instances as you need, all belonging to the same globally accessible Service. You can also have multiple Services, although normally an application responds to only one service name.
Can I use the controls without putting them on a form?
Yes, if you go to the DDClient download area there are DDClient samples which you can download. Note that you must set a reference to the control OCX, not add it as a component, for CreateObject() to work. If you mistakenly include the OCX as a control you will probably have to remove it by editing it out of the project file. The line to remove starts "Object=". Lines specifying references start "Reference=". Warning ! CreateObject() fails unless the design time licence is present, do not use this technique in applications which are required to run on any machine except your development machine. For guidance on how to write your code with the control on a form see Why does CreateObject fail when I run my program on another machine?.
Why does CreateObject fail when I run my program on another machine?
CreateObject fails unless the design time licence is present, otherwise the licence requirement would be circumvented. The answer is to put DDClient or DDServer onto a form so that you do not need to use CreateObject. When you do this the licence information is compiled into the code.
Instead of using CreateObject in the code of a form, simply put the control anywhere on the form instead, it is never visible at run time.
If you wish to have the control in a class module there are two choices. The first is to rewrite the class as an invisible form. This is not hard because forms are just a special sort of class. You create an instance of the form by loading it. Alternatively, declare a variable whose type is your form and use the Set statement with New. You can then use properties and methods just like a normal class module.
Alternatively, you can create an invisible form just for the control, and change the class module code to use the instance of DDClient on the hidden form. The program WebSpy, which comes with complete source code, is an example of DDServer and DDClient on an invisible form.
Why does the program crash or give errors when it stops?
In short, because Uninitialize has not been called. DDE is messages being sent to windows. If the infrastructure supporting a window is not intact but the window still exists the program will probably crash if a message arrives. This situation occurs when a VB program is being terminated.
The help files emphasize that Uninitialise MUST be called before forms and class modules are terminated. Uninitialise destroys the windows which receive the DDE messages, preventing the attempted use of partly torn-down code. We have found that relying on the Class_Terminate event is not always reliable. It is usually satisfactory to call Uninitialise in the Form_Unload event of the form on which DDClient or DDServer is located .
Running in the IDE - A DDEML instance has a separate life to the switch between design and run mode. If you interrupt a program run or it crashes, the uninitialisation code is skipped. DDEML and the callback routine in the control are unaware that the program has stopped. This can cause GPFs and other problems, because the objects used by the callback routine no longer exist. It can also result in the control failing to initialise when the program is restarted and you may get 'object variable not set' when calling Connect. If you close the IDE and restart it the DDE control will work again.
To prevent these problems, always stop the run by closing the program in the way that it will be done when compiled into an executable. This should cause DDEML and the control to uninitialise properly. If that is impossible, have a single DDE shutdown procedure and call it from the Immediate window.
Why does my test program lock up or crash the computer?
DDE works by one application placing Windows messages on a queue, and another removing them. The applications are independent. If one of them posts messages faster than the other is removing them, the queue fills up. This is probably what your test programs are doing. Different versions of Windows react to this situation in different ways, for details go to the DDE FAQ page. To overcome the problem, you may be able to use a timer to throttle whichever program is the one overloading the queue.
The most likely cause is that the server is sending updates on an advise loop faster than the client can process them. If you can tolerate missing some updates, use the fAckReq flag when starting the Advise or Notify loop.
Why do I get errors on Disconnect?
The other party may disconnect your conversation, whether you are acting as server or client. The conversation object has a Disconnected property, which you should check. The control's reference is removed from the Collection when the other party disconnects, so you cannot access it any longer via its key, which is a Collection key. If you have kept a reference the conversation object will not be destroyed until you set your reference to Nothing.
The best way to handle this error is to trap it, because it is caused by the sequence of events and not a programming error. If you are not using your own reference to a Conversation object, the possibility of testing if it is already disconnected does not exist. You can catch the DDEDisconnect event in DDClient and DDServer if you need to know when the other party disconnects.
Private Function ddeDisconnect() As Boolean On Error GoTo Handler ddeDisconnect = False If DDClient.Disconnect(ConvKey) Then ddeDisconnect = True End If Handler: Exit Function
Why do I get unpredictable error 9, index out of bounds?
When the other party disconnects your conversation, the DDEDisconnect Event is raised to allow the program to react and not try to use the conversation again. If the disconnect is ignored, several errors can occur. The VB error 'Subscript out of range' will be raised if you try to use the conversation key to obtain a reference to the conversation. If you keep a reference to a disconnected conversation you may get other errors such as 'Object variable not set' when you invoke its methods.
This is not the only situation in which error 9 can be raised, see also other errors.
What about other VB errors?
When a DDClient or DDServer method returns a value, it reflects the value that was returned by the underlying call to DDEML where possible. It is a documented design choice that all VB errors are passed up to the calling application, genuine DDE errors are reported by return values.
Errors are usually caused by the program supplying an invalid parameter to a method, or calling the methods in the wrong order. Invalid parameters can be caused by either a programming error, or because the DDE state has altered in the meantime without the program being aware.
Disconnect requires several steps and may result in a VB error. The control must attempt to close down all the advise and request loops, and clean up any outstanding transactions, before attempting to close the conversation with DDEML. There is no guarantee that the DDE server is behaving correctly, and even if it is race conditions can occur, because DDE uses Windows messaging and is asynchronous.
All applications should follow good programming practice and make use of the On Error statement. The test programs included in the download do not, so you may see error boxes.
Do I have to close the log file and start another to change the options?
No you don't have to. The logging options can be changed at any time by setting the Options property.
Do DDClient and DDServer support multithreading?
No, and there are no plans to allow it. There can only be one instance of each control per executable, and DDEML is thread specific. The reason for the one control per executable limit is explained in Why does the second instance of DDClient or DDServer fail to work?
Why do I get 'object variable not set' when calling Connect?
This happens if DDClient has not been initialised successfully. The conversations collection has not been created so attempting to add to it fails. The previous call to Initialise has almost certainly returned False.
One possible reason for Initialise failing is attempting to use a second instance of the control. See Why does the second instance of DDClient or DDServer fail to initialise? Another possible cause is stopping the program in the IDE with the IDE's "Stop" feature, instead of allowing the program to close itself down. See Why do I get errors when I stop the program in the IDE?
You should always check the return value of methods and handle errors originating in the control with an 'On Error' statement.
I seem to be loosing data, or get back unexpected values
If you are using Asynchronous methods,
the Visual Basic program may be ignoring events fired by controls. This happens if you use the MsgBox routine built into VB. The documentation says that the default mode is application modal. However, the VB meaning of this term does not correspond to the normal Windows convention, which is that user input is disabled but other processing carries on as normal. In VB the program ignores all events.
To overcome this problem, you can either use a VB form or the Windows API MessageBox. The API call is simpler and the result from the users point of view is identical to MsgBox. For details go to How and Why to use the MessageBox API instead of MsgBox.
If you are using synchronous methods,
you may not be handling the situation in which your specified timeout is too short. For example,
Set datareturn = DDECL1.Conversations(convkey).Request(MyItemName, 1000)
If the data is not received within one second the request fails. However, the message from the server may arrive later. If you make another request, the delayed reply may be interpreted as belonging to the second request. This sort of situation is likely if the server is sending messages faster than the client can process them. Windows queues messages, they may be delayed according to how the operating system is scheduling the server, client and other tasks. There is a discussion of how the different versions of Windows behave on the DDE FAQ page. There are a number of measures you can take to increase the efficiency of your DDE client, see the topic How can I improve the performance of my DDE client?
Why does my synchronous DdeClientTransaction call return immediately or not at all?
Probably, the Timeout interval you have specified is not appropriate. Be careful to use only positive numbers as the timeout interval. Minus one is the value which signifies an asynchronous transaction, the call returns immediately. Other negative numbers will be interpreted as large positive numbers.
Why don't I get any advise data from Reuter's IDNDDE?
IDNDDE may send data on an advise loop before it has sent an acknowledgement to the client accepting the advise loop request. The client is not expecting data at that point so does not acknowledge it, stopping IDNDDE from sending any more data.
DDClient has a mechanism for handling this early advise data. The property HandleEarlyAdvises should be made True before starting any advise loops. After an advise loop has been accepted invoke the method ProcessEarlyAdvises. An Advise event will be raised for any data received out of the proper sequence.
How can I improve the performance of my DDE client?
In summary, by reducing the number of Windows DDE messages to the minimum and processing them as soon as possible.
First and foremost, if you are using synchronous calls such as Request, rewrite to use asynchronous transactions such as RequestA. When you make a synchronous request, the client process stops until either a response arrives, or the timeout expires. You will get much better performance if the client is completely event driven.
In outline, before making a request for a piece of data, advise loop etc., see if there is already an outstanding request for it. If so, do not make another request but wait for the first request to be answered. You could cancel the transaction and start another, but that will double the number of Windows messages. If you need multiple items of data and use Request(), each must wait for the previous one to succeed or time out. If you use RequestA() you can ask for them all at once, Windows will keep the messages in the queue and send them to the server when it is free. As each data item arrives a TransactionRequestCompleted event is raised. The data can come in any order depending on the server, your client must be programmed to allow for this.
Secondly, use Advise loops instead of Request loops if you need the data which has changed. A Request loop uses 3 Windows messages when the data changes (Notify, Request, Data), instead of only 1 with an Advise loop (Data). If you are not interested in the actual data, only the fact that it has changed, then a request loop is appropriate.
Why does Excel crash with the DDServer test program?
When you put a DDE formula in a cell, Excel asks the server for a number of data formats, starting with XLTable. Excel crashes if the server does not respond in a timely manner. The DDServer test program does not have the format XLTable available, so if you use the formula "=MyService|Test!Count" it puts up a message box asking if this format is to be provided. The delay in answering "No" causes Excel to crash.
The solution is to modify the test application so that it automatically rejects any data format that is not built in. You can either remove the event procedures for DDENewDataRequested and DDENewAdviseRequested or make them return without doing anything. This causes DDServer to reject the requests for unavailable data formats immediately. Excel does eventually ask for CF_TEXT which the test program can provide. With this modification, Excel receives data and updates without problems, including a runaway count.
Excel sets up a hot link (advise loop) with the DDE server. It uses the fAckReq flag to stop more data being sent until the last has been acknowledged, which makes sense for a spreadsheet. There is information about fAckReq in the topic The missing data and unreliability problems.
Why does the shell give errors when I use the evaluation DDServer in the ExecTest sample?
The ExecTest demonstration application uses the full DDServer control. If you use the evaluation version instead, you will find that it can open files with an already running instance of an application.
However, the shell displays an error if you try to open a document when the application is not already running. The reason is that when the evaluation control is created, it shows its about box which you must click away. This prevents the shell from establishing a DDE conversation with an application immediately after it has started it. You may see the About box of DDServer appear briefly. If the application is already running the about box has gone, so the shell can connect immediately.
You can test this response by replacing the full control with the evaluation one in the ExecTest project. Alternatively, you can use the evaluation control in your own program, copying code from the ExecTest project if you wish.
Adding Items to a Topic gets slower after a few hundred. What can I do about it?
The increasing time required is taken in adding the item to a growing Collection. Finding an existing item in the Collection is not much affected. There is nothing you can do about it if you absolutely must generate all the data items at startup.
However, it is much better to generate only the Services and Topics you need, so that clients can connect. When a client requests an item you have not created (or have removed) the DDENewDataRequested event is raised, giving you the opportunity to create it. There is no requirement to keep any data items at all. They can be deleted at any time except in the event procedures, to be recreated when a client makes a request or must be advised of a change.
Why does NotifyClients affect all services with the same Topic and Item name?
If you support more than one service with DDServer, each may have a topic with the same name, and each topic may have a data item of the same name and clipboard format. When you call NotifyClients on any of these identically named data objects, a notification is sent from all of them.
This is unavoidable and due to the way DDEML works. The API call to notify clients does not have a parameter for the service name. DDEML keeps is a list of all the conversations with notifies on a given topic/item pair. It asks the server for the data for each of those conversations for onward transmission to the clients. It is fortunate that DDEML asks separately for the data for each conversation, instead of assuming that it is the same for all. DDServer knows which service each conversation belongs to, so it is able to send the correct data.
The only way to avoid these unnecessary notifications is to have a separate EXE for each service. As explained in Why does the second instance of DDClient or DDServer fail to initialise?, it is not possible to have more than one instance of DDEML per VB application.
How and Why to use the MessageBox API instead of MsgBox
The documentation says that the default mode for MsgBox is application modal. The Visual Basic meaning of this term differs from the normal Windows convention. Normally, user input is disabled but other processing carries on as normal. In VB the program ignores all events, so event driven DDE stops working. Events are frozen only when the program is running in the IDE, when it is compiled events work normally.
To overcome the problem, you can either use a VB form or the Windows API MessageBox. The API call is simpler and the result from the users point of view is identical to MsgBox. InputBox behaves in the same way as MsgBox, but there is no simple API call which can be used in its place.
Here is the declaration of MessageBox.
Public Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hWnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long
hWnd - The parent window of the message box, whose user input is disabled whilst the message
box is on display. All the children of that window are also disabled. Typically, you would use Me.hwnd in
form code. This parameter can be 0, the desktop is then the parent and no windows are disabled.
The flag values start MB_ and are listed below. Where there is a vbxxx equivalent it has the same value. The effects of the parent and mode flags can be summed up as follows.
The return value is one of the IDxxx values which are also listed. Again, they correspond to the VB constants.
Flags determining which buttons are shown
Flags determining which is the default button
Flags determining which icon is shown
Flags determining the mode
Other flag values
MessageBox return values