Ask Dr. GUI #50

Happy New Year!

January/February 2000

Happy New Year to all!

You'll note that it's not yet "Happy New Millennium!," "Happy New Century!," nor even "Happy New Decade!" None of those start until January 1, 2001.

But with Millennium Mania in full swing, Dr. GUI is despairing of making this point stick with anybody. He's even heard clergy in Christian churches talk of 2000 as the start of the new millennium. You would expect that they, of all people, would know better—after all, the years are numbered from the year of Jesus's birth in 1 C.E. (That's according to an old calculation, now thought to be in error—most scholars now believe Jesus was born sometime around 4 B.C.E.)

So, Dr. GUI's giving up. Call it the new millennium, if you like. Perhaps it's OK—after all, it's only off by 1/1000, or 0.1%—basically a rounding error. But it's a good thing that Jesus said something along the lines of, "Forgive not seven times, but seventy times seven." Those are words the good doctor needs to hear at trying times like these.

And Now, Your Questions…

Vipers, Adders, and ASPs! Oh My!

Dear Dr. GUI,

How can I detect the user's country in ASP? We use IIS (if that is part of the solution) but I can't find any information about this at all.

Thanks.

Marcel van den Bos

Dr. GUI replies:

If memory serves correctly, didn't Cleopatra have a problem with an asp, too? But don't worry: The good doctor assures you he won't be making an asp of himself. (At least not any more than usual.)

Active Server Pages (ASP) do not provide a method for retrieving the regional setting from the client's Windows Control Panel, because this information is not transmitted to the Web server. But if it's really the client's default language you're after, you can take advantage of the header HTTP_ACCEPT_LANGUAGE:

dim userLocale
userLocale = request.servervariables("HTTP_ACCEPT_LANGUAGE")

The HTTP_ACCEPT_LANGUAGE header contains a string representing the languages the user has selected for their browser.

In Microsoft® Internet Explorer, from the Tools menu, select Internet Options. On the General tab, click the Languages button to view a list of languages and language codes.

Examples of these language codes are [en-us] for English (U.S.) and [de] for German (Germany).

For more details on localization issues with scripting, see the article "It's All Greek to Me" in the MSDN Online Web Workshop at: http://msdn.microsoft.com/workshop/languages/clinic/scripting080999.asp.

ADO and RDO, E-I-E-I-O!

Dear Dr. GUI,

The following lines of code in Visual Basic 6.0 using ADO 2.1 give me an error on the statement DigiConn.BeginTrans:

'Code begins
Dim DigiConn As New ADODB.Connection
Dim DigiRs As New ADODB.Recordset
Private Sub Form_Load()
    DigiConn.Open "DSN=Newton;UID=admin;PWD=input"
    DigiRs.Open "Select * from Nwt_system_column", DigiConn,
      adOpenKeyset, adLockOptimistic, adCmdText
    DigiConn.BeginTrans     ' You get an error message here
    DigiConn.Execute "Create view abcd as select * from Nwt_New_Cit"
    DigiConn.CommitTrans
    DigiConn.Close
End Sub
'Code Ends

The error message is:

[Microsoft][ODBC Microsoft Access Driver] Attribute cannot be set now.

This error occurs only on an Access database, not on SQL Server or Oracle. Please give me some tips and solutions ASAP.

Sanjay Sadanandan

Dr. GUI replies:

Old MacDonald and Dr. GUI both usually stay away from predictions about the future, but in the upcoming new millennium, the good doctor fears a time when we shall all be speaking in TLAs (three-letter acronyms) or FLAs (four-letter acronyms). The computer and high-technology business is full of them—everything from BTW (by the way) to IMHO (in my humble opinion) to LGR (let's get real!).

So, without further ado, HYA (here's your answer):

The Access ODBC driver does not support transactions on a connection that has open Resultsets. The Resultset must be opened and closed within the scope of the transaction.

The following code does not give this error, because the Resultset is being opened after BeginTrans:

Dim DigiConn As New ADODB.Connection
Dim DigiRs As New ADODB.Recordset
    DigiConn.Open "DSN=Jet_Northwind"
    DigiConn.BeginTrans     ' I get an error message here
    DigiRs.Open "Select * from orders", DigiConn, adOpenKeyset, 
adLockOptimistic, adCmdText
'Brought this line down from before BeginTrans
    DigiConn.Execute "Create view abcd as select * from orders"
    DigiConn.CommitTrans
    DigiConn.Close

RDO (Remote Data Objects) also exhibits similar behavior. Take a look at this Microsoft Knowledge Base article for details:

Forming at the Window

Dear Dr. GUI,

Is it possible to open a new window within the <FORM> tag? For example:

function openWin()
{
  window.open("phonelist.asp","newphone",
"height=200,width=400,status=yes,toolbar=no,menubar=no,location=no
);
}

action="openWin()" ; or something like that..
<FORM method=post action=phonelist.asp id=form1 
name=form1 target="newphone">
<BR>
<INPUT id=formFullName name=formFullName 
size=15 maxLength=50>
<BR>
<INPUT id=submit1 name=submit1 type=submit value=Submit>
</FORM>

Thanks.

Dion Heskett

Dr. GUI replies:

So close, yet so far—you nearly had it!

You can open the new window in the onsubmit event of the form. Because the form target is that new window's name, the form is automatically posted there. All I did was add the onsubmit="openWin();" to your form tag:

<FORM method=post action=phonelist.asp id=form1 name=form1 
   target="newphone" onsubmit="openWin();">

… and Bob's your uncle, as they say in England, the form is submitted to the new window.

Natural Selection

Dear Dr. GUI,

Is there a way I can pass values from one multiple-select box to another?

Thanks for your help.

Josh Bell

Dr. GUI replies:

Believe it or not, this is a pretty common request. Fortunately, it isn't that difficult. There is one big heads up on this, though: If you are trying to do this across frames you should read the following Knowledge Base article:

Create a Web page with two multiple select boxes, one named "possible" and the other named "wishlist." It will look something like this:

<SELECT Name="possible" size=10 width=25 multiple>
  <OPTION Value="New Red Corvette">New Red Corvette</OPTION>
  <OPTION Value="Vintage Red Corvette">Vintage Red Corvette</OPTION>
  <OPTION Value="Old Red Corvette">Old Red Corvette</OPTION>
</SELECT>
<SELECT size=10 Name="wishlist" multiple>
  <OPTION Value="Old Red Jalopy">Old Red Jalopy</OPTION>
</SELECT>

Add two buttons to the page that call the function for moving items:

<INPUT type=button value="Add to wishlist" OnClick="MoveItem(possible,wishlist);">
<INPUT type=button value="Remove from wishlist" OnClick="MoveItem(wishlist,possible);">

The following script block contains MoveItem, which moves each selected option from one select box to the other. Note that I work my way from the end of the list to the beginning, because I am deleting items as I go. If I didn't do it this way I would miss some items as the options get moved up to fill in the gaps created by deleting:

<SCRIPT Language=JavaScript><!--
function MoveItem(fromObj,toObj)
{

   for ( selIndex = fromObj.length; selIndex -- ; selIndex >0 )
   {
      // Is this current option selected?
       if (fromObj.options[selIndex].selected)
       {
         // get the values from this option
         var newText = fromObj.options[selIndex].text;
         var newValue = fromObj.options[selIndex].value;
         // create the new element for other select box var newOption = new Option(newText,newValue)
         // put the new option at the end of the current list toObj[toObj.length] = newOption;
         // delete the old one.
         fromObj[selIndex] = null;
      }
   }

}
//--></Script>

The Frame Game

Dear Dr. GUI,

I am making a "weblication" in which I have an ASP page with three different frames that display data from files from within SQL Server. However, after scouring through all the documentation, I still have been unable to find a way to do this.

My problem is simple: I have an Input field and button on the main page. Also, there are three frames; each has an individual ASP page, requesting value available on the main page. On the main page, I would like to write an OnClick event that can take the value from an input field and refresh the ASP page displayed in frame1. I would like to take the same value and refresh the ASP page displayed in frame2, and so on and so forth, all by clicking one button, once. Is it even possible?

Many thanks.

Abbas F. Arsiwala

Dr. GUI replies:

With apologies to Shirley Ellis (~1967):

Frames frames,

bo brames,

banana fana fo frames,

me mi mo frames,

frames!

Sorry, but the doctor simply can't resist an opportunity to make a great rhyme (or even a mediocre one) out of anything. Those of you who don't happen to remember that great tune, "The Name Game," must not be quite as old as the good doctor. But enough of this nostalgia.

The answer to your question depends on what you mean by "frames." Because you refer to three frames embedded within a larger HTML page, I assume you mean Internet Explorer's <IFRAME>, which lets you display HTML pages within another HTML page (as opposed to the <FRAMESET> tag, which takes ownership of the entire page).

You probably tried to pass the information from the main page by appending it as a GET string argument to a new URL and then calling (via Microsoft Visual Basic® Scripting Edition):

iframe_name.src = "http:/foot/ball.asp?name1=val1"

… and found that doesn't work. Fortunately, IFrames have an undocumented document property, which is just the Document Object Model (DOM) for the contained page. Through document, you can change the page loaded by the IFrame like this:

iframe_name.document.location = "http:/foot/ball.asp?name1" & 
   frm1.elements("txt1").value

Be careful not to get drunk on this newfound power and access script-defined variables and functions defined within an IFrame's page. A known bug prevents IFrames from firing the readyState property correctly, so you never know when your IFrame has finished loading. If you access your IFrame before it finishes loading, your code throws an error trying to access the IFrame's unfinished DOM.

For more on this problem, see the following Knowledge Base articles:

If you need extensive inter-page communication, I'd suggest rearchitecting your application to use framesets rather than IFrames. Your main page would become another frame in the frameset, and the same technique just described could be used to change the location of the three "subordinate" frames:

window.frames("frame1").document.location = 
   "http:/foot/ball.asp?name1" & frm1.elements("txt1").value

One final note for those people who hate running into surprises: Keep all your FRAME and IFrame pages within the same domain. Internet Explorer implements Cross Frame Security, which prevents script from a page on one domain from accessing the DOM of a page from another domain. For more information about cross-frame scripting and to see a limited workaround, see the following Knowledge Base article:

To bypass cross-frame security entirely, you'll need to use Internet Explorer 5's new HTML Applications (HTA). The following Knowledge Base article gives you all the juicy details:

Good luck, and keep on rhyming!

Always Stay COM

Dear Dr. GUI,

I have recently been creating a good number of ATL COM classes. These all seem to work very nicely. However, I had occasion to embed one class inside another, and this caused a few problems.

When I tried to instantiate the inner class (for example, a class describing an organization contained in a class describing a person), I could not instantiate the person within the organization. Strange! In the end, I have re-imported the person class via the type library. Is this the correct way to do this? I think not. I have tried to instantiate the class directly, but get complaints about not being able to reference the members of IUnknown.

Please help!

Iain Cox

Dr. GUI replies:

It's good to hear you have been creating a good number of ATL COM classes and want to reuse them. It would be a shame to let all of that great ATL code go to waste and have to generate wrapper classes from the type library to access the COM objects.

In fact, the problem you are having is often run into by programmers using ATL who want to create instances of the ATL COM objects internally without using wrapper classes or using a COM API function such as CoCreateInstance(). Dr. GUI recommends using the static method CreateInstance() from the ATL class CComObject to create ATL objects that are used internally. You can find more information on this method in MSDN by searching for "CreateInstance."

Check out the following Knowledge Base article for a lot more information on this topic:

Laughing 'til I Cry

Dear Dr. GUI,

I am having a "funny" experience with my Property Sheets. My application does not run correctly unless Windows 95/98 has 32MB RAM or more. When there is too little RAM, the Property Sheets do not show up. I am running Visual Studio 6.0 Enterprise Edition and I have just installed SP3.

I have traced the error and PropSheet() returns -1, when there is too little RAM, but GetLastError() returns 0.

Code:

iResult = PropertySheet(&psh);
if (iResult == -1)
{
   FormatMessage( 
       FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
        NULL,
        GetLastError(),
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
        (LPTSTR) &lpMsgBuf,
        0,
        NULL);
   MessageBox(NULL, lpMsgBuf, "GetLastError", MB_OK | MB_ICONINFORMATION);

I would be happy to get some hints.

Peter Lundbo

Dr. GUI replies:

The good doctor agrees this would indeed be a "funny" experience. While this could happen if the property sheet you are displaying has some property pages with a large number of controls or Microsoft ActiveX® controls, it is most likely due to one specific control on a property page.

Usually, this type of problem arises because your system is running short of Windows 9.x system resources (especially GDI resources). Windows causes the API to fail because it does not have enough system resources to perform the operation you requested. Since these resources are separate from the main memory heap, you cannot solve this problem by adding memory to your system.

You might try using a resource meter to see how much of the system and GDI resources are available on each computer you are testing on before executing your code. From the Start menu, click Run, type in Rsrcmtr.exe, and then click OK.

Dr. GUI's prescription? Narrow down which control is causing the problem. To do this, remove all of them and add them back one by one. You should see that the problem shows up after a specific control is added or a specific number of controls are added to a page. It is not the sheet itself, but the controls on the sheet that are causing the problem. Once you've figured out which control is causing the problem, you can check to see whether it has a bug that's causing it to use an inordinate amount of resources or replace it.

Good luck.

Thanks…

Dr. GUI wants to acknowledge that this column could never have happened without the assistance of so very many people: interns, residents, other doctors, and all of you out there who are looking for answers. The doctor sends his most sincere thanks to all of you.

In this issue, the doctor acknowledges the help of staff members Jay Allen, Heidi Housten, Nathan Manis, Rupali Shanker, and Lori Turner. A special tip of the hat to Tom Moran and Penny Riker, who are on call all the time.