This news broadcast covers several developing international situations. It reports on escalating tensions between the United States, Israel, and Iran concerning Iran’s nuclear program, including the deployment of US B-52 bombers to the Middle East and diplomatic efforts involving Saudi Arabia. The broadcast also examines internal and external issues facing Lebanon’s new government, especially regarding Hezbollah’s influence and the country’s relationship with Syria and other countries. Finally, it addresses upcoming US-Russia talks on the war in Ukraine without Ukraine’s participation, sparking concerns among European leaders and Zelensky about transatlantic relations. The tone is one of analysis, reporting on potential negotiations, possible military actions, and shifting political landscapes. Several experts are interviewed to provide insight on the issues.
Middle East Tensions and Geopolitical Shifts: A Study Guide
Quiz
Answer each question in 2-3 sentences.
What was the significance of the B-52H bomber flight data and where were they headed?
What conditions did the US, represented by Rubio and Trump’s advisor Wallace, set for negotiations with the Islamic Republic?
What role is Saudi Arabia willing to play in the potential negotiations between the United States and the Islamic Republic?
According to Hossein Aghaei, what are the two options being discussed in relation to the Islamic Republic’s nuclear facilities, and which one is more likely?
What are the contradictory reports about the protests in Dehdasht, Iran, and what do they suggest about the government’s approach?
What are the criticisms levied against Prince Reza Pahlavi regarding the composition of his close associates and his claim to leadership?
What is the Lebanese government trying to achieve by excluding Hezbollah from the cabinet?
What specific actions has the Lebanese government taken to reduce Hezbollah’s influence?
What are the reasons behind the ban on flights to Beirut International Airport, and how does this affect Hezbollah’s operations?
What is Zelensky’s reaction to the US-Russia negotiations on the war in Ukraine without Ukraine’s presence?
Quiz Answer Key
The flight data of two US Air Force B-52H bombers showed their departure from the UK towards the Middle East, emphasizing the seriousness of the US’s stance amid heightened tensions with the Islamic Republic. This flight suggests a show of force and potential military readiness in the region, particularly in light of ongoing nuclear debates.
The US set the condition that the Islamic Republic must completely dismantle its nuclear program, including reducing centrifuges and stopping uranium enrichment, as a prerequisite for any negotiations to begin. These demands reflect the US’s firm stance against the Islamic Republic’s nuclear ambitions and its commitment to preventing nuclear proliferation.
Saudi Arabia has offered to mediate between the United States and the Islamic Republic in order to facilitate negotiations, which is a new phenomenon compared to the Obama era. This willingness indicates Saudi Arabia’s desire to play a more active role in regional diplomacy and security.
According to Hossein Aghaei, the two options being discussed are negotiations and an attack on the Islamic Republic’s nuclear facilities. He believes that military pressure and serious threats are more likely if the Islamic Republic does not quickly make concessions sought by the US.
There are contradictory reports about the protests in Dehdasht, with the IRGC Intelligence Organization reporting the arrest of protest leaders for sabotage, while the provincial police chief denies the protests ever occurred. These contradictory reports indicate an attempt by the government to downplay the significance of the protests and suppress dissent while controlling the narrative.
Criticisms against Prince Reza Pahlavi include concerns about the lack of diversity among his close associates, who primarily represent a specific political spectrum. He is also criticized for claiming leadership of the transition period without broader recognition from other political parties or opposition groups.
By excluding Hezbollah from the cabinet, the Lebanese government is trying to reduce the group’s influence to the lowest possible level. This action is aimed at weakening Hezbollah’s political power and limiting its ability to veto important government decisions.
The Lebanese government’s actions include electing a president and prime minister who are not aligned with Hezbollah, forming a cabinet without Hezbollah members, and supporting the ban on flights to Beirut International Airport, which were allegedly used to transport money and weapons to Hezbollah. These moves are aimed at diminishing Hezbollah’s political power and operational capabilities.
The ban on flights to Beirut International Airport is due to concerns that these flights were carrying large amounts of money and potentially weapons to Hezbollah. This ban is intended to cut off a crucial supply route for Hezbollah, hindering its ability to rebuild and resume operations.
Zelensky has stated that Ukraine does not recognize the US-Russia negotiations, highlighting Ukraine’s opposition to being excluded from discussions about its own future and security. This reaction underscores Ukraine’s insistence on being a central participant in any diplomatic efforts to resolve the conflict.
Essay Questions
Analyze the strategic implications of the US military presence in the Middle East, considering the flight of the B-52H bombers and the potential for either negotiation or military action against the Islamic Republic.
Assess the impact of Saudi Arabia’s willingness to mediate between the United States and the Islamic Republic on regional power dynamics and the prospects for diplomatic resolution.
Discuss the significance of the protests in Dehdasht, Iran, and the conflicting reports surrounding them, in the context of broader domestic challenges facing the Islamic Republic.
Evaluate the potential for a new, independent Shiite movement to emerge in Lebanon, considering the current government’s efforts to reduce Hezbollah’s influence and separate the Shiite population from the group.
Examine the implications of the US-Russia negotiations on the war in Ukraine, particularly in light of Zelensky’s opposition and the potential reshaping of transatlantic relations.
Glossary of Key Terms
B-52H Bombers: Long-range, heavy bomber aircraft used by the United States Air Force, capable of carrying a wide array of munitions, including nuclear weapons.
JCPOA (Joint Comprehensive Plan of Action): An international agreement on the nuclear program of Iran reached in Vienna on 14 July 2015 between Iran, the P5+1 (the five permanent members of the United Nations Security Council—China, France, Russia, United Kingdom, United States—plus Germany), and the European Union.
Islamic Republic: A term referring to the government of Iran, which is based on Islamic principles and law.
Hezbollah: A Lebanese Shia Islamist political party and militant group, heavily supported by Iran.
Centrifuges: Devices used to enrich uranium for nuclear fuel or weapons.
Uranium Enrichment: The process of increasing the concentration of the isotope Uranium-235 in natural uranium to produce fuel for nuclear reactors or material for nuclear weapons.
IRGC (Islamic Revolutionary Guard Corps): A branch of the Iranian Armed Forces, separate from the regular army, tasked with protecting the country’s Islamic system.
Rafik Hariri Airport: The main international airport in Beirut, Lebanon.
Shiite Movement: A branch of Islam whose adherents believe that the leadership of the Muslim world should be passed down through the direct descendants of Muhammad.
Transatlantic Relations: The political, economic, and cultural relations between North America and Europe.
Rastakhiz Party: A former political party in Iran, founded by Mohammad Reza Pahlavi in 1975.
SAVAK: The secret police, domestic security and intelligence service in Iran during the reign of the Pahlavi dynasty.
Liberal Democrat: A political ideology combining liberal politics with a more overt commitment to social justice.
Reformists: Individuals or groups advocating for gradual change and improvement in a political or social system.
Fundamentalist: A strict adherence to the basic principles of any subject or discipline.
Monarchists: Individuals or groups who support a system of government headed by a monarch.
Republicans: Individuals or groups advocating for a form of government in which the head of state is not a monarch, typically an elected president.
Constituent Assembly: A body or assembly of popularly elected delegates which is assembled for the purpose of drafting or adopting a constitution or similar document.
Deterrence: The action of discouraging an action or event through instilling doubt or fear of the consequences.
Decline Factor: Internal conditions and trends that can weaken or undermine the stability and power of a state or regime.
Strategic Point: A location or position of military or political importance.
Middle East Geopolitics and US-Russia Negotiations Analysis
Okay, here’s a detailed briefing document summarizing the key themes and ideas from the provided text.
Briefing Document: Analysis of News Headlines
Date: October 26, 2023 (based on the Monday, February 29th, reference) Subject: Analysis of Geopolitical Tensions and Domestic Unrest in Iran and the Middle East.
Executive Summary:
This document analyzes a series of news headlines and discussions covering escalating tensions between the US, Israel, and Iran, domestic unrest in Iran, political maneuvering surrounding Prince Reza Pahlavi, and the complex situation in Lebanon, as well as US-Russia talks on Ukraine. The overarching themes are: the increasing possibility of confrontation between Iran and its adversaries, internal dissent within Iran, shifting alliances in the Middle East, and the potential for a new geopolitical order.
Key Themes and Ideas:
1. Escalating US-Iran Tensions and Nuclear Concerns:
Threat of Military Action: The deployment of US B-52 bombers to the Middle East, coupled with statements from US and Israeli officials, suggests a heightened threat of military action against Iran’s nuclear facilities. “Two US Air Force B-52H bombers departed from the UK’s Fay Ford Air Base, accompanied by at least 4 tanker aircraft, and crossed the Mediterranean Sea towards the Middle East.” Mark Rubio and Netanyahu “emphasized that they will not allow Tehran to obtain nuclear weapons.”
Preconditions for Negotiation: The US is setting strict preconditions for negotiations with Iran, including the complete dismantling of its nuclear program. “Mr. Wallace has raised the point that the Islamic Republic must put aside its entire nuclear program… Mr. Rubio… believes that the Islamic Republic must put aside all of its nuclear ambitions altogether, and this could be a prelude to starting negotiations with the Islamic Republic.”
Iranian Defiance: Iranian officials are publicly dismissing the threats and asserting their defensive capabilities, though some analysts believe this is primarily for domestic consumption. “The Leader of the Islamic Republic, Ali Khamenei, also said in a speech that our issue is not a hardware or defense threat, and there is no problem in this regard.” One analyst stated that the Iranian Leaders view that the “Islamic Republic has absolutely no problems in terms of defense capabilities… are primarily misleading, incorrect, contrary to the facts on the ground.”
Saudi Mediation: Saudi Arabia is offering to mediate between the US and Iran, indicating a potential shift in regional dynamics and a desire to de-escalate tensions. “Riyadh and Saudi Arabia are willing to mediate between the United States and the Islamic Republic in order for negotiations to take place.”
Diplomatic Window: The “diplomatic window” for negotiations is considered very limited and that increased military pressure is being considered. “The diplomatic window that Trump has opened will be very, very limited. I think that the maximum will be this summer at best, and it may not even reach that time.”
2. Internal Unrest and Political Division in Iran:
Protests in Dehdasht: Reports of protests in Dehdasht, Iran, are met with contradictory official responses, with some sources denying the protests occurred and others reporting arrests of protest leaders. “Contradictory statements about the Dehdasht protest rallies continue, while the Kohgilu police commander has completely denied the occurrence of the rally in Dehdasht.”
Criticism of Leadership and Dissension: Dissension and unrest in the population are creating a “factor of decline” that are creating internal fears within the Republic. “The main source of concern for the Islamic Republic is internal rather than external. They are trying to prevent that collapse factor in some way.”
Prince Reza Pahlavi’s Role: The role and potential leadership of Prince Reza Pahlavi, is generating discussion, with some moderate groups welcoming his pronouncements and other groups questioning those around him. “Prince Reza Pahlavi’s comments criticizing the slogan ‘King Reza Pahlavi’ not to insult the opposition and not to raise the issue of monarchy or republic at the current stage… More moderate groups are welcoming the talks of Prince Reza Pahlavi.” However, others state that “we don’t see this diversity among his main close associates.”
Lack of Unified Opposition: The lack of a unified opposition is being criticized, with some arguing that critics of Pahlavi should focus on organizing themselves instead of solely criticizing him. “The time for you to criticize Mr. Pahlavi but not organize yourself… not be able to present an alternative leadership is over.”
3. Lebanon’s Internal and External Challenges:
Israeli Withdrawal and Hezbollah: Lebanon faces both the issue of Israel’s withdrawal from southern Lebanon and the need to manage Hezbollah’s influence within the country. The Lebanese government faces external pressures from Israel and internal tensions related to Hezbollah’s power. “The Israeli issue, which is due to withdraw from southern Lebanon tomorrow, coincident with the end of the 60-day ceasefire with Hezbollah.”
Reduced Hezbollah Influence: The new Lebanese government is attempting to reduce Hezbollah’s influence, including excluding them from cabinet positions, supported by US policy. “The current Lebanese government is trying to reduce Hezbollah’s influence to the lowest possible level.”
Saudi Support for Lebanese Government: Saudi Arabia’s support for the Lebanese government signifies a shift in regional power dynamics and a challenge to Iran’s influence. “Saudi Arabia’s support for the Lebanese government in dealing with the protesters is another sign of the new balance of power in Lebanon, which will not be good news for the Islamic Republic, the party’s main supporter.”
New Shiite Movement: The possible emergence of a new Shiite movement independent of Hezbollah and Iran is being speculated on. “I think we are witnessing the birth of a new Shiite movement in Lebanon, which is neither Hezbollah nor Amal, a Shiite movement that has learned from and no longer wants to be subordinate to a foreign country.”
4. US-Russia Negotiations on Ukraine:
Negotiations without Ukraine and Europe: The US is engaging in negotiations with Russia on the war in Ukraine without the direct involvement of Ukraine or European countries, causing concern among those parties. “US Secretary of State Mark Rubio, along with the National Security Advisor and the US Special Envoy for the Middle East, will meet with Russian representatives… Zelensky… said that we do not recognize the US-Russia negotiations.”
Trump’s Approach: Donald Trump is taking a different approach to the conflict, prioritizing negotiations and potentially seeking a quick resolution, which is causing friction with transatlantic allies. “Mr. Trump is rewriting transatlantic relations, that is, relations between the two sides of the Atlantic Ocean.”
Potential Outcome: The implications of these negotiations for the future of Ukraine and European security are uncertain. “Kremlin spokesman Dmitry Peskov has also confirmed that representatives from the Russian Federation are going to be present. This will be the first stage, so that the two sides can sit down and work out their differences.”
Conclusion:
The news headlines paint a picture of a volatile and uncertain geopolitical landscape. The potential for conflict in the Middle East, coupled with internal challenges within Iran and shifting alliances, highlights the need for careful diplomacy and strategic planning. The US-Russia negotiations on Ukraine also point to a potential reshaping of the international order. These developments will require close monitoring and analysis in the coming weeks.
US, Iran, and Middle East Tensions: Analysis and Developments
FAQ
What is the significance of the US Air Force B-52H bombers being deployed to the Middle East? The deployment of B-52H bombers to the Middle East, especially following threats from Rubio and Netanyahu against Iran, is a significant show of force and a signal of the US and Israel’s determination to prevent Iran from obtaining nuclear weapons. It suggests a potential escalation of tensions and a readiness to consider military options, although it could also be interpreted as a deterrent intended to bring Iran to the negotiating table.
What are the preconditions for negotiations between the US and Iran, according to US officials? According to remarks attributed to Mr. Wallace, Trump’s National Security Advisor, and US Secretary of State Rubio, a key precondition for negotiations between the US and Iran is that Iran must completely dismantle its nuclear program, including ceasing uranium enrichment and reducing its centrifuges. This position aligns with Trump’s stated desire for negotiations, but only under stringent conditions.
What role is Saudi Arabia attempting to play in the US-Iran dynamic? Saudi Arabia has expressed a willingness to mediate between the US and Iran. This marks a shift from the Obama era, where the US did not seek Saudi Arabia’s input on the JCPOA. Saudi Arabia’s involvement suggests a regional desire for de-escalation, but also a preference for a deal that addresses their concerns about Iran’s regional influence and nuclear ambitions.
What is Ali Khamenei’s response to the increasing pressure and threats from the US and Israel? Ali Khamenei has dismissed concerns about Iran’s defense capabilities, stating that Iran has no problems in this regard and is ready to confront serious threats. However, experts cited in the broadcast suggest that these statements are primarily for domestic consumption and may not accurately reflect the actual situation. The Islamic Republic may be more concerned with internal factors and a potential decline in domestic support.
What is happening with the protests in Dehdasht, Iran, and how are they being reported? Protests have taken place in Dehdasht, with citizens chanting anti-government slogans. However, the Iranian government’s media approach has been one of denial. While some news agencies affiliated with the IRGC have reported arrests of individuals allegedly planning sabotage, others deny the protests even occurred. This contradictory reporting and the reported use of violence against protesters indicate a sensitive security situation and an effort by the government to suppress dissent and control information.
What are the different perspectives on Prince Reza Pahlavi’s role and leadership within the Iranian opposition? Prince Reza Pahlavi’s recent comments at a convergence meeting have sparked debate among the Iranian opposition. While some, particularly more moderate groups, welcome his criticisms of extremist rhetoric and calls for unity, others criticize the lack of diversity among his close associates and question his self-proclaimed leadership of the transition period. Some argue that those around him push polarizing views. There are conflicting opinions regarding whether he is a unifying figure or is surrounded by advisors who are too partisan and right-wing.
What is the situation with Hezbollah and the Lebanese government, and what is Saudi Arabia’s role? The Lebanese government, under President Joseph O’Neill and Prime Minister Nawab Salam, is facing both external pressure from Israel and internal challenges related to Hezbollah’s influence. The new government is trying to reduce Hezbollah’s influence, culminating in a cabinet without Hezbollah members. Saudi Arabia is supporting the Lebanese government, signaling a new balance of power in Lebanon that is unfavorable to Iran, Hezbollah’s main supporter.
What is the status of US-Russia negotiations on the war in Ukraine, and how are European leaders and Zelensky reacting? The US and Russia are scheduled to hold negotiations in Riyadh regarding the war in Ukraine, without the presence of Kiev or European countries. Zelensky has stated that Ukraine does not recognize these negotiations. European leaders are holding emergency meetings to discuss their response, amidst growing pressure from the US for them to shoulder more of the financial burden of supporting Ukraine. The situation reflects a potential shift in transatlantic relations and concerns about the future of the conflict.
US-Iran Tensions: Nuclear Program, Military Threats, and Negotiations
Tensions between the U.S. and Iran are a prominent topic in the news, particularly regarding Iran’s nuclear program and regional influence. Here’s a breakdown of the situation:
Nuclear Ambitions: The U.S., along with Israel, is concerned about Iran’s pursuit of nuclear weapons. Both countries have stated they will not allow Iran to obtain them. The U.S. has indicated that negotiations with Iran are contingent on Iran completely halting uranium enrichment and dismantling its centrifuges.
Military Threats and Deterrence: The U.S. has sent B-52H bombers to the Middle East as a signal of its commitment to prevent Iran from acquiring nuclear weapons and to deter military action. There are discussions about potential military actions against Iran’s nuclear facilities if negotiations fail.
Iranian Response: Iran maintains that its nuclear program is for peaceful purposes. Iranian officials have asserted that threats against Iran are a violation of international law and that Israel “cannot do anything wrong”. Iran’s Leader, Ali Khamenei, has stated that the country’s defense capabilities are robust, though some analysts believe this is primarily for domestic consumption to prevent internal dissent.
Negotiation Prospects: There are talks of potential mediation by Saudi Arabia or Russia between the U.S. and Iran. However, the U.S. conditions for negotiation, which include eliminating all perceived threats from Iran, may be difficult for Iran to accept in a short timeframe. Some analysts believe the window for diplomacy is limited.
Regional Influence: Iran’s regional influence is also a point of contention. Israel believes Iran is trying to compensate for its reduced regional influence by developing nuclear weapons.
Nuclear Negotiations: US and Iranian Perspectives
Here’s what the sources and our conversation history say about nuclear negotiations and related issues:
Conditions for Negotiation: The U.S. has indicated that any negotiations with the Islamic Republic of Iran are conditional on Iran’s willingness to significantly reduce its centrifuges and completely stop enriching uranium. The U.S. preconditions also include eliminating all combined threats from the Islamic Republic, with a focus on the nuclear issue.
U.S. Stance: The U.S., under the Trump administration, is seeking negotiations with Iran but insists that these negotiations will only commence when Iran demonstrates a willingness to curtail its nuclear program. The seriousness of the United States and Israel in preventing the Islamic Republic from obtaining nuclear weapons has become more prominent.
Iranian Perspective: An excerpt indicates that the Islamic Republic might be ready to do anything to have the sanctions lifted. However, it seems unlikely that the Islamic Republic will want to make concessions in the shortest possible time in relation to all those combined threats.
Potential Mediation: There is potential for Saudi Arabia and even Russia to mediate between the U.S. and the Islamic Republic.
Limited Diplomatic Window: The diplomatic window that Trump has opened will be very limited, perhaps only until the summer. It is unlikely that Trump will allow the Islamic Republic to waste time if it does not intend to negotiate and make the concessions sought by the U.S..
Consequences of No Deal: If the Islamic Republic does not come to the negotiating table and offer the concessions sought by the U.S., the U.S. and Israel may enter a military phase or make credible and serious military threats against the Islamic Republic.
Iranian Nuclear Ambitions: Israel believes that Tehran is trying to compensate for its reduced regional influence by producing nuclear weapons. Gideon O’Sarr confirmed that Israel will not allow the Islamic Republic to obtain nuclear weapons.
B-52 Bomber Flights: The United States Air Force sent B-52H bombers to the Middle East, which could be seen as a signal to the Islamic Republic regarding America’s determination to confront a military attack on the nuclear facilities of the Islamic Republic.
Lebanon’s Government: Challenges, Composition, and Hezbollah’s Influence
The Lebanese government faces significant internal and external crises. Here’s a summary of the key points regarding Lebanon’s government:
External Pressures: The Lebanese government is dealing with the issue of Israel’s presence in southern Lebanon and is trying to ensure Israel withdraws on time through political and diplomatic efforts.
Internal Challenges with Hezbollah: The Lebanese government is trying to reduce Hezbollah’s influence. The current Lebanese government, unlike previous ones, does not include any Hezbollah members, which is an unprecedented development.
Government Composition: Joseph Onn was elected as the country’s president, and Nawaf Salam was elected as the prime minister despite Hezbollah’s opposition. The current cabinet does not include any Hezbollah members, preventing them from vetoing important decisions.
U.S. Influence: The U.S. has been দ্রুত in opposing Hezbollah’s presence in the Lebanese government, with the U.S. administration stating that a strong Lebanese government that can depose Hezbollah is its goal.
Recent Clashes: Recent clashes around Beirut’s Rafik Hariri Airport and the ban on flights to Lebanon are signs of Hezbollah’s increasing predicament.
Saudi Arabia’s Support: Saudi Arabia is supporting the Lebanese government in dealing with protesters, which is indicative of a new balance of power in Lebanon that is not favorable for Iran, Hezbollah’s main supporter.
Efforts to Separate Shiite Identity from Hezbollah: The Lebanese government is trying to separate the Shiite community from Hezbollah, as millions of Shiites live in Lebanon, and the government aims to create a new Shiite movement that is not subordinate to a foreign country and wants to integrate with other Lebanese groups.
Hezbollah’s Revival Efforts: Hezbollah is planning a grand event to announce its revival, but the Lebanese government has closed an air route that was allegedly used to transport money and weapons to them.
Dehdasht Protests: Anti-Government Unrest, Arrests, and Media Response
Here’s what the sources say about the Dehdasht protests:
Protests and Slogans: Protests occurred in Dehdasht, with citizens chanting anti-government slogans. These protests followed a pattern seen in many parts of the country.
Timing: The Dehdasht protests began on the evening of the 2nd of Bahman and continued for at least four nights.
Media Coverage and Government Response: Initially, the Islamic Republic’s media denied the occurrence of the Dehdasht protests. Later, the Fars News Agency, affiliated with the Revolutionary Guard Corps (IRGC), reported the arrest of individuals described as leaders and ringleaders of the protests. However, this report did not explicitly acknowledge that protests had taken place, instead claiming that the arrested individuals intended to carry out sabotage.
Contradictory Statements: There have been contradictory statements regarding the Dehdasht protests. While the Fars News Agency reported arrests, the provincial police chief denied that any gathering had occurred.
Arrests and Violence: Reports and images indicate that direct shooting was carried out against protesting citizens. Human rights media have published the names of at least 25 people reportedly arrested in Dehdasht by the IRGC Intelligence Organization and the Ministry of Intelligence. The arrests were accompanied by violence.
Security Atmosphere: A security atmosphere prevails in Dehdasht following the protests.
Past Activism: Dehdasht was also an active city in the Women’s Uprising of Freedom protests, during which at least two people were killed.
Allegations Against News: There are claims that certain media outlets undermine Prince Reza Pahlavi, attacking those around him with unsubstantiated claims.
US-Russia Negotiations on Ukraine: A Summary of Perspectives
Here’s what the sources say about the Ukraine war:
US-Russia Negotiations: The U.S. and Russia are holding negotiations in Riyadh regarding the war in Ukraine. These negotiations are occurring without the presence of Kiev or European countries.
US Representatives: Mark Rubio, along with the National Security Advisor and the US Special Envoy for the Middle East, will meet with Russian representatives.
Zelensky’s Stance: Zelensky has stated that Ukraine does not recognize the US-Russia negotiations.
European Response: An emergency meeting of European leaders began in Paris. They are determining their next steps and how to have a foothold in the negotiations between the U.S. and Russia.
Trump’s Perspective: Donald Trump believes that both sides consider the war to be over and will probably meet with Putin soon.
American Press: Headlines in the American press are full of ambiguities. Bloomberg reports that Zelensky considers talks without Ukraine to be dangerous. The Wall Street Journal notes that Trump is rewriting transatlantic relations.
Kremlin Confirmation: Kremlin spokesman Dmitry Peskov has confirmed that representatives from the Russian Federation will be present at the negotiations.
US Explanation to Zelensky: Trump’s special envoy for Ukraine, Keith Kluge, told Zelensky that past peace efforts failed because too many parties were involved. Kluge has stated that it is in the interest of Ukraine and Europe to sit out the initial talks between the US and Russia to allow the two countries to work out their differences.
Jay Dunes’s Speech: Jay Dunes criticized European Union leaders for not defending democracy and for putting the cost of defending Ukraine on American voters and taxpayers. He specifically criticized Germany, which is heading towards elections during a turbulent political atmosphere.
تیتر اول با نیوشا صارمی: پرواز دو بمبافکن بی۵۲ به سمت خاورمیانه، هشدار برای تهران؟
The Original Text
In the first headline tonight with me Nyusha Sarmi B-52 bombers on their way to the Middle East One day after Ruby and Netanyahu’s threatening speech against the Islamic Republic, Khamenei said we are ready to confront serious threats and that my defense is fine Domestic and foreign crisis in Lebanon Israel’s presence and the country’s government’s pressure on Hezbollah after the formation of a cabinet without the presence of members of this group affiliated with the Islamic Republic and Riyadh hosting US-Russian talks on Ukraine without the presence of Kiev Zelensky says we do not recognize the talks Welcome to the first headline Good morning Welcome to the first headline today, Monday’s flight data shows that two US Air Force B-52H bombers departed from the UK’s Fay Ford Air Base, accompanied by at least 4 tanker aircraft, and crossed the Mediterranean Sea towards the Middle East Yesterday, Mark Rubio and Netanyahu emphasized that they will not allow Tehran to obtain nuclear weapons The US Secretary of State today, in response to a question about a possible Israeli attack on the Islamic Republic’s nuclear facilities and Donald Trump’s position on the matter, said that Israel will act based on its national interests Today The Leader of the Islamic Republic, Ali Khamenei, also said in a speech that our issue is not a hardware or defense threat, and there is no problem in this regard. The spokesman for the Iranian Foreign Ministry also said that Israel cannot do anything wrong. Threatening others is a gross violation of international law and the UN Charter, and that in relation to a country like the Islamic Republic of Iran, his answer is that he cannot do anything wrong. At the same time, the Israeli Foreign Minister said today that Tehran is trying to compensate for its reduced regional influence by producing nuclear weapons. Gideon O’Sarr confirmed that his country will not allow the Islamic Republic to obtain nuclear weapons. We will examine this and other news during the program with a team of experts and journalists, but first of all, we will go directly to the White House in the US capital. My colleague Samira Karai is there to talk to her more. Samira, after Marco Rubio’s visit to Israel, perhaps it can be said that now we can have a more accurate picture of what both sides, specifically Israel and the United States, have in mind to confront the issue of the Islamic Republic, and specifically if we look at Mr. Wallace, Mr. Trump’s National Security Advisor,’s remarks about the preconditions for negotiations. We know that no preconditions have been officially announced, but Mr. Wallace has raised the point that the Islamic Republic must put aside its entire nuclear program. Let’s look at what Mr. Rubio, the US Secretary of State, said. He also believes that the Islamic Republic must put aside all of its nuclear ambitions altogether, and this could be a prelude to starting negotiations with the Islamic Republic. So what we know now is that Mr. Trump has been saying in recent weeks that he is looking for negotiations, but negotiations will only begin when the Islamic Republic is willing to completely reduce its centrifuges, to completely stop enriching uranium, and this is a condition for these negotiations to take place. On the other hand, we heard that Riyadh and Saudi Arabia are willing to mediate between the United States and the Islamic Republic in order for negotiations to take place. This is also an interesting phenomenon. It is a new phenomenon during the Obama era. Although Saudi Arabia officially welcomed The JCPOA, but it was never interfered with. Obama never asked Saudi Arabia or the countries in the region what they thought about the JCPOA. Now we see that Saudi Arabia is willing to play a role in these negotiations and to support this. So the overall picture is that if Saudi Arabia wants to be involved, if the words of the European diplomat that were made a while ago that the Islamic Republic is ready to do anything to have the sanctions lifted, if we look at all of this in a nutshell, it means that the Islamic Republic must agree to shut down its entire nuclear program. What happened in Libya, for example, and negotiations after that, will probably be the same negotiations in the region. On the other hand, the seriousness of the United States and Israel in not letting the Islamic Republic get its own nuclear weapons has become more prominent than ever. They will not allow the Islamic Republic to do so under any circumstances. The flight of these B- 52Hs could also be noteworthy in these days when we are hearing so much about the Islamic Republic and the nuclear debate. The British take off with the B-52. H-4 is also accompanying them. They are entering the Middle East from Italy. This is also a point of interest. These developments in Nyusha are continuing. We need to see whether they will talk more about the Islamic Republic’s nuclear issue during their trip to Riyadh. Thank you. My colleague Samira Qarai was with us from in front of the White House. But we will continue with Hossein Aghaei, a researcher in international relations and strategic affairs. Mr. Mr. It seems that the countdown has begun for one of the two options that have been discussed in the past few days, negotiations or an attack on the Islamic Republic’s nuclear facilities. If there is an appointment for negotiations, it should be clear in the next week or two. It may be so. However, the diplomatic window that Trump has opened will be very, very limited. I think that the maximum will be this summer at best, and it may not even reach that time. In any case, it is the Islamic Republic that will decide, although there is some talk about mediation from Saudi Arabia or even Russia right now. It seems unlikely that Putin and Trump will hold talks themselves. If the conditions set by the US are somehow in line with the White House’s instructions, which is to eliminate all the combined threats of the Islamic Republic, focusing on the nuclear issue, if that continues, any mediation is also assumed. It seems unlikely that the Islamic Republic will want to make concessions in the shortest possible time in relation to all those combined threats. That is why this makes it more likely that the Trump administration will try, now that Israel has somehow taken away that diplomatic and military support from Washington, they will most likely try to increase military pressure, set deadlines. I think we will see more threats in the rhetoric of American officials, and we will move towards a direction where if the Islamic Republic does not intend to come to the negotiating table anyway, it does not seem likely that Trump will give the Islamic Republic time to negotiate, that he will waste time. If the Islamic Republic does not come to the negotiating table and those concessions sought by the US are not given. It is unlikely that the US and Israel will enter a military phase or before that, they will make credible and serious military threats against the Islamic Republic. Mr. Aghaei, Ali Khamenei spoke again today, but he did not mention negotiations. Instead, he said that our defense is fine, which of course we know could be a response to Donald Trump, who said that it would not be difficult to damage them in terms of defense. How much do you think that with the situation becoming more complicated and the seriousness of Israel and the United States, the Islamic Republic will ultimately reach the deal you are talking about? Come and see me and let me point out that a realistic view shows us that the statements of the Leader of the Islamic Republic regarding the level of military and defense capabilities of the Islamic Republic against serious threats, which I mean by serious threats is probably the same as the possible attack of the United States and Israel, and that the Islamic Republic has absolutely no problems in terms of defense capabilities, according to the Leader of the Islamic Republic. These are primarily misleading, incorrect, contrary to the facts on the ground, and in a way, it has more domestic consumption, and in a way, the Islamic Republic, I think, is more afraid of the factor of decline, meaning that the main source of concern for the Islamic Republic is internal rather than external. They are trying to prevent that collapse factor in some way, and since Trump’s maximum pressure will continue, I think this will cause those trust-denial faults in the main institutions of power among the people in that core, in those layers close to the core, to become more active. In any case, I think that the Islamic Republic, even if it makes a deal, in a short-term game, assuming that it gives big concessions, will be weaker. It will actually lose. And if it makes a deal, well, if it doesn’t make a deal, it will want to enter a confrontation phase, which it will actually lose. And this American action to send and dispatch these B-52 Stratofortress bombers from Britain along with KC- 135 tankers, all of these are signals to the Islamic Republic. Yes, now American and Russian officials are in Saudi Arabia. They want to strengthen deterrence. Trump and Putin may meet in Saudi Arabia in the coming days. They will send these as a deterrent, but at the same time, there is a signal from Washington to the Islamic Republic that America’s determination and will to confront The military attack on the nuclear facilities of the Islamic Republic and other targets they are considering is very, very serious. I am very grateful to you, Hossein, a researcher in international relations and strategic affairs, for being with us in Iran. Contradictory statements about the Dehdasht protest rallies continue, while the Kohgilu police commander has completely denied the occurrence of the rally in Dehdasht. The Fars news agency reported the arrest of the leader of the Dehdasht rally, Kandeh, along with several others, by the IRGC Intelligence Organization. Fars claimed that these people intended to carry out what he said were sabotage operations and were handed over to the judicial authorities. Last week, a group of Dehdasht citizens held a protest rally for several consecutive nights and chanted anti-government slogans. At the same time, reports were published about the security situation and the arrest of a number of citizens in this city. In this regard, Masoud Kazemi, a journalist from Munich, joins us. Mr. Kazemi, what do we know about what happened in Dehdasht and why are the comments so strangely contradictory? How can the leader of the rally be arrested? A news agency affiliated with the IRGC say this after that. The judiciary says that there was no gathering at all. Well, the beginning of the Dehdasht protests was symbolic. That is, on the evening of the 2nd of Bahman, which the next day was the 22nd of Bahman, like in many parts of the country, slogans were chanted that night. But the Dehdasht protests continued after that. According to the information we have, at least until the 4th night after that, a large number of citizens took to the streets and chanted anti-government slogans. But from the beginning, the Islamic Republic’s media approach to the incident was denial. That is, you couldn’t find even a short news story about the Dehdasht protests in the domestic media until yesterday, when the Fars News Agency, affiliated with the Revolutionary Guard Corps, announced, as you said, that a number of those who they said were the leaders and ringleaders of the protests were arrested. At the same time, the provincial police chief denied it outright. Of course, the Fars News Agency news report didn’t say that protests had taken place. It said that some leaders were arrested who wanted to carry out sabotage. But the information we have, the images we have, the video that I think IRA International released yesterday, showed that in a Direct shooting was being carried out against protesting citizens, until now, human rights media and those who work for human rights have published the names of at least 25 people who were arrested in this city by the IRGC Intelligence Organization and the Ministry of Intelligence on the 8th. We know that Dehdasht was also an active city in the protests of the Women’s Uprising of Freedom, and at least two people were killed. Pedram Azarnoosh and Behnam Mehdad were killed in the protests and now the arrests. The news indicates that it was accompanied by violence. There is still a security atmosphere. For example, Motahereh Tahereh Motahernia. I apologize that she was arrested. She was also arrested in the 1401 protests. There is a security atmosphere, but the government’s approach is to deny the incident. Apparently, at least up to now. Journalist Masoud Kazemi. Thank you to Munich. Mr. Kazemi. But another issue is the reactions to Prince Reza Pahlavi’s positions at the convergence meeting. Prince Reza Pahlavi’s comments criticizing the slogan “King Reza Pahlavi” not to insult the opposition and not to raise the issue of monarchy or republic at the current stage. There were opponents and supporters of Prince Reza Pahlavi’s meeting last night. With some of the victims and detainees of the nationwide protests of 1401, including Armita Abbasi, we are also discussing this issue here with Hamed Shivan Irad, the first secretary of the Iran Novin Party from Geneva, and Arash Azizi, a writer and historian who joined us from New York. Welcome, Mr. Azizi. It can be seen right now that more moderate groups are welcoming the talks of Prince Reza Pahlavi. I can point to the reformists with a name and tradition in Iran, who I paid for, who welcomed these talks. How much do you think will put more groups on the path with the prince? His talks yesterday at the Munich meeting? Yes, exactly. You mentioned an important point. I saw the same exact lobby groups that we mentioned. I saw at least one or two people. Look, Mr. Pahlavi’s story is clear. Every time he speaks against the extremists around him, every time he criticizes King Reza Pahlavi’s slogan, for example. He criticized the Resurrection Party of his father’s time and said, ” Don’t go around me, I’ll have to name them.” He meant exactly which shops. Every time he moves like this, more people come around him and more people are attracted. However, the problem and contradiction here is that we don’t see this diversity among his main close associates. His main close associates are people like Mr. Etemadi and Mr. Ghasemi Nejad. It is important to name them because you see, these represent a specific political spectrum, and Mr. Pahlavi’s close associates and team do not have that diversity at all. People like Mr. Shabay Rad, whose speech we are hearing now, do not follow Mr. Pahlavi’s position in any way. Mr. Pahlavi says this, but we do not see that approach of attraction in the approach of his colleagues and his close associates. Therefore, this is a film that, unfortunately, has been released. I have one last point to make, Mr. Pallow, there is another important contradiction. He is in They said, “We shouldn’t create a leader now, but we shouldn’t say King,” and all that. But in this very meeting of the parties, whose list I’ve been trying to get for several hours, I won’t declare him as their leader. He said, ” I am the leader of the transition period,” while no one has recognized him in any way, nor have any parties outside the spectrum of parties. As far as I understand, participating in them is all from a very small spectrum. It even seemed that the Constitutional Party of Iran Liberal Democratic Party did not agree with that clause. Of course, I couldn’t understand this clearly. So there is also the issue that Mr. Pahlavi, I just want to say that Mr. Pahlavi, I welcome this action. I think it was good to move in this direction. However, if they want to do this job completely, they should stop claiming a single leadership, try to be an important national figure, a figure who can be a very acceptable figure, and try to create unity among the opponents of the Islamic Republic and get closer to the opponents of the Islamic Republic and not Presenting that anti-union, extremist, and undemocratic path that he criticized, but most of his close associates still follow, Mr. Shibai Rad, I saw in the words of many that the group that was present at the meeting was clearly a group that had previously supported Prince Reza Pahlavi. There were no other groups in that meeting that now approved under the leadership of Prince Reza Pahlavi. How do you see this in addition to Mr. Azizi’s words? Yes, thank you very much. Mr. Azizi’s words were along with following a line that, unfortunately, I must say that in cyberspace, especially in cyberspace, it is a line that is followed by the unknown forces of the Ministry of Intelligence. Now I will explain what it is for. Look, when those who want to undermine and weaken Prince Reza Pahlavi and know that because of his high popularity and the position he holds, they cannot directly attack and attack him, they try to attack those around him with words that have no evidence and are not true, and that The famous saying that he himself is good and his entourage says, one is that the leader of the Shah is Reza Pahlavi and no one else has claimed to be the leader of the revolution. This is number one and number two, instead of talking about how those around him do not agree with the prince’s statements, they should provide examples of where and when this conversation took place. For example, let me mention myself. I am not talking about others. Right now, you can see in cyberspace that a lot of hate has started against me, starting yesterday. While if you are some of those who apparently support Shah Reza Pahlavi, if what you said was true, then I would support them. Yes, yes, I will support them. This means that what you are saying has no evidence whatsoever. We are talking about Shah Reza Pahlavi, not about the mortar. We are talking about other people, other dear friends who are working hard in Iran and are not talking about them. But another important point that you said is that the other opposition forces, I would still like you to support them. It is very important to name who they are. For example, people like you who choose Mousavi as their leader. It means that it should be clear that those political forces that you are talking about are not other groups. I want to return to your question, dear Niyoshi. What groups did you see in this meeting? It was completely diverse. There were groups that might not agree with the leadership of the prince in such a context. There were groups that might strongly agree. There were groups that even said, let’s go ahead and use another title. Incidentally, that diversity itself showed that group. But if what someone meant is groups that have been defending the Islamic Republic until now, or like Mr. Azizi himself, someone like Mousavi who calls Khomeini the criminal, that awake soul, and whose wish is to return to the golden age of the Imam, they are not talking about fever. Because 57 and all those who continue to defend the 57 discourse, in this, if even we accept it, we as political forces are other people. They do not accept the slogan of reformist and fundamentalist. The whole thing has been in the dialogue since 2017. What can I say? It has entered the hearts of the Iranian people. Anyway, I will summarize so that we can get to other discussions. I think that we should talk with examples, name the groups we are talking about. If we want to talk in general, maybe the mind is divided. Yes, they are right that there were no other groups, but which groups were there? Which spectrum is better to talk about? Dear Mr. More importantly, what is it, dear Mr. Just today, Prince Reza Pahlavi tweeted again and said that they welcome other groups to be present in this space? You mentioned those around him. For example, the same Mr. Etemadi that you mentioned was with him. He was probably the one who consulted him about who he wanted to meet. In any case, he is not his political advisor. But another issue that arises is that those who do not accept the leadership of Prince Reza Pahlavi should choose their own leadership or have their own council. Or why is that coalition without Prince Reza? Pahlavi did not last. How do you see this? I think this criticism is 100% wrong. I invite all those who consider themselves republicans, such as Mr. Hamed Esmailiou, friends of various leftist and republican parties, Mr. Mehdi Fatpour, I can name them. Come together, get organized. The time for you to criticize Mr. Pahlavi but not organize yourself, not organize yourself, in fact, not be able to present an alternative leadership is over. And I completely agree with those who say, “We don’t want a leader because we are not a party.” And they don’t understand politics. Apparently, politics without organization, which organization must have a name, now you can call it a leader, you can call it a spokesperson, you can call it a leadership council, but you have to have something. Where in the world is there a party or movement that says, “We don’t want a leader,” and I don’t know. My leader, in your leadership, this is a very bad thing that should be put aside, but Mr. Sheba, let’s bring evidence and examples to this statement. It’s very good. I’m going to name one by one. First, they talk about Mr. Mirsin Mousavi like this. He is a person who has been under siege and demanding the resignation of the Islamic Republic. You must know that Mr. Mousavi has been calling for the convening of a constituent assembly for two or three years. Well, Mr. Pahlavi should say one thing and two things. Wake up, Khomeini. Let me finish my speech. Mr. Pahlavi should come and say this. Sir, I don’t get along with anyone who supports Mr. Mousavi. But not only did he not say this, Mr. Pahlavi praised Ayatollah Montazeri in his recent speech at the American Jewish Anti-Defamation League. Now let me ask you, Mr. Shahbani Rad, do you also praise Ayatollah Montazeri ? Mr. Pahlavi praised Nasroon Sotoudeh. Mr. Sheibani Rad, do you praise Nasrin Sotoudeh? No, Mr. Pahlavi completely accepted and criticized the tortures of SAVAK during his father’s time and condemned the Rastakhiz Party. Mr. Sheibani Rad, do you approve of the tortures of SAVAK and express your disgust for them and condemn the Rastakhiz Party ? Mr. Pahlavi of the Green Movement spoke with a completely positive approach, both at that time and in the years that followed. Mr. Shabd, do you do this or not? I can say by tomorrow morning that Mr. Pahlavi himself always speaks as a liberal democrat, but those around him, like Mr. Shebin Rad, like Mr. Kiani, like others, are actually in the right position. I will also say one more thing. Just two days ago, I had a debate and actually a conversation with Mr. Kiani in Washington. Mr. Kiani said that while they are from his party, they are from the New Iran Party. He said that he considers the main achievement of the past two or three years to be polarization, and that polarization is between those who are called Iranists, which means They themselves, those around them, and the rest of us, for example, we who are Marxists, specifically the name of H. Of course, Mr. Ezz has another conversation, another story with someone else. I don’t want the discussions to interfere with us trying to answer someone else in this conversation. I am allowed to speak. Kiani, the editor-in-chief of Fereydoun magazine. Mr. Kiani, the editor-in-chief of Nash, is just a face like this. Just say, “Oh, let’s hear Mr. Sham’s words. Let’s listen to Mr. Sham’s speech. Is he against it? Is he against it? Or is he in agreement? Mr. Shabi’s answer. In addition to Mr. Ezair’s answer, we want to address the same differences between the royalists and the pro-prince faction. You yourself mentioned that you have been attacked, and even the prince himself is being criticized. Tell us about that too. Yes, look. I am very happy, very happy, especially for the people who see the scenes where no one can object to Prince Reza Pahlavi himself, the one who is elected as the leader. And I think that the three or four minutes that Mr. People like me, like Alireza Kiani and these people, and we, that we did not claim leadership, yes, no, let me talk, let me talk, we also did not claim leadership, that is, this is very important, and the more important point is that the question is about the future, that is, they ask whether you are or not, they do not say, “You said this somewhere.” This is an important point, so you have no examples so far. You are asking whether you are or not. In any case, I will say, let me talk. If the topic of the program is a carrier of evil, I will answer each and every one of these questions. You think that the program will not answer it, but I will give an example that I want to tell you and all the friends who call the pro-Iranian community the pro-Iranian community, the pro-Iranian movement, that we do not necessarily agree with Shah Zapala in these cases, and this shows the beauty of this movement. Incidentally, we know Prince Dad Pahlavi as the leader of this movement because we know that he is the one who can be that umbrella that covers many groups. When we are a political party, that is, We have a specific political ideology and we are not a partisan like them and their position. When a political party is supposed to be a partisan political party, it is not called a political party. It is a political party with a right-wing liberal ideology that has its name written in its motto. As the first secretary of the party, I can tell you about the New Iran Party why we believe so. But the Mecca is here. The leader of this movement is someone like Princess Reza Pahlavi. He is someone who has a partisan vision and a vision that can challenge other groups. I will just say one final point. Dear Princess Reza Pahlavi, the Renewal Party has also said many times that it fully supports in this regard that anyone, anyone I am talking about, should cross the Islamic Republic and see the entirety of this system as negative and see Bahman 57 as the real thing for Iran. Whoever wants to be, the doors must definitely be open to the nation and the nation must also fight the Islamic Republic. He should also try for the future of Iran, but the name you mentioned, for example Mir Hossein Mesin, I didn’t see him saying, ” Get rid of the Islamic Republic.” Yes, he has passed Seyyed Ali Khamenei, but his life is still alive. Khomeini is an executioner who we all know what he did. There are no exceptions. Yes, anyone who wants to get rid of the Islamic Republic should do so. In the remaining time, there are 10 minutes left. We will consider one minute for each of us to have a summary. Mr. Azizi agrees with some of the words of Shah Reza Pahlavi. He also knows that his role is to get rid of the Islamic Republic and then the situation at the ballot box is clear. Until then, see what your basic problem is. The thing is that you, the set of politicians’ behavior, are always calculating, not just talking. Our problem with Mr. Pahlavi is that I don’t know why he is my leader. Let him not talk. I do not accept Mr. Reza Pahlavi as a leader of the transition period in any way. As an Iranian citizen, I accept him as a political figure. For what reason? I don’t accept it because you see Mr. Shabani Rad saying, “Well, Mr. Pahlavi is a general, but we are a criminal, but we are not.” That’s right, but Mr. Pahlavi is managing this. From among those close to him, he asks for a consultant. There should be at least two people from another faction, but everyone agrees with Mr. Shabani Rad. Then this film has already been broadcast. And Mr. Pahlavi, I will simply tell you this. As an Iranian, you can see that Republican faction, Mr. Pahlavi, all the claims of those from Vahdat and Ittehad are nullified because Mr. Qasem Nejad, who sits next to him, Mr. Etemadi, who sits next to him. Right now, I don’t want to repeat myself, but he is the one who used the word “terrorist” for Iranian journalists abroad. You know, this is political behavior. Mr. Pahlavi cannot play this game, that he himself speaks like the Liberal Democrats, and then he speaks like this, for example, even the supporters of dear Mir Hossein like this. They should clarify their own duties. Mr. Pahlavi says, “What a right-wing sect. My opportunity is very limited. Mr. Sheibani. Why are there no people from other parties with other tendencies in Mr. Pei’s circle of close friends? Those who are close to Prince Pahlavi and are part of his team are not present in any party. They do not do any political activity. Precisely for this reason, Prince Reza Pahlavi and his close friends and the team that works with him are causing a cross-party movement. Mr. Azizi says that other groups, for example, are always interested in saying that they want to be monarchists. Of course, they use the term monarchist, which unfortunately has an offensive connotation in the Iranian political atmosphere. I think that their intention was to insult, but they do not pay attention to the fact that at this very meeting yesterday, a group of Republicans supporting Prince Pahlavi were present. At this very meeting yesterday, there were groups that did not agree that we should include the title of leader, so that those who might not even accept Prince Pahlavi as a leader would not be closed. While I personally strongly oppose this because if I talk about this and finish my speech, when I am not finished, you will see that there is a ship sailing in a stormy sea and we are fighting the monstrous republic of the Islamic Republic that has no mercy. We believe that the helm of this ship should be in the hands of someone and that person is none other than Shah Reza Pahlavi and this is us who I am talking about, including a large majority, perhaps an absolute majority, of Iranian society. You say that no, we are not our leader. Very well, you cannot ask us to let go of the helm, God willing, so that this ship will sail in the storm because you do not accept it or you are the leader and you declare to each other. If we see that in any case, these leaders can form an alliance with each other, have a conversation, talk and guide this ship together, or you sit on the sidelines and try to kick, throw stones, we can bring this ship to shore. We will defeat the Islamic Republic and we will develop and liberate Iran. Thank you both for the detailed discussion. I hope it can be continued at another time. Hamed Sheibani Rad, First Secretary of the Iran Novin Party from Geneva, and Arash Azizi, writer and historian from New York. Thank you both, gentlemen. But in other news, for decades, numerous internal and external crises have become an inseparable part of Lebanon, and of course, the new government of this country, headed by President Joseph O’Neill and Prime Minister Nawab Salam, is no exception to this rule. However, it can be said that the Lebanese government is facing two major external and internal crises. The Israeli issue, which is due to withdraw from southern Lebanon tomorrow, coincident with the end of the 60-day ceasefire with Hezbollah. Earlier, reports had been published that Israel does not intend to evacuate the strategic point in southern Lebanon. Today, the country’s army bombed some Hezbollah positions in eastern Lebanon, just one day after the warning of the Secretary General of Hezbollah. The only responsibility of the Lebanese government at this time is to make its maximum efforts, whether through political pressure and diplomatic communications or other means, to ensure that Israel withdraws on time and then move on. Internally, this crisis also comes back to Hezbollah. The current Lebanese government is trying to reduce Hezbollah’s influence to the lowest possible level. This bold approach began at the same time as Israel shattered Hezbollah’s military capabilities, and perhaps it can be said that it began on the first day of the ceasefire. The first major development in this regard in the Lebanese political arena was the attempt to elect a new president. Joseph Onn was elected as the country’s president after two votes by the Lebanese parliament. The second development was the election of Nawaf Salam as the 53rd prime minister despite Hezbollah’s opposition. But the climax of the story was the formation of the Lebanese cabinet, which this time, unlike previous periods, does not include any Hezbollah members. This unprecedented development was never imagined before. To better understand the story, it is enough to know that Hezbollah and its allies formed a total of 13 ministers in the previous Lebanese cabinet, while in the current government, only the post of minister has been transferred to the Hezbollah United Action Movement. Some decisions of the Lebanese government must be approved by two-thirds of the cabinet members, but the composition of the current cabinet Lebanon is in a situation where Hezbollah will not have the ability to veto important and fundamental decisions, unlike before. Another development has taken place during the 60-day ceasefire period. The formation of the second Trump administration was much faster in opposing Hezbollah, to the point that Morgan Ortagus bluntly declared that Hezbollah’s presence in the government is a US red line. However, it seems that from the current US administration’s perspective, even this reduction in Hezbollah’s weight in Lebanese domestic politics is not enough. Regarding Lebanon, our goal is a strong Lebanese government that can depose Hezbollah. On such a basis, one can understand the root of the recent clashes in Lebanon around Beirut’s Rafik Hariri Airport. The ban on two monthly flights to Lebanon and Hezbollah’s protest against this decision are signs of the group’s increasing predicament. On the other hand, Saudi Arabia’s support for the Lebanese government in dealing with the protesters is another sign of the new balance of power in Lebanon, which will not be good news for the Islamic Republic, the party’s main supporter. Mohammad Javad Akbar, a journalist from Paris, will join us to talk more about this matter. Mr. Akbarin In the midst of these internal and external crises, what kind of Lebanese should we expect after this? Can Hezbollah be left out of the Lebanese political arena for a long time? Good luck. Look, everything depended on the Islamic Republic. These planes that were banned from landing at Beirut International Airport were planes that, according to an announcement that the Secretary General of Hezbollah actually confirmed, the government had been informed that a large amount of money was accompanying these flights. Until now, they have been saying why extend the ban on these flights because of the risk that if money or weapons reach this flow, it will be possible to rebuild it. In the past, the Islamic Republic had access to this flow through land and sea routes in order to deliver weapons and money to them. Now, the land route has been closed. Due to the situation in Syria, the Islamic Republic no longer has the possibility of presence, penetration, or passage through Syria. There were also routes left by sea and by air that are now under strict protection and surveillance when flights are banned or if flights are resumed. This route will also be closed. Therefore, an important event will happen in the next few days. On February 23rd, next Sunday, Hezbollah is going to hold a grand funeral for Nasrallah and Safiadin. They said that even 400 media outlets from around the world are going to be there, and they are going to be hosting guests since two days ago. Well, Hezbollah intends to announce its revival in this program, to announce its resumption. The Islamic Republic’s effort, that is, its role, was to have Israel leave these sensitive points in southern Lebanon tomorrow, which it says are Hezbollah’s points of influence, and then this money was going to be delivered to them so that this grand ceremony could be held immediately after which Hezbollah’s next operations and Hezbollah’s next work could begin. What has happened now is that the Lebanese government closed an air route. Israel said that until my assignment with these five strategic points in the south is determined, I will not leave here. And we still do not have accurate information about whether an agreement and contentment have been reached or not. And third, Hezbollah will not be able to rebuild itself until these obstacles are removed, even though it still insists that The show on February 23rd can be held next Sunday with maximum splendor to see how the next paths will open up. But can you imagine Hezbollah trying to break away from this and continue on its own path without the support of the Islamic Republic? Look, the Lebanese government has done something that practically will not eliminate the Shiite movement. The Shiite movement in Lebanon cannot be eliminated. Millions of Shiites live in Lebanon. Even now, as your detailed report said, Hezbollah does not have a minister in the cabinet, but we have a Shiite in the cabinet who is not a member of the Hezbollah movement. What the new Lebanese government has done is to try to separate the Shiite account from Hezbollah. The quota of the Amal movement is another Lebanese Shiite party, even though it was allied with Hezbollah. Tell them to separate their account so that you can remain, but leave the rest of the Shiites alone. I think we are witnessing the birth of a new Shiite movement in Lebanon, which is neither Hezbollah nor Amal, a Shiite movement that has learned from and no longer wants to be subordinate to a foreign country. At the same time, it wants to blend in with other Lebanese sects so that it can participate in the new era of Lebanon, which is supposed to be an era of development and freedom, and not be excluded. Javad Akbar, this journalist from Paris, came. Thank you. But let’s talk about the US- Russia negotiations on the war in Ukraine, which will be held in Riyadh tomorrow without the presence of Kiev or European countries. US Secretary of State Mark Rubio, along with the National Security Advisor and the US Special Envoy for the Middle East, will meet with Russian representatives. Donald Trump said that both sides consider the war to be over and will probably meet with Putin soon. Today, Zelensky, who traveled to the United Arab Emirates, said that we do not recognize the US-Russia negotiations. At the same time, an emergency meeting of European leaders began in Paris an hour ago. Arash Ali, my colleague from the US Congress, will join us in Washington. Arash, how can you describe the atmosphere? How soon can you say that the end of the Russia-Ukraine war will come? Matt Mossour was a videographer. I had already said on the program that the Trump train is moving very fast, and now the storm that Mr. Jay Dunes unleashed after the Munich Security Conference. The speeches that the Vice President of the United States made in front of the heads of the European Union and many of these people who work in the parliament in Brussels, he addressed them one by one, asked their countries, asked why their countries cannot defend democracy, why each of these countries, Germany, France, named these countries one by one, and why they are now putting more pressure on American voters and taxpayers because they are putting the cost on the shoulders of the United States, and he criticized Germany in particular, and now Germany is heading towards elections during the election period and the political atmosphere is turbulent, while now Mr. Rubio has arrived in Saudi Arabia with Michael Wallace, the national security adviser, and Mr. Steve Whitaker, who is Trump’s representative for Middle East affairs, and now you can see live images of the Elysee Palace, where the leaders of the European Union are meeting behind closed doors and are deciding what their next step should be. The point is, what options does the European Union really have, how does the pyramids want to put pressure now? On America or Russia, in order to have a foothold in the negotiations that are going to take place tomorrow. And if you read the American press, the headlines of most of the press are full of various ambiguities. For example, I’m reading it now from memory. Bloomberg says that Vladimir Zelensky has said that talks without Ukraine are dangerous. Talks between the United States and Russia are very dangerous for Europe. Or the Wall Street Journal says that Mr. Trump is rewriting transatlantic relations, that is, relations between the two sides of the Atlantic Ocean. The main issue is that now Mr. Trump’s advisers and ministers are going to meet with the Russians. Kremlin spokesman Dmitry Peskov has also confirmed that representatives from the Russian Federation are going to be present. This will be the first stage, so that the two sides can sit down and work out their differences. Mr. Keith Kluge, who is Trump’s special envoy for Ukraine, told Zelensky that the last time that efforts for peace did not work out, Mr. Keith Kluge told Zelensky that the reason was that there were too many parties involved. There were so many parties at the table. The issue has become confused, and now Mr. Keith Klatt has said that it is in Ukraine’s interest, in your interest, and in Europe’s interest, to sit this part out for now, let the US and Russia have the initial talks, and then get into the details and maybe be able to have an impact on the negotiations between the US and Russia. My colleague, thank you from the US Congress, Arash. Today we reach the end of the first headline, Monday, February 29, at this moment, until tomorrow at the same level, right here.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Want an early peek at what’s new in VFP 7? This is the first article in a series where Doug Hennig discusses the language enhancements Microsoft is implementing and how to take advantage of some of them now.
In my article in the December 2000 issue of FoxTalk (“Reusable Tools: Have Your Cake and Eat it, Too”), I discussed “version envy”: wanting to use features planned in an upcoming version of a product in the current version. In that article, I discussed several new functions in VFP 7 and showed PRGs that can provide their functionality in VFP 6. After reading this article, FoxTalk editor Whil Hentzen thought it would be a great idea to extend this to covering all of the new commands and functions in VFP 7, both to familiarize you with them and to enable you, where possible, to use them in VFP 6.
So, we’ll start the new year off with this idea and spend several months looking at language enhancements in VFP 7. We won’t look at everything that’s new in VFP 7 (such as IntelliSense, editor improvements, DBC events, and so forth); it would take a whole book to cover such a broad topic. Instead, we’ll focus on functions and commands with three things in mind:
We’ll briefly consider the purpose of each command and function without getting too bogged down in details (for example, I won’t discuss parameters or return values in detail; you can get that from the VFP 7 Help).
We’ll look at practical uses for the commands and functions.
Where possible, we’ll explore ways to get the functionality now in VFP 6.
Keep in mind that, as of this writing, VFP 7 isn’t even in beta yet (it’s considered a Tech Preview version), and these articles are based on the version distributed at DevCon in September 2000. So, commands and functions might be added, removed, or altered.
Let’s start off with improvements to existing commands and functions, going through them in alphabetical order. Once we’re finished with these, we’ll move on to new commands and functions.
ALINES()
ALINES() has been a favorite function of mine since it was introduced. It parses a string (which may be in a variable or memo field) into lines terminated with a carriage return (CR), linefeed (LF), or carriage return-linefeed combination (CRLF) and puts each line into its own element in an array. ALINES() can now accept one or more parse characters, so the “lines” can be delimited with something other than CR and LF. For example, a comma-delimited field list can easily be converted to an array. Why would you want to do this? Because it’s easier to process an array than a comma-delimited string. Here’s a brute-force way of processing the items in such a string:
This is ugly code to both read and write. Here’s a more elegant approach that converts commas to CR, then uses ALINES():
In VFP 7, the first two lines in this code can be reduced to the following:
AMEMBERS()
This is one of those functions you likely don’t use very often, but it’s very useful at those times when you need it. AMEMBERS() fills an array with the properties, events, and methods (PEMs) of an object or class. There are two changes to AMEMBERS() in VFP 7: You can now get the PEMs of a COM object, and you can specify a filter for the PEMs.
Passing 3 for the third parameter indicates that the second parameter is a COM object. Here’s an example that puts the PEMs for Excel into laPEMs:
A new fourth parameter allows you to filter the list of PEMs so the array contains only a desired subset. For example, you might want only protected, hidden, or public PEMs, native or user-defined PEMs, changed PEMs, and so forth. This is handy, because prior to VFP 7, the only way to filter a list of PEMs was to use PEMSTATUS() on each one. For example, I use the following routine, CopyProperties, in VFP 6 to copy the properties of one object to another instance of the same class. Why would you want to do that? Imagine a form that you pass an object to and have the user modify the properties of the object in the form’s controls. What if the user wants to press a Cancel button to undo his or her changes? I decided to copy the object’s properties to another object of the same class, and then have the form work on the new object and, if the user chooses OK, copy the properties of the edited object to the original object. If the user chooses Cancel instead, the original object isn’t touched. So, the form creates another instance of the passed object’s class and then calls CopyProperties to copy the properties of the original object to the new instance. Here’s the code for CopyProperties (you’ll also find it in COPYPROPERTIES6.PRG in the accompanying Download file):
The VFP 7 version is a bit simpler. First, it uses the new “G” flag so the array only contains the public properties of the source object—we don’t have to use PEMSTATUS() to later ignore protected or hidden properties. Next, although there’s currently a bug that prevents it from working with array properties, we’ll be able to use the “C” flag so the array only contains properties that have changed from their default values; when this bug is fixed (notice that I’m being optimistic and didn’t say “if” <g>), we’ll be able to remove the PEMSTATUS() check for changed properties. Finally, I’ve submitted an enhancement request (ER) to Microsoft to provide a flag for read-write properties. If this ER is implemented, we’ll be able to remove the PEMSTATUS() check for read-only properties. Thus, the VFP 7 version will be simpler and faster than its VFP 6 counterpart. Here’s the code for COPYPROPERTIES7.PRG (I removed a few comments that duplicate those in the VFP 6 version in order to conserve space):
By the way, if you examine COPYPROPERTIES7.PRG, you’ll see that the header comments include my e-mail address and Web site, and that they appear blue and underlined, just like a hyperlink in your browser. Clicking on either of these gives the expected action (a Send Message dialog box with my e-mail address already filled in or my Web site in your browser). This editor enhancement makes it simple to direct other developers to your Web site for support, more information or documentation, updates, and so on.
TESTCOPYPROPERTIES.PRG shows how CopyProperties can be used. Change the statement calling CopyProperties6 to CopyProperties7 to see how the VFP 7 version works.
Here’s another use of AMEMBERS() that’s somewhat similar. PERSIST.PRG provides a way to persist the properties of an object so they can be restored at another time (for example, the next time the user runs the application). It creates a string that can be stored, for example, in a memo field in a table. This string contains code that can be used to restore the properties of an object. For example, the string might look like this:
After retrieving this string from wherever it’s stored, you’d then do something like this to restore the saved properties (in this example, lcPersist contains the string):
This example uses the new VFP 7 EXECSCRIPT() function, which I discussed last month.
I won’t show the code for PERSIST.PRG here, both because of space limitations and because it’s quite similar to COPYPROPERTIES7.PRG. To see this routine in action, run TESTPERSIST.PRG.
ASCAN()
Two things I’ve wished for a long time that Microsoft would add to ASCAN() are the ability to specify which column to search in and to optionally return the row rather than the element (to avoid having to subsequently call ASUBSCRIPT() to get the row). My wish was granted in VFP 7, plus ASCAN() gains the ability to be exact or case-insensitive. The new fifth parameter specifies the column to search in, and the new sixth parameter is a “flags” setting that determines whether the return value is the element or the row and whether the search is exact or case-insensitive.
Because I always want the row and never want the element number, normally want a case-insensitive but exact search, and have a difficult time remembering exactly which values to use for the flags (no wisecracks from younger readers <g>), I created ArrayScan, which accepts an array, the value to search for, the column number to search in (the default is column 1), and logical parameters to override the exact and case-insensitive settings. Here’s the code in ARRAYSCAN7.PRG (I omitted header comments and ASSERT statements for brevity):
In VFP 6, we can do something similar, but since we don’t have the new capabilities of ASCAN(), we have to use a different approach: We’ll use ASCAN() to find the value anywhere in the array, then determine whether it’s in the correct column. If not, we’ll change the starting element number and try again. ARRAYSCAN6.PRG has almost the same functionality as ARRAYSCAN7.PRG (although it’s slower and has more complex code), except support for case-insensitivity—to implement that feature, you’d have to do it via the brute-force method of going through each row in the array and looking for a case-insensitive match in the desired column. Here’s the code for ARRAYSCAN6.PRG:
TESTARRAYSCAN.PRG demonstrates how both ARRAYSCAN6.PRG and ARRAYSCAN7.PRG work.
ASORT()
VFP 7 adds one new feature to ASORT(): a case-insensitivity flag (in VFP 6, ASORT() is always case-sensitive).
BITOR(), BITXOR(), and BITAND()
These functions can now accept more than the two parameters they do in VFP 6; they’ll accept up to 26 parameters in VFP 7. This is useful in cases (such as some API functions and COM objects) where several flags have to be ORed together; in VFP 6, you have to use something like BITOR(BITOR(BITOR(expr1, expr2), expr3), expr4) to do this.
BROWSE
At long last, we get a NOCAPTION option for BROWSE that displays the actual field names rather than the captions defined for the fields in the database container. You can get this behavior in VFP 6 by running BROWSEIT.PRG instead of using BROWSE. This relies on the fact that a browse window is really a grid, so we can change the caption of each column to the actual field name. Here’s the code for BROWSEIT.PRG:
COMPILE
In VFP 7, the COMPILE commands (COMPILE, COMPILE CLASSLIB, COMPILE REPORT, and so forth) respect the setting of SET NOTIFY. With SET NOTIFY OFF, no “compiling” dialog box is displayed. This is important for two reasons: In-process COM servers
can’t display any user interface, and you likely don’t want your users to see such a dialog box. In VFP 6, we can suppress the dialog box by using the Windows API LockWindowUpdate function, which prevents updates to a window similar to VFP’s LockScreen property (although this won’t help in-process COM servers, since the dialog is still being called). The Download file includes LOCKWINDOW.PRG, which accepts .T. to prevent window updates and .F. to restore window updates. Here’s the code for this PRG:
To prevent the “compiling” dialog box from being displayed, use code similar to the following:
Conclusion
Next month, we’ll carry on examining improved commands and functions in VFP 7. When we’re finished with them, we’ll move on to new commands and functions.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the January 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Driving the Data Bus: Chatting on Company Time–Building a VFP Chat Module
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Driving the Data Bus: Chatting on Company Time—Building a VFP Chat Module
Andrew Coates
Passing data to and from applications over the network makes the synchronization of data simple. In this article, Andrew Coates uses a messaging hub server to develop a chat component that can carry more than just typed conversations between application users.
The chat system allows users across the network to exchange data in real time. Most commonly, this data consists of typed conversations, but the power of the system lies in its ability to pass other data as well. This could be in the form of audio or video or, as will be presented here, other text data that will allow the two chatting parties to retrieve the same database record by sending a primary key or keys from one to the other.
The chat system
To allow chatting, each client registers itself with a chat server as it starts up. The chat server maintains a list of connected clients and can detect disconnections automatically. If a client goes offline either by choosing to disconnect or due to network or some other failure, the server removes that client from the list. A schematic representation of the setup is shown in Figure 1.
Each client consists of a chat handler, which sends and receives the messages, and zero or more chat forms. A chat conversation is carried out between chat forms, which are created specifically for that conversation and destroyed at the conclusion of the conversation. A client may be involved in many chats simultaneously, so to ensure that messages are routed to the correct form, the chat handler employs a concept of “chat slots” or a collection of instances of the chat form so they can handle multiple chats simultaneously and not get crossed lines. A schematic of the clients, the chat handlers, and the chat forms is shown in Figure 2.
In Figure 2, there are two conversations being conducted. Client 1 is talking to client 2 and client 3. Because client 3 addresses its messages to client 1, chat slot 2, they never get mixed up with messages from client 2, which are addressed to client 1, chat slot 1.
Initiating a chat
To establish a conversation between two clients, one of the clients needs to initiate the chat. The initiating client sends a request to the intended recipient, and the recipient either accepts or declines the invitation to chat.
The process from the chat initiator’s point of view is shown in Figure 3.
When a client decides to start a chat, it creates a chat form and assigns the form a slot number. Next, it sends a request to the recipient that includes the sender’s ID and slot index. It waits for a response from the recipient, and if it gets the response, it shows the form and the chat begins. If it doesn’t get a response, it destroys the form and tells the user that the request timed out.
Responding to a request for a chat
The process from the chat recipient’s point of view is shown in Figure 4.
When the recipient receives a request to chat, it decides to either accept or decline the request. If it declines to chat, it simply sends a message back to the requester declining the invitation. If it decides to accept the invitation, it creates an instance of the chat form and assigns it to a chat slot of its own. As long as it creates the form successfully, it sends a message to the requester accepting the chat and informing it of the recipient’s chat slot index. It then displays the form, and the chatting begins.
Chatting
To chat, the two clients send messages to each other addressed using the client ID and the chat slot index. When a client receives a message, it passes it to the form in the applicable chat slot, and the form displays the message.
Ending the chat
When a chat form is closed, it removes itself from the chat slot collection and sends a message to the other client that it’s being closed. When a form receives a message that the other party has closed the form, it remains open so any further action can be taken by the client, such as logging the conversation to a table or other file, but it won’t allow the form to send any more messages.
InterCom System
The InterCom System server (available from www.cns.nl/) is a messaging hub—that is, an application that sits somewhere on the network where all users can access it and allows applications to connect to it, register interest in certain events (subscribe), trigger events for other users, send messages to specific users, and disconnect from the hub. It sits somewhere on an IP network (note that the system is restricted to running over an IP network), and any client on the network—which could include the entire Internet—can connect to the server. Any connected client can then send a message to any other connected client. Clients can also trigger an “event,” which is broadcast to all of the other connected clients that have subscribed to that event. The triggered event can have data associated with it. Therefore, clients can communicate:
with all other interested clients (by triggering an event to which other interested parties have subscribed);
with a specific client (by sending a message to a specific client ID); or
with the server to get information about the other clients connected to the system.
The client is in the form of an ActiveX control and can therefore be placed on a VFP form and its methods, properties, and events accessed as for any other such control. The InterCom System provides a client with three types of functionality—Notification, Messaging, and Inventory. The InterCom System is discussed in more detail in my article “Pushing the Point” in the August 1999 issue of FoxTalk.
At the time of this writing, the InterCom System costs $399 USD for a royalty-free, single-developer license. There’s also an evaluation version of the system available for free download. The evaluation system is limited to a maximum of three simultaneous connections, but it’s otherwise identical to the full version.
Wrapping the client
The InterCom client has one feature that makes it awkward to use in VFP. It employs arrays passed by reference to return lists of information. VFP doesn’t handle this data type well (the COMARRAY() function only appears to work with COM servers created via a CREATEOBJECT call, not with ActiveX controls). To overcome this limitation, a wrapper for the ActiveX control was developed in VB. The wrapper intercepts the method calls and translates the array into a delimited string, which VFP handles very well. The wrapper class is included in the accompanying Download file.
The chat handler
The core of the chat component on each client is the chat handler. This object is instantiated when the application starts up and provides the communications link to the InterCom server. It’s based on the Form base class, so it can provide a container for the ActiveX InterCom client control. The load method of the class simply checks that the wrapped InterCom client is installed on the system. The init method accepts a reference to the calling object (for callbacks) and the name or IP address of the InterCom server machine. It then attempts to connect to the server and returns a logical indicating success.
To initiate a chat, the application calls the chat handler’s StartChat() method, passing the descriptor of the client with whom the chat is requested. The sequence of events shown in Figure 3 then begins. The code for the StartChat() method is shown in Listing 1.
Listing 1. The chat handler’s StartChat() method.
In the example shown here, there’s the facility to pass two additional pieces of information with the chat request—a Company ID and a Contact ID. In this application, these are primary keys to two main tables. Passing these keys to the other client allows that client to retrieve the data about which the initiator wishes to chat, perhaps even displaying information from the relevant rows in the table as part of the chat dialog box.
After checking the validity of these key parameters, the method requests a list of clients matching the descriptor from the InterCom server. The request is rejected if no clients match. If there’s a matching client, the method obtains a chat slot and populates it with an instance of the chat form. The chat form sends a message to the remote client’s chat handler requesting the chat and sets a timer that will fire if the operation times out. It’s then up to the other client to respond within the timeout period.
The chat handler form class contains only one (significant) control—the InterCom client control. That control has only one overridden event—the one that processes incoming messages, which is called, originally enough, the OnMessage() event. The event code simply directs the message to the appropriate chat handler method. The OnMessage() event code is shown in Listing 2.
Listing 2. The chat handler’s InterCom client OnMessage() event code.
The important point to note from the OnMessage() event code is that the type of message being sent is stored in the message subject. All the OnMessage() handler does is work out what kind of message is being sent by reading the subject and then route the message to the appropriate message-handling method of the chat handler object.
The chat handler object has four main message-handling methods:
HandleRequest()—Handles an invitation to chat from a remote client.
HandleAccept()—Handles the acceptance of an invitation to chat.
HandleMessage()—Handles a standard message (usually a line of typed conversation).
HandleDisconnect()—Handles the message sent by the other party when they terminate the chat session.
HandleRequest()
The HandleRequest() method is fired on the remote client when an invitation to chat is received. The method initiates the sequence shown in Figure 4. The code for the HandleRequest() method is shown in Listing 3.
Listing 3. The chat handler’s HandleRequest() method.
The HandleRequest() method starts by reading the message and breaking it down into its component parts. Each part is sent in the message’s data property on a separate line. The handler prompts the user, inviting them to accept the chat, and if the user rejects the invitation, it simply sends a message back to the initiator rejecting the request. If the request is accepted, the chat handler finds the next available free chat slot (or creates a new slot if there isn’t one free already). It then populates that slot with a new instance of the chat form. If it’s instantiated successfully, the chat form handles the notification of the acceptance of the chat. If it’s not instantiated successfully, the chat handler sends a message notifying the initiator that the chat was accepted, but that technical difficulties prevented it from occurring.
HandleAccept()
The HandleAccept() method is fired on the initiating chat client when it receives acceptance of an invitation to chat from the remote client. The code for the HandleAccept() method is shown in Listing 4.
Listing 4. The chat handler’s HandleAccept() method.
The HandleAccept() method begins by reading the constituent parts of the message from the data parameter. It then checks to see whether the chat was accepted or rejected, either because the remote user declined or technical difficulties prevented the chat from occurring. If it was accepted, the remote chat slot is assigned to a property of the appropriate chat form, the timeout timer is disabled, and the chat form is displayed—everyone is ready to chat! If it’s rejected, a message is displayed to that effect, and the chat form is released and the chat slot cleared.
HandleMessage()
The HandleMessage() method is fired on receipt of a standard message—the type of message that’s passed back and forth between clients during the course of a chat. The code for the HandleMessage() method is shown in Listing 5.
Listing 5. The chat handler’s HandleMessage() method.
The HandleMessage() method simply breaks out the chat slot (so it knows where to send the message) and sends the text of the message to the appropriate chat form for handling.
HandleDisconnect()
The HandleDisconnect() method is fired when the chat handler receives notice that the remote client has disconnected from the chat. The code for the HandleDisconnect() method is shown in Listing 6.
Listing 6. The chat handler’s HandleDisconnect() method.
The HandleDisconnect() method simply breaks out the chat slot (so it knows where to send the message) and fires the HandleDisconnect() of the appropriate chat form.
The chat form
The other half of the chat client component is the chat form itself. This is the visible manifestation of the chat component where the user types messages and reads the messages typed by the other user. The chat form in our sample chat app is shown in Figure 5.
The chat form handles much of the communication once the chat handler has established the conversation. To do this, it uses the following key methods:
Init()—Responsible for notifying the remote client of some pertinent details and for actually displaying the form.
HandleDisconnect()—Responsible for handling the notification that the remote client has ended the chat session.
ReceiveMessage()—Responsible for displaying the text of a message received from the remote client.
SendDisconnect()—Responsible for notifying the remote client that the local client is terminating the chat session.
SendMessage()—Responsible for sending a line of text to the remote client.
Init()
The Init() method has two different behaviors, depending on whether the chat form is being instantiated as a chat initiator or a chat receiver. In the end, the functionality of each type of chat form is identical, but the process of creating the form differs depending on its role. The code for the Init() method is shown in Listing 7.
Listing 7. The chat form’s Init() method.
The Init() method accepts quite a list of parameters. The first is the mode in which this form is being instantiated. The allowable values are CHAT_RECEIVER or CHAT_CALLER (defined in chat.h). This information is used to determine the behavior of the object later in the Init() process. The next parameter refers to the local chat handler’s chat slot to which this chat form has been assigned. Next, a reference to the chat handler object is passed so the chat form can access its properties and methods. The next two parameters are additional data used in this sample application to pass the primary keys of two sample tables.
The keys can be used by the form to display the data applicable to the chat. The descriptor for the remote client is the next thing to be passed. This will be displayed in the form’s caption so the user can tell who this chat session is with. Finally, if the chat slot for this chat on the remote client is known, this is passed as the last parameter. If this is the chat receiver, the remote chat slot will be known, but if this is the chat initiator, the remote chat slot will be passed back as part of the chat acceptance message.
The parameters are assigned to properties of the form for later use, and the caption is set. Next, an application-specific method, SetButtonState(), is called. In this case, this method is designed to allow the retrieval of the linked data if primary keys have been passed to the form.
Now the code forks. If the form is a chat recipient, it sends a message back to the chat initiator, accepting the chat and telling the initiator the chat slot ID that’s been assigned for use on the chat receiver, and makes the form visible. If the form is a chat initiator, it sends a message to the chat receiver requesting the chat and sets a timer so the chat requester doesn’t wait forever for a response.
HandleDisconnect()
The HandleDisconnect() method informs the user that the remote user has disconnected and sets a local property of the chat form to indicate that the chat is no longer live. It doesn’t close the form, as the local user might wish to review the contents of the chat before closing the form. The code for the HandleDisconnect() method is shown in Listing 8.
Listing 8. The chat form’s HandleDisconnect() method.
ReceiveMessage()
The ReceiveMessage() method adds a line of text to the list box chat log. The code for the ReceiveMessage() method is shown in Listing 9.
Listing 9. The chat form’s ReceiveMessage() method.
SendDisconnect()
The SendDisconnect() method sends a line of text from the local client to the remote client. It also displays the line in the list box chat log for later reference. The code for the SendDisconnect() method is shown in Listing 10.
Listing 10. The chat form’s SendDisconnect() method.
The SendDisconnect() method builds a message string that simply consists of the chat slot ID on the remote client. It then calls the SendMessage() method of the chat handler’s InterCom client control. The message is addressed to the remote client’s client ID; it has a subject of CHAT_DISCONNECT (defined in chat.h), and the text of the message consists of the remote chat slot ID.
SendMessage()
The SendMessage() method sends a line of text from the local client to the remote client. It also displays the line in the list box chat log for later reference. The code for the SendMessage() method is shown in Listing 11.
Listing 11. The chat form’s SendMessage() method.
The SendMessage() method builds a message string that consists of the chat slot ID on the remote client and the text of the message. It then calls the SendMessage() method of the chat handler’s InterCom client control. The message is addressed to the remote client’s client ID; it has a subject of CHAT_MESSAGE (defined in chat.h), and the text of the message consists of the remote chat slot ID and the line of text to be displayed. Finally, the line of text is added to the chat log list box on the local chat form.
Sample code
To demonstrate the use of the chat component, you’ll need the following:
The InterCom System server installed somewhere on a TCP/IP network. For the purposes of this exercise, it’s assumed that the server is installed at IP address 192.168.0.1.
The InterCom System client installed on all machines that are going to act as chat clients.
The InterCom client wrapper (available in the Download file) installed and registered on all machines that are going to act as clients.
An instance of VFP for each chat client (either running on the same machine or on separate machines). Note that the evaluation version of the InterCom server only allows three concurrent connections. The full version has no such limitations.
The class library CHAT.VCX (available in the Download file) extracted to a commonly accessible location. For the purposes of this exercise, it’s assumed that the path to the class library is \SERVERUTILSVFPCHAT.
Once these steps are complete, issue the following commands from the VFP command window for each instance of VFP. Substitute a unique number for n.
Next, on one of the clients, enter the following command, where n is the number of another client:
The second client should pop up a message box asking whether to accept the chat, and, if the chat is accepted, a chat form should be displayed on both the caller and receiver. Messages typed on one client should appear on the other (after the Enter key is pressed).
Conclusion
Being able to communicate with other users of an application in real time and with the facility to link the conversation with data from the application adds another powerful resource to the programmer’s toolbox. The techniques presented in this article combine a commercially available solution to inter-application communication with VFP’s data-handling and UI.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the February 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
ClassNavigator
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
ClassNavigator
Jim Booth
Multi-tier system designs often require that we use base classes that can’t be defined visually. This means using PRGs to define our classes. The Class Browser built into VFP is a wonderful tool for navigating the hierarchy of class definitions. However, when we use programmatically defined classes, the value of the Class Browser is lost. This month, Jim Booth introduces Michael G. Emmons of Flash Creative Management (now GoAmerica), who has a solution for us.
Most of the classes we create in Visual FoxPro can be built using the visual class designer. For these visual classes, we have the Class Browser tool. However, there are a number of classes in Visual FoxPro that can’t be created in the visual designer. Classes like the column, page, session, and others must be created in program code using the DEFINE CLASS construct. For these classes the Class Browser fails, but the Class Navigator from Michael Emmons succeeds.
Simple installation
One thing that I dislike is a developer tool that takes a genius to install it and get it working. Michael has given us a utility that installs as simply as is possible. Just copy the APP file, ClassNavigator.app, to any directory on your machine, and it’s installed and ready to go.
The test run
To test this tool, I created two program files, as demonstrated in the following code:
Notice that the class defined in TestClass2 is a subclass of the one defined in TestClass.
Running ClassNavigator
Next, I ran ClassNavigator.app and was greeted by the screen shown in Figure 1.
There are four tabs—labeled Classes, Files, Options, and About—that are used in viewing the classes. The first tab we’ll visit is the Files tab, and we’ll select the Add button. In the file selection dialog box, I chose TestClass.prg; the resulting display is shown in Figure 2.
Switching to the Classes tab and expanding the tree gives the display shown in Figure 3.
The display has been expanded to show the existing details. You can see the filename and base class under Info, the Customer property under Properties, and the Custom method under Methods, just like the Class Browser would show us for a visual class definition.
Double-clicking on the class name in this display will open the editor with the program loaded for editing.
Hierarchies from multiple programs
Now return to the Files tab and open the TestClass2.prg file. The new Classes display is shown in Figure 4.
The Classes tab now shows us the hierarchy of these two classes, including the file information regarding each class.
Where to get ClassNavigator
This tool is included in the accompanying Download file. It’s also available at www.comcodebook.com, where future updates will be posted first (as well as the COM Codebook application framework). The source code for the Class Navigator is included in the download. The tool is freeware; you’re free to use or modify it to your desire, but you’re restricted from selling it to anyone else.
Summary
Michael G. Emmons has given us a utility that allows us to view classes that are defined in programs rather than visual class libraries. With his tool, we can see the inheritance hierarchy of these classes even when they cross multiple program files. A simple double-click opens any one of the classes for editing.
Michael and Flash Creative Management (now GoAmerica) have graciously made this tool, as well as many other tools including a complete application framework, available to us all at no charge.
The Component Gallery: VFP’s Best Kept Secret
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
The Component Gallery: VFP’s Best Kept Secret
Markus Egger
Visual FoxPro 6.0 shipped with a great new tool called the “Component Gallery.” This tool is mainly thought to be a productivity-enhancing tool that makes the use of components, classes, and other items found in Visual FoxPro much easier. Unfortunately, so far, very few third-party vendors and development teams have utilized the Component Gallery to the extent one would expect, making this great new addition to VFP one of VFP’s best kept secrets. In this article, Markus Egger not only demonstrates how the Component Gallery can be used, but also how you can create your own catalogs.
When you start the Component Gallery from the Tools menu, it starts up with several default catalogs already loaded. Catalogs are simply collections of items stored in hierarchical fashion. You can think of each item stored in the Component Gallery as a shortcut to a Visual FoxPro item such as a class, a form, or a report. You might wonder what makes this tool valuable since we already have access to these items through the Class Browser as well as the Project Manager. That’s correct; however, there’s a significant difference: The Class Browser as well as the Project Manager are built as tools for the developer who needs access to every single item in the project. They show all classes, support classes, include files, bitmaps, and much more. The Component Gallery, on the other hand, simply provides access to the top-level items such as classes or components, but hides the complexity under the hood. You can compare this to a car mechanic who needs access to every part of the car, including the engine, transition, and car alarm, while the driver who simply uses the car to get to work every day doesn’t worry about all of those things.
If you’re the “application mechanic,” you’ll be better off using the Class Browser or the Project Manager. It will provide access to every little bit of the application. This is especially true if you wrote the entire application by yourself. However, if you’re using third-party tools or even components written by your fellow co-workers, you might not need this level of granularity. In fact, it will make it much harder to use those components. Imagine that a team member provides you with a class library that you can utilize for a specific task. This library contains a number of classes, one of which is useful to you. All of the other classes are really just helper classes that the main class depends on, such as parent classes, members of the class, or other classes that are utilized by the main class to do its job. In addition, you get a number of external files such as include (.H) files, bitmaps, and XML definition files that are also required to make this component work.
Do you really care about all of this information? Of course not! Or, well… you shouldn’t. Why would you want to become the mechanic of somebody else’s “car”? However, in reality, you’ll have to familiarize yourself with all of those components to make sure you add them all to your project and to pick the right class in the first place. Little do you know at this point that your co-worker tried to make this class as easy to use as possible and even provided a builder that can be used to configure this class.
The Component Gallery will help you in this scenario. Your colleague can create a simple catalog that provides a link to the main class. When you drag and drop that link on your form, not only will the class get added, but, at the same time, all external references are taken care of, and immediately the provided builder starts to make it easy for you to configure the required properties rather than finding out manually which properties have to be set.
You say this isn’t a problem for you, because you don’t work in a team, or perhaps the team is so small you can simply ask the co-worker? Well, what about third-party products? At EPS, we produce a product line called the FEC (Fox Extension Classes). This is simply a set of classes stored in class libraries, plus a few external dependencies, just as described in the preceding scenario. We gave a lot of thought to the product architecture, resulting in a very flexible environment that farms most of the functionality out to special behavior objects that can be configured and extended at will. The downside of this architecture is that 80 percent of the classes shipped in the libraries aren’t made to be used directly, but are designed for internal use only. How would you like to navigate all of the information through the Class Browser? So there!
Another great advantage of the Component Gallery is its hierarchical representation of the information. It allows organizing items into folders. This is surely much easier to use than the inheritance-view provided by the Class Browser, or the flat-view provided by the Project Manager.
Using the Component Gallery
So let’s have a look at the Gallery. When you start it the first time, it shows several default catalogs that provide access to the FFC (Fox Foundation Classes) as well as VFP’s samples (see Figure 1).
Most of the items referenced in the default catalogs are FFC classes. Note that the Gallery can maintain a large number of different items, such as forms, reports, wizards, files, utilities, ActiveX controls, and much more. Basically, any Visual FoxPro as well as Windows item can be referenced through the Gallery. In addition to static links to items, the Gallery also supports a feature called “Dynamic Folder.” Figure 1 shows a reference to such a folder. It’s named “Installed Controls.” This folder automatically shows a list of all COM components and ActiveX controls installed on your system.
To use a specific class such as the FFC VCR Buttons class, simply select the item and drag and drop it to your form or class that’s open in the designer (note that the Gallery can also be used for source code editing). It will automatically add an instance of the class to your form. You don’t have to worry about what library it’s stored in, nor are you bothered with all of the other classes stored in the same library. The Gallery abstracted all of that complexity away and provides simple, categorized access to this component. Note also that the Gallery displays a description of the component you selected.
You want to know how much easier this is than using the Class Browser? Well, just right-click on the item and select “View in Browser.” This automatically opens the library in which the class is stored and switches the Component Gallery into Class Browser mode (the Gallery and the Browser internally are really the same application). The result is shown in Figure 2.
As you can see, this view is much more cryptic. Not only do you see all kinds of classes you have no interest in (how about those custom classes—what’s your guess, can they be used by themselves or not?), but you also see class names that are much harder to understand, and the Class Browser doesn’t even provide a description.
Another good example is the list of samples that ship with VFP. Perhaps you’re not aware of this, but the FoxPro team has produced a large number of examples that explain many aspects of the Visual FoxPro environment and language. In previous versions, this information was hard to find. Samples are scattered over all kinds of directories, and who wants to run all of them just to figure out what they’re doing? The Component Gallery ships with a special catalog that lists all of the included samples and provides access to them in ways that make sense for each individual example (see Figure 3).
As I mentioned earlier, the Component Gallery can also be used to automatically trigger builders whenever a complex class is dropped in a designer. Try dropping the “Field Mover” class from the Data Navigation folder in the main Visual FoxPro catalog. Immediately, the builder shown in Figure 4 starts up and asks you to provide important property values. You can drop the same class from the Class Browser or the Form Controls toolbar, but then you’d have to go through an almost endless list of properties and try to figure out what they’re for, whether or not they’re important, and what the appropriate settings are.
Creating your own catalogs
So, by now are you convinced of the usefulness of this tool and eager to provide your own catalogs for your libraries, or perhaps even commercial third-party products? Well, you’ve come to the right place.
Creating a new catalog is easy. Simply click the option button and select the Catalogs page in the options dialog box (see Figure 5). Click the New… button and specify the name of the catalog file (which is a regular DBF file). Initially, the new catalog is listed by its filename. We’ll define a friendlier name a little later. Click the OK button to close the dialog box and open the new catalog right away.
To rename the catalog, right-click the item and choose Properties (the Rename feature doesn’t appear to work). This launches the dialog box shown in Figure 6. Note that you might not see all of the same options shown in Figure 6. If this is the case, open the Options dialog box and check the “Advanced Editing Enabled” feature on the first page. You can use the Properties dialog box not only to change the name, but also to set descriptions as well as icons that are to be displayed by the Gallery.
To make the new catalog useful, you’ll have to add some items. First of all, add a new folder that provides links to the classes you’d like to keep track of. You can create new items, including folders, by right-clicking in the right-hand pane (see Figure 7). Again, you have to open the Properties dialog box to rename the folder and set other properties such as the icon.
You can now proceed to add items to that new folder, in the same way you created the folder itself: Right-click in the right-hand pane and choose to add a new Class. The Gallery will present you with a GetClass() dialog box to select the class. The item added to the Gallery will automatically be assigned the name of the class, but again, you can change this to a friendlier name through the Properties dialog box.
This is all you have to do to create a catalog for your classes. Note that the Gallery is smart enough to attach the proper behavior to your new class item. You can double-click on your class to edit it; you can drag and drop it to the form or class designer to create an instance; you can right-click on the item and open the class in the Class Browser; and much more. As you can see in Figure 7, the Gallery can handle a large number of different items and attaches the right behavior to them. This way, report items can be printed or previewed, video files can be played, tables can be opened, and so forth.
But what if you’d like to add items that the Gallery isn’t aware of? A little while ago I wrote a public domain utility called GenRepoX (you can download it for free from www.eps-software.com). It extends the Visual FoxPro Report Writer, but it uses the internal report engine. Reports can be modified in the same way regular Visual FoxPro reports can be modified, but to execute the reports, a special function has to be called. Otherwise, the special features provided by GenRepoX will be lost. The Gallery is aware of reports and allows modifying, printing, and previewing them, but of course it isn’t aware of GenRepoX. Luckily, there are ways to teach the Gallery new behaviors.
All Component Gallery behaviors are encapsulated in individual objects. The source code of those classes ships with Visual FoxPro in the _gallery.vcx and vfpglry.vcx class libraries. The behavior you need for GenRepoX is very similar to a regular report behavior, which is defined in the _reportitem class in the vfpglry.vcx library. To reuse what’s already there, you can simply subclass this behavior. I chose to call the subclass “GenRepoXItem.”
There are two methods we’re interested in: Run() and SetMenu(). Those methods do what you think they would. Run() executes the item, and SetMenu() creates the right-click menu. Our major modification will be making the Run() method aware of GenRepoX, which can be done like so:
I basically overwrite all of the default behavior and replace it with my own, which is rather simple in this case, since all I do is execute the report in preview mode. In the second line, I create a command that executes the report. Note that the cFileName property tells us what report filename the item is linked to (once it’s used in a catalog). In line 3, I execute the report by running it through the GenRepoX() method.
This is enough to make the new item work. However, I’d also like to give the user a little visual clue that the item at hand isn’t a standard report, so I decided to modify the menu. The SetMenu() method is responsible for displaying the menu. Each behavior provided by the Gallery has default menu items. Some of those items are defined in each behavior, others (such as Cut, Copy, and Properties) are provided by the parent class used for all items, named “_item.” In our scenario, I’d like all of the default items, but I don’t want report-specific items, since I want to introduce my own. In do this in the following fashion:
In line 3, I execute the item defined in the _item class. Note that I specifically name the class rather than issuing a DoDefault(), because I intend to skip the behavior defined in the direct parent class, which is the report item.
In the next line, I add a new menu item using the AddMenuBar() method, which exists on every Gallery item. Parameter one specifies the caption, and parameter two specifies the method that’s to be executed when the item is selected. In this case, I simply execute the Modify() method, which I inherited from the standard report item. Note the special “oTHIS…” syntax. oTHIS is a special pointer that allows me to access the current object. The SetMenu() method is called by a Gallery internal mechanism, so by the time the menu is actually displayed, my object isn’t accessible through the THIS pointer anymore, which is the reason for this special naming convention used by the Gallery.
The last parameter is interesting, too. From within an item, “THIS.oHost” always links you to an instance of the Gallery itself. The Gallery has a property named “lRunFileDefault” that tells you whether the Gallery is configured to run items whey they’re double-clicked (.T.) or modify them (.F.). In the menu, I’ll print the default item in bold as required by the Windows interface guidelines. I specify this using the last parameter. So if lRunDefaultFile is set to .F., I pass a .T. as the parameter, and vice versa.
I’m sure you can now figure out the last line by yourself. It simply displays another menu item labeled “Preview GenRepoX,” and it executes the Run() method (the one I coded earlier) when selected.
Now all that’s left to do is tell the Gallery about the new item type. I do that in the Properties dialog box of the main catalog item (see Figure 8).
From now on, the new item shows up in the New Item menu (see Figure 9).
Once you’ve added a GenRepoX report item, you can right-click it to see and use the behavior you added. Note that the item also exhibits default behavior that makes a lot of sense for our use. The icon defaults to a report, for instance (see Figure 10). In addition, the item uses the custom menu and behavior. You can use the right-click menu or double-click the item to execute the GenRepoX report.
The options outlined in this article only represent a small example of what’s possible with custom items. The possibilities are truly endless. You can find more information about this subject on the Microsoft Visual FoxPro Web site (https://msdn.Microsoft.com/vfoxpro), as well as in the documentation and in several books (such as my own <s>).
So far, so good. The new catalog is coming along nicely. But what if a user I’ve provided this catalog to has questions? Perhaps I should provide a link to my Web site. I can do this easily by adding a dynamic folder to my catalog. I add the folder just like any other folder, but this time, I set the folder’s “Dynamic folder” property (in the Node page of the folder’s Properties dialog box) to www.eps-software.com. That’s all there is to it!
Conclusion
The Component Gallery is a very powerful tool. Unfortunately, there have been very few products and projects that make use of it. Partly, this appears to be due to the poor documentation, but it’s also due to the not terribly obvious advantages the tool provides. However, once you’ve started using the Gallery, you’ll have a hard time living without it.
If you have questions regarding the Gallery’s use, custom catalogs, and even AddIns (a possibility I couldn’t discuss in this article), feel free to e-mail me at Markus@eps-software.com.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the April 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Understanding COM+ with VFP, Part 1
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Understanding COM+ with VFP, Part 1
Craig Berntson
When COM+ debuted in Windows 2000, it introduced many new capabilities to enhance n-tier applications. Now with VFP 7, you can use these enhancements to make your applications more robust. In this first installment of a series, Craig Berntson reviews COM and MTS, and then introduces COM+.
For many years, we’ve heard about the importance of breaking applications into multiple tiers or services. By splitting the user interface, business rules, and database access, we can easily modify or completely replace one service without affecting the others.
Historically, Visual FoxPro applications have been single-tier solutions, even if the data resides on the server. This is because developers have mixed the user interface, business rules, and data access into one application or even the same form.
With the use of SQL Server, we move to a two-tier scenario. The data is normally accessed via ODBC by the use of Remote Views or SQL pass through. Stored procedures are often called on the server, and the SQL SELECT statement is resolved before sending any data across the wire. It’s the splitting of the processing onto the server and the workstation that makes this design two-tier.
In a three-tier solution, the user interface only displays data and accepts input from the user. There might be some minor data validation, such as ensuring that the required fields are populated or limiting the user’s selection via a list or combo box. However, all of the actual processing of the data takes place in a separate component that holds all of the business rules. Calculations of totals or taxes, validation of data, or the generating of report data are examples of things that occur in the middle-tier business logic. Finally, the data tier is responsible for the reading and writing of data into the data store. The user interface should never directly access the data services, but should go through the business services layer to get at the data.
This separating of multiple tiers is what Microsoft calls the Distributed interNetworking Architecture, or DNA. The different components of each service can reside on the same computer, making a logical separation of each service—or, on multiple computers, providing a physical separation of the tiers. Typically, the user interface resides on the client computer, while the business and data components reside on an application server with the data store on a second server. When access is via a Web browser, an additional server for IIS is often added to the mix.
COMmon knowledge
The way to access these components is via the Component Object Model. COM is a specification that allows components written in different languages to interact with each other. Therefore, we can create a component in VFP that can be accessed from a VB or Delphi application, or even from Word or Excel. ActiveX controls are another example of COM objects. When you control Word or Excel from your VFP application, it’s done via COM.
The first thing to consider when creating a COM component is how it will fit in with the other pieces of your application. In other words, you need to determine whether it should run in-process or out-of-process.
An in-process component is compiled as a DLL and must be hosted by an executable program. It runs in its host’s memory space—hence the name in-process—which makes instantiating (running) the component fast. Data is marshaled (passed) across the COM boundary. Because the component runs in the same memory space as the application, if the component crashes, it most likely will cause the application to crash. One other thing to keep in mind: In-process servers written in VFP can’t have any user interface exposed.
An out-of-process server is compiled as an EXE and runs in its own memory space. When it’s instantiated, there’s some overhead required such as allocation of memory, process id, and so on. This all takes time, which makes instantiating an out-of-process server slower than an in-process server. In addition, it takes longer to marshal data across the process boundaries from the application to the component, so it runs slower. However, because the COM server is running in a different memory space than the client application, if the component crashes, the application will quite possibly keep running.
Creating a COM component in VFP is quite easy. The OLEPUBLIC keyword tells VFP to compile the code with the proper COM information needed for access from other applications:
When you build the component (see Figure 1), you can choose “Win32 executable/COM server (exe)” to create an out-of-process server. To build an in-process server, select either “Single-threaded COM server (dll)” or “Multi-threaded COM server (dll).” I’ll talk more about the difference between the two types of DLLs later. Building the component will automatically register it on the development computer. You then instantiate it using the CreateObject() function:
Many of the rules and intricacies of COM are automatically handled for us by VFP. However, we have to manually follow one rule. That rule states that we should never change the interface of a component. If we do, we need to create a new ID for the component. By interface, I don’t mean user interface, but the public methods and parameters of the component. Let’s look at the preceding example. If we add a third parameter to the Multiply method, we change the interface and need to create a new component ID. This is done on the Build dialog box. The last option on the dialog box is “Regenerate Component IDs.” Check the option to create a new GUID for the component.
When you start deploying your COM components on remote servers, you’ll access them via Distributed COM (DCOM). Historically, under DCOM, you distribute an out-of-process server and set up the calling information on the client computer. Chapter 16 of the VFP Programmer’s Guide goes into detail about how to do this. When you install the component on a remote server, the code runs on the server, not on the client workstation. Don’t have any UI in your server because it will display on the server, not the client workstation.
MTS to the rescue
Microsoft saw the need for a better way for remote components to run, so they created Microsoft Transaction Server (MTS). Originally available for NT and Windows 9x through the NT 4.0 Option Pack, MTS solved a number of problems by providing a host for COM DLLs. It also provided a wizard that set up all of the DCOM calls on the client station for you. Some other features of MTS include:
Just-in-Time Activation: A component is kept on disk and then brought into memory (activated) only when needed.
Object Request Broker (ORB): MTS will handle multiple calls to the same component from multiple clients.
Transaction Services: Commits and aborts are handled by the Distributed Transaction Coordinator (DTC) instead of the application. This makes it possible to have a transaction that spans multiple databases.
Role-based Security: The security scheme allows you to determine who can access your components based on NT logins and groups. If a user doesn’t have authorization to access a component, an error message is returned to the client indicating that the component can’t be instantiated.
Connection Pooling: Typically, an application will make a connection to the data store, and then hold that connection during the life of the application. MTS allows multiple clients to use the same connection.
Creating components for use under MTS requires that you think differently about your application design. First, your application should be stateless. This means that your client program should instantiate the component, make calls to a method, and then release the component. The connection to the component should be as short a time as possible. You should avoid setting properties and pass all of the needed information as parameters. Note that COM doesn’t allow parameters to be passed to the Init method.
You also need to think about threading. We typically think of threading as single or multi-threading. VFP creates single-threaded applications. That is, it can only do one thing at a time. This is like going to the grocery store and only having one checkout stand open. All customers must go through the same line. Only one customer at a time can be helped. The others wait in the queue for their items to be processed.
Multi-threading allows your application to split processing into different pieces, all running simultaneously. Using the grocery store example, you can unload parts of your shopping cart into different lines and have all of your groceries rung up at the same time.
MTS uses a third type of threading, apartment model. Again using our grocery store example, customers may choose any open checkout stand, but once you’ve chosen one, you always have to use the same one. Luckily, MTS will open a new line for us when all are used.
So, how do we make use of MTS in our VFP components? First, we have to add some code. Let’s modify our Multiply example to handle MTS.
The Context object contains information about our particular instance of the COM component. Also, note the call to SetComplete(). This will commit any open transactions. If we need to abort the transaction, we would call SetAbort() instead.
When we build our component, we can’t use “Win32 executable/COM server (exe).” MTS requires that components be DLLs. That leaves us with two choices: single or multi-threaded.
The single-threaded DLL is a good choice when the method call will be very fast or there’s the possibility that only one user will hit it.
The multi-threaded DLL isn’t truly multi-threaded. It’s apartment-model threaded. Make this choice when the method call is slow or many users will simultaneously call your component.
Once you’ve built your component, you install it on the server with the appropriate VFP runtime libraries. Then, you create an MTS package and import your component using the MTS Explorer. Using MTS Explorer, you can set security and transactional support, and export a client setup program.
You can get more information on MTS from Randy Brown’s article, “Microsoft Transaction Server for Visual FoxPro Developers,”.
Windows 2000
When Microsoft introduced Windows 2000, it came with several new services. One of those is COM+. Basically, COM+ is the marrying of COM and MTS, but new COM+ features were also introduced. Under Windows NT, MTS ran on top of the operating system. Under Windows 2000, it’s integrated into the OS. COM+ is only available in Windows 2000. However, Windows 95, 98, Me, and NT users can use COM+ components running on a Windows 2000 server. COM+ not only includes (and enhances) the features of MTS, but also introduces new services: Queued Components (QC), Loosely Coupled Events (LCE), Object Pooling, and Dynamic Load Balancing. In the next installment of this series, we’ll begin to delve into these services in detail.
COM+ Applications are administered through the Component Services Manager (see Figure 2). You’ll find it in the Administrative Tools group in the Windows Control Panel. Let’s walk through registering the component that we saw earlier.
Expand the tree under Component Services until COM+ Applications is available.
Click on COM+ Applications to make it the currently selected node, and then right-click on COM+ Applications.
From the context menu, select “New Application” to launch the COM Application Wizard. Then click Next.
Click “Create an empty application” (see Figure 3).
Enter the name for your application. In the example, I’ve called it “MyFirstCOMApp.” Then select the Activation Type. Normally, you’ll select Server application because your component will run on a server. If you install the component on a workstation and want it to run in your application’s memory space, then select Library application (see Figure 4). Click Next.
Select the User ID that the component will run under. When installing on a server, it’s a good idea to set up a user specifically for your component. Be sure to assign the proper rights to the user so that the component will have access to all of the drives, directories, and resources that will be needed (see Figure 5). Click Next, then Finish.
We now have the application set up, but it doesn’t contain any components. We have to add the component to the application:
Click the plus sign (“+”) next to our new COM+ Application to expand the tree.
Click on Components, and then right-click on Components. Select New Component from the context menu to launch the Component Install Wizard. Click Next.
The wizard gives you three options: Install new component, Import component(s) that are already registered, or Install new event class(es). We’ll use the third option when I talk about Loosely Coupled Events. The second option, Import component(s) that are already registered, is used when you’ve previously installed the component on the computer. However, at the time this was written, there was a bug in Windows 2000 that caused this option to not work correctly. That leaves option 1. Click the button next to this option (see Figure 6).
You’ll next be prompted to select the DLLs to install. If you don’t have the proper VFP runtime files installed, you won’t be able to select and install your component (see Figure 7). Once you’ve selected your components, click Next, then Finish.
Now that your component is installed, how do you access it? The same way as before. Just use CREATEOBJECT() to instantiate the component and you’re ready to go.
Summary
We’ve covered quite a bit of ground in this article, but most of it should be review. You might be wondering whether all of this COM stuff is still useful in a .NET world. The answer is Yes! COM still exists in .NET. In fact, .NET was originally called COM+ 2.0. In upcoming articles in this series, I’ll discuss security, distribution, loosely coupled events, transactions, queued components, and other COM+ features.
Sidebar: It’s GUID for Me, is it GUID for You?
A GUID (pronounced GOO-id) is a Globally Unique Identifier. It’s a 128-bit Integer and looks something like {1EF10DF8-8BF9-4CD7-860A-8DCD84EA3197}. The GUID is generated using a combination of the current date and time, a counter, and the IEEE machine identifier from the network card. The chances of two GUIDs being the same are extremely remote.
So how is this GUID used? When you build a component, three files are produced. The first is the DLL, and the second is a Type Library (TLB). The TLB is a binary file that lists all of the public classes, properties, methods, and events in your automation server. The third file is a Registry file (VBR). This lists the GUIDs for your server and is used to register your component.
When you register the component, the VBR file is used to make Registry entries about your component. Things like the directory location of the DLL, its threading model, and its public interfaces are placed in the Registry. When you instantiate the component, the server name—for example, Excel.Application—is looked up in the Registry. The GUID will then be used to get additional information about the component, such as the directory location and public interfaces.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the May 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Understanding COM+ with VFP, Part 2
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Understanding COM+ with VFP, Part 2
Craig Berntson
In Part 2 of this series, Craig Berntson examines client distribution, security, and error handling.
In Part 1 of this series (see the May 2001 issue of FoxTalk), I reviewed COM and MTS and introduced COM+ services under Windows 2000. I also showed you how to create a new COM+ application and install a component on the server. Now in Part 2, I’ll look at how to get the client to use the component on the server, security, and error handling.
Is the client always right?
Last month, I showed you how to install your application on the server. But that doesn’t do much good if you can’t get to the component from the client. The good news is, Windows 2000 makes it easy to set things up on the client. Let’s go back to the component we built and installed last month. As a quick review, here’s the component code:
I call this the world’s dumbest COM component. All it does is multiply two numbers. However, keeping the sample code simple allows us to concentrate on the COM+ aspects of the example.
Now we need to do an install on the client. Let’s go back to the Component Services Manager. Expand the tree under COM+ Applications and select MyFirstCOMApp. This is the COM+ Application that we built and installed last month. Now right-click on MyFirstCOMApp and select Export. The COM Application Export Wizard will appear (see Figure 1). You’re first prompted to enter the full path and filename for the application file. Enter C:COMAppsMyFirstCOMAppProxy. Then make sure you’ve selected “Application Proxy—Install on other machines to enable access to this machine.” (The other option, “Server Application,” is used when you want to install the component on another server.) Then click Next and then Finish.
The wizard has created two files—MyFirstCOMAppProxy.MSI.CAB and MyFirstCOMAppProxy.MSI. These files can be copied and installed on the client computer. These files don’t include the component. You don’t need it on the client. They contain information on the public methods and properties of the component and pointers to the server so that Windows can find the component and instantiate it.
Again, you never install the component on the client. Instead, you install a proxy. Your application thinks the component is installed and running locally, but it isn’t. Make note of this. A component installed on an application server never runs on the client. For some reason, this is a difficult concept for some people to understand.
You instantiate the server component exactly the same way you instantiate a local component. Try this in the Command Window:
Why did this work? VFP makes a call to Windows, asking for the component to be instantiated. Windows looks up the component information in the Registry and finds that the component lives on an application server. Windows then instantiates a proxy to the component and makes a call to the server to instantiate the component. VFP doesn’t know it’s talking to the proxy; it thinks it’s talking directly to the component. When you call the Multiply method, the proxy sends the call to the server via DCOM and the result is passed back to the proxy, which passes it on to VFP.
Are you feeling insecure?
Now that the component is installed on the server and the client proxies are installed, let’s see how easily we can grant or prohibit access to the component. COM+ uses role-based security. A role is a type of user. For example, in a bank you might have tellers, managers, loan officers, customer service people, and so forth. Each of these people is a role. You want to prohibit tellers from making loans, so in COM+, you could set up security on a loan component to prohibit this. The nice thing is that this is a configuration option and easy to change using the Component Services Manager.
Roles are based on Windows users and groups, so the first step in setting up the security scheme is to establish the Windows security groups. It can be confusing to understand where roles fit in the hierarchy of groups and users. The COM+ Help file states, “Roles are categories of users that have been defined for the application for the purpose of determining access permissions to the application’s resources. The developer assigns the roles (as symbolic user categories) to the application.” That sounds a lot like a Windows user group to me, so to keep it easy, think of a role as a user group that’s application-specific.
Now, getting back to our bank example, we’d have four groups: tellers, managers, loan officers, and customer service. Go ahead and create them on the server using Windows User Manager in NT or the Computer Management Applet in Windows 2000. Enter the first user group, and then the other three.
Once those groups are created, go back to MyFirstCOMApp in the Component Services Manager. Click on Roles, and then right-click and select New Role. Enter the first role, Teller, and click OK (see Figure 2). It’s not necessary to name the roles the same as the Windows user groups, but it makes management easier. Now create the three remaining roles of Manager, Loan Officer, and Customer Service.
Next we need to identify the users in each role. Expand the tree under Teller. You’ll see a folder labeled “Users.” Click on the folder, and then right-click and select New User. Scroll down the list of Windows users and groups and select the Tellers user group, and then click Add. Do the same for Managers and Customer Service. Then click OK. You’ll see each of the Windows Security groups added to the Teller role in Component Services (see Figure 3).
When new users are added to the system, they’re added to the proper Windows group, which in turn automatically puts them in the proper role. So far, we’ve identified the roles, but we haven’t told Component Services to use any security. Click on MyFirstCOMApp, right-click and select Properties, and then select the Security tab. Check “Enforce access checks for this application,” and then click OK (see Figure 4). Ignore the other options for now, I’ll discuss them shortly.
Now right-click on MyComm.Math and select Properties, then the Security tab. You’ll see that “Enforce component level access checks” is selected. You’ll also see the security roles listed. Simply check the roles that are to have access to this component (see Figure 5).
You can drill down and assign the security access to the interface or method level if you want. This enables you to have a single component with several interfaces, each having different security levels. If a user doesn’t have the proper access to use a component, an error message is returned stating that the component couldn’t be instantiated.
Now, let’s go back to the Application Security dialog box (see Figure 4). There are some additional options that I need to discuss. The first is Security level. This controls when COM+ validates the user’s security. With the first option, “Perform access checks only at the process level,” role-checking won’t be done at the component, interface, or method levels. Under the second option, “Perform access checks at the process and component level,” the security role is checked when a call is made to the component. You’ll almost always use the second option. However, the first option is useful when you’ve already validated the user.
The next setting is “Authentication level for calls.” There are six options, as described in Table 1. As you move through the list, each option gets progressively more secure. Note that the higher the level of security, the longer it takes to validate the user. The default level is Packet.
Table 1. Authentication levels.Expand table
Level
Description
None
No authentication.
Connect
Checks security only when the client connects to the component.
Call
Check security at the beginning of every call.
Packet
Checks security and validates that all data was received.
Packet Integrity
Checks security and validates that none of the data was modified in transit.
Packet Privacy
Checks security and encrypts the packet.
Finally, we have “Impersonation level.” This sets the level of authority that the component gives to other processes. There are four different levels as described in Table 2. The default is Impersonate.
Table 2. Impersonation levels.Expand table
Level
Description
Anonymous
The second process knows nothing about the client.
Identify
The second process can identify who the client is.
Impersonate
The second process can impersonate the client, but only for processes on the same server.
Delegate
The second process can impersonate the client in all instances.
Thus far, I’ve discussed declarative, role-based security, which is defined and managed at runtime. You can also use programmatic security. This allows you to branch your code based on the access level of the user. For example, a loan officer might only be able to authorize a loan up to $50,000. After that, it takes a manager’s approval to authorize the loan.
By using both role-based and programmatic security, you can support just about any security scheme that you need.
Error handling
One of the questions I often see on online forums is, “How do I report an error back to the user?” If you think about this, the answer doesn’t seem easy. You can’t display any dialog boxes with the error from your component. It’s running on a different computer than the user interface. VFP has the COMRETURNERROR() function to send the error message back to the client. COMRETURNERROR() takes two parameters. The first is the name of the module where the error occurred. The second is the message to display to the user. Let’s look at the code.
Now compile the code into a DLL and instantiate it.
You might expect “This will never be displayed” to show on the VFP desktop. However, an error dialog box appears instead.
You can return any error information you want in the message parameter. You also might want to enhance the error method by capturing additional information using AERROR() or writing information to an error log. There’s one caveat: The Error method won’t fire if an error occurs in the Init method.
Conclusion
That pretty much covers installation, security, and error handling. Next month, I’ll discuss transactions and see how COM+ and VFP 7 allow us to include VFP data in transactions, something that couldn’t be done with MTS and VFP 6.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the June 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Understanding COM+ with VFP, Part 3
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Understanding COM+ with VFP, Part 3
Craig Berntson
Transactions are an important part of any data update mechanism. Part 3 of this series by Craig Berntson introduces the Distributed Transaction Coordinator and explains how to use transactions under COM+.
Transactions, transactions, transactions. Without them, we can’t be sure that data is getting written to all of the Tables involved in an update. Before digging into how transactions work in COM+, let’s do a quick review of transactions.
A review of transactions
Joe wants to transfer $100 from his savings account to his checking account. He walks up to the ATM, inserts his card, and presses the buttons to initiate the transfer. Behind the scenes, this transfer can be accomplished two ways. The first way is that the balance of Joe’s savings account can be reduced by $100 and then his checking account balance increased by $100. The second option is that his checking account balance can be increased and then his savings account balance decreased. But what happens if there’s a system crash between the two updates? Under the first scenario, Joe isn’t happy. He’s lost $100. Under the second example, Joe is very happy. He’s $100 richer, but the bank has now lost $100. What we want is that, in the event of the aforementioned crash, either both accounts must be updated or neither of the accounts updated. To ensure that this happens is the purpose of transactions.
Transactions should follow the ACID rule—that is Atomicity, Consistency, Isolation, and Durability. Atomicity means that either all or none of the update is committed. Consistency means that if the transaction fails, the data is returned to the same state as before the transaction started. Isolation means that one transaction doesn’t know what another transaction is doing. Finally, Durability means that the transaction state is kept, no matter what happens to the system. This is generally handled through the use of a transaction log.
We can implement transactions on our VFP data by using the BEGIN TRANSACTION/END TRANSACTION/ROLLBACK commands. This pseudo-code shows the preceding example:
The transactions in VFP only work with VFP data, and VFP transactions fail the Durability rule of ACID. There’s no transaction logging.
If we’re using SQL Server, we need to use a different mechanism. We can use ADO commands to tell SQL Server when to begin and end a transaction or we can code it in a stored procedure. However, what happens if the savings account is in SQL Server and the checking account data is in Oracle? SQL Server transactions will only work on SQL Server, and Oracle transactions will only work on Oracle. We need a single transaction that will work against both databases. That’s where the Distributed Transaction Coordinator (DTC) comes in.
Using the DTC
The DTC allows us to have transactions that cross databases. This means that we can have data in both SQL Server and Oracle that will be updated by the same transaction. Under MTS and VFP 6, Fox data couldn’t participate in a DTS transaction. (This changes under COM+ and VFP7. More on that later.) Back to the DTC.
The DTS uses a two-phase commit. Basically, in phase one, DTS asks the database if it can do an update. If all of the databases respond yes to this question, then phase two begins, in which DTS tells the database to update the data. If any of the databases respond with a no, then DTS tells all of the databases to roll back the update.
Let’s look at how transactions were handled under MTS:
Note that when we either commit or abort the transaction, MTS will also release the component. This means that if the very next command in our application needs the component, we have to do another CREATEOBJECT() on the client. Let’s see how this changes in COM+:
In this example, we can either commit or abort the transaction with SetMyTransactionVote, but still keep the instance of the component active until we explicitly call SetDeactivateOnReturn with a .T. parameter.
You might now be wondering where the DTC fits in. COM+ automatically calls the DTC for us. The DTC talks to the database through the use of a Resource Manager. MTS and COM+ ship with Resource Managers for Oracle and SQL Server, but not for VFP. That’s why VFP data couldn’t take part in MTS transactions.
It turns out that Resource Managers are very difficult to implement. So, COM+ has what are called Compensating Resource Managers (CRMs). CRMs are easier to implement than a Resource Manager. By using a CRM we can have our Fox data be involved in DTC transactions.
A CRM consists of two parts, the CRM Worker and the CRM Compensator. These correspond to the two-phased commit of the DTC. A detailed discussion of the CRM is beyond the scope of this article. However, VFP 7 ships with a CRM sample application. You’ll find it in the SAMPLESCOM+CRM folder under your VFP 7 installation.
Configuring COM+ transactions
Like many of the features in COM+, transactions are managed at runtime. Open the Component Services Manager applet and drill down to one of your components. Right-click on the component and select Properties, and then select the Transactions tab. You’ll see five transaction settings (see Figure 1 and Table 1).
Table 1. Transaction types supported for COM+ components.Expand table
Setting
Description
Disabled
Transactions aren’t needed. Set this when you don’t want the extra overhead of a transaction—for example, a component that doesn’t update any data.
Not Supported
Prevents a component from using a transaction, regardless of the transactional state of the calling component.
Supported
The component participates in the transaction if one is active.
Required
The component participates in the transaction if one is active. If there’s no active transaction, a new one is started.
Requires New
A new transaction is always started.
You’ll also notice the “Override global transaction timeout value” check box. When you select Required or Requires New transactions, this check box becomes enabled. You can then enter the number of seconds the transaction will run before timing out.
That’s it for transactions. In Part 4, we’ll look at a COM+ feature that gives you asynchronous calls: Queued Components.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the July 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Decorating for the Busy Developer
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Decorating for the Busy Developer
Lauren Clarke
Decorating or “wrapping” an object represents a flexible alternative to subclassing. In this article, Lauren Clarke explains what a decorator is, demonstrates a novel way to implement the pattern in Visual FoxPro, and provides some application ideas.
If you’re like me, your eyes tend to glaze over a bit when object-oriented design pattern terms are thrown about. The word “decorator” is one of those terms, and due in part to the image of Martha Stewart it immediately conjures up, and the complete lack of tasteful decorations in most programmers’ lairs, it leaves one wondering what decorations and programming have to do with each other. Decorator (a noun) is a design pattern that uses aggregation (another OOP term, which means one object holds a reference to another) to “wrap” an object and perhaps provide some additional features not present in the wrapped object. Simply put, a decorator is a class that provides a “skin” around another object to which one wishes to add new responsibilities at runtime. A simple example follows.
Consider the object that SCATTER NAME creates in VFP:
This line of code creates an object with properties that correspond to the fields of the table in the current workarea, and property values corresponding to the values of those fields. This process represents a nice, lightweight alternative to assigning the properties of a custom data object from the fields of a table. However, wouldn’t it be nice if loData could do something other than store field values? It’d be nice if it had methods you could use in your day-to-day life as a developer working with this object. For example, what if it could validate itself, or render itself as an XML string, or save itself? The trouble is that we don’t have access to the internal Visual FoxPro class used to create loData. We can’t create subclasses of it, and even if we could, we couldn’t force SCATTER NAME to use our subclass instead of the base class. So we have an object that we’d like to endow… decorate… with new abilities, and we can’t (or don’t want to) create a subclass to deal with these new responsibilities. So, we’re going to implement a method of extending the abilities of the SCATTER NAME object at runtime. Enter the decorator pattern.
Well, that was a bunch of work (but don’t worry, I’m going to show you a way to avoid most of it). Let’s go over the highlights of this class. First, note that we have a property for each field in our customer table. Also, we have a property oData that will hold a reference to our SCATTER NAME generated data object. Finally, we have a property cFullName that will provide the customer’s full name based on the cFname and cLname fields. Then, we have a bevy of access and assign methods that serve to link the oData properties to the decorator’s properties. With this construct in place, the following expressions will both return the same value:
The access and assign approach ties us to version 6.0 or later of VFP. The alternative is to use GetProp() and SetProp() methods to provide access to the wrapped object. For example, we could have a GetFname() function that would return oData.cFname, but this really makes the wrapper less than transparent, which isn’t desirable. Finally, note that the INIT() method takes as a parameter a reference to the data object to be wrapped by the class. Okay, let’s put our decorator to work.
We’ll dissect the following code snippet line by line:
First, we create a standard SCATTER NAME object. The BLANK keyword indicates the object will be created with empty properties.
Next, we assign our new object a first and last name.
Nothing spectacular so far, but this next line is the point at which the rabbit goes into the hat.
Here, we instantiate a class DecoCust1, the init function of this class takes an object as a parameter, and we send our SCATTER NAME generated loData object for this parameter. DecoCust1 wraps itself around loData and augments its abilities. Note that we reuse the loData variable name to store a pointer to our new instance of DecoCust1. This isn’t necessary, but since DecoCust1 will seamlessly wrap our data object, presenting all of its original properties as if it were the old loData, it’s natural to do so. In our case, DecoCust1 has an IsValid() method, and we can use this to validate Mr. Yellow.
If we pass this test, a record is added to the current table and we fire the Save() method, which will save the record to the table.
Now that the object has been created and is decorated with additional properties and methods, we can check our record with IsValid(), save it with Save(), render it to XML with toXML(), and all the while we can still reference the objects’ original properties:
And we also have an additional property calculated from underlying fields.
To summarize, we have a class DecoCust1, which adds a cFullName property, IsValid(), Save(), and toXML() methods (and potentially many other useful methods) to our SCATTER NAME generated data object. From the developer’s standpoint, there’s very little practical difference between the wrapper and a bona fide subclass of the Visual FoxPro data object class. The key point, of course, is that you can’t subclass Visual FoxPro’s data object class. Also note that these abilities were added at runtime, meaning we have the option of adding these abilities if and when they’re needed without instantiating a large feature-ridden class each time a record object is needed.
Another real-world sample of a decorator, csZIP/csUnZip, is available in the accompanying Download file. It decorates the popular DynaZip utility to provide some simplifications and extensions that are helpful when using the utility from within VFP.
Subclasses vs. decorators
Decorators offer an alternative to subclassing, but they must be used judiciously. The advantages come from the fact that you can gain subclass-like behavior for classes that you can’t actually subclass. Also, you’re given more flexibility at runtime to endow your objects with abilities only if they need it to fulfill the current task. In our example, there might be many places in an application where the base FoxPro data object will suffice. This being the case, it would be a shame to have to instantiate and use a complicated and expensive class where the lightweight class would do. Decorators offer “pay as you go” options where one can add functionality as needed. It’s possible to nest decorators inside decorators. If we decided that mixing the validation code and the XML rendering code in one class made it too large and inflexible, we could create a separate decorator for each task. For example, we could start with the basic data object:
Then, if necessary, endow it with the ability to validate its properties:
Then, if needed, we could decorate again and gain some rendering functionality:
Which would allow us to do things like this:
or this:
And, unless you need to validate and render your data object every time you use it, you can save a lot of overhead by avoiding the giant do-everything data class.
The following lists summarize the pros and cons.
Advantages of decorators:
They allow extension of classes that we can’t directly subclass.
They allow us to avoid deep class hierarchies.
They provide a pay-as-you-go option, which avoids instantiating large feature-ridden classes when few of the features are needed.
Disadvantages of decorators:
Pass-through code must be maintained.
Passing requests through to the decorated component requires a performance hit.
They can complicate debugging.
From the developer’s perspective, the single most important issue here is the maintenance of pass-through code. The access and assign code must be maintained in concert with the wrapped object. In our example, this means that each time the structure of the table changes, we’ve got some work to do in our decorator class definition. This issue is exacerbated when we’re wrapping classes that have methods as well as properties, as we have to write pass-through code for each method. In short, if the interface of the wrapped object changes, so must the wrapper. Until recently, this fact was enough to really cool one’s feet to the idea of using the decorator in anything but the most dire situations. However, version 6.0 of Visual FoxPro gives us an opportunity to generalize decorator classes and completely eliminate this fragile use-case specific pass-through code. Our rescue comes in the form of the THIS_ACCESS method.
THIS_ACCESS overview
THIS_ACCESS is a method that can be added to any subclass in VFP. This method will fire every time the class is accessed. This means that every time a property is set or accessed or a method is called, the THIS_ACCESS method will fire prior to that action taking place. THIS_ACCESS takes as a parameter the name of the member being accessed. (Side note: It’s too bad that THIS_ACCESS only takes the called member as a parameter; if one could also access the value being sent [in the case of a property assignment] or the parameters being sent [in the case of a method call] inside the THIS_ACCESS method, it would open a world of possibilities, but that’s off topic for this article.) THIS_ACCESS must also return a reference to the object being accessed. It’s this last requirement that we leverage to implement an almost codeless delegation scheme for a generic decorator class. Let’s redo our prior example using this new approach.
Redecorating with THIS_ACCESS
Here’s what the class definition for DecoCust might look like when we utilize THIS_ACCESS:
That’s it. Notice the substantial reduction in lines of code from our previous DecoCust1 example. The “big idea” here is the THIS_ACCESS method that first checks to see whether the requested member belongs to the decorator class, and, if not, a reference to the wrapped data object is returned. This way, the decorator can decorate by adding functionality like this IsValid() while forwarding requests for the oData properties directly to the oData object.
Also, notice that the DecoCust2 class is very generic. The IsValid() and toXML() methods could be removed and we’d have a nice BaseDeco class to wrap any component that we could subclass to add things like IsValid() for specific implementations.
Paying the piper
Wrapping a class has some costs in terms of both development and runtime. The development costs come from the need to keep the interface of the wrapper synchronized with the interface of the wrapped component. If you choose to manually maintain the interface, this can be a costly proposition—especially if the wrapped class is changing often. Using the aforementioned THIS_ACCESS trick can vastly reduce your development load, as the interface will be updated automatically. However, since THIS_ACCESS fires each time the object is used, there’s a runtime cost to be paid for this approach. Table 1 will give you an idea of the runtime costs for these different approaches.
Table 1. The different approaches and their associated runtime costs.Expand table
Task
Subclass
Deco1
Deco2
Instantiation
1
1.81
2.66
Access decorator property
1
0.95
9.90
Assign decorator property
1
2.20
11.34
Call decorator method
1
0.96
4.48
Access decorated property
1
0.96
6.81
Assign decorated property
1
1.14
5.37
Call decorated method
1
2.15
4.88
Key: • Subclass = no decorator, subclass only. • Deco1 = a hardwired decorator with explicit pass-through code. • Deco2 = a decorator implemented with THIS_ACCESS.
This table has been normalized to be machine-independent and more readable. For each task, the “Subclass” option has been given a weight of 1 and the others scaled accordingly. So, for example, Deco2 takes 9.90 times longer to access a property than a traditional subclass. To get actual time values for your system, just run the perfcheck.prg provided in the Download file.
Some of these factors look pretty alarming, but keep in mind the times we’re talking about here. My system, a PIII that’s limping along at 500 Mhz, takes 0.000006 seconds to access a property from Deco1, and a staggering 0.00003 to access the same property through a decorator using THIS_ACCESS (Deco2). In a real use-case, say a middle-tier data object, an application might access a data object 100 times to serve a user’s request. In this situation, the THIS_ACCESS method represents a cost of no more than 0.003 seconds in our benchmark classes. Considering the THIS_ACCESS method might eliminate hundreds of lines of high-maintenance pass-through code, this might represent a good tradeoff. However, these results do make one pause to consider carefully where to implement these techniques.
WITH/ENDWITH and GATHER gotchas
If you plan on using a THIS_ACCESS decorated class in a WITH/ENDWITH loop, you’ll be in for a surprise. VFP exhibits some peculiar behavior in this area. In the April 2001 issue of FoxTalk, Randy Pearson wrote an article on the advantages of the WITH/ENDWITH command. It’s likely you’ll want to use this construct with a decorated class at some point. The trouble is that VFP won’t let you. The following code won’t fire the THIS_ACCESS method of loData and will result in an error:
A workaround is to reference the decorated component directly in the WITH construct:
There are some differences in the behavior here between VFP 6 and VFP 7 Beta 1. Both are odd and not really consistent with the documentation on access and assign methods. There’s a program in the Download file you can use to explore the differences.
In addition to this, while the GATHER NAME command works fine with the property-level access methods, it seems to ignore the THIS_ACCESS method at this time.
Conclusion
The decorator pattern offers a nice alternative to subclassing. The THIS_ACCESS method of building decorators allows us to avoid writing reams of pass-through code when building decorators in VFP. This convenience comes with a performance price, but in many situations I think the price is more than justified. I’ll leave you with one possibly interesting diversion. Look up “multiple inheritance” in a good general OOP reference. Then, take a look at our DecoCust2 class, and consider the possibility of aggregating more than one object at a time and replacing the IF/THEN in the INIT() clause with a CASE statement. Bon voyage!
(Lauren thanks thehttp://fox.wikis.comcommunity for their help in refining and testing some of the ideas presented in this article.)
Sidebar: References
Design Patterns, Elements of Object Oriented Software, by E. Gamma, R. Helm, R. Johnson, and J. Vlissides (Addison Wesley, 1994, ISBN 0201633612).
“Simulating Multiple Inheritance,” by Michael Malak, in the April 2001 issue of Journal of Object-Oriented Programming.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the August 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
TYPE() to StringType()–Bridging a Gap
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
TYPE() to StringType()—Bridging a Gap
Pradip Acharya
Intuitively, you’d think that the string “123” represents a number and the string “ABC” is of type character. Right? Not according to the standard TYPE() function in VFP. If ABC happens to be a date variable, for example, the type of “ABC” will be returned as D. Yet, in many situations, we need to know the data type of the contents of an unknown string. In this article, Pradip Acharya looks at the problems associated with determining the data type of a string and describes the creation of a new function, StringType(), suited for this purpose.
One of my customers called and said, “When I type an X in your program, I get an error message, and nothing happens. Am I doing something wrong?” I rushed over and discovered that, as always, the customer was right, and I needed to create a new function to get the customer going again.
A generic text input box was presented to the user for entering a search parameter for scanning a specific column in a table for the presence of such a value. For example, if the user needs to look at the invoices created on a certain date, a date will be typed in. To look for all five HP motors, one would enter a numeric value of “5” and so on. I had validation code that ensured that if the user entered data that clearly wasn’t of the same type as the field to be scanned, he or she would be notified and asked to re-enter the value. For a date type field, the user incorrectly entered an X. My validation, for some reason, failed to alert the user, and an unhandled data type mismatch error led to the previously noted disruption of work.
Dual use of the TYPE() function
Simply put, the TYPE() function in VFP accepts as input a character string and determines whether a memory variable or a field exists by that name and, if so, what its data type is. However, this description is deceptive and limiting. More strictly, TYPE() resolves the string as an expression and checks to see whether the result makes any sense.
To make sure that the slate is clean:
Now let’s define a memory variable:
?TYPE(“XYZ”) will display D. In this mode, the TYPE() function determined the data type of a prevailing memory variable (in scope). Next, if I do ?TYPE(“123”), I’ll get N. Obviously, in this second instance, my intention has been to use the TYPE() function to check the nature of a string and not the existence of a variable, field, object, or property.
In trying to use the TYPE() function for the two unrelated purposes, we fall into a trap. In the case study presented earlier, if the user types 123, I’ll correctly determine that the type of the value entered is numeric. If, however, the user types XYZ instead of the desired data type C, I’ll incorrectly determine the data type to be D because a date variable already exists by the name of XYZ. This was the reason behind the failure of my validation code that then led to a data type mismatch error. The target table field was of type date; the user typed a character instead of a date, and I incorrectly inferred that a date had been typed in because a date variable by that name existed and TYPE() fooled me.
To make matters worse, if the user enters a perfectly valid date such as 07/25/2001 (assuming that the date format is set to MDY), I’ll incorrectly determine that the data type of the string is N and not D. Why N? Because 07/25/2001 evaluates to 0.0001399 as an expression, having interpreted each “/” as a division operator—unintuitive indeed! What this proves is that using the TYPE() function to determine the data type of the content of a string is a misapplication of the TYPE() function. I created a new function, StringType(), to determine the type of data contained within a string, based on a string parsing strategy, and at the same time to internally take advantage of the TYPE() function.
Function StringType()
The purpose of this function is to determine the type of data contained in a text string that’s passed as the only argument. Case is immaterial, and leading and trailing white space, not just spaces, are ignored (see Table 1). White space is defined in Listing 1. (Note that there’s no U for undefined category.)
****
Table 1. The function returns a single-character uppercase letter.Expand table
L
Logical
N
Numeric
I
Integer
Y
Currency (for example, $53.27)
D
Date, either strict or conforming to the prevailing date format, but independent of SET CENTURY
C
Not one of the above, character, default
****
Listing 1. Code for function StringType().
This function can be useful in any situation where an unknown string is encountered and one needs to determine what kind data it contains prior to taking further action. In addition to Listing 1, the file STRTYPE.PRG is available in the Download file. Table 2 presents a comparison of the output.
****
Table 2. Comparison of output.Expand table
String
TYPE()
StringType()
“.T.”
L
L
“.False.”
U
L
“123.52”
N
N
“123”
N
I
“$53.68”
U
Y
“01/01/01”
N
D
“//”
U
D
“123abc”
U
C
“m.aVarName”
Depends
C
An empty string
An empty string or a string made up of white space isn’t interpreted. The function returns “C.” You may reserve a word BLANK, for example, and ask the user to enter BLANK if a distinction is to be made between no value entered and an intended empty value. Then test for “BLANK” on return. This isn’t done inside the StringType() as supplied, although you might wish to incorporate such a test inside the code yourself and assign a special return character—perhaps E.
Data type logical
The function will return L if the input string is one of the following:Expand table
.T.
.F.
.True.
.False.
The inclusion of the last two is an extension.
Data types numeric and integer
Normally, the function will return “N” if the string contains a number. As an extension, it will return “I” if the value entered is truly an integer. “123” and “123.” will both return “I”. The distinction between “N” and “I” might be useful, for example, prior to SEEKing an integer field of Invoice numbers. Under special circumstances, strings containing character data might be incorrectly identified as numeric in the presence of embedded operators. See the “Limitation” section later in the article.
Data type currency
Since parsing is involved, we might as well make a special case out of numeric data when the leading character is a $ sign. For example, “$ 52.68” will return Y instead of N. Probably no one will use this option.
Data type date
Correctly determining a string to be of type date is a vexatious problem. The problem is split into two parts. What’s an empty date? And what’s a valid date? If the input string is enclosed in curly brackets—that is, {…}—the result returned is always “D” regardless of the validity of what lies inside the brackets. In keeping with VFP convention, an input string like “{abc}” will return a value of “D.”
Only two character representations are recognized by StringType() as a blank date:
• //
• {}
In-between white space is ignored. Therefore, /ss/ or { ss} will also return “D”.
As for valid dates, internally, the TYPE() function is put to use. The problem is that VFP is highly forgiving in interpreting dates. For example, ?{4.7} will print as 04/07/2001, whereas, for our purposes, we’d like to interpret 4.7 as a numeric value and certainly not a date. Accordingly, reasonable parsing constraints have been introduced in StringType() before a string can be declared to be a date. For example, there must be two and only two identical separator characters, and the rest must be digits. Dates entered in the strict date format will also be correctly identified as date.
Limitation—no check for operators
In this version of StringType(), no attempt has been made to isolate an individual data item from an expression with embedded operators. “53.86” and “2+7” both will return N (or I). Should we interpret a string such as 27*3 as “C” or “N”? I don’t know. Furthermore, StringType() normally doesn’t depend on which work areas are open. Not checking for operators leaves a loophole in this regard. An input string such as “53 * Invoice.Total” will produce unpredictable output depending on whether Invoice.Total is a visible field or not. If you code a version that checks for operators and expressions and closes this loophole, I’ll be happy to get a copy.
A wish
TYPE() as it stands identifies a valid property of an object. If TYPE(“m.oMyObject.Size”) returns “U” or “O,” it’s not a valid property. Otherwise, the property exists. As a logical and consistent extension to this interpretation, it makes sense if TYPE() also identifies an unprotected method and returns, for example, “H” if a method by this name exists for the object. I believe this generalization will be useful.
Conclusion
The standard TYPE() function will return the data type of a variable but isn’t suitable for determining the data type of the contents of a string. The new function StringType() has been designed specifically for this purpose, with a few limitations. In a future article, I’ll present a utility for generic output formatting of any type of value into a printable character string.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the September 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Create Modern Interfaces with VFP 7
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Create Modern Interfaces with VFP 7
Doug Hennig
It seems that every new version of Microsoft Office changes user interface standards. Whether you like it or not, your users expect your applications to keep up with this ever-moving target. Fortunately, VFP 7 adds new features that make it easier to create interfaces similar to Office 2000. Doug Hennig explains.
In addition to new language features (see my “Language Enhancements in VFP 7” series of articles in the January to June 2001 issues of FoxTalk), database events, support for COM+, Web Services, and a ton of other new features, VFP 7 provides some user interface improvements, including hot tracking and modern-looking toolbars and menus.
Hot tracking
Hot tracking means controls appear flat (rather than the three-dimensional appearance we’re used to) but change appearance as the mouse pointer moves over them. Most controls will then appear sunken (the way they normally appear with hot tracking off), except for check boxes, option buttons, and command buttons, which appear raised. For an example of hot tracking, look at the toolbars in Microsoft Office 2000 applications. As you can see in Figure 1, toolbar controls appear flat (for example, the command buttons have no outlines) until you move the mouse over them.
Hot tracking is easy to turn on in VFP 7: Simply set the SpecialEffect property to 2 (for check boxes and option buttons, you also have to set Style to 1-Graphical). For control classes that might have to be used in earlier versions of VFP, you should set this property programmatically (such as in the Init method) rather than in the Property Window to prevent an error when the control is used in those versions. Here’s an example (taken from SFToolbarButton in SFBUTTON.VCX):
clVFP7ORLATER is a constant defined in SFCTRLS.H, the include file for SFToolbarButton, as follows:
Since version(5) was added in VFP 6, the type() test and use of evaluate() in this statement ensure that it will work even in VFP 5.
You can create other types of effects with code in the new MouseEnter and MouseLeave events. For example, you can set This.FontBold = .T. in MouseEnter and This.FontBold = .F. in MouseLeave to make a control appear bolded when the mouse is over it. You can also change the foreground or background color, and do pretty much anything else you want in these events.
SwitchboardButton in MYCLASSES.VCX is an example. It’s used as a button in “switchboard” forms, forms that provide quick access to the major functions of an application. In VFP 7, as the user moves the mouse pointer around the form, the SwitchboardButton object under the mouse is surrounded with a blue outline (see Figure 2 for an example). SwitchboardButton is actually a container class with an image and a label. Its BorderColor is set to 0, 0, 255 (blue) and its Init method sets the BorderWidth to 0 (it’s left at the default of 1 in the Property Window so you can see it in the Class or Form Designers). The MouseEnter event sets BorderWidth to 3 and MouseLeave sets it back to 0.
In addition to the SpecialEffect property and MouseEnter and MouseLeave events, command buttons have a new VisualEffect property. This property, which is read-only at design time, allows you to programmatically control the raised or sunken appearance of the control at runtime. Although you won’t often use this, it’s handy when several buttons should change appearance as a group. We’ll see an example of that later.
Although you can use hot tracking wherever you want, I personally don’t care for hot tracking except in controls in toolbars (none of the dialogs in Microsoft Office use hot tracking, for example). So, rather than setting SpecialEffect to 2 in my base classes (those in SFCTRLS.VCX), I’ll do it in specific subclasses that I use for toolbars.
To see an example of hot tracking for different types of controls, run TESTHOTTRACKING.SCX and see what happens as you move the mouse over each control.
Toolbars
Like other “modern” applications, toolbars in VFP 7 now have a vertical bar at the left edge when docked to provide a visual anchor to grab to move or undock the toolbar (see Figure 1). Another improvement related to toolbars is the addition of a Style property to the Separator base class; setting this property to 1 makes a Separator appear as a vertical bar at runtime (at design time, Separators are still invisible, which is kind of annoying). As with hot tracking, you might want to set this property programmatically to prevent problems with earlier versions of VFP; I use the following code in the Init method of SFSeparator (in SFCTRLS.VCX):
Figure 3 shows the same toolbar running in VFP 6 and 7. The VFP 7 version looks and acts like a toolbar in a more modern application.
A new style of toolbar button showing up in more and more applications is the dual button/menu control. Figure 4 shows an example of such a button, taken from Internet Explorer 5.5. Clicking on the left part of the control (the button with the image) causes an action to occur, while clicking on the down arrow displays a drop-down menu of choices. Another place I’ve seen such a control used is in West Wind Technologies’ HTML Help Builder to open Help projects. Clicking on the button displays an Open File dialog, while clicking on the down arrow displays a “most recently used” (or MRU) list of files. The advantage of this control is that it doesn’t take up much screen real estate, yet it can have a large list of choices.
SFBUTTON.VCX has a couple of classes used to create such a control. SFDropDownMenuTrigger is a subclass of SFToolbarButton that’s sized appropriately and displays a down arrow (Caption = “6,” FontName = “Webdings,” FontSize = 6). It also has assign methods on its FontName and FontSize properties so they aren’t inadvertently changed programmatically by something like SetAll(). SFDropDownMenuButton is based on SFContainer, our container base class in SFCTRLS.VCX, and it contains an SFToolbarButton object named cmdMain and an SFDropDownMenuTrigger object named cmdMenu. The MouseEnter and MouseLeave events of each button set the VisualEffect property of the other button to 1 and 0, respectively, so the buttons’ hot tracking are synchronized. The Click event of cmdMain calls the ButtonClicked method of the container, which is empty since this is an abstract class and the desired behavior must be coded in a subclass or instance. The MouseDown event of cmdMenu has the following code to display the drop-down menu:
Since SFContainer already has methods and code for handling shortcut menus (see my column in the February 1999 issue of FoxTalk, “A Last Look at the FFC”), why reinvent the wheel? As a refresher, the ShowMenu method of SFContainer instantiates an SFShortcutMenu object (defined in SFMENU.VCX), which is an adaptation (not subclass) of the FFC _ShortcutMenu class. SFShortcutMenu handles all of the work of displaying a shortcut menu; you just call the AddMenuBar and AddMenuSeparator methods to define the bars in the menu, and then call the ShowMenu method to display it. SFContainer.ShowMenu calls the ShortcutMenu method to do the actual work of defining the bars (that method is abstract in SFContainer).
However, one issue SFDropDownMenuButton has to address that SFContainer doesn’t is menu placement. SFShortcutMenu automatically places the menu at the current mouse position, but if you look at Figure 4, you’ll notice the menu appears directly below the control, aligned with its left edge. To support that, I added nRow and nCol properties to SFShortcutMenu so you can control the position of the menu; if they contain 0, which they do by default, SFShortcutMenu will figure out where the menu should go, so the former behavior is maintained. The ShortcutMenu method of SFDropDownMenuButton, however, has to place the menu at the right spot, so it calculates the appropriate values for the nRow and nCol properties.
What’s the right spot? That depends on if and where the toolbar hosting the control is docked. If the toolbar is docked at the right or bottom edges, the menu has to be placed to the left or above the control so it appears inside the VFP window. Otherwise, it has to be placed below and at the left edge of the control. The code to perform these calculations is fairly long and complex (I adapted—okay, ripped off <g>—the code from NEWTBARS.VCX in the SOLUTIONSEDONA subdirectory of the VFP samples directory), so it isn’t shown here.
To use SFDropDownMenuButton, drop it or a subclass on a toolbar. To see an example, look at the instance named ColorPicker in the MyToolbar class in MYCLASSES.VCX, included in the Download file. ColorPicker is just a simple demonstration of this control; it allows the user to change the background color of the active form from either a pre-selected list of colors (the drop-down menu) or a color dialog (when you click on the button). The ButtonClicked method, called when the user clicks the button, displays a color dialog and sets the background color of the active form to the selected color:
The ShortcutMenu method has the following code:
toMenu is a reference to the SFShortcutMenu object. The first parameter for the AddMenuBar method is the prompt for the bar, and the second is the command to execute when that bar is chosen.
Menus
Modern applications usually provide many different ways to perform the same action: main menu selections, toolbar buttons, shortcut menu selections, and so on. I’ve already discussed toolbars, and the SFShortcutMenu class makes it easy to create shortcut menus for every form and object in your application. So, let’s talk about the main menu.
Menus haven’t changed much in FoxPro since FoxPro 2.0 (although in my August 2001 column, “Objectify Your Menus,” I presented a set of classes that make it easy to create object-oriented menus). New in VFP 7, however, are the abilities to specify pictures for bars (either the picture for a VFP system menu bar or a graphic file) and to create inverted bars that only appear when the user clicks on a chevron at the bottom of a menu popup (“MRU” menus, although the meaning of MRU here is different from how I used it earlier). These features allow us to create Office 2000-style menus.
Specifying a picture is easy. In the VFP Menu Designer, click on the button in the Options column for a menu bar, and in the Prompt Options dialog, select File if you want to specify a graphic file or Resource if you want to use the picture for a VFP system menu bar. If you select File, you can either enter the name of the file in the picture text box or click on the button beside the text box and select it from the Open File dialog. If you chose Resource, either enter the name of the VFP system menu bar (for example, “_mfi_open”) or click on the button and select it from the dialog showing the prompts of system menu bars. In either case, a preview of the picture is shown in the Prompt Options dialog. The settings result in the PICTURE or PICTRES clauses being added to the DEFINE BAR command that will ultimately be created for this bar. If you’re using the OOP menus I presented in August, set either the cPictureFile or cPictureResource property of an SFBar object to the desired value.
The MRU feature is more difficult to use, and much more difficult to implement in a practical manner. The DEFINE BAR command has new MRU and INVERT clauses, but because there are no specific options for either clause in the Menu Designer, you end up having to use a trick: Enter “.F.” followed by either “MRU” or “INVERT” in the Skip For option for the bar. VFP 7’s menu generator, GENMENU.PRG, is smart enough to see that you’re really using the Skip For setting as a way of sneaking other clauses into the DEFINE BAR command that the generator will create, so it leaves off the SKIP FOR .F. part of the command.
However, that’s only the beginning. You’re responsible for managing what happens when the user selects the MRU bar (the chevron at the bottom of the menu) yourself. Typically, you’ll remove the MRU bar from the menu and add bars with the INVERT clause to the menu, but since the Menu Designer doesn’t create those bars for you, you have to code the DEFINE BAR statements yourself (although you could create the desired bar in the Menu Designer, generate the MPR file, copy the DEFINE BAR statement for the bar from the MPR, and then remove it in the Menu Designer). Also, once the user has selected one of the inverted bars, you have to add the MRU bar back to the menu and remove the inverted bars, except perhaps the selected one, which you may decide to leave in the menu as Office applications do. But then you have the complication of changing it from an inverted bar to a normal one and not adding that bar the next time the user selects the MRU bar, and that’ll only last until the user exits the application. See what I mean by “much more difficult to implement in a practical manner”?
I can’t think of any application I’ve written in the past 20 years that was complex enough to actually use this type of MRU feature, but at least the OOP menu classes I presented in August manage a lot of this stuff for you. Set the lMRU property of an SFPad object to .T. if that pad should have an MRU bar in it, and set the lInvert property of any SFBar object to .T. to have that bar appear when the MRU bar is selected and disappear after a menu selection is made. You’ll have to subclass SFPad if you want different behavior, such as changing an inverted bar into a normal one if it’s selected.
A more useful version of an MRU feature is the one I referred to earlier—a list of things the user has accessed recently. Office 2000 applications use this: The bottom of the File menu shows a list of the most recently accessed documents. Most VFP applications don’t use the concept of “documents,” but they do use records. It might make sense in some applications to put the most recently accessed records at the bottom of a menu so users can quickly return to a record they were working with before. Rather than automatically doing that, you might want to provide a function the user can select to add the current record to the MRU list.
The sample application included in the Download file has an example of such a feature. First, a button in the MyToolbar class, used as the toolbar for the customers form, allows the user to “bookmark” the current record; it does so by calling the Bookmark method of the active form. That method in CUSTOMERS.SCX has the following code:
This code expects that the Bookmark class, which we’ll look at in a moment, has been instantiated into a global variable called oBookmark. The AddBookmark method of that class expects two parameters: the command to execute when the bookmark is selected and the caption for the bookmark. In this case, the command tells VFP that if the active form is the customers form, call the Seek method of that form with the customer’s CUST_ID value (that method positions the form to the specified key value); if there’s no active form or it isn’t the customers form, call the DoForm method of the application object, telling it to run the customers form and passing the CUST_ID value (the Init method of the customers form accepts an optional CUST_ID value and calls the Seek method if it’s passed). The company name is used as the caption for the bookmark.
The Bookmark class, in MYCLASSES.VCX, is a simple class based on SFCustom. It has a two-dimensional array called aBookmarks to store the bookmarks; the first column is the command to execute and the second is the caption. The nMaxBookmarks property determines how many bookmarks can be stored. The AddBookmark method adds a bookmark to the array and to the bottom of the File menu. Here’s the code:
Before the first bookmark is added to the File menu, a separator bar is added above the Exit bar. Then bars for the bookmarks are added above that separator. The cBarPosition property is used to control the bar positions.
Figure 5 shows an example of the File menu after I bookmarked four records. Selecting a bookmark opens the customers form (if necessary) and displays the chosen record.
The Bookmark class has a couple of other methods, SaveBookmarks and RestoreBookmarks, that save and restore the bookmarks, using BOOKMARKS.DBF. These methods ensure that the user’s bookmarks are persistent between application sessions.
Tying it all together
The sample application shows all of the techniques discussed in this article. DO MAIN starts the application. MAIN.PRG instantiates some objects, including a simple application object and the Bookmark class, and creates a menu for the application. It then runs the SWITCHBOARD form and issues a READ EVENTS. The only functions in the menu and switchboard that do anything are Customers (which runs CUSTOMERS.SCX) and Exit.
The switchboard form uses the SwitchboardButton class mentioned earlier to show hot tracking. The menu shows the use of MRU and inverted bars, includes pictures for some bars, and demonstrates the use of most recently used (bookmarked) records. The customers form isn’t fancy, but the toolbar it uses shows the SFDropDownMenuButton class (as a color picker), includes a button to bookmark the current record, and demonstrates the new features of VFP 7 toolbars, including buttons with hot tracking and vertical separator bars.
VFP 7 has several new features that make it easier to create applications that look and act like Office 2000. Of course, Office XP raises the bar yet again, but for now, our applications can look more modern than VFP 6 applications could.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the October 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Become the Master of All You Survey–Using XML as a Flexible Data Capture and Retrieval Medium
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Become the Master of All You Survey–Using XML as a Flexible Data Capture and Retrieval Medium
Andrew Coates
Customer Relationship Management (CRM) systems often require that a survey or script be played for a particular customer while an agent is on the phone with them. Often, the exact content of the survey or script depends on the type of customer. In this article, Andrew Coates develops a system for capturing and retrieving such data. This technique uses XML as the storage medium for the answers to the questions and displays the enormous flexibility afforded by the use of a standard that is at once both free-form and structured.
I recently was asked to retrofit an existing Customer Relationship Management system with the ability to capture information that my client wanted to gather from a targeted group of his customers via a telephone survey. My first thought was to add a new table to the database with a one-to-one relationship to the main table and a field for each question in the survey, as shown in Figure 1. This was probably the simplest approach, but on further reflection I realized that it lacks flexibility should my client ever want to undertake additional surveys.
My next thought was to still have a table specific to the survey, but to have a one-to-many table allowing the entity to be linked to many survey tables. This would add some flexibility to the system in that new surveys could be added by adding a new table and adding rows to a survey master table. Information about which table referred to which survey could be stored in a survey master table, and information about which companies were eligible for which survey could be stored in a many-to-many table. This approach is illustrated in Figure 2.
The downside to the second approach is that every time a new survey is added, a new table has to be added to the database. In addition, if a survey needs fine-tuning by adding or changing questions, then the data structure needs to be updated. While these aren’t insurmountable problems, I’d rather not have to muck around with the data structure every time my client wants to tweak the survey.
What I needed was a flexible way of storing answers to survey questions that could still be retrieved in a structured way. I decided to store the answers directly in a text (or memo) field in the many-to-many table. To make sure that the data was still retrievable in a sensible manner, I decided to use XML as the storage format within the text field. The final layout of my database is shown in Figure 3.
Data—the object of my desire
The architecture I decided on for this project was one of data objects. Each set of answers to a survey is represented by a data object that has a property for each answer. The object is then converted to and from an XML representation using Rick Strahl’s ObjectToXML and XMLToObject from his (free) wwXML library. I’ve included the version of wwXML (with permission—thanks, Rick) that was current at the time of this writing in the accompanying Download file, but I strongly suggest that you visit Rick’s site (www.west-wind.com) to check whether there’s a more recent version. Rick’s constantly adding great stuff both to existing libraries and as completely new sets of useful things.
As a companion to the data objects, there’s a GUI class. This class instantiates the appropriate data object and then the data object’s properties as control sources for its various controls. The GUI class itself does no data manipulation or handling (apart from displaying the data and allowing the user to enter or change answers). Instead, it calls the methods of the data object, which knows how to load and save itself from and to the XML data on the back end.
Data object abstract class
Both the data object and the GUI object are defined in the first instance as abstract classes—that is, classes that are only ever subclassed, never themselves instantiated. The data class standard properties and methods are shown in Table 1. ******
Table 1. Methods and properties of the data object abstract class.Expand table
Property/Method
Description
LoadData()
Loads the XML data from the back end for the SurveyID/CompanyID combination specified in the properties.
SaveData()
Persists an XML representation of the object’s properties to the back end.
CreateCursor()
Creates a cursor with a field of the same name and of the appropriate type for each data property of the class.
cComments
Standard free text field available in all surveys.
cConnectString
Connect string for use when instantiating the object and connecting to a remote back end.
nCompanyID
ID of the company to which this set of answers applies.
nSurveyID
ID of the survey from which this set of questions is taken.
tLastUpdated
Last date/time this survey was saved for this CompanyID/SurveyID combination. Note that the default value is an empty time. You can’t just use {} for this, as this is interpreted as an empty date and the parser chokes on time values later on. You need to convert the empty date into an empty time like this: =DTOT({}).
The code for the Init() method is shown in Listing 1.
****
Listing 1. The data object’s Init() method.
When the object is instantiated, the Init code accepts two parameters. The first parameter is an integer representing a handle of an already established connection to a back-end data source via SQLCONNECT() or SQLSTRINGCONNECT(). This gives the object an opportunity to share a connection and thus reduce the resource requirements of the application. The second parameter (which is only used if the connection handle passed is non-numeric or <= 0) allows the overriding of the SQL connect string used to establish a connection to the back end if there’s no handle passed.
If there’s no connection handle passed, the data object’s first job is to establish a connection to the back end. If it can’t do this, there’s no use continuing and it bails out. Next it attempts to create an instance of the wwXML helper class. This class is vital for loading and saving the data to the back end, so again, if it can’t create an instance, it just bails out.
The grunt work of the object is done by the LoadData() and SaveData() methods, shown in Listing 2 and Listing 3, respectively.
****
Listing 2. Loading data from the back end.
****
Listing 3. Saving the answers back to the database.
If the method is passed XML as a parameter, it just uses that to load the values into the properties of the object (we’ll get to why this is useful a little later on). To load data from the back end, the method first checks that the company is listed for this survey. It then generates a SQL statement to retrieve any currently stored data for this company/survey combination. Finally, if there were already answers stored in the XML field, then the values are transferred to the data object’s properties with the single line:
This is the powerhouse of the method. This single line of code transfers each property’s stored value from the XML. There are a couple of cool things to note here:
If the property’s been changed or added since the data was persisted to the table, the default value is used—there’s no requirement to go back through the data retrofitting changes to the survey. (Of course, it’s possible you may want to do some translations for business reasons, but the architecture we’ve used means that it’s not required from a technical standpoint.)
Deleting a property from the data object means that the data for that property simply won’t appear in the new version of the object, and next time the object is persisted to the table, the deleted data will simply disappear.
Saving the properties in a data object is just as simple as loading them. Again the powerhouse of the method is a single line:
This takes the properties in the current object and converts them to an XML representation. A sample of the XML generated by this call is shown in Listing 4.
****
Listing 4. Survey answers stored in XML format.
The rest of the method simply writes the XML string into the text field. Note that I’ve used a parameterized update command:
The ?lcXML tells the ODBC connection to ask VFP for the value of the lcXML variable. Using this construct eliminates a large number of issues with encoding of illegal characters and so on. All of that is handled behind the scenes by the ODBC driver interface. Similarly, ?ltUpdateTime tells ODBC to ask VFP for the value of the ltUpdateTime variable. Dates are another great source of incompatibility between various back-end data sources. Letting the ODBC driver do the conversion and formatting in this way eliminates a potential headache if you change back ends.
Adding a new survey
The steps for adding a new survey are:
Decide what questions are to be asked (or, perhaps more accurately, which answers will be recorded) and what type their answers will be.
Subclass the surveybasedata class and add a property initialized to a value of the appropriate type for each answer. Override the CreateCursor() method to create a cursor with columns with the same names as the properties just added (don’t forget to add the four properties from the data base class—nSurveyID, nCompanyID, cComments, and tLastUpdated).
Update the nSurveyID property for the data object subclass so this survey has a unique number.
Create a subclass of the surveybase class that will present the survey to the user.
Add a control or controls to the subclassed form with controlsources in the form thisform.oDataObject.<data object property>
If there are any dependencies between the questions (for example, questions that should only be asked if others were answered in a certain way), then put code in the UpdateControls() method to change control states. Note that this method is called after the data object’s LoadData() method is called, so the control’s states are set initially. You should also call this method whenever there’s a potential need for a refresh. For example, if a check box state changes to true, another set of questions might become relevant.
Update the cDataObjectName property with the name of the subclass you created in step 2. This ensures that the UI object instantiates the correct data object when it’s created.
Make an entry in the Survey_Master table with the SurveyID from step 3, a description for the survey, the name of the data class from step 2, and the name of the UI class from step 4.
For each company that’s eligible for a survey, add a row to the Company_Survey table with that company’s CompanyID and the SurveyID from step 3.
While this might seem to be a lot of work, remember that you only need to do it once for each survey.
Putting it into practice
For the purposes of this exercise, I designed a simple survey with the questions shown in Table 2. ******
Table 2. The survey questions.Expand table
No.
Question
Comments
1
How did you hear about our company?
C(3) options are: WOM: Word of mouth YP: Yellow Pages WWW: Web search ADT: TV advertisement ADR: Radio advertisement OTH: Other
1a
If Other—where did you hear about us?
C(20) Only available if OTH selected for question 1
2
Gender?
I 1: Male 2: Female
3
Do you use SQL Server?
L
4
Do you use FoxPro (Visual or otherwise)?
L
5
Do you use Visual Basic (not VBA or VBS)?
L
Next, I created a subclass of surveybasedata called surveycustomerdata and added properties for each of the questions. The subclass is included in the Download file, but the properties added were: cSource, cSourceOther, nSex, lSQLServer, lFoxPro, and lVB. I also overrode the CreateCursor() method to create a cursor with a field of matching name and data type for each property.
I assigned this new survey an ID of 1.
The next step was to create a subclass of the surveybase GUI class. I called this subclass surveycustomer.
I added controls to the subclassed form—a drop-down for the source, a text box for the other source description, an option group for the gender, and check boxes for each of the development tool questions. These controls were in addition to those provided by the base class—companyID, last update date/time, and comments, as well as the Save and Cancel buttons.
Because the other source text box should only be available if the user chooses Other from the source drop-down, I added enable/disable code to the UpdateControls() method that checks the value of the drop-down and takes appropriate action.
I set the value of the cDataObjectName property to “surveycustomerdata” so the correct data object is instantiated by the GUI class.
I updated the survey_master table by adding a row with the values SurveyID = 1, Survey_Name = “Generic Customer Survey,” Data_Class = “surveycustomerdata,” UI_Class = “surveycustomer.”
Finally, I added two new rows to the Company_Survey table, one each for CompanyIDs 1 and 2, both with SurveyID 1.
To display the survey, I typed the following in the command window:
Note that the second and third parameters of the CreateObject() call are passed to the Init() method of the GUI object. In this case, the 1 means that I want to load the survey data for CompanyID 1, and the .t. means that I want to open the form in edit mode (rather than just viewing the data). The result is shown in Figure 4.
Answering the questions and clicking the Save button fires the form class’s SaveSurveyData() method, which in turn fires the data object’s SaveData() method. The XML generated and saved to the memo field is shown in Listing 4.
Retrieving the data
“But wait,” I hear you cry. And you’re right. Storing data in this format doesn’t make querying and retrieval a simple matter of running a SQL statement. The data is stored in a free-form memo field, and most of us have had experience with how much of a hassle it is to retrieve data from there. This is where the power of the XML format (and one of Visual FoxPro’s most useful commands) comes to the fore. It’s a simple matter to retrieve all of the data from the text field into a cursor, and once it’s in the cursor the whole might of the native data engine is available to you.
The code in Listing 5 (included as ExtractData.PRG in the Download file) shows how simple it is to retrieve all of the data from the XML memo fields into a single usable cursor.
****
Listing 5. Retrieving VFP data from the memo field quagmire.
After checking that the appropriate class library is loaded, the extraction program instantiates a data object and connects to the back end using the data object’s connect string. It then retrieves a cursor containing all of the rows with answers to survey questions. Next it calls the data object’s CreateCursor() method, which generates a cursor with a column of the appropriate type and name for each data object property.
Scanning through the list of answer text fields, the code then passes the XML for each set of answers to the data object’s LoadData() method. Remember when I said that the tcXML parameter of the LoadData() method would come in handy? Well, here it is. It means that there’s no requirement to go back to the back end to retrieve the answers for each company. We can just get them all into a cursor with one back-end hit and then use the local data engine to scan through them and pass the XML to the data object.
Once the data object has been loaded up with the answers, it’s time to add a row to the results table and populate it with the date. Two simple but very powerful lines of code do this:
GATHER NAME is a wonderful command. It makes the process so much simpler. The alternative would be to iterate through all of the properties of the object and do a REPLACE for each one with the corresponding field in the cursor. I haven’t benchmarked this, but I imagine that having a native command to do this results in significant efficiencies.
After scanning through the entire list of answers, this code will leave you with a cursor called ResultSet, which has a row for every company and a column for every answer. From there, the reporting process is up to you.
Making changes to a survey
The last thing I want to mention is how much flexibility this approach gives you. Let’s say that in our example you want to add a new question about the respondent’s income range and another about the number of computers at their primary working location. Let’s also suppose that you discovered the 30 characters you’d allocated to the other source field was too small—40 would be better—and that your boss is no longer interested in the answer to whether people are using VB. The following is all you’d have to do:
Add a property called cIncomeRange and another called nComputerCount to the surveycustomerdata class.
Remove the lVB property from the same class.
Update the surveycustomerdata::CreateCursor() method to include cIncomeRange C(3) and nComputerCount I. Remove lVB and change the length of the cSourceOther field from 30 to 40.
Update the surveycustomer class to include new controls for the income range (probably a drop-down like the source drop-down) and the number of computers (probably a spinner or text box). Ensure that their control sources are set to the matching properties of the data object. Remove the VB check box.
That’s it! No data changes are required either to capture or to retrieve the data. The next time a survey is opened that was completed using the old format, the default values will be used for the new properties, the VB property will be ignored, and the existing 30 characters will be used for the other source field. As soon as it’s saved, the data will be in the new format.
Extra credit
You can use the metadata stored in the survey_master table to build a list of the surveys to which a particular company is “subscribed.” You could present a list to the user, including the description of the survey and the data that was last completed for this company in a GUI. The UI_Class field would then allow you to instantiate an appropriate form for viewing or editing survey responses.
It’s quite simple to change the back-end database used for this class. In fact, I developed the system using a SQL Server back end. All you need to do is change the cConnectString property of the surveydatabase class to one that’s appropriate for your back end of choice and then set up the tables with the structure shown in Figure 3 on that back end. That’s it—the conversion’s complete.
Conclusion
Using XML to store data in a memo field provides an extremely flexible architecture while allowing the structured retrieval of the data. While it’s not essential that XML be used (it could have just as well been a proprietary text or even binary format in the memo fields), the fact that XML is a standard for this type of work means that tools like Rick Strahl’s wwXML library make working with the format simple and quick. I encourage you to get comfortable with this powerful data exchange format.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the November 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Subclassing Existing Applications
Article
06/30/2006
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we’ve left these URLs in the text, but disabled the links.
Subclassing Existing Applications
Richard David Hodder
Most developers have a store of reusable code they rely on during development. Most (if not all) of our apps use or subclass things from this store. In this article, Richard David Hodder presents an approach for reusing application functionality across applications.
You create subclasses to reuse existing functionality in a class and to extend/change its behavior. You can “subclass” an application to reuse existing functionality in the application and to extend/change its behavior. I use the term “subclassing” somewhat loosely here, but I think it conveys the right intent.
Let’s say that I have an existing application “Oingo” and I wish to create a new application “Boingo” that’s based on Oingo. There are several possible reasons to subclass the Oingo application:
Add functionality: I want Boingo to have all of the functionality of Oingo plus some new features.
Substitute/override functionality: I want Boingo to have all of the functionality of Oingo with substitutions for some existing functionality (for example, an Import form that looks totally different).
Remove functionality: I want Boingo to have all of the functionality of Oingo with some existing functionality removed (for example, remove the Import option from the menu).
The first approach that might come to mind would be to create a new project file for Boingo, within the Oingo application directory. This approach mixes code bases and leaves you at high risk for breaking Oingo while trying to extend behavior in Boingo.
The most direct approach would be to create a new directory for Boingo, copy all of the files from the Oingo application into the new directory, make changes/add functionality, rebuild, test, and distribute, right? The problem with this approach is that the “connection” between Oingo and Boingo no longer exists: They no longer stem from the same code base. There are now two distinct code bases: When changes to Oingo are made, the changes must also be applied to Boingo to maintain the same functionality.
In a perfect world, several things would happen:
The Boingo project would have its own directory.
The project file for Boingo would be pointing to the same code base that’s found in the Oingo project file. This would allow the changes to be made to Oingo and “ripple” to Boingo.
The project directory for Boingo should have markedly less code—only the code that implements the additions, substitutions, and removals of Oingo functionality.
Changes to the application layer (“a-layer”) code in Oingo would be inherited by Boingo.
Changes to the a-layer in Boingo wouldn’t be inherited by Oingo: Again I refer you to the subclass metaphor. Changes in subclasses don’t affect the superclass; there’s no “upward” inheritance.
One of my favorite quotes is, “Wherever you go, there you are.” In a perfect world, you’d know the exact locations where functionality would need to be added, substituted, and removed before the originating application (Oingo in this example) is designed. This would allow you to do things like place hooks into and data-drive the application in order to control what’s in an application and how to extend it. Most times it’s not possible, and you may not know beforehand that the application needs to be “subclassed.” That’s why I included the word “existing” in the title of this article: You’ve already created the application. Now you want to make use of your existing work.
The keys to subclassing an application
The approach I’ll present later for subclassing an application relies on being able to “trick” VFP into using other functions and classes. I’ll present the general concepts first, and then I’ll demonstrate how I applied them in Codebook applications I’ve written.
The keys to subclassing an application are:
Managing the program stack (SET PROCEDURE)
Managing the class stack (SET CLASSLIB)
Managing the pathing (SET PATH)
Copying the project file and massaging its contents
Copying supporting files
****
Management of the program stack
When you call a function, VFP goes through the list of PRGs in the SET PROCEDURE list until it finds the function and then executes it. It goes through the list in the order of the PRGs in the SET PROCEDURE list. If two functions with the same name exist in one or more of the PRGs, then VFP executes the first one it encounters. Most of us have made the mistake of accidentally creating two functions with the same name. It gets really fun (read: frustrating <g>) when you keep modifying the function that’s later in the stack, and then run the application and find that things still seem broken. It always happens at 3 a.m. when you’re tired and out of caffeine. Consider the following three programs (Test, Foo, and Bar):
When you run TEST.PRG, you’ll always get the message box saying “I’m broken,” no matter how many times you change the “I’m fixed” string in the Hello function in BAR.PRG. That’s because when walking through the list of PRGs in SET PROCEDURE, VFP finds the Hello function in FOO.PRG first. The same goes for instantiating classes stored in PRGs: The first class encountered in the SET PROCEDURE list is instantiated.
You might be asking, “What’s that got to do with subclassing an existing app?” Consider the following situation: Oingo has a stock price importing form that calls a function named ImportStockQuotes, which retrieves stock quotes from a Web site and imports them into a database. You want Boingo to import stock quotes from a file on disk rather than from a Web site. To change just the ImportStockQuotes “behavior” in Boingo, add a PRG to the Boingo project (for example, BoingoFunctionOverrides.PRG) and in that PRG create a function called ImportStockQuotes and fill it with the code to import the quotes from a file.
The only step left in order to override the ImportStockQuotes behavior is to make sure that Boingo loads BoingoFunctionOverrides.PRG into the SET PROCEDURE list earlier than Oingo’s ImportStockQuotes. If you want to change the behavior of a class called DataImporter stored in a PRG, add a PRG to the Boingo project (for example, BoingoClassOverrides.PRG). In that PRG, create a class called DataImporter and fill it with the code that performs the behavior you want that class to execute in Boingo. You can be surgically “precise” with what you change by using this approach.
****
Management of the class stack
Managing the class stack is very similar to managing the program stack. When you instantiate a class that exists in a VCX class library, VFP goes through the list of class libraries in the SET CLASSLIB list until it finds the class and then instantiates it. It goes through the list in the order of the class libraries in the SET CLASSLIB list. If two classes with the same name exist in one or more of the class libraries, then VFP instantiates the first one it encounters. There’s one important difference between managing the program and class stacks: The class stack can’t contain two class library files with the same name. For example, suppose you had two directories (Oingo and Boingo) and each had a class library called DeadMansParty.VCX. If you executed the following command:
SET CLASSLIB TO ;
OingoDeadMansParty, ;
BoingoDeadMansParty
you’d get the cryptic error “Alias name is already in use.” Obviously, behind the scenes VFP is opening the class libraries as tables, and you can’t have two tables with the same alias (DeadMansParty) open in the same workarea. You could get around this by using the ALIAS parameter of the SET CLASSLIB command:
As with the program stack, the trick to subclassing an application is to make sure that the a-layer class libraries for the subclassed app (Boingo) are loaded into SET CLASSLIB earlier than the class libraries from the superclass app (Oingo).
****
Management of the pathing
Managing the path by using SET PATH can also be important. Codebook developers can test their applications without having to build executables by taking advantage of path settings. Boingo may rely on the setting of path to open a file that’s in the Oingo directory. ******
Copying the project file and massaging its contents
A few paragraphs ago I mentioned that it’s important to make sure that Oingo and Boingo are based on the same code base. What’s the quickest way to do that? Just copy the Oingo project file (PJX and PJT) into the Boingo directory, right? Close but no cigar. Now Boingo’s project thinks that its directory contains Oingo’s code: The pathing to the project items is wrong. The pathing needs to be adjusted so that the items in the Boingo project file point to the items in Oingo’s directory. Some care has to be taken with this because some items still should exist in the Boingo directory—supporting files, for example. ******
Copying supporting files
Some items in a project support a framework or application and are application-specific, and therefore copies should be made of them, rather than pointing to the superclass app’s files. Databases and tables are a good example of this. What good is it to point the Boingo project at Oingo’s data, when one of the things that may be changing from Oingo to Boingo is data structures? Also, for development environment purposes it’s better if Boingo has its own data structures, even if there are no changes. Other examples of supporting files are reports and INI files. Support files will differ based upon which framework(s) you develop with (for instance, Codebook, INTL, WebConnect, and so forth).
Frameworks can make most if not all of the keys to subclassing an application simpler to achieve because usually they’ve been addressed in the framework. For example, as you’ll soon see, Codebook keeps a table of the programs and class libraries to load. By changing the framework slightly, these programs and classes can be loaded in a manner that supports the task of a-layer ordering of items in SET CLASSLIB and SET PROCEDURE.
Subclassing a Codebook application
Rather than trying to address all possible frameworks and libraries, I’m going to stick to territory that’s familiar for me: the Codebook framework. ******
Management of the program and class stacks in Codebook
Codebook applications build a list of the programs and class libraries used by the application by querying the contents of the project file. In a function named BuildMetaData in Setup.PRG, the project file is opened with the alias “_project” and then the following query is run:
SELECT NAME, TYPE ;
FROM _project ;
WHERE !DELETED() AND ;
(TYPE = "V" OR ;
TYPE = "P") ;
ORDER BY TYPE ;
INTO CURSOR cTemp
Records with a type equal to “V” are class libraries (VCXs), and records with a type equal to “P” are programs (PRGs). The contents of this cursor are saved to a table called METADATA, which is built into Codebook executables. In another program named SetPath, the information in this cursor is looped through. All of the program records (type=”P”) are used to build a comma-delimited list of the programs in the project. The function then SETs PROCEDURE to this list of files. All of the class library records (type=”V”) are used to build a comma-delimited list of the class libraries in the project. The code then SETs CLASSLIB to this list of files.
Although this list is organized by type, this doesn’t order the files so that the subclassed application’s a-layer will get loaded earlier. Then I came up with an idea. I had the project file opened as a table, and I was looking at the Name field of the PJX file. The Name field uses relative pathing to point to project items. Therefore, framework files all started with “..” (for example, “..common50libscapp.vcx”). Files that were in the a-layer, on the other hand, did not start with “..” because they were either in the project’s directory or a subdirectory of the project’s directory (for example, “progssolvetheworldsproblems.prg”).
I decided to change the contents of MetaData.DBF and reorder the records so that names not beginning with “..” float to the top of the list. Here’s the modified query that I used:
I created a new field (LAYER) that holds the first two characters of the name of the file. This field is only used for the ORDER BY of this query. The framework doesn’t use the field for anything. All non-a-layer code will have a LAYER equal to “..” and all a-layer code will not. Due to the fact that the period character “.” has a lower ASCII value than the alphabetic characters, it was necessary to order the list by descending LAYER so that all a-layer records would be at the top of the cursor. Being at the top of the cursor, they get loaded into the respective stacks first!
****
Management of the pathing in Codebook
The main reason for managing pathing in Codebook is to create a development environment that allows you to develop an application without constantly having to rebuild the executable. When subclassing an application, Codebook must know two things: first, whether the current application is a subclassed application (like Boingo), and second, if it is, where the superclass application (Oingo) resides so that it can adjust its paths to point at Oingo. To solve this, I create a #DEFINE called APPLICATION_SUPERCLASS_DIRECTORY that contains the superclass’s directory. This #DEFINE only exists if the current application is a subclass. Therefore I was able to use the #IFDEF directive to change the pathing in the SetPath function in Setup.PRG:
#IFDEF will only execute its code if the #DEFINE exists (if the application is a subclass).
****
Copying supporting files and the project file and massaging its contents in Codebook
I created a simple tool called the Codebook Application Subclasser (see Figure 1).
The form SUBCLASS.SCX is available in the accompanying Download file. I won’t show all of the code here, but once the superclass application directory and subclass application directory are chosen, the following steps are taken:
Let’s look at the last four function calls in this code snippet.
BuildIncludeFile—This routine creates the application include file for the subclass application (Boingo). It #INCLUDEs the application include file from the superclass application (Oingo). This is done so that #DEFINEs added to Oingo in the future will be “inherited” by Boingo. Boingo’s include file can be added to manually: #DEFINEs added to this file don’t affect Oingo (as I said before, inheritance doesn’t move upward).
ModifyMainProgram—Main.PRG is the main program for Codebook applications. Main.PRG gets copied over from the superclass. ModifyMainProgram uses LLFF (low-level file functions) to add the equivalent of the following to the bottom of Main.PRG:
This has no effect on the code, but it makes sure that the next time the Boingo project is built, the aappBoingo class library will be added to the Boingo project.
ModifyStartCB—StartCB.PRG is a program that sets up the development environment (paths and so on) for an application. It isn’t built into the application; it merely sets up the environment. ModifyStartCB adds code to StartCB so that the superclass application’s paths are included.
CreateApplicationObjectSubclass—Every application created with the Codebook framework has an application object stored in AAPP.VCX. The application object holds global information for the application. When subclassing an application, it may be necessary to add or change properties and functionality on the subclassed application’s application object. For example, Boingo might need to attach a timer object to the application object to remind the user to import stock prices (a feature not available in Oingo). Therefore, I create a subclass of the superclass app’s application object and place it in the LIBS directory of the Boingo project’s directory. Boingo-specific application object changes get made to this object. In order to avoid the class library naming collision problem mentioned earlier, I name the class library “AAPP<Subdirectory of CDBK50 To Create>” (for example, AAPPBoingo).
When to subclass an application
Just because cause you can subclass an application doesn’t mean that you should. I can think of two occasions when subclassing an application would be appropriate:
If you sell a product and receive a lot of requests for “one-off” functionality.
If products will be internationally distributed. I worked on products that were developed in the U.S., but with the foresight to make them ready for the international market by incorporating Steven Black’s INTL Toolkit (www.StevenBlack.com). As international versions of the product were developed, there was locale-specific functionality that needed to be incorporated. Spain required new reports, Italy needed enhancements to the import form, and so on.
Lessons learned
Having coding standards—particularly frameworks and standard directory structures and the like—made this process simpler: This is true for coding in general. I’m not suggesting that you use Codebook specifically (although four out of five VFP developers surveyed… <g>). Rather, I suggest that you pick (or create) standards and stick with them.
Also, use of the NEWOBJECT() function could actually get in your way because it hard-codes the location of the class library of the class you wish to instantiate. I’ve never really been a fan of this function for that very reason. I prefer to let the environment figure it out (particularly if I need to refactor classes into new class libraries), but that’s just my own bias.
Note: This is not a Microsoft Corporation website. Microsoft is not responsible for its content.
This article is reproduced from the December 2001 issue of FoxTalk. Copyright 2001, by Pinnacle Publishing, Inc., unless otherwise noted. All rights are reserved. FoxTalk is an independently produced publication of Pinnacle Publishing, Inc. No part of this article may be used or reproduced in any fashion (except in brief quotations used in critical articles and reviews) without prior consent of Pinnacle Publishing, Inc. To contact Pinnacle Publishing, Inc., please call 1-800-493-4867 x4209.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
Microsoft® Visual FoxPro® database development system is a powerful tool for quickly creating high-performance desktop, rich client, distributed client, client/server, and Web database applications. Employ its powerful data engine to manage large volumes of data, its object-oriented programming to reuse components across applications, its XML Web services features for distributed applications, and its built-in XML support to quickly manipulate data.
Join the conversation and get your questions answered on the Visual FoxPro Forum on MSDN.
Visual FoxPro 9.0 Overview
With its local cursor engine, tight coupling between language and data, and powerful features, Visual FoxPro 9.0 is a great tool for building database solutions of all sizes. Its data-centric, object-oriented language offers developers a robust set of tools for building database applications for the desktop, client-server environments, or the Web. Developers will have the necessary tools to manage data—from organizing tables of information, running queries, and creating an integrated relational database management system (DBMS) to programming a fully-developed data management application for end users.
Data-Handling and Interoperability. Create .NET compatible solutions with hierarchical XML and XML Web services. Exchange data with SQL Server through enhanced SQL language capabilities and newly supported data types.
Extensible Developer Productivity Tools. Enhance your user interfaces with dockable user forms, auto-anchoring of controls, and improved image support. Personalize the Properties Window with your favorite properties, custom editors, fonts, and color settings.
Flexibility to Build All Types of Database Solutions. Build and deploy stand-alone and remote applications for Windows based Tablet PCs. Create and access COM components and XML Web Services compatible with Microsoft .NET technology.
Reporting System Features. Extensible new output architecture provides precision control of report data output and formatting. Design with multiple detail banding, text rotation, and report chaining. Output reports supported include in XML, HTML, image formats, and customizable multi-page print preview window. Backward compatible with existing Visual FoxPro reports.
Download samples, along with the final product updates including service packs for Visual FoxPro to ensure maximum productivity and performance from your Visual FoxPro development.
Visual FoxPro 9.0 Updates
Visual FoxPro 9.0 Service Pack 2 (SP2) Download Service Pack 2 for Microsoft Visual FoxPro 9.0. SP2 provides the latest updates to Visual FoxPro 9.0 combining various enhancements and stability improvements into one integrated package.
GDI+ Update for Visual FoxPro 9.0 SP1 Security update patch for Visual FoxPro 9.0 SP1 for fixing Buffer Overrun in JPEG Processing (GDI+). Note: We highly recommend that you install Service Pack 2, then apply the GDI+ SP2 update.
Visual FoxPro 9.0 ‘Sedna’ AddOns AddOn pack for Visual FoxPro 9.0. This download contains six components: VistaDialogs4COM, Upsizing Wizard, Data Explorer, NET4COM, MY for VFP and VS 2005 Extension for VFP.
XSource for Visual FoxPro 9.0 SP2 Download XSource for Visual FoxPro 9.0 SP2. XSource.zip has its own license agreement for usage, modification, and distribution of the Xbase source files included.
Microsoft OLE DB Provider for Visual FoxPro 9.0 SP2 The Visual FoxPro OLE DB Provider (VfpOleDB.dll) exposes OLE DB interfaces that you can use to access Visual FoxPro databases and tables from other programming languages and applications. The Visual FoxPro OLE DB Provider is supported by OLE DB System Components as provided by MDAC 2.6 or later. The requirements to run the Visual FoxPro OLE DB Provider are the same as for Visual FoxPro 9.0. Note: This version of the VFP OLE DB provider is the same version as the one included with Visual FoxPro 9.0 SP2.
VFPCOM Utility Extend Visual FoxPro interoperability with other COM and ADO components with the VFPCOM Utility. This utility is a COM server that provides additional functionality when you use ADO and access COM events with your Visual FoxPro 9.0 applications. For installation instructions and more details on the issues that have been addressed, consult the VFPCOM Utility readme.
Visual FoxPro ODBC Driver The VFPODBC driver is no longer supported. We strongly recommend using the Visual FoxPro OLE DB provider as a replacement. Please refer to the following article for more information and related links to issues when using the VFPODBC driver: https://support.microsoft.com/kb/277772.
Visual FoxPro 8.0 Updates
Visual FoxPro 8.0 Service Pack 1Download Microsoft Visual FoxPro 8.0 Service Pack 1 (SP1), which provides the latest updates to Visual FoxPro 8.0. SP1 combines various enhancements and stability improvements into one integrated package. The download contains all the documentation for these updates. For installation instructions and more details on SP1, consult the Service Pack 1 readme.
Visual FoxPro 8.0 SP1 Task Pane Source CodeSource code for Task Pane Manager component included in SP1 for Visual FoxPro 8.0. SP1 for VFP 8.0 included an updated Task Pane Manager component as an .APP application file but did not contain the update source code files associated with the updated version.
Visual FoxPro 8.0 Localization Toolkit OverviewOverview document of the Localization Toolkit project results for making available various language versions of the design-time IDE DLL and help documentation as add-ons to the English version of Visual FoxPro 8.0.
Visual FoxPro 7.0 Updates
Visual FoxPro 7.0 Service Pack 1Download Microsoft Visual FoxPro 7.0 Service Pack 1 (SP1), which provides the latest updates to Visual FoxPro 7.0. SP1 combines various enhancements and stability improvements into one integrated package. The download contains all the documentation for these updates. For installation instructions and more details on SP1, consult the Service Pack 1 readme.
Code Samples
.NET Samples for Visual FoxPro DevelopersThis download contains different projects and source files which are designed to show how how some common Visual FoxPro functionally is created in Visual Basic .NET.
Visual FoxPro 8.0 SamplesThis download contains different projects which are designed to show how new features in Visual FoxPro 8.0 can be used. Each project is self-contained and can be run independently of any other. There is a readme text file contained in each project that describes each sample program.
To install Microsoft Visual FoxPro 9.0, you need:Expand table
Minimum Requirements
Processor
PC with a Pentium-class processor
Operating System
Microsoft Windows 2000 with Service Pack 3 or later operating systemMicrosoft Windows XP or laterMicrosoft Windows Server 2003 or later
Memory
64 MB of RAM minimum; 128 MB or higher recommended
Hard Disk
165 MB of available hard-disk space for typical installation; 20 MB of additional hard-disk space for Microsoft Visual FoxPro 9.0 Prerequisites
Drive
CD-ROM or DVD-ROM drive
Display
Super VGA 800 X 600 or higher-resolution monitor with 256 colors
Mouse
Microsoft Mouse or compatible pointing device
Frequently Asked Questions
Article
08/29/2016
Find answers to your frequently asked questions about Visual FoxPro.
Q: What operating system is required for Visual FoxPro 9.0?
Developing applications with Visual FoxPro 9.0 is supported only on Microsoft Windows 2000 Service Pack 3 or later, Windows XP, Windows Server 2003 and Windows Vista. You can create and distribute run-time applications for Windows 98, Windows Me, Windows 2000 Service Pack 3 or later, Windows XP, Windows Server 2003 and Windows Vista. Installation on Windows NT 4.0 Terminal Server Edition is not supported.
Yes. Visual FoxPro will continue to be supported as per the lifecyle policy (https://support.microsoft.com/lifecycle/?p1=7992). Visual FoxPro 9 will be supported until 2014. In support of these products we may release patch updates from time to time. These typically fix problems discovered either internally or by a customer and reported to our product support engineers.
Q: Will there be a service pack 3 for Visual FoxPro 9?
At this time there are no plans to release a service pack for Visual FoxPro. However if there arises a need to publish a collection of fixes we may release a service pack. We will make announcements on the Visual FoxPro home page.
Q: What types of applications can I build with Visual FoxPro 9.0?
With its local cursor engine, tight coupling between language and data, and powerful features, such as object-oriented programming, Visual FoxPro 9.0 is a great tool for building database solutions of all sizes, from desktop and client/server database applications to data-intensive COM components and XML Web services.
Visual FoxPro 9.0 is an application development tool for building extremely powerful database applications and components. Its data-centric, object-oriented language offers developers a robust set of tools for building database applications on the desktop, client/server, or on the Web, through components and XML Web services. Developers will have the necessary tools to manage data from organizing tables of information, running queries, and creating an integrated relational database management system (DBMS) to programming a fully developed data management application for end users.
Q: Can I use Visual FoxPro to build Web applications?
Visual FoxPro COM components can be used with Internet Information Services (IIS) to build high-powered Internet database applications. This is because Visual FoxPro components can be called from Active Server Pages (ASP). Visual FoxPro is compatible with ASP but works even better in conjunction with the more modern ASP.NET. The components will retrieve and manipulate data, and will build some of the HTML returned to the user.
Q: Can you consume XML Web services with Visual FoxPro?
Yes, Visual FoxPro 9.0 makes it easy to consume XML Web services by integrating the SOAP Toolkit into the product.
Q: Is Visual FoxPro a part of MSDN Subscriptions?
Yes, Visual FoxPro 9.0 is included in the Professional, Enterprise, and Universal levels of MSDN Subscriptions. Visual FoxPro 9.0 is available for download to MSDN Subscribers via MSDN Subscriber downloads.
Q: How long will Visual FoxPro be supported by Microsoft?
Visual FoxPro 9.0 has standard support by Microsoft through January 2010 and extended support through January 2015 as per the developer tools lifecycle support policy.
Q: How long will the SOAP Toolkit included in Visual FoxPro 9.0 be supported by Microsoft?
Licensed users of Visual FoxPro 9.0 have a special lifecycle support plan for the SOAP Toolkit, supported by Microsoft on the same support plan as Visual FoxPro 8.0 which is through April 2008 and extended support through September 2013.
Q: Is Visual FoxPro 9.0 compatible with Visual Studio 2005 and SQL Server 2005?
Yes. We improved XML support and added new data types in Visual FoxPro 9.0 which improves .NET interop and SQL Server compatibility. Moreover the ‘Sedna’ add-on pack includes improvements to the Data Explorer and the Upsizing Wizard. These have significant improvements to support SQL Server 2005.
Q: How does Visual FoxPro 9.0 compare to SQL Server?
We do not contrast Visual FoxPro versus SQL Server. We position SQL Server as a database engine and Visual FoxPro as a developer tool. While Visual FoxPro has a database engine built-in, it is not positioned as a stand-alone database engine only. The trend is for an increasing amount of Visual FoxPro based applications to use SQL Server as the data storage in the solution. Of course, this is not required; it depends on the requirements of the solution. SQL Server offers security, reliability, replication, and many other features of a full relational database engine while the Visual FoxPro database system is an open file based DBF system that does not have many of those features. We leave it up to developers and companies to position and to compare various Microsoft products and technologies with each other and decide which ones are best for them to use when and how.
Q: Are there plans to enhance the 2 GB database size limit in Visual FoxPro?
The 2 GB limit is per table, not per database. We do not have any plans to extend the 2 GB table size limit in Visual FoxPro due to many reasons including the 32-bit architecture that already exists within the product. For large, scalable databases we recommend SQL Server 2008.
Q: Is Visual FoxPro supported on Windows Vista?
Yes. Visual FoxPro 9 Service Pack 2 is fully supported on Windows Vista.
Q: Are there plans for Visual FoxPro to support 64-bit versions of the Windows operating system?
No. While Visual FoxPro will remain 32-bit and not natively use 64-bit addressing; it will run in 32-bit compatibility mode. Visual Studio 2008 supports creating native 64-bit applications.
Q: How do you position Visual FoxPro in relation to Microsoft Access?
Microsoft Access, the database in Office, is the most broadly used and easiest-to-learn database tool that Microsoft offers. If you are new to databases, if you are building applications that take advantage of Microsoft Office, or if you want an interactive product with plenty of convenience, then choose Microsoft Access. Visual FoxPro is a powerful rapid application development (RAD) tool for creating relational database applications. If you are a database developer who builds applications for a living and you want ultimate speed and power, then choose Visual FoxPro.
Q: Is Visual FoxPro part of Visual Studio .NET?
No. Visual FoxPro 9.0 is a stand-alone database development tool which is compatible and evolutionary from previous versions of Visual FoxPro. Visual FoxPro 9.0 does not use or install the Windows .NET Framework. Visual FoxPro 9.0 is compatible with Visual Studio .NET the area of XML Web services, XML support, VFP OLE DB provider, and more. Visual FoxPro and Visual Studio are complimentary tools that work great together, such as Visual FoxPro 9.0 plus ASP.NET for adding WebForm front ends and mobile device front ends to Visual FoxPro applications.
Q: What is Microsoft’s position on Visual FoxPro related to Visual Studio and .NET?
We do not have plans to merge Visual FoxPro into Visual Studio and .NET, and there are no plans to create any sort of new Visual FoxPro .NET language. Instead, we are working on adding many of the great features found in Visual FoxPro into upcoming versions of Visual Studio, just like we’ve added great Visual Studio features into Visual FoxPro. If you want to do .NET programming, you should choose a .NET language with Visual Studio.
A Message to the Community
Article
08/29/2016
March 2007
We have been asked about our plans for a new version of VFP. We are announcing today that there will be no VFP 10. VFP9 will continue to be supported according to our existing policy with support through 2015 (https://support.microsoft.com/lifecycle/?p1=7992). We will be releasing SP2 for Visual FoxPro 9 this summer as planned, providing fixes and additional support for Windows Vista.
Additionally, as you know, we’ve been working on a project codenamed Sedna for the past year or so. Sedna is built using the extensibility model of VFP9 and provides a number of new features including enhanced connectivity to SQL Server, integration with parts of the .NET framework, support for search using Windows Desktop Search and Windows Vista as well as enhanced access to VFP data from Visual Studio.
Concurrently, the community has been using CodePlex (https://www.codeplex.com) to enhance VFP using these same capabilities in the VFPx project. Some of these community driven enhancements include:
Support for GDI+
An enhanced class browser
Support for Windows Desktop Alerts
An object oriented menu system
Integration with MSBuild
A rule-based code analysis tool similar to fxCop in Visual Studio
An Outlook Control Bar control
To reiterate, today we are announcing that we are not planning on releasing a VFP 10 and will be releasing the completed Sedna work on CodePlex at no charge. The components written as part of Sedna will be placed in the community for further enhancement as part of our shared source initiative. You can expect to see the Sedna code on CodePlex sometime before the end of summer 2007.
Summary: Provides Microsoft Visual FoxPro developers with an overview of ActiveX Data Objects (ADO) and shows how to incorporate ADO into Visual FoxPro applications. Discusses the ADO object model and implementing Remote Data Services (RDS). (52 printed pages)
Contents
IntroductionWhat are OLE DB and ADO?Why Incorporate ADO into a Visual FoxPro Application?ADO Object ModelRemote Data ServicesSummary
Introduction
Microsoft®ActiveX® Data Objects (ADO) is perhaps the most exciting new Microsoft technology in quite some time. Because ADO is concerned with data, this new technology is of particular interest to Microsoft® Visual FoxPro® developers. Of course, you may ask, “Why do I need ADO? Visual FoxPro already has a high-performance local data engine.” It’s a good question.
This paper provides the Visual FoxPro developer with a background of what ADO is and how to incorporate ADO into Visual FoxPro applications. After reading this paper, you should have enough information to readily answer the question: “Why do I need ADO?”
A Brief Word About ADO Events
One limitation of Visual FoxPro has been an inability to surface COM events. While Visual FoxPro can respond to events raised by ActiveX controls, objects created with the CreateObject function cannot. In Microsoft®Visual Basic®, COM Events are handled by using the WithEvents keyword. In Visual FoxPro, the new VFPCOM.DLL achieves the same results. The topics VFPCOM, ADO Events, and how to integrate ADO and Visual FoxPro will be discussed in another white paper. This paper is dedicated to providing the Visual FoxPro developer, with a comprehensive overview of ActiveX Data Objects, Remote Data Services (RDS), their respective objects, and how those objects work.
This paper covers the following topics:
What are ADO and OLE DB?
Why incorporate ADO into a Visual FoxPro application?
The ADO object model
Remote Data Services
What Are OLE DB and ADO?
When discussing ADO, we are really talking about two distinct elements: the ActiveX data objects themselves and Microsoft Universal Data Access technology, more commonly known as OLE DB.
OLE DB and Universal Data Access
In simple terms, OLE DB is the succeeding technology to the Open Database Connectivity (ODBC) standard. OLE DB is a set of low-level interfaces that facilitate the Microsoft Universal Data Access strategy. ADO is a set of high-level interfaces for working with data.
While both ODBC and OLE DB have the ability to make data available to a client, the capabilities of the two technologies are very different. ODBC is primarily designed for use on relational data. However, data exists in nonrelational as well as relational formats. In addition to new data formats, data resides in new places such as the Internet. Finally, the Microsoft Component Object Model (COM) framework requires better data access technology. Clearly, ODBC does not address these needs; a new technology is needed. That technology is OLE DB, and it is here to stay.
The following graphic best illustrates how OLE DB and ADO work together. Clients can work directly with OLE DB or can work with OLE DB through the ADO interface (the latter is typically the case). Note that OLE DB can access SQL data either directly or through ODBC. An OLE DB provider provides direct access by OLE DB. Also note that OLE DB can also be used to access a variety of non-SQL data, as well as data that exists in mainframes. The ability to access data through a common interface, without regard to data location or structure, is the real power behind ADO and OLE DB.
Whereas ODBC uses drivers, OLE DB uses providers. A provider is a software engine that provides a specific type of data that matches the OLE DB specification. Several OLE DB providers exist today, including those for Microsoft SQL Server™ and Oracle. Because there is such widespread use of ODBC, an OLE DB provider for ODBC has also been created in order to ease the migration from ODBC to OLE DB. Several nonrelational providers are currently under development. Perhaps the most anticipated of these is the OLE DB Provider for Microsoft Outlook®. A special provider, MS Remote, allows direct data access over the Internet. This brief list of providers shows the third-party community commitment to OLE DB, and many new providers are currently under development. For the latest news on available providers, refer to https://www.microsoft.com/data/.
ADO Overview
OLE DB is then a set of low-level interfaces that provide access to data in a variety of formats and locations. While powerful, OLE DB interfaces can be cumbersome to work with directly. Fortunately, ADO provides a set of high-level, developer-friendly interfaces that make working with OLE DB and universal data access a relatively simple task. Regardless of the programming environment you use, any Visual Studio® or Microsoft Office product such as Visual FoxPro, Visual Basic, Visual C++®, or Word, the interface you will use to access data remains constant. That interface is ADO, which in turn uses OLE DB.
ADO itself is just a set of objects. By itself, ADO is not capable of anything. In order to provide any functionality, ADO needs the services of an OLE DB provider. The provider in turn uses the low-level OLE DB interface to access and work with data. One ADO connection may use a SQL Server OLE DB provider and another ADO connection may use an Oracle OLE DB provider. While the interface is constant, the capabilities may be very different because OLE DB providers are very different, which highlights the polymorphic nature of OLE DB.
As developers, we crave consistency. ADO provides us with a consistent interface for our program code.
ADO Version Summary
The current version of ADO (2.1) is the fourth version of ADO to be released in less than two years. ADO 1.0 was primarily limited to working with Active Server pages. Only one OLE DB provider existed, the OLE DB Provider for ODBC Drivers.
ADO (2.1)—Ships with the newest version of Microsoft Web browser, Internet Explorer 5.0. When discussing data or anything related to the Internet, it is almost impossible to do so without mentioning XML. XML, the Extensible Markup Language, is a mark-up language that allows users to create custom tags to describe data. XML is quickly becoming the universal format for storing and streaming data. The primary storage format in Office 2000 for document data will be XML. ADO (2.1) client-side recordsets can be saved as XML documents.
ADO (2.0)—Represented a huge gain in functionality. One of the most notable new features was the ability to create client-side recordsets. To go along with this, also added were the abilities to create filters and indexes, and the ability to sort recordsets. These abilities are very much the same as those that exist with Visual FoxPro cursors. Finally, the ability to persist client-side recordsets was also added. In effect, data could be acquired from a server into a client-side recordset. The client-side recordset could then be saved as a file on the local hard-drive that could be opened at a later time without being connected to the network.
ADO (1.5)—Introduced new capabilities and providers to ADO. Among the new providers was the OLE DB Provider for Jet (the JOLT Provider). The MS Remote Provider, which powers the Remote Data Services (RDS), was introduced as well. This version also introduced the ability to create disconnected recordsets.
What You Need to Get Started
In order to work through the examples presented in this paper, you will need the following:
SQL Server 6.5 or 7.0 with the sample Northwind database installed
A system DSN called TasTrade that points to the TasTrade Visual FoxPro Sample Database
A system DSN called Northwind that points to the SQL Server Northwind database
Why Incorporate ADO into a Visual FoxPro Application?
Have you ever wanted to pass a cursor as an argument to a function or class method? Or have you wanted to pass data to automation server applications such as Microsoft Word or Excel? Perhaps you have created a Visual FoxPro DLL and have needed a way to pass data from the user interface to a class method in the DLL. Maybe you have been looking for a way to stream data across the Web. If your answer is “yes” to at least one of these, ADO can help you today!
Until now, the world of component-based development has lacked one thing: a method of effectively moving data between processes. Now, whether ADO is hosted by Visual FoxPro, Visual Basic, Excel, or Word, the interface is consistent. The new COM capabilities of Visual FoxPro 6.0 enable creating of ADO recordsets, populating them with data, and passing them to a variety of processes. This all goes to support the strategic positioning of Visual FoxPro, a creator of middle-tier components.
Just about everything in Visual FoxPro is an object, except for reports, menus, and data. One of the biggest feature requests from Visual FoxPro developers has been the ability to work with data as a set of objects. Data objects provide several benefits, including an enhanced event model and the ability to overcome limitations of Visual FoxPro cursors. While many limitations are gone, many benefits of Visual FoxPro cursors have been retained. As you work with ADO, there’s good reason to think are many similarities to Visual FoxPro; ADO is based on the Visual FoxPro cursor engine. So, for those who have wanted data objects in Visual FoxPro, the wait is over with ADO.
ADO is not a replacement for Visual FoxPro cursors. Rather, Visual FoxPro cursors and ADO are complementary. When used together, very powerful applications can result. The following pages detail the ADO object model and the common properties and methods you will work with, including:
Remote Data Services (RDS), technology which allows for the streaming of data over the Internet via HTTP.
VFPCOM.DLL, which enables the handling of COM events in Visual FoxPro.
ADO Integration into Visual FoxPro.
This section has several comprehensive examples on strategies you may employ when integrating ADO into your Visual FoxPro Applications.
ADO Object Model
Connection Object
ProgID: ADODB.Connection
The purpose of the Connection object is to provide access to a data store. To illustrate, the following code creates an ADO Connection object:
oConnection = CreateObject("adodb.connection")
Once an ADO Connection object has been created, you can access its data store. An active connection can be established by providing a few pieces of key information and invoking the Open( ) method of the Connection object. The following code opens a connection to the Visual FoxPro TasTrade database:
oConnection.Open("TasTrade")
Alternatively, the following code accesses the SQL Server Northwind database:
oConnection.Open("Northwind","sa","")
These two examples work with the OLE DB Provider for ODBC drivers. Different OLE DB providers can be used as well. The following example sets some common properties of the Connection object and uses the OLE DB Provider for SQL Server:
The syntax of the ConnectionString property appears complicated. Fortunately, you don’t have to code this by hand. When you install the Microsoft Data Access Components (MDAC), you can create a data link file.
To create a data link file:
Right-click your desktop and choose NewMicrosoft Data Link from the pop-up menu.
Specify a name for the file.
Right-click and select Properties to modify the file properties.
In the Properties dialog box, click the Provider tab, and choose a provider. The OLE DB Provider for ODBC is the default choice. For this example, select the OLE DB Provider for SQL Server.
Click the Connection tab.
Specify the name of the server, your user name and password, and the name of the database you wish to connect to.
Open the UDL file in Notepad.Now, it is just a matter of copying and pasting the information. Alternatively, you can use the file itself:oConnection.Open(“File Name=c:temptest.udl”)
ADO recognizes four arguments in the ConnectionString:
File Name: Specifies the name of a UDL file to use.
Provider: Specifies the name of an OLE DB provider to use.
Remote Provider: Specifies the name of a provider to use with Remote Data Services (RDS).
Remote Server: Specifies the server on which data resides when using Remote Data Services (RDS).
Any additional arguments passed in the ConnectionString are passed through to the OLE DB provider being used.
In addition to the Open method, the following are the common methods you are likely to use with the Connection object:
BeginTrans, CommiTrans, and RollBackTrans—These methods work like the Begin Transaction, End Transaction, and RollBack statements in Visual FoxPro. The Connection object controls all transaction processing. For more detail, see the section Transactions/Updating Data. Note that not all OLE DB providers support transaction processing.
Close—This method closes an open Connection object.
Execute—This method runs a SQL statement, stored procedure, or OLE DB provider-specific command. In reality, a Command object, which actually does the work of executing the command, is created on the fly. More on the Command object and the flat object hierarchy of ADO later in this paper.
OpenSchema—This method returns information regarding defined tables, fields, catalogs, and views into an ADO Recordset object. This method works like the DBGetProp( ) function in Visual FoxPro.
Errors collection
ADO does not trap errors, nor does it have an error handler. Instead, ADO can record the occasions when errors occur. It is up to the host application, Visual FoxPro in this case, to both trap and handle the error. ADO only reports what errors have occurred. Note that the error is actually reported by the specific OLE DB provider. ADO is merely a vehicle to report the error.
The Errors collection is part of the Connection object and consists of zero or more Error objects. When an error occurs, an Error object is appended to the Errors collection. The following code illustrates how the Errors collection works. In this example, the name of the database has been misspelled purposely in order to generate an error:
oConnection = CreateObject("adodb.connection")
With oConnection
.Provider = "SQLOLEDB.1"
.ConnectionString = "Persist Security Info=False;User
ID=sa;Initial Catalog=Nothwind;Data Source=JVP"
.Open
EndWith
*/ At this point an error will occur – causing VFP's default error
*/ handler – or the active error handler to invoke
*/ At this point, we can query the Errors Collection of the
*/ Connection Object
For Each Error In oConnection.Errors
?Error.Description,Error.Number
Next Error
Recordset Object
ProgID: ADODB.Recordset
Once you establish an ADO connection, you can open a recordset of data. The Recordset object is very much like a Visual FoxPro cursor. Like the Visual FoxPro cursor, an ADO recordset consists of rows of data. The recordset is the primary object that you will use while working with ADO. Like the Connection object, the Recordset object also provides an Open method. To illustrate, the following code opens the Customer table of the Visual FoxPro Tastrade database:
oRecordSet = CreateObject("adodb.recordset")
oRecordSet.Open("Select * From Customer",oConnection)
The first argument of the Open method specifies the source of data. As you will see, the source can take on several forms. The second argument of the Open method specifies a connection to use for retrieving the data specified by the source. At a minimum, this is all you need to open a recordset. Additional examples will expand on the additional arguments the Open method accepts.
With a Recordset object created, one of the most common actions you will perform is navigating through records. Depending on the type of ADO recordset that has been created, certain navigational capabilities may or may not be available. The different types of possible ADO recordsets will be discussed shortly. The following code illustrates how to navigate through an ADO recordset:
Do While !oRecordSet.Eof
oRecordset.MoveNext
EndDo
The following paragraphs briefly describe the most common recordset properties and pethods you are likely to use. It is by no means a replacement for the ADO documentation, which gives both a complete description of the properties and methods and complete descriptions of acceptable enumerated types and arguments. ADO is well documented in the Microsoft Data Access Components (MDAC) SDK. You can download the MDAC SDK from https://www.microsoft.com/data.
In addition, I highly recommend ADO 2.0 Programmers Reference, by David Sussman and Alex Homer, from Wrox Press.
RecordSet types
You can create four types of recordsets in ADO:
Forward Only—This type of recordset can be navigated only in a forward direction. It is ideal when only one pass through a recordset is required. Examples include populating a List box or a Combo box. The RecordCount property is irrelevant with this type of recordset.
Keyset—This type of recordset keeps acquired data up to date. For example, if you retrieve 100 records, data modified by other users to those 100 records will be visible in your recordset. However, modifications regarding new or deleted records made by other users will not be visible in your recordset. Both forward and backward navigation are supported. The RecordCount property returns a valid value with this type of recordset.
Dynamic—With this type of recordset, all underlying data is visible to the Recordset object. Because the number of records in the underlying table can change, the RecordCount property is irrelevant with this type of cursor. However, forward and backward navigation are supported.
Static—Both the number of records and data are fixed at the time the Recordset object is created. The only way to get the latest version of data and all records is to explicitly invoke the Requery method. You can use the RecordCount property. In addition, both forward and backward navigation is permitted.
RecordSet locations
Recordset objects can exist in either of two locations, the server or the client:
Server—The most common examples of server-side ADO recordsets are those created through Active Server Pages (ASP).
Client—A recordset that resides on a workstation is useful when creating disconnected recordsets or recordsets on which you wish to apply filters, sorts, or indexes.
The most common properties you are likely to use with ADO recordsets include the following:
ActiveCommand property—An object reference to the Command object that created the recordset.
ActiveConnection property—An object reference, to the Connection object, that provides the link to an underlying data source.
AbsolutePosition property—Specifies the relative position of a record in an ADO recordset. Unlike the Bookmark property, which does not change, the AbsolutePosition property can change depending on the active sort and filter.
Bookmark property—A unique record identifier that, like the record number in a Visual FoxPro cursor or a record number in Visual FoxPro, does not change during the life of a recordset.
BOF/EOF properties—Beginning of File and End of File, respectively, that work just like the BOF( ) and EOF( ) functions in Visual FoxPro.
EditMode property—Specifies the editing state of the current record in an ADO recordset.
Filter property—The string that represents the current filter expression. This property is like the SET FILTER statement in Visual FoxPro. Unlike the Find method, multiple expressions linked with AND or OR operators are allowed. This property is only applicable to client-side recordsets.
Sort property—A comma-delimited set of fields that specifies how the rows in an ADO recordset are sorted. This property is only applicable to client-side recordsets.
State property—Specifies the state of an ADO recordset. Valid State properties are closed, open, connecting, executing, or fetching.
Status property—Specifies the editing status of the current record. Valid Status properties include unmodified, modified, new, and deleted. This property can be any one of the values contained in RecordStatusEnum.
MarshalOptions property—Specifies how records are returned (marshaled) to the server. Either all or only modified records can be returned. This property is only applicable to client-side disconnected recordsets
MaxRecords property—Specifies the total number of records to fetch from a data source.
RecordCount property—Specifies the number of records in a recordset. This property is like the Recc( ) function in Visual FoxPro.
Source property—Specifies the command or SQL statement that provides data for the recordset.
NoteThe type and location of a cursor as well as the OLE DB provider you select will affect the recordset properties that are available.
Use the following table as a guide to help you make the right recordset type and location decision:
Table 1. PropertiesExpand table
Type
Bookmark
RecordCount
Sort
Filter
MarshalOptions
Forward Only
Key Set
4
4
Dynamic
Static: Client
4
4
4
4
4
Static: Server
4
4
Only client-side recordsets can be sorted and filtered. If the CursorLocation property of ForwardOnly, KeySet, and Dynamic recordset types is set to adUseClient, making them client-side cursors, the CursorType property is automatically coerced to the Static Cursor type.
NoteThis is the behavior of the OLE DB Provider for SQL Server. The OLE DB Provider for ODBC supports only ForwardOnly and Static recordsets, regardless of where the recordset resides.
As with properties, method availability can also vary:
Table 2. Available MethodsExpand table
Type
MoveFirst
MovePrevious
MoveNext
MoveLast
Resync
Requery
Forward Only
4
4
Key Set
4
4
4
4
4
Dynamic
4
4
4
4
4
Static – Client
4
4
4
4
4
4
Static – Server
4
4
4
4
4
The following list describes some of the common methods you will use in the ADO Recordset object:
MoveFirst, MovePrevious, MoveNext, MoveLast, and Move methods—Navigational methods that work as their respective names imply. The Move method accepts two arguments, the number of records to move and the position from which to begin the move. The Move method is similar to the Go statement in Visual FoxPro. MoveFirst and MoveLast work like Go Top and Go Bottom, respectively. Finally, MovePrevious and MoveNext work like Skip 1 and Skip –1, respectively.
Find method—Accepts a criterion string as an argument and searches the recordset for a match. If a match is not found, depending on the search direction, either the BOF or EOF property will evaluate to true (.T.). This method works much the same way as the Seek and Locate statements in Visual FoxPro. Unlike the Filter property and the Seek and Locate statements in Visual FoxPro, the ADO Recordset object does not allow multiple search values joined by the And or the Or operator. Using anything other than a single search value will result in an error.
Open method—Opens an existing ADO Recordset object. This method accepts several arguments and is discussed in detail later in this section.
Close method—Closes an ADO Recordset object. Many properties, such as CursorType and LockType, although read/write, cannot be modified while the recordset is open. The Close method must be invoked before those and other properties are modified.
Update and UpdateBatch methods—Update writes changes for the current record to the underlying data source; UpdateBatch writes pending changes for all modified records to the underlying data source. The UpdateBatch method is only relevant when Optimistic Batch Locking is used.
Cancel and CancelBatch methods—The Cancel method cancels modifications made to the current record; the CancelBatch method cancels pending changes to all modified records.
Resync method—Refreshes the Recordset object with data from the underlying data source. Invoking this method does not rerun the underlying command. Options exist for which records are actually refreshed.
Requery method—Unlike the Resync method, reruns the underlying command, which causes any pending changes to be lost. In effect, issuing a Requery is like invoking the Close method then immediately invoking the Open method.
Supports method—Specifies whether or not the recordset supports a function, based on a passed argument. For example, you can use this method to specify whether a recordset supports bookmarks, or the addition or deletion of records, or the Find, Update, and UpdateBatch methods, to name a few. Because what is supported is depends on the OLE DB provider used, it is a good idea to use this method to make sure a needed function is supported.
GetRows method—Returns a set of records into an array.
GetString method—Returns a set of records into a string.
The moral of the story is that before relying on the existence of anything in ADO, know and understand the OLE DB provider you are using, because the capabilities available to you can vary dramatically.
Lock types
There are four different locking schemes in ADO recordsets. These locking schemes are similar to those in Visual FoxPro.
Read-Only—As the name indicates, the recordset is opened for read-only purposes only. When you don’t need to modify data, this is the best locking scheme to use from a performance standpoint. This scheme applies to both server and client-side recordsets.
Lock Pessimistic—In this scheme, a lock attempt is attempted as soon as an edit is performed. This locking scheme is not relevant for client-side recordsets. Pessimistic Locking in an ADO recordset is like Pessimistic Locking with Row Buffering in a Visual FoxPro cursor.
Lock Optimistic—In this scheme, a lock attempt is made when the Update method is invoked. This locking scheme applies to both server and client-side recordsets. Optimistic Locking in an ADO recordset is like Optimistic Locking with Row Buffering in a Visual FoxPro cursor.
Lock Batch Optimistic—This scheme is like the Lock Optimistic scheme, except that more than one row of data is involved. In this scheme, a lock is attempted on modified records when the UpdateBatch method is invoked. This scheme is like Optimistic Locking with Table Buffering in a Visual FoxPro cursor.
The following table illustrates the availability of some common methods depending on the locking scheme used:
Table 3. Method Availability (Depending on Lock Type)Expand table
Lock Type
Cancel
CancelBatch
Update
UpdateBatch
Read Only
4
Pessimistic
4
4
4
4
Optimistic
4
4
4
4
Optimistic Batch
4
4
4
4
With the concepts of cursor types, locations, and locking schemes out of the way, we can discuss the real abilities of ADO recordsets. The most notable of these abilities are updating, sorting, and filtering of data. Before undertaking that discussion, however, take a few moments to review the Fields Collection object.
Fields collection object
Associated with the Recordset object, is the Fields Collection object. The Fields Collection object contains zero or more Field objects. The following code enumerates through the Fields Collection of a Recordset object:
For Each ofield In oRecordset.Fields
With oField
?.Name,.Value,.Type,.DefinedSize
?.ActualSize,.NumericScale,.Precision
EndWith
Next oField
The common Field properties you will work with:
Name—Specifies the name of the Field object. This corresponds to the name of the data element in the underlying data source. It is easy to define the name element as the name of the field in the underlying table. However, note that ADO and OLE DB work with both relational and nonrelational data. Given that, while you may be working with ADO, the underlying data may come from Outlook, Excel, Word, or Microsoft® Windows NT® Directory Services.
Value—Indicates the current value of the Field object.
OriginalValue—Indicates the Value property of the Field object before any modifications where made. The OriginalValue property returns the same value that would be returned by the OldVal( ) function in Visual FoxPro. When you invoke the Cancel or CancelUpdate methods of the Recordset object, the Value property of the Field object is replaced by the contents of the OriginalValue property. This behavior is similar to that exhibited when TableRevert( ) is issued against a Visual FoxPro cursor.
UnderlyingValue—Indicates the current value in the data source. This property corresponds most closely to the CurVal( ) function in Visual FoxPro. To populate the Value property of each Field object in the Fields collection, you need to invoke the Resync method of the Recordset object. With a client-side cursor, this property will return the same value as the OriginalValue property, since the recordset may or may not have an active connection.
Type—Indicates the data type of the Field object. The value of this property corresponds to a value contained in DataTypeEnum. Examples of values in DataTypeEnum are adBoolean, adInteger, and adVarChar.
Defined Size—Specifies the size of the field containing a data element in the data source. For example, in SQL Server, the Country field in the Customers table of the Northwind database is 15 characters long. Therefore, the DefinedSize property of the Country Field object is 15.
ActualSize—Represents the length of the actual data element in a datasource. To illustrate, consider the Country Field object again. In the case where the value is Germany, the ActualSize property is 7, while the DefinedSize property is still 15.
NumericScale—Specifies how many digits to the right of the decimal place are stored.
Precision—Specifies the maximum number of digits to be used for numeric values.
In addition to these properties, GetChunk is one interesting method you are likely to use. This method allows you to progressively fetch portions of the contents of a field object. This method is very useful when dealing with large text fields. It can be used only on fields where the adFldLong Bit set of the Attributes property is set to true (.T.). See the next section for details on the Attributes property. Understand that fields of the type ADLongVarChar have the adFldLong Bit set. The Notes field of the Employees table is of the type adLongVarChar.
The following code fetches data from the notes field in 10-byte chunks:
Local nBytesRead,cChunkRead
nBytesRead = 0
cChunkRead = Space(0)
Do While .T.
nBytesRead = nBytesRead + 10
cChunkRead = oRecordset.Fields("notes").GetChunk(10)
If IsNull(cChunkRead) Or;
nBytesRead > oRecordset.Fields("notes").ActualSize
Exit
Else
?cChunkRead
Endif
EndDo
Successive calls to GetChunk continue where the previous call ended. The GetChunk method is very useful when you need to stream data or only need to see the first few characters of a large text field.
Along with GetChunk, examine the AppendChunk method. The first time this method is called for a field, it overwrites any data in the field. Successive calls then append the data, until pending edits are cancelled or updated. The following code illustrates how this method works:
For x = 1 To 100
oRecordset.Fields("notes").AppendChunk(Str(x)+Chr(10)+Chr(13))
Next x
Both the GetChunk and AppendChunk methods are ideal for dealing with low memory scenarios.
The Attributes property
An attribute specifies the characteristics of something. As a person, you have many attributes, eye color, height, weight, and so forth. In the OOP world, objects have many attributes. Most of the time, attributes are exposed in the form of properties. A Visual FoxPro form has several properties such as Width, Height, and BackColor, just to name a few. The same is true for objects in ADO. Sometimes, however, it is not convenient to have a one-to-one correspondence between attributes and properties. Often, you can pack large amounts of information into a smaller space through the power of setting bits. A bit is much like a switch. It is either on or off or 1 or 0. If you string these bits together, you gain the ability to store multiple values in a small space. This is how the Attributes property works.
The Connection, Parameter, Field, and Property objects all have an Attributes property. If you have never worked with bit operations before, working with this property can be quite challenging. In some situations, as is the case with the GetChunk and AppendChunk methods, you will need to refer to the Attributes property of the Field object to determine whether those methods are available.
Using the Field object to illustrate how the Attributes property works, you can associate the following attributes with a Field object and its associated binary values:
AdFldMayDefer—Indicates that the field contents are retrieved only when referenced—0x00000002
adFldupdateable—Indicates that the field can be updated—0x00000004
adFldUnkownupdateable—Indicates that the provider does not know whether the field is updateable—0x00000008
adFldFixed—Indicates that the field contains fixed length data—0x00000010
adFldIsNullable—Indicates that the field can accept a null value during a write operation—0x00000020
adFldMayBeNullable—Indiates that the field may contain a null value—0x00000040
adFldlong—Indicates that the field contains long binary data and that the GetChunk and AppendChunk methods can be used—0x00000080
adFldRowID—Indicates that the field contains a row ID and cannot be updated. This does not relate to a field that may contain the identity value or some other auto-incrementing value. Rather, it relates to a ROW ID that is unique across the database. Oracle has this feature—0x00000100
adFldRowVersion—Indicates whether the field indicates the version of the row. For example, a SQL TimeStamp field may have this attribute set—0x00000200
adFldCachedDeferred—Indicates that once this field has been read, future references will be read from the cache—0x00001000
Usually, more than one of these attributes are present at any given time. Yet the Attributes property is a single value. Using the Employees table Notes field as an example, you will see that the Attributes property yields a value of 234. The value 234 represents the sum of the attributes for that field. For example, nullable and long attributes have decimal values of 32 and 128 respectively. This means that the Attributes property evaluates to 160. This works like the Windows Messagebox dialog box with regard to specifying the icon and types of buttons that are present.
Knowing that the Attributes property is a sum of the attributes of a Field object does not help in determining whether a specific attribute is present. This is where understanding bit operations comes in handy. The first step is to convert the sum (such as 234, above) into a binary equivalent:
11101010
Working from right to left, (or from the least significant bit to the most significant)—and beginning with zero, see that bits 1, 3, 5, 6, and 7 are set, (indicated by their values of 1 in those positions). Bits 0, 2, and 4 are not set. The next step is to determine whether a field is “long.”
To determine whether a field is a long field, we must first convert the adFldLong constant, which specifies which bit if set, indicates that the field is long. The adFldLong constant has a hex value of 0x00000080. This translates into a decimal value of 128. The following is the binary equivalent:
10000000
Converting a hex value to decimal in Visual FoxPro is simple. The following code illustrate how to convert hexadecimal values to decimal:
x = 0x00000080
?x && 128
And, if you ever need to convert back to hexadecimal:
?Transform(128,"@0") && 0x00000080
Using our original hex value, 11101010, and working from right to left and beginning with zero, see that the seventh bit is set. Therefore, the seventh bit of the Attributes property, if set, means the field is long. Going further, whatever attributes occupy bits 1, 3, 5, and 6, also apply to this field. The following table of field attributes should help to sort things out:
Table 4. Field AttributesExpand table
Hex Value
Decimal Value
Field Attribute Constant
Bit
0x00000002
2
AdFldMayDefer
1
0x00000004
4
AdFldupdateable
2
0x00000008
8
AdFldUnkownUpdateable
3
0x00000010
16
AdFldFixed
4
0x00000020
32
AdFldIsNullable
5
0x00000040
64
AdFldMayBeNull
6
0x00000080
128
AdFldLong
7
0x00000100
256
AdFldRowID
8
0x00000200
512
AdFldRowVersion
9
0x00001000
4096
AdFldCacheDeferred
12
So, along with being a long field, the field is deferred, updateable, can have a null written to it, and it may also already contain a null value. Visually, this makes sense. How can you do this programmatically?
If you refer to online examples (almost always programmed in Visual Basic), you will see code like this:
If (oField.Attribute AND adFldLong) = adFldLong
' The field is long
End If
This is pretty slick in that you can test for whether a specific attribute bit is set by using the AND operator with the attribute property and the constant. If you try this in Visual FoxPro, you will get data type mismatch errors. Fortunately, there is a way. Visual FoxPro contains a host of bit functions. One function, BITTEST, does as its name implies. It tests whether a specified bit in a passed argument is set. To review, we need to see if the seventh bit in the value 234 is set. The following Visual FoxPro code demonstrates how to use the BITTEST function:
If BitTest(234,7)
*/ The Field is long
Endif
To find out if the field is nullable:
If BitTest(234,5)
*/ The Field is long
Endif
The Attributes property of the Connection, Parameter, and Property objects works in the same manner as illustrated above. The differences are the names and quantity of attributes that are present.
ADO and COM defined constants
ADO and OLE DB, like any COM components, make extensive use of defined constants in the examples that document the usage of properties, events, and methods. Other development environments in Visual Studio such as Visual Basic and Visual Interdev provide IntelliSense technology, because of their respective abilities to interact directly with the type libraries of COM components. For these development environments, you can reference defined constants just as if they were a part of the native language. So, working with published examples is a fairly trivial task. On the other hand, in the Visual FoxPro development environment there is, in fact, a bit of a challenge. The question always seems to be “How can I use the Visual Basic samples in Visual FoxPro?” The biggest stumbling block is usually in finding the value of the defined constants. In Visual FoxPro, you need to use the #Define statement for each constant.
One solution for obtaining the value of the ADO defined constants is to obtain the MDAC SDK from Microsoft. The MDAC SDK can be downloaded from https://www.microsoft.com/data/download.htm.
Once you install the SDK, locate the IncludeADO directory. In that directory, you will find the ADOINT.H file, which contains all of the enumerated types and the values for the defined constants.
A second, and perhaps easier, solution is to use the resources already installed on your machine. If you are working through the sample code in this paper, you already have the Microsoft Data Access Components installed on your workstation. The Visual Basic Development Environment (both the full Visual Basic IDE and the Visual Basic Editor in desktop applications like Word and Excel) has a great resource called the Object Browser. This could, in fact, be the most underutilized tool on the planet.
To illustrate its functionality, open any desktop application that uses Visual Basic, such as Word or Excel. Or, if you have the Visual Basic Programming System installed, you can open that as well.
If you opened a VBA application
From the View menu, choose Toolbars.
From the Toolbars menu, choose Visual Basic.
On the Visual Basic toolbar, click Visual Basic Editor.
From the Tools menu, choose References.
Check the Microsoft Data Access Objects 2.x Library.
If you opened the Visual Basic IDE
Create an empty project.
From the Project menu, select References.
Check the Microsoft Data Access Objects 2.x Library.
Now, whether you are in the VBA Editor or the VB IDE
Press F2 to display the Object Browser.
In the first combo box, select ADODB.
In the second box, type ADVARCHAR.
Press Search or Press Enter.
Clearly, the Object Browser is a powerful tool for the developer who works with COM components. Not only are the defined properties, events, and methods accessible in the Object Browser, so also are the defined constants and their respective values. Notice the value of adVarChar in the lower pane of the Object Browser.
Opening, sorting, and filtering data
One of the big advantages of using a development platform such as Visual FoxPro is its local data engine. Not only does the engine provide superior query performance, but it also provides some very flexible capabilities when it comes to both working with and presenting data. There isn’t a Visual FoxPro application that fails to sort or filter data to some degree. In Visual FoxPro, sorting is accomplished by creating a set of index tags for a table. Filtering is accomplished by using the Set Filter command. Fortunately, ADO has these capabilities as well.
You can see in the Field Attribute table that the availability of features depends on the location in which the recordset is created. It is clear that we must ensure that a client-side recordset is created.
For example, create a Connection object to the TasTrade or SQL Server Northwind database. The following code assumes that the Connection object, oConnection, has been created before you open the Recordset object.
First, we need to implement a few required #Defines:
oRecordset.Open("Select * From Customer",;
oConnection,;
adUseClient,;
adLockBatchOptimistic)
SQL Server and Visual FoxPro open data differently. Remember that when using SQL Server, you are using the OLE DB Provider for SQL Server. When you access data in Visual FoxPro, use the OLE DB Provider for ODBC, since there is no native OLE DB provider for Visual FoxPro.
The difference rests with the optional fifth argument of the Open method. The SQL Server OLE DB Provider is designed to recognize when you pass just a table name. With the ODBC OLE DB Provider, you must specify how it should interpret the Source property. By default, the ODBC OLE DB Provider expects a SQL statement. When you pass a SQL statement, there is no need to explicitly state how the provider should interpret things. The Visual FoxPro ODBC driver generates an “Unrecognized Command Verb” error message if you only specify a table name as the source and you fail to use the optional fifth argument. Note that if you use the ODBC OLE DB Provider to access SQL Server, you must employ the same technique that is needed for Visual FoxPro.
Which method should you employ when you populate the properties individually before invoking the Open method or passing the arguments to the Open method? Once again, it is a matter of preference. Of the two, manually populating the properties makes for more readable code.
Sorting and filtering data are just matters of manipulating the Sort and Filter properties respectively. The following code sorts the recordset created from TasTrade in the example above, by country, ascending, then by region, descending:
oRecordset.Sort = "Country,Region Desc"
The following code displays the sort and the functionality of the AbsolutePosition and Bookmark properties.
oRecordset.MoveFirst
Do While Not oRecordset.Eof
With oRecordset
?.Fields("country").Value,;
.Fields("region").Value,;
.AbsolutePosition,;
.Bookmark
.MoveNext
EndWith
EndDo
Setting a filter is as easy as setting the sort. The following code filters for records where the country is Germany:
oRecordset.Filter = "Country = 'Germany'"
The Filter property also supports multiple values:
oRecordset.Filter = "Country = 'Germany' Or Country = 'Mexico'"
Finally, wild card characters are also supported:
oRecordset.Filter = "Country Like 'U*'"
To reset either the Filter or Sort properties, set them equal to an empty string:
oRecordset.Sort = ""
oRecordset.Filter = ""
Finding data
Another important capability of an ADO recordset is the ability to find records based on a search string. This capability works like searching for records in a Visual FoxPro cursor. Unlike the Seek or Locate statement in Visual FoxPro, the Find method provides control over the scope of records that are searched. The following code searches for a country that begins with the letter “B.”
oRecordset.Find("country Like 'B%'")
Although multiple criteria are not allowed, wild card searches are permitted:
oRecordset.Find("country Like 'U*'")
Searches for multiple criteria, such as the following, would result in an error:
oRecordset.Find("country Like 'G*' Or country Like 'B*'")
Transactions/updating data/conflict resolution
Updating data in an ADO recordset is a fairly simple process. As in any environment, conflict resolution in multi-user environments is always an issue to be dealt with. This is where the Errors collection comes into play. Error trapping and handling needs to become an integral part of your ADO-related code. The following code samples employ a simple error handling scenario and use the Errors collection to determine whether conflicts have occurred. For a complete list and description of ADO error codes, consult the online documentation.
When you update data, you can update either a single row, or several rows at a time in batch mode. These methods most closely correspond to row and table buffering, respectively, in Visual FoxPro. Building on the recordset already created, the lock type is Batch Optimistic. While updates are normally conducted in batches, you can also update one row at a time, just as in Visual FoxPro.
The following code modifies the CompanyName field and attempts to update the SQL Server data source:
Depending on a variety of scenarios, this code may or may not work. Perhaps a contention issue exists? Perhaps the user does not have rights to modify data. Hundreds of issues can cause an attempted update to fail. Therefore, anytime you attempt an update, you should employ error trapping. The following code expands the previous example and makes it a bit more robust:
Local Err,cOldErr,oError
cOldError = On("Error")
On Error Err = .T.
oRecordset.Fields("companyname").Value = "Ace Tomato Company"
oRecordset.Update
If Err
For Each oError In oRecordset.ActiveConnection.Errors
With oError
?.Number,.Description
EndWith
Next oError
Endif
On Error &cOldErr
If you are thinking, “Hey, maybe I should write a wrapper class to better encapsulate and centralize code,” you’re on the right track. The following code creates a custom class that can serve as a starting point:
Local oRecordsetHandler
oRecordsetHandler = CreateObject("RecordsetHandler")
oRecordset.Fields("companyname").Value = "Alfreds Futterkiste"
If !oRecordsetHandler.Update(oRecordset)
oRecordsetHandler.Cancel(oRecordset)
Endif
Define Class RecordsetHandler As Custom
Protected oRecordset
Protected ErrFlag
Procedure Update(oRecordset)
This.oRecordset = oRecordset
oRecordset.UpdateBatch
Return !This.ErrFlag
EndProc
Procedure Cancel(oRecordset)
This.oRecordset = oRecordset
oRecordset.Cancel
Return !This.ErrFlag
EndProc
Procedure Error(nError, cMethod, nLine)
Local oError
For Each oError In This.oRecordset.ActiveConnection.Errors
With oError
?.Number,.Description
EndWith
Next oError
This.ErrFlag = .T.
EndProc
EndDefine
There’s a better way to determine whether an update proceeded successfully. The preferred approach is to trap events that ADO fires. Visual FoxPro by itself does not surface COM Events. Fortunately, the new VFPCOM.DLL component provides this capability to Visual FoxPro. The previous example can be modified to show how using COM Events makes for more robust code and class design.
Now we can improve the code of our example. Most of the time, for efficiency, you will want to batch your updates that comprise multiple records. Often, when you update multiple records, transaction processing is required. In other words, either updates to all records must succeed or none should occur. To illustrate, let’s say you must apply a 10 percent price increase to the products you sell. The prime requirement is that all records in the Products table need modification. Without transactional capabilities, the possibility exists that, for example, after the first 10 records are updated, an error generated on the eleventh record prevents a complete update. Transaction processing provides the ability to rollback changes.
The following example incorporates error trapping and the three transaction methods of the Connection object:
Local Err,cOldErr
cOldErr = On("error")
On Error Err = .T.
oRecordset.ActiveConnection.BeginTrans
Do While !oRecordset.Eof
If Err
Exit
Else
With oRecordset
.Fields("unitprice").Value = ;
.Fields("unitprice").Value * 1.1
.Movenext
EndWith
Endif
EndDo
oRecordSet.UpdateBatch
If Err
oRecordset.ActiveConnection.RollBackTrans
oRecordset.CancelBatch
Else
oRecordset.ActiveConnection.CommitTrans
Endif
On Error &cOldErr
Additional operations you are likely to employ with recordsets deal with adding new records and deleting existing records. Both of these processes are very simple. The following code adds a new record:
oRecordset.AddNew
As in Visual FoxPro, in ADO the new record becomes current. Once the AddNew method is invoked, the field can be populated and, depending on the LockType, you then invoke either the Update or UpdateBatch methods to modify the data source.
Deleting records is just as easy. The following code deletes the current record:
oRecordset.Delete
Once again, after deleting the record, a call to Update or UpdateBatch will update the data source.
SQL Server identity fields and parent/child relationships
SQL Server, like most server RDBMSs and Microsoft® Access®, creates an auto-incrementing field that can serve as a primary key for a table. Typically, the data type for this field is Integer. In SQL Server, this type of field is called the Identity field. Fields of this type are read-only. It begs the question, “When adding records, how can one determine what these values are?” Knowing that the next generated value is a requirement for maintaining referential integrity when child tables are involved. The following example code shows a recordset in which the first field, ID, is the auto-incrementing field. After new field is added, checking the value of the ID field yields a character with a length of zero. Attempting to update the field results in an error. However, once the recordset is updated, checking the value again will yield a valid identity value.
oRecordset.AddNew
?oRecordset.Fields("id").Value && empty string
oRecordset.UpdateBatch
?oRecordset.Fields("id").Value && returns new identity value
With the new identity value available, you can add records in child tables, using the identity value in the parent table as the foreign key in the child tables.
But, what do you do in cases where you have disconnected recordsets?
This section details an important capability in ADO—the ability to have recordsets without an active connection to the backend data source. At this point you can freely add new records to disconnected records. When the recordset is eventually reconnected, those newly added records are then sent to the backend data source. How do you know what the identity value will be in those cases? Simply put, you don’t know. At the same time, however, you still need to be able to add both parent and child records locally. You need some method that maintains the relationship locally, while at the same time, supporting the use of the identity value when the data is sent to the backend.
The simplest solution to this problem is to include a field in each table that serves as the local ID. You need this extra field because the identity field will be read-only. On the client side, you can use several methods for producing an ID that is unique. One approach is to use the Windows API to fetch the next Global Unique Identifier (GUID). The following procedure outlines how the local process unfolds:
Add a new parent record.
Fetch the next GUID.
Update the local primary key column with the GUID.
Add a new child record.
Update the local primary key column with the GUID.
Update the foreign key column of the child with the GUID from its parent.
At some point, you will reconnect to the server. The update process could be performed within the context of a transaction, done one row at a time by navigating through each record. Checking the recordset Status property, which indicates whether the current record has been newly created, modified, deleted, and so on, determines whether the current row should be sent back to the server. If the record should be sent back, the parent record can be updated via the UpdateBatch method. The UpdateBatch method accepts an optional argument that specifies that only the current record be updated. By default, UpdateBatch works on all records. If the value of one is passed—corresponding to the adAffectCurrent constant—only the current record is updated. Once the update occurs, the identity value generated by the server is available. This value would then be used to update the foreign key columns of any related children. Once that process is complete, the records for that parent would be sent back to the server as well. This same process would be used if grandchild and great-grandchild relationships also existed.
The following Visual FoxPro code, from Visual FoxPro 6 Enterprise Development, by Rod Paddock, John V. Petersen, and Ron Talmage (Prima Publishing), illustrates how to generate a GUID:
Local oGuid
oGuid = CreateObject("guid")
?oGuid.GetNextGuid( )
*/ Class Definition
Define Class guid AS Custom
*/ Create protected members to hold parts of GUID
Protected data1
Protected data2
Protected data3
Protected data4
Procedure GetNextGuid
*/ The only public member. This method will return the next GUID
Local cGuid
cGuid = This.Export( )
UuidCreate(@cGuid)
This.Import(cGuid)
cGuid = This.Convert(cGuid)
Return cGuid
EndProc
Protected Procedure bintoHex(cBin)
*/ This method converts a binary value to Char by calling the Hextochar
*/ Method
Local cChars, nBin
cChars = ""
For nDigit = 1 To Len(cBin)
nBin = Asc(Substr(cBin, nDigit, 1))
cChars = cChars + This.Hex2Char(Int(nBin/16)) + ;
This.Hex2Char(Mod(nBin,16))
EndFor
Return(cChars)
EndProc
Protected Procedure hex2char(nHex)
*/ This method converts a hex value to ASCII
Local nAsc
Do Case
Case Between(nHex,0,9)
nAsc = 48 + nHex
Case Between(nHex,10,15)
nAsc = 65 + nHex - 10
EndCase
Return(Chr(nAsc))
EndProc
Procedure import(cString)
*/ This method takes the binary string and populates the 4 data
*/ properties
With This
.Data1 = Left(cString, Len(.Data1))
cString = SubStr(cString, Len(.Data1)+1)
.Data2 = Left(cString, Len(.Data2))
cString = SubStr(cString, Len(.Data2)+1)
.Data3 = Left(cString, Len(.Data3))
cString = SubStr(cString, Len(.Data3)+1)
.Data4 = Left(cString, Len(.Data4))
EndWith
Return cString
EndProc
Protected Procedure export
*/ This method creates the buffer to pass to the GUID API.
With This
.Data1 = Space(4)
.Data2 = Space(2)
.Data3 = Space(2)
.Data4 = Space(8)
EndWith
Return(This.Data1 + This.Data2 + This.Data3 + This.Data4)
EndProc
Protected Procedure Convert(cGuid)
*/ This method makes the call to the BinToHex that
*/ converts the data in the 4 data properties from
With This
cGuid = .BinToHex(.Data1) + "-" + .BinToHex(.Data2) + "-" + ;
.BinToHex(.Data3) + "-" + .BinToHex(.Data4)
Return cGuid
Endwith
EndProc
Procedure Init
*/ Declare the function in the DLL
Declare Integer UuidCreate ;
In C:WinntSystem32RPCRT4.DLL String @ UUID
Return
EndProc
EndDefine
Output is produced as follows:
Disconnected/Persisted Recordsets
One of the most powerful features of ADO is the ability to create both disconnected and persisted recordsets. A disconnected recordset is a client-side recordset that does not have a current ActiveConnection. SQL data sources, such as SQL Server, Oracle, and so on, are licensed according to the number of concurrent connections. For example, the number of people that using an application connected to SQL Server is 300. However, it has been determined that at any time, only 50 users actually use the services of a connection. A connection is needed only when data is being requested, updates are made, or a stored procedure on the database server is invoked. From a financial standpoint, it is far less expensive for a company to only purchase 50 licenses than to purchase 300. From a resource standpoint, performance should improve because the server only has the overhead of 50 connections instead of 300, of which 250 are idle at any time.
Using the ADO recordset of customer data already created, the following code disconnects the client-side recordset:
oRecordSet.ActiveConnection = Null
If you attempt to do this with a server-side recordset, an error occurs stating that the operation is not allowed on an open recordset. Once the recordset is disconnected, you can continue to work with and modify records. The following code will work:
oRecordset.MoveFirst
Do While !oRecordset.Eof
?oRecordset.Fields("companyname").Value
oRecordset.Fields("companyname").Value = ;
Upper(oRecordset.Fields("companyname").Value)
oRecordset.MoveNext
EndDo
With modified records in a client-side recordset, three basic options exist.
Cancel local changes
Marshall local changes to the server
Save (persist) the recordset locally.
You can save (persist) the recordset locally for both later use and, ultimately, for marshalling those persisted changes back to the server.
The first choice is pretty simple to implement, since it takes one line of code:
oRecordset.CancelBatch
The second choice is also simple to implement. Much of the work in updating multiple records and transactions has already been detailed. This procedure really involves two separate steps:
Then the code marshals the records by attempting the updates
Local Err,cOldErr
cOldErr = On("error")
On Error Err = .T.
With oRecordset
.ActiveConnection.BeginTrans
.UpdateBatch
If Err
.ActiveConnection.RollBackTrans
.CancelBatch
Else
.ActiveConnection.CommitTrans
Endif
EndWith
On Error &cOldErr
Often, however, there’s a need to shut things down and then reopen the recordset at another time. To be effective, the recordset must reflect incremental changes. This cycle may repeat any number of times.
To illustrate how to persist a recordset, consider again the following code that modifies records in a Recordset object:
oRecordset.MoveFirst
Do While !oRecordset.Eof
?oRecordset.Fields("companyname").Value
oRecordset.Fields("companyname").Value = ;
Upper(oRecordset.Fields("companyname").Value)
oRecordset.MoveNext
EndDo
Now you can invoke the Save method to persist the recordset:
oRecordset.Save("c:tempcustomers.rs")
At a later time, you can open the persisted recordset:
After the persisted recordset is reopened, you can use the same code, which establishes a connection to a disconnected recordset, to make additional modifications. You can marshal changes made in the persisted recordset to the underlying data source.
Hierarchical/Shaped Recordsets
Visual FoxPro not only provides the ability to work with local data, but also the ability to set up relations using the Set Relation command. When you move the record pointer in the parent table, the record pointer automatically moves in any child tables that exist. This makes working with and building interfaces for one to many relationships very simple in Visual FoxPro. Fortunately, the same capability exists in ADO, in the form of hierarchical recordsets, also referred to as shaped recordsets.
There are two necessary components when creating and working with hierarchical recordsets:
The Microsoft DataShape OLE DB Provider, MSDataShape
The Shape language, a superset of the SQL syntax
The first requirement is fairly easy to fulfill because it only entails setting the Provider property of the ADO Connection object to the proper value:
oConnection.Provider = "MSDataShape"
The second requirement, using the Data Shape language, is a bit more challenging. When you first see Data Shape language, it can be fairly intimidating, just as FoxPro may have been when you first worked with it. But like anything else, with a bit of practice and patience, Microsoft Data Shape language will become second nature.
To examine Shape language, consider a parent-child common scenario of customers and orders. For each customer, zero or more orders can exist. In turn, each order can contain one or more line items. The following code employs Shape syntax to relate customers and orders in the SQL Server Northwind database:
SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND ({SELECT *
FROM "dbo"."Orders"} AS Orders RELATE "CustomerID" TO "CustomerID") AS
Orders
If your first thought is, “Gee, this is like setting relations in Visual FoxPro,” you are indeed correct. It is exactly the same principle. If the Shape syntax is broken down, the task becomes manageable. The first clause in the code begins with the keyword SHAPE, to signify that what follows is not pure SQL, but rather, Data Shape language. The Data Shape language is a super-set of SQL, which is why you need to use MSDataShape as the OLE DB provider. MSDataShape can interpret and execute Shape commands. Finally, the last portion of the first command specifies that the results of the SQL statement are to be aliased as Customers.
In the next set of commands, things get a bit complicated, especially when the hierarchy is nested an additional one or two levels (this is the case when order details are added, as we’ll do in the next example).
You can interpret the keyword APPEND as “Append the results of the next SQL statement to the results of the previous SQL statement.” Of course, just appending records won’t suffice. Rather, you must provide a rule that specifies how the records are to be related. This is where the RELATE keyword comes into play.
You can interpret the RELATE keyword as, “When appending records, do so based on these join fields.” In this case, the join is between the CustomerID column in the Customers table and the CustomerID column in the Orders table.
Finally, we need to alias the data that was just appended as Orders. The following code sets up the objects and creates the hierarchical recordset:
#Include adovfp.h
Local oRecordset,oConnection,oCommand, cShpStr
oRecordset = CreateObject("adodb.recordset")
oConnection = CreateObject("adodb.connection")
cShpStr = 'SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers '
cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Orders"} ;
AS Orders '
cShpStr = cShpStr + 'RELATE "CustomerID" TO "CustomerID") AS Orders'
With oConnection
.Provider = "MSDataShape"
.ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security ;
Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
.Open
EndWith
With oRecordset
.ActiveConnection = oConnection
.Source = cShpStr
.CursorType = adOpenStatic
.LockType = adLockBatchOptimistic
.CursorLocation = adUseClient
.Open
EndWith
The question at this point is, “How is the data appended?” The technique is rather clever. When you append a recordset to another recordset, you do so through a Field object. If you query the Count property of the Fields collection, you discover that the value of 12 is returned. However, in SQL Server, you see that the Customers table only has 11 fields. The twelfth field, in this case, is actually a pointer to the Orders recordset. The rows in the Orders recordset for a given row in the Customers recordset are only those for that customer. The following code illustrates just how powerful hierarchical recordsets are:
oRecordset.MoveFirst
Do While !oRecordset.Eof
With oRecordset
?.Fields("Customerid").Value,.Fields("CompanyName").Value
EndWith
oOrders = oRecordset.Fields("orders").Value
Do While !oOrders.Eof
With oOrders
?Chr(9),.Fields("Customerid").Value,.Fields("orderdate").Value
.MoveNext
EndWith
EndDo
oRecordset.MoveNext
EndDo
With the basics of hierarchical recordsets out of the way, we can turn our attention to a more complicated, real-life example. The following example adds several dimensions to the recordset.
First, the Order Details table is appended to the Orders child recordset. In this case, a new field that will in turn point to the OrderDetails recordset, is added to the Orders recordset. The Products table is then appended to the OrderDetails recordset providing three levels of nesting. Appended to the Products recordset are two tables, Categories and Suppliers. Traversing up the hierarchy to the Orders recordset appends the Employees table.
This list illustrates the hierarchy and shows all the tables involved as well as the nesting scheme. When creating reports, it is quite possible that you will need all of these tables. The ability to relate tables in this fashion and the ability to display the data in a user interface or a report have always been true powers of Visual FoxPro. Before ADO, attempting all this work outside Visual FoxPro was extremely difficult, sometimes bordering on the impossible.
The following is the Shape syntax to create the hierarchical recordset:
SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND (( SHAPE
{SELECT * FROM "dbo"."Orders"} AS Orders APPEND (( SHAPE {SELECT * FROM
"dbo"."Order Details"} AS OrderDetails APPEND (( SHAPE {SELECT * FROM
"dbo"."Products"} AS Products APPEND ({SELECT * FROM "dbo"."Categories"}
AS Categories RELATE 'CategoryID' TO 'CategoryID') AS Categories,({SELECT
* FROM "dbo"."Suppliers"} AS Suppliers RELATE 'SupplierID' TO
'SupplierID') AS Suppliers) AS Products RELATE 'ProductID' TO
'ProductID') AS Products) AS OrderDetails RELATE 'OrderID' TO 'OrderID')
AS OrderDetails,(( SHAPE {SELECT * FROM "dbo"."Employees"} AS Employees
APPEND (( SHAPE {SELECT * FROM "dbo"."EmployeeTerritories"} AS
EmployeeTerritories APPEND (( SHAPE {SELECT * FROM "dbo"."Territories"}
AS Territories APPEND ({SELECT * FROM "dbo"."Region"} AS Region RELATE
'RegionID' TO 'RegionID') AS Region) AS Territories RELATE 'TerritoryID'
TO 'TerritoryID') AS Territories) AS EmployeeTerritories RELATE
'EmployeeID' TO 'EmployeeID') AS EmployeeTerritories) AS Employees RELATE
'EmployeeID' TO 'EmployeeID') AS Employees,({SELECT * FROM
"dbo"."Shippers"} AS Shippers RELATE 'ShipVia' TO 'ShipperID') AS
Shippers) AS Orders RELATE 'CustomerID' TO 'CustomerID') AS Orders
This is just about as complicated as it gets. Nobody in their right mind would want to hammer this code out manually. Fortunately, there is a visual way to build this code. The DataEnvironment designer that ships with Visual Basic allows you to visually design ADO connections, recordsets, and hierarchical recordsets. The following illustrates how this hierarchical recordset appears in the designer:
The extensive Shape syntax can be copied and pasted into Visual FoxPro, or any other environment that can host ADO. For complete details on how to use the DataEnvironment designer, consult the Visual Basic documentation on the MSDN CDs that ship with Visual Studio.
The following Visual FoxPro code traverses the hierarchical recordset and displays the data:
#Include adovfp.h
oRecordset = CreateObject("adodb.recordset")
oConnection = CreateObject("adodb.connection")
cShpStr = 'SHAPE {SELECT * FROM "dbo"."Customers"} AS Customers APPEND'
cShpStr = cShpStr + '(( SHAPE {SELECT * FROM "dbo"."Orders"} AS Orders '
cShpStr = cShpStr + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Order
Details"} AS OrderDetails '
cShpStr = cShpStr + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Products"}
AS Products '
cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Categories"} AS
Categories '
cShpStr = cShpStr + 'RELATE "CategoryID" TO "CategoryID") AS
Categories,'
cShpStr = cShpStr + '({SELECT * FROM "dbo"."Suppliers"} AS Suppliers '
cShpStr = cShpStr + 'RELATE "SupplierID" TO "SupplierID") AS Suppliers)
AS Products '
cShpStr = cShpStr + 'RELATE "ProductID" TO "ProductID") AS Products) AS
OrderDetails '
cShpStr = cShpStr + 'RELATE "OrderID" TO "OrderID") AS OrderDetails,'
cShpStr = cShpStr + '(( SHAPE {SELECT * FROM "dbo"."Employees"} AS
Employees '
cShpStr = cShpStr + 'APPEND (( SHAPE {SELECT * FROM
"dbo"."EmployeeTerritories"} AS EmployeeTerritories '
cShpStr = cShpStr + 'APPEND (( SHAPE {SELECT * FROM "dbo"."Territories"} AS Territories '
cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Region"} AS Region '
cShpStr = cShpStr + 'RELATE "RegionID" TO "RegionID") AS Region) AS
Territories '
cShpStr = cShpStr + 'RELATE "TerritoryID" TO "TerritoryID") AS
Territories) AS EmployeeTerritories '
cShpStr = cShpStr + 'RELATE "EmployeeID" TO "EmployeeID") AS
EmployeeTerritories) AS Employees '
cShpStr = cShpStr + 'RELATE "EmployeeID" TO "EmployeeID") AS Employees,'
cShpStr = cShpStr + '({SELECT * FROM "dbo"."Shippers"} AS Shippers '
cShpStr = cShpStr + 'RELATE "ShipVia" TO "ShipperID") AS Shippers) AS
Orders '
cShpStr = cShpStr + 'RELATE "CustomerID" TO "CustomerID") AS Orders '
With oConnection
.Provider = "MSDataShape"
.ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security
Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
.Open
EndWith
With oRecordset
.ActiveConnection = oConnection
.Source = cShpStr
.CursorType = adOpenStatic
.LockType = adLockBatchOptimistic
.CursorLocation = adUseClient
.Open
EndWith
Do While !oRecordset.Eof
With oRecordset
?.Fields("CustomerID").Value,.Fields("CompanyName").Value
EndWith
oOrders = oRecordset.Fields("orders").Value
Do While !oOrders.Eof
oShippers = oOrders.Fields("shippers").Value
oEmployee = oOrders.Fields("employees").Value
oEmployeeTerritories =
oEmployee.Fields("employeeterritories").Value
oTerritories = oEmployeeTerritories.Fields("territories").Value
oRegion = oTerritories.Fields("region").Value
?"Order ID: ",oOrders.Fields("orderid").Value,;
"Order Date: ",oOrders.Fields("orderdate").Value
oOrderDetails = oOrders.Fields("orderdetails").Value
?"Territory: ",
oTerritories.Fields("territorydescription").Value,;
"Region: ",oRegion.Fields("RegionDescription").Value
?"Shipper: ",oShippers.Fields("companyname").Value
oEmployee = oOrders.Fields("employees").Value
With oEmployee
?"Employee: ",.Fields("employeeid").Value,;
.Fields("firstname").Value + " " + .Fields("lastname").Value
EndWith
?"Order Details: "
Do While !oOrderDetails.Eof
oProducts = oOrderDetails.Fields("Products").Value
oCategories = oProducts.Fields("categories").Value
oSuppliers = oProducts.Fields("suppliers").Value
?Chr(9),;
oProducts.Fields("productname").Value,;
oSuppliers.Fields("companyname").Value,;
oCategories.Fields("categoryname").Value,;
oOrderDetails.Fields("Quantity").Value,;
oOrderDetails.Fields("UnitPrice").Value
oOrderDetails.MoveNext
EndDo
oOrders.MoveNext
EndDo
oRecordset.MoveNext
EndDo
The output appears as follows:
Because a hierarchy exists, the ability to create drill-down interfaces becomes a fairly simple task. The preceding Visual FoxPro code illustrates how to traverse the hierarchy.
Perhaps you want to use Microsoft Word or Excel as a reporting tool. With a combination of Visual FoxPro COM servers, ADO, and Automation, the process becomes manageable. The first and third parts of the solution have been around. However, only now that a set of COM objects exists to handle and work with data as Visual FoxPro does natively can the solution become a reality.
Hierarchical recordsets and recursive relationships
One of the nice features of SQL Server, and of most other server back ends is provision for recursive relations. The following is the SQL Server 7.0 database diagram for the Northwind database:
In the Northwind database, the Employees table employs recursion to support a manager/staff relationship. Both managers and staff are employees. In some cases, it happens that some employees report to other employees. In Visual FoxPro, you can create the same sort of relation by opening a table twice using two different aliases. In ADO, the task is totally supported and is quite easy to implement. The following is the Shape syntax:
SHAPE {SELECT * FROM "dbo"."Employees"} AS Managers APPEND ({SELECT *
FROM "dbo"."Employees"} AS Staff RELATE 'EmployeeID' TO 'ReportsTo') AS
Staff
The following Visual FoxPro code displays a list of managers and the staff that reports to each manager:
#Include adovfp.h
oRecordset = CreateObject("adodb.recordset")
oConnection = CreateObject("adodb.connection")
cShpStr = 'SHAPE {SELECT * FROM "dbo"."Employees"} AS Managers '
cShpStr = cShpStr + 'APPEND ({SELECT * FROM "dbo"."Employees"} AS Staff '
cShpStr = cShpStr + 'RELATE "EmployeeID" TO "ReportsTo") AS Staff '
With oConnection
.Provider = "MSDataShape"
.ConnectionString = "Data Provider=SQLOLEDB.1;Persist Security
Info=False;User ID=sa;Initial Catalog=Northwind;Data Source=JVP"
.Open
EndWith
With oRecordset
.ActiveConnection = oConnection
.Source = cShpStr
.CursorType = adOpenStatic
.LockType = adLockBatchOptimistic
.CursorLocation = adUseClient
.Open
EndWith
Do While !oRecordset.Eof
oStaff = oRecordset.Fields("staff").Value
If oStaff.Recordcount > 0
With oRecordset
?.Fields("firstname").Value + " " + ;
.Fields("lastname").Value ,;
.Fields("Title").Value
Do While !oStaff.Eof
With oStaff
?Chr(9),;
.Fields("firstname").Value + " " + ;
.Fields("lastname").Value ,;
.Fields("Title").Value
EndWith
oStaff.MoveNext
EndDo
EndWith
Endif
oRecordset.MoveNext
EndDo
The output appears as follows:
Finally, note that hierarchical recordsets are updateable. The following code expands the previous example to illustrate how to make a simple update:
Do While !oRecordset.Eof
oStaff = oRecordset.Fields("staff").Value
If oStaff.Recordcount > 0
With oRecordset
Do While !oStaff.Eof
With oStaff
.Fields("firstname").Value = ;
Upper(.Fields("firstname").Value)
.Fields("lastname").Value = ;
Upper(.Fields("lastname").Value)
.Fields("Title").Value = ;
Upper(.Fields("Title").Value)
EndWith
oStaff.MoveNext
EndDo
*/ Write changes to Staff recordset
oStaff.UpdateBatch
EndWith
Endif
oRecordset.MoveNext
EndDo
The ability to view related records, coupled with the ability to make updates, places the ADO hierarchical recordset capability on par with similar capabilities in Visual FoxPro.
Multiple recordsets
Use of hierarchical recordsets represents only one method for returning data from multiple recordsets in one object. For starters, building hierarchical recordsets is not the most straightforward of propositions. In many cases, a simpler alternative may be all that is required.
Consider the case where you need a specific customer record and the orders for that customer. Yes, you could use a hierarchical recordset. But, there is a simpler way: run two SQL statements.
Some OLE DB providers can process multiple SQL Statements. The OLE DB Provider for SQL Server has this capability. Attempting to do this with Visual FoxPro tables via the OLE DB Provider for ODBC will not work.
When using this technique, you have two choices on where the logic exists to perform the task. One choice is to build the SQL on the client and pass it to the server through a Command object. The other choice is to invoke a stored procedure on the database server through a Command object. I’ll illustrate both techniques. The Command object will be discussed in detail later in this paper.
To illustrate the stored procedure method, the following stored procedure must be created on the SQL Server Northwind database:
CREATE PROCEDURE CustomerAndOrders @CustomerID nchar(5)
AS
Select * From Customers Where Customers.CustomerID = @CustomerID
Select * From Orders Where Orders.CustomerID = @CustomerID
With the stored procedure created, the following code will create the recordset:
#Include adovfp.h
oConnection = CreateObject("adodb.connection")
oCommand = CreateObject("adodb.command")
With oConnection
.Provider = "SQLOLEDB.1"
.ConnectionString = ;
"Persist Security Info=False;User ID=sa;Initial
Catalog=Northwind;Data Source=JVP"
.Open
EndWith
With oCommand
.CommandText = "CustomerAndOrders"
.ActiveConnection = oConnection
.CommandType = adCmdStoredProc
EndWith
oCommand.Parameters("@CustomerID").Value = "ALFKI"
oRecordset = oCommand.Execute
Do While !oRecordset.Eof
?oRecordset.Fields(1).Value
oRecordset.MoveNext
EndDo
oRecordset = oRecordset.NextRecordset
Do While !oRecordset.Eof
?oRecordset.Fields(0).Value
oRecordset.MoveNext
EndDo
Like any recordset, the recordset just produced can be navigated. Once the first set of records from the Customers table have been navigated, the NextRecordset method is invoked. This causes the recordset produced by the second SQL statement to become available. Thus, the next set of commands loops through the records from the Orders table. This technique is ideal in those situations where you may need to populate Combo or ListBox controls.
The previous example references a collection that has not been discussed yet, the Parameters collection. The Parameters collection and the individual Parameter objects that it contains serve several purposes. One purpose is to provide the capacity to create parameterized queries. Another purpose is to provide the ability to send arguments to, and return data from, a stored procedure. For more information on the Parameters collection, see the Command Object section of this paper.
Alternatively, you can produce the SQL on the client if you wish. The following code illustrates the difference:
With oCommand
.CommandText = "Select * From Customers Where CustomerID =
'ALFKI'" + Chr(13) + "Select * From Orders Where CustomerID =
'ALFKI'"
.ActiveConnection = oConnection
.CommandType = adCmdText
EndWith
oRecordset = oCommand.Execute
The same result is achieved. The difference lies in how the result is achieved.
Which approach is better?
It depends on what your requirements are. The first option, which uses stored procedures, is more secure; the code is set and you can assign permissions with regard to who can execute the stored procedure. The second option provides more flexibility, but less security.
Fabricated recordsets
Up to this point, recordset objects have been presented in the context of origination from an ADO connection. In many cases, you may want to create an ADO recordset with data that does not come from a data source, just as you may in some cases use the Create Cursor command in Visual FoxPro. For example, you may have an application that works with a small amount of data, such as an array or Visual FoxPro cursor. Perhaps you need to dynamically build a table structure. Whatever the reason, the ability to create ADO recordsets from scratch is powerful.
To illustrate this capability, consider the need to fetch a list of files from a specified directory. In Visual FoxPro, a handy function, ADIR( ), performs this sort of task. However, what if you need to pass the data to another application? Or, perhaps you need to persist the list to a file on disk. While Visual FoxPro arrays are powerful, ADO recordsets provide a compelling alternative. The following code fetches a list of files from a specified directory, fabricates a recordset, and copies the values from the array into the newly created recordset:
*/GetFiles.prg
#INCLUDE "adovfp.h"
Local Array aFiles[1]
Local nFiles,nField,nFile,oRS
nFiles = Adir(aFiles,Getdir( )+"*.*")
oRS=Createobject("adodb.recordset")
With oRS
.CursorLocation=ADUSECLIENT
.LockType=ADLOCKOPTIMISTIC
*/ Adding new fields is a matter of appending
*/ new field objects to the Fields Collection.
.Fields.Append("File",ADCHAR,20)
.Fields.Append("Size",ADDOUBLE,10)
.Fields.Append("DateTime",ADDBTIME,8)
.Fields.Append("Attributes",ADCHAR,10)
.Open
EndWith
For nFile = 1 To nFiles
*/ Add a new record. This automatically makes
*/ the new record the current record - just
*/ like VFP.
oRS.AddNew
With ors
.Fields("File").Value = aFiles[nFile,1]
.Fields("Size").Value = aFiles[nFile,2]
.Fields("DateTime").Value = ;
Ctot(Dtoc(aFiles[nFile,3]) + " " + aFiles[nFile,4])
.Fields("Attributes").Value = aFiles[nFile,5]
EndWith
Next nItem
Return oRS
With the new recordset created and populated, it can be navigated like any other recordset:
oFiles = GetFiles ( )
Do While !oFiles.Eof
?oFiles.Fields("File").Value
oFiles.movenext
EndDo
ADO recordsets instead of arrays
Referring to the previous example, let’s say that the list needs to be sorted by file size, descending. Arrays in Visual FoxPro can be sorted, when all columns in the array are of the same data type. In this case, there are three data types: Character, Numeric, and DateTime. With a client-side ADO recordset, the process becomes simple. The following code does the trick:
oRS.Sort = "Size Desc"
Sorts are not limited to just one column. Perhaps you need to sort by size, descending, and then by file, ascending:
oRS.Sort = "Size Desc,File"
And, when it comes to sorting, such properties as Bookmark and AbsolutePosition that have already been demonstrated are available here as well.
Perhaps you need to find a specific value. The ASCAN( ) function in Visual FoxPro enables you to do this. However, it does not allow you to specify a particular column to search. Rather, once the first occurrence of a specified value is found, regardless of the column, the search is stopped. With ADO recordsets, more granular control is provided. The following code checks to see if a file called VFP6.EXE is in the recordset:
oRS.Find("File Like 'VFP6.EXE'")
If !oRS.Eof
*/ Found it
Else
*/ Not found
Endif
Finally, you may wish to filter the list based on the file size being greater than a specified value:
oRS.Filter = "size > 50000"
When evaluating the tools at your disposal for local data handling, be sure to consider fabricated ADO recordsets. Also, if you find yourself running into obstacles with Visual FoxPro arrays, fabricated ADO recordsets may provide a sound alternative.
Command Object
ProgID: ADODB.Command
The purpose of the Command object is just as the its name implies, to run commands. For example, you may need to run a SQL update against a SQL Server table. To illustrate, the following code applies a 10 percent increase in the UnitPrice field in the Products table of the SQL Server Northwind database:
oCommand = CreateObject("adodb.command")
With oCommand
.ActiveConnection = oConnection
.CommandText = "Update Products Set unitprice = unitprice * 1.1"
.Execute
EndWith
The ActiveConnection property
To review, both the Command object and Recordset object have the ActiveConnection property. A Command object needs to know what data source it is to execute commands against. A Recordset object needs to know what data source contains the data it is to retrieve. The way you accomplish this is by setting the ActiveConnection property.
The ActiveConnection property presents a great opportunity to talk about the flexible nature of the ADO object model. The ADO object model is very flat, in that you do not have to create a series of objects in order to gain access to other objects. For example, the following is one way to create and open both a Connection and a Recordset object:
Now, you can reference the Connection object because it has been implicitly created from the passed connection string:
?oRecordset.ActiveConnection.ConnectionString
The same is true for the Command object. While a Command object was not explicitly created, a Command object was in fact created and actually did the work of creating the recordset. Using the recordset just created, the following command will yield “Products” as the CommandText:
?oRecordset.ActiveCommand.CommandText
Which method should you use?
It is really a matter of preference. The latter method, which uses only the RecordSet object, is somewhat overloaded. It carries the same overhead as the former method because you must still create a Connection object. The former method is probably a better way to go as it makes for more readable code.
Parameters collection
The Parameters collection works with the Command object. The primary use of the Parameters Collection is to both pass arguments to, and accept return values from stored procedures. To illustrate, consider the CustOrderHist stored procedure in the SQL Server Northwind database:
CREATE PROCEDURE CustOrderHist @CustomerID nchar(5)
AS
SELECT ProductName, Total=SUM(Quantity)
FROM Products P, [Order Details] OD, Orders O, Customers C
WHERE C.CustomerID = @CustomerID
AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND
OD.ProductID = P.ProductID
GROUP BY ProductName
To illustrate how the Parameters collection is used in conjunction with the Command object, consider the following comprehensive example:
At this point, information can be obtained from the Parameters collection:
For Each Parameter in oCommand.Parameters
?Parameter.Name,Parameter.Size,Parameter.Type
Next Parameter
The first Parameter object is reserved for the value that the stored procedure may return. Regardless of whether the stored procedure explicitly returns a value, this Parameter object will be created. Examining the CustOrderHist stored procedure, note that a single argument, a customer ID, is accepted.
With a Command object and Parameter object in place, the real work can begin. To get things rolling, a value needs to be assigned to the Parameter object that will in turn be passed to the stored procedure. In this case, a SQL statement is executed that totals the quantity, by product, that a specified customer has purchased. The following code provides a customer ID and executes the stored procedure:
Yet another way to produce a Recordset object is through the execution of a stored procedure. The resulting Recordset object contains two fields that correspond to the select statement in the CustOrderHist stored procedure. Need a different history? Just update the Value property of the Parameter object and invoke the Execute method of the Command object.
The Parameters collection also comes into play in the area of parameterized queries. Consider the following SQL Statement:
Select * ;
From Customer ;
Where country = ? And max_order_amt > ?
As with views, either local or remote, in Visual FoxPro, so too can queries be parameterized in ADO. In ADO, the question mark acts as a placeholder for parameters. The following example illustrates how to put this all together.
First, a connection and a Command object need to be created:
The arguments for the CreateParameter method are as follows:
Name—The name of the parameter.
Type—The data type of the parameter. A list of valid values is contained in DataTypeEnum.
Direction—The direction of the parameter. Parameters sent to a command are input parameters. Arguments passed back from a command are output parameters. A list of valid values is contained in ParameterDirectionEnum.
Size—The length of the parameter.
Value—The initial value of the parameter.
Alternatively, the parameter could have been created like this:
Once the parameter has been created, it needs to be appended into the Parameters collection of the Command object:
oCommand.Parameters.Append(oCountryParameter)
With the parameter in place, the value of the parameter can be set. In this case, the parameter will be set so that any country that begins with the letter U will be returned into a Recordset object:
With oCountryParameter
.Size = 2
.Value = "U%"
EndWith
Now, a Recordset object can be created:
oRecordset = oCommand.Execute
A useful feature of specifying parameters is that this enforces characteristics such as size, data type, and so on. For example, the preceding parameter was defined as a character. If a value based on a different data type was assigned to the Value property of the Parameter object, an error would result. The same is true if the assigned value is greater in length than what has been specified by the Size property.
Finally, if a list of customers in Mexico were required, the following code would complete the task:
Recall the earlier assertion that, by itself, ADO is incapable of doing anything? ADO in fact just provides an interface. OLE DB providers give ADO the ability to do anything. So then, what distinguishes one OLE DB provider from another? More specifically, how can you determine what an OLE DB provider can and cannot do, or what attributes it does or does not possess? Depending on the OLE DB provider you use, or the type of recordset you use (client or server), what is supported will likely differ.
The Properties collection applies to the Connection, Recordset, and Field objects. The Command object also has a Properties collection, which is identical to the Recordset object Properties collection.
Multiple result sets provide a good example of varying OLE DB provider support. To determine if multiple result sets can be obtained, you can refer to the “Multiple Results” properties:
If oConnection.Properties("Multiple Results").Value = 1
*/ Supports multiple result sets
EndIf
While the OLE DB providers for SQL Server and ODBC both support multiple results, the OLE DB provider for Jet does not. To illustrate, the following is valid syntax for SQL Server:
oRecordset.Source="SELECT * FROM customers;"+"SELECT * FROM orders"
oRecordset.Open
?oRecordSet.Fields.Count && number of fields in customers table
oRecordset = oRecordset.NextRecordSet
?oRecordSet.Fields.Count && number of fields in orders table
In this case, the OLE DB Provider for SQL Server can return multiple recordsets. If you attempt the same thing with the OLE DB Provider for ODBC, which you need to use when accessing Visual FoxPro data, you will receive an error message stating that the requested action is not supported by the OLE DB provider.
Another example involves the way in which the Properties collection deals with the location of a Recordset object. Recordsets can either exist locally as client-side recordsets or they can exist remotely as server-side recordsets. Client-side recordsets, as will be discussed shortly, have several capabilities that server-side recordsets do not have. One of these abilities is to create indexes. The following code creates a client-side recordset:
In the absence of a declaration of where a Recordset object should reside, the Recordset object, by default, resides on the server. Attempting to reference the Optimize property results in an error stating that the specified property could not be found in the collection.
While the ADO interface is constant, depending on the provider you use, the capabilities may be very different. Be sure to consult your provider’s documentation.
Remote Data Services
One of the most powerful data access capabilities introduced by Microsoft is Remote Data Services (RDS). Although a separate set of objects exists for RDS, RDS is really just another component for use with ADO. There are two ways you can implement RDS.
Use the same ADO objects described in this paper
Use the RDS data control
Let’s discuss the RDS data control option first, since it represents some uncharted territory.
The RDS Data Control
The following code creates an instance of the RDS data control:
oRDSDataControl = Createobject("rds.datacontrol")
Once the data control is created, only three properties need to be populated: Server, Connect, and SQL.
Because we’re using the SQL Server OLE DB Provider, the SQL property can consist of just the table name. The following code retrieves the same recordset, but does so with the OLE DB provider for ODBC:
From this point on, you can work with the recordset the same way you work with any other ADO client-side recordset:
Do While !oRecordset.Eof
orecordset.Fields(1).value = ;
Proper(orecordset.Fields(1).value)
oRecordset.Movenext
EndDo
oRecordset.Updatebatch
Alternatively, you can replace the last line of code with a call to the SubmitChanges method of the RDS data control:
oRDSDataControl.SubmitChanges
Implementing RDS Through the ADO Interface
You can invoke RDS by using the same ADO Connection object discussed above. As with hierarchical recordsets, the first step involves the selection of an OLE DB provider. In this case, the MSRemote provider is required. The following code sets up the Connection object:
The ADO ConnectionString property supports only four arguments. The first two, Provider and File Name, have already been discussed. The third and fourth, Remote Provider and Remote Server, are used by the RDS in the example above. The Remote Provider is the same OLE DB provider used when you create local connections. The additional parameters that specify the database, user ID, and password are used by the OLE DB Provider for SQL Server that in turn is located on the remote server. The following code connects the Recordset object and Connection object and with one difference, is basically the same as the previous examples in this paper:
The only difference is that properties such as CursorLocation and CursorType are omitted since all recordsets created through RDS must exist on the client. Additionally, all client-side recordsets are static types. If you like, you can still specify the properties explicitly. Any incompatible properties will be coerced to a valid value. For example, if you specify the CursorType to be a ForwardOnly cursor and you specify the recordset exists on the client, when the Open method is fired, ADO forces the cursor type to be static. The same is true if you specify the CursorLocation to be on the server and you use the MSDataShape provider. Since all hierarchical pecordsets must exist on the client, the CursorLocation is coerced to the proper value.
Summary
The goal of this paper has been to provide you with a fairly comprehensive overview of both ADO and RDS from the perspective of Visual FoxPro applications. Note that ADO is not a replacement for the Visual FoxPro Cursor Engine. Rather, regard it as another tool at your disposal. Both Visual FoxPro cursors and ADO recordsets have their relative strengths and weaknesses.
ADO is ideal in situations where your application is component based, or in situations where you need to pass data to other applications such as Excel in automation operations. Fabricated ADO recordsets can provide an interesting alternative to arrays when more robust data handling requirements are necessary.
For most local data handling operations however, Visual FoxPro cursors will usually provide better results.
John V. Petersen, MBA, is president of Main Line Software, Inc., based in Philadelphia, Pennsylvania. John’s firm specializes in custom software development and database design. He is a Microsoft Most Valuable Professional and has spoken at many developer events, including Visual FoxPro Developers Conference, FoxTeach, the Visual FoxExpress Developer’s Conference, DevDays, and TechEd. In addition, John has written numerous articles for FoxTalk and FoxPro Advisor. John is co-author of Visual FoxPro 6 Enterprise Development and Hands-on Visual Basic 6—Web Development, both from Prima Publishing. John’s latest project is the ADO Developer’s Handbook, from Sybex Publishing, due September 1999.
Summary: Describes how the Microsoft® Visual FoxPro® version 6.0 Application Framework, including the Application Wizard and Application Builder, can be used by the beginning developer to turn out polished applications and customized by the more experienced developer to create more detailed applications. (32 printed pages)
Contents
OverviewExamining Framework ComponentsDesignating the Classes You WantSpecifying Your Own Framework ComponentsA Closer Look at the Standard Application WizardA New Application WizardA Few Parting Thoughts about Team PracticesAppendix 1Appendix 2Appendix 3Appendix 4Expand table
Click to copy the appfrmwk sample application discussed in this article.
Overview
The Visual FoxPro 6.0 Application Framework offers a rapid development path for people with little experience in Visual FoxPro. With a few simple choices in the Application Wizard and the Application Builder, beginning developers can turn out polished and practical applications.
Under the hood, however, the framework offers experienced developers and teams much more. This article shows you how to adapt the framework components so they fit your established Visual FoxPro requirements and practices.
In the first section of this article you’ll learn about the files and components that support the framework and how they work together while you develop an application. This information is critical to moving beyond simply generating framework applications to experimenting with framework enhancements.
The second section teaches you how to apply your experiences with the framework to multiple applications. After you’ve experimented with framework enhancements for a while, you will want to integrate your changes with the framework, for standard use by your development team. By customizing the files the Application Wizard uses to generate your application, you’ll make your revisions accessible to team members—without sacrificing the framework’s characteristic ease of use.
Examining Framework Components
This section shows where the framework gets its features and components, and how these application elements are automatically adjusted during your development process.
Once you see how and where framework information is stored, you can begin to try different variations by editing the versions generated for a framework application. When you’re satisfied with your changes, you can use the techniques in the next section to migrate them to your team’s versions of the framework components.
NoteLike most Visual FoxPro application development systems, the framework is composed of both object-oriented programming (OOP) class components and non-OOP files. This distinction is important because you adapt these two types of components in different ways; classes can be subclassed, while non-OOP files must be included as is or copied and pasted to get new versions for each application. The framework is minimally dependent on non-OOP files, as you’ll see here, but these files still exist.
Throughout this article we’ll refer to the non-OOP framework files as templates, to distinguish these components from true classes.
Framework Classes
The Visual FoxPro 6.0 framework classes are of two types:
Framework-specific classes. These classes have been written especially for the application framework and provide functionality specific to the framework. The standard versions of these classes are in the HOME( )+ Wizards folder, in the _FRAMEWK.VCX class library.
Generic components. These features come from class libraries in the HOME( )+ FFC (Visual FoxPro Foundation Classes) folder.
_FRAMEWK.VCX
The _FRAMEWK.VCX class library (see Figure 1) contains all the classes written specifically to support the framework. Each framework application you create has an application-specific VCX containing subclasses of the _FRAMEWK.VCX components. The Application Wizard puts these subclasses in a class library named <Your projectname> plus a suffix to designate this library as one of the wizard-generated files. To distinguish these generated, empty subclasses, it adds a special prefix to the class names as well.
Figure 1. _FRAMEWK.VCX framework-specific class library, as viewed in Class Browser, is found in the HOME( )+ Wizards folder.
Framework superclass: _Application
The _Application class is a required ancestor class, which means that this class or a subclass of this class is always required by the framework. This class provides application-wide manager services. For example, it manages a collection of modeless forms the user has opened.
You designate a subclass of _Application simply by using CREATEOBJECT( ) or NEWOBJECT( ) to instantiate the subclass of your choice. (By default, the framework provides a main program to do this, but this PRG contains no required code.) When your designated _Application subclass has instantiated successfully, you call this object’s Show( ) method to start running the application.
NoteIn this article, we’ll refer to the object you instantiate from a subclass of _Application as the application object. We’ll continue to refer to “your subclass of _Application” to mean the class definition instantiating this object, which will be in a VCX belonging to your application (not _FRAMEWK.VCX). You’ll also see references to “_Application“, that refer specifically to code and properties you’ll find in the superclass located in _FRAMEWK.VCX.
At run time, the application object instantiates other objects as necessary to fill all the roles represented by the other classes in _FRAMEWK.VCX except _Splash. The framework identifies these roles as important to various application functions, but, as you’ll see in this section, you have full control over how the roles are carried out.
NoteThe _Splash class is an anomaly in _FRAMEWK.VCX; it isn’t instantiated or used by the framework application directly. (If it were instantiated by the application object, your splash screen would appear too late to be useful.) Instead, _Splash merely provides a default splash screen with some of the same attributes as _Application (for example, your application name and copyright). The Application Builder transfers these attributes to your application’s subclass of _Splash at the same time it gives them to your application’s subclass of _Application, so they stay synchronized. The default main program delivered with a framework gives you one way to instantiate this splash screen before you instantiate your application object.
You certainly don’t need to use the method shown in the default main program for your splash screen. In fact, many applications do not need a splash screen at all. For those that do, you may prefer to use the Visual FoxPro –b<file name> command-line switch, which displays a bitmap of your choice during startup, rather than a Visual FoxPro form of any description.
Framework superclass: _FormMediator
You’ll grasp most of the “roles” played by the subsidiary classes in _FRAMEWK.VCX easily, by reading their class names and descriptions. (If you can’t read the full class description when you examine _FRAMEWK.VCX classes in a project, try using the Class Browser.) However, you’ll notice a _FormMediator class whose purpose takes a little more explaining.
You add an object descended from the _FormMediator custom class to any form or form class, to enable the form to communicate efficiently with the application object. This section will show you several reasons the form might want to use services of the application object. With a mediator, your form classes have access to these services, but the forms themselves remain free of complex framework-referencing code.
The _FormMediator class is low-impact. It doesn’t use a lot of resources, and its presence will not prevent your forms from being used outside a framework application. Using this strategy, the framework can manage any forms or form classes your team prefers to use, without expecting them to have any special inheritance or features.
Like _Application, _FormMediator class is a required ancestor class. You can create other mediator classes, as you can subclass _Application to suit your needs, but your mediators must descend from this ancestor.
We’ll refer to _FormMediator and its descendents as the mediator object, because (strictly speaking) your forms will see it as the “application mediator” while the application object treats it as a “form mediator.”
The Visual FoxPro 6.0 Form Wizards create forms designed to take advantage of mediators when the framework is available. You can see some simple examples of mediator use in the baseform class of HOME( )+ WizardsWIZBASE.VCX.
Examine _FormMediator‘s properties and methods, and you’ll see that you can do much more with the mediator in your own form classes. For example, the application object calls mediator methods and examines mediator properties during its DoTableOutput( ) method. (This method allows quick output based on tables in the current data session.) Your mediator for a specific form could:
SELECT a particular alias to be the focus of the output.
Prepare a query specifically for output purposes (and dispose of it after the output).
Inform the application object of specific classes and styles to be used by _GENHTML for this form.
Change the output dialog box caption to suit this form.
The mediator also has methods and properties designed to specify context menus for the use of a particular form. If the application object receives this information from the mediator, it handles the management of this menu (sharing it between forms as necessary).
You’ll find one example of mediator use in the ErrorLogViewer class. (This use is described in Appendix 1, which covers the options system.) A full discussion of the _FormMediator class is beyond the scope of this document. The more information you give a mediator or mediator subclass, however, the more fully your forms can use framework’s features, without making any significant changes to the forms themselves.
NoteThe _Application class includes a property, lEnableFormsAtRuntime (defaulting to .T.), which causes the application object to add mediators at run time to any form not having a mediator of its own. You can specify the mediator subclass that the application adds to a form at run time. Keep in mind, however, that mediators added at design time will have a more complete relationship with their form containers, because these forms can include code referencing their mediator members. During a form’s QueryUnload event, for example, the form can use the mediator to determine whether the form contains any unconfirmed changes. Without code in the form’s QueryUnload method, the mediator can’t intercede at this critical point.
Additional _FRAMEWK.VCX classes
The other classes in _FRAMEWK.VCX are all dialog box and toolbar classes to perform common functions within an application. None of these classes are required ancestors; you can substitute your own user interfaces and class hierarchies for these defaults at will. Two of them (_Dialog and _DocumentPicker) are abstract; that is, they are never instantiated directly, existing only to provide properties and methods to their descendent classes. Others will not instantiate unless you pick specific application characteristics. For example, if you don’t write “top form” applications (MDI applications in their own frames) you will never use _TopForm, the _FRAMEWK.VCX class that provides the MDI frame window object.
Once you have examined these classes, and identified their roles, you will know which ones supply the types of services you need in applications you write—and, of these, you will identify the ones you wish to change.
Designating the Classes You Want
For each class role identified by the framework, the application object uses corresponding xxxClass and xxxClassLib properties to determine the classes you want. To change which class is instantiated for each role, you change the contents of these properties in your subclass of _Application.
For example, _Application has cAboutBoxClass and cAboutBoxClassLib properties, and it uses these properties to decide what dialog box to show in its DoAboutBox( ) method (see Figure 2).
Figure 2. Class and ClassLib property pairs in the _Application object
If you fill out a class property but omit the matching Classlib property, _Application assumes that your designated class is in the same library as the _Application subclass you instantiated. If your _Application subclass is in the MyApplication.vcx and cAboutBoxClass has the value “MyAboutBox” but cAboutBoxClassLib is empty, a call to the Application object’s DoAboutBox( ) method instantiates a class called MyAboutBox in MyApplication.vcx.
If you call the method instantiating one of the subsidiary classes when the matching class property is empty, _Application attempts to provide appropriate behavior to the specific situation. For example, if the cAboutBoxClass property is empty, DoAboutBox( ) will simply do nothing, because it has no alternative. By contrast, if the cErrorViewerClass property is empty, the _Application DisplayErrorLog( ) method will ask its cusError member object to use its default error log display instead.
Except for the cMediatorClass and cMediatorClassLib properties, which must specify a class descending from _FormMediator in _FRAMEWK.VCX, remember that there are no restrictions on these dialog boxes and toolbars. You don’t have to subclass them from the classes in _FRAMEWK.VCX, or even follow their examples, in your own classes fulfilling these framework roles.
Even when you design completely different classes, you will still benefit from investigating the defaults in _FRAMEWK.VCX, to see how they take advantage of their relationship with the framework. For example, all the classes descended from _Dialog have an ApplyAppAttributes( ) method. When the framework instantiates these classes, it checks for the existence of this method. If the ApplyAppAttributes( ) method exists, the application object passes a reference to itself to the form, using this method, before it calls the Show( ) method. In this way, the dialog box can derive any framework-specific information it needs before it becomes visible. For instance, the About Box dialog box might adjust its caption using the _Application.cCaption property.
If the ApplyAppAttributes( ) method does not exist in yourcAboutBoxClass class, no harm is done. The _Application code still tries to harmonize your dialog box with its interface, in a limited way, by checking to see whether you’ve assigned any custom value to its Icon property. If you haven’t, _Application assigns the value in its cIcon property to your dialog box’s icon before calling its Show( ) method.
NoteThis strategy typifies the framework’s general behavior and goals:
It tries to make the best use of whatever material you include in the application.
When possible, it does not make restrictive assumptions about the nature of this material.
It avoids overriding any non-default behavior you may have specified.
Investigating the default _Options dialog box class and _UserLogin default dialog boxes will also give you insight into the _Application options and user systems. While the dialog boxes themselves are not required, you will want to see how they interact with appropriate _Application properties and methods, so your own dialog boxes can take advantage of these framework features. In particular, the _Application options system has certain required elements, detailed in Appendix 1.
FoxPro Foundation Generic Classes
You may be surprised that _FRAMEWK.VCX contains only two required classes (the application and mediator objects), and in fact even when you add the other subsidiary classes, _FRAMEWK.VCX doesn’t contain much of the functionality you may expect in a Visual FoxPro application. You will not find code to perform table handling. You won’t find dialog boxes filling standard Visual FoxPro roles, such as a dialog box to select report destinations. You won’t find extensive error-handling code.
_FRAMEWK.VCX doesn’t include this functionality because there is nothing framework-specific about these requirements. Instead, it makes use of several Visual FoxPro Foundation Classes libraries, useful to any framework or application, to perform these generic functions. The _Application superclass contains several members descending from FFC classes, and it instantiates objects from other FFC classes at run time as necessary. Then it wraps these objects, setting some of their properties and adding some specific code and behavior to make these instances of the FFC classes especially useful to the framework.
For example, _Application relies on its cusError member, descended from the _Error object in FFC_APP.VCX, to do most of its error handling, and to create an error log. However, as mentioned earlier, _Application code displays the error log using a framework-specific dialog box. The application object also sets the name and location of the error log table to match its own needs, rather than accepting _Error‘s default.
The framework uses four FFC class libraries: _APP.VCX, _TABLE.VCX, _UI.VCX, and _REPORTS.VCX. Figure 3 shows these libraries in Class Browser views, as well as in a Classes tab for a framework application project.
Figure 3. A framework application uses generic Visual FoxPro Foundation Classes, from HOME( )+ FFC folder, to supplement the framework-specific classes in _FRAMEWK.VCX.
Unlike the subsidiary classes in _FRAMEWK.VCX, the FFC classes and their complex attributes are used directly by _Application, so you don’t specify alternative classes or class libraries for these objects. You can still specify your own copies of these class libraries, as you’ll see in the next section.
If you examine the Project tab in Figure 3, or the project for any framework application, you’ll find this list of libraries built in. You’ll see _FRAMEWK.VCX, and there will be at least one class library containing the subclasses of _FRAMEWK.VCX for this application.
You’ll see one more FFC library: _BASE.VCX, which contains the classes on which _FRAMEWK.VCX and all the FFC libraries are based. Your framework project must have access to a library called _BASE, containing all the classes found in _BASE. However, neither the framework nor the four FFC class libraries it uses require any specific behavior or attributes from these classes. You are free to create an entirely different _BASE.VCX with classes of the same name, perhaps descending from your team’s standard base library.
Framework Templates
The framework templates are of three types:
Menu templates, a collection of Visual FoxPro menu definition files (.mnx and .mnt extensions)
Metatable, an empty copy of the table the framework uses to store information about the documents (forms, reports, and labels) you use in your application
Text, a collection of ASCII supporting files
Unlike the .vcx files used by the framework, Visual FoxPro doesn’t deliver separate versions of these templates on disk. Because the templates are copied, rather than subclassed, for framework applications, the templates don’t need to be available to your project as separate files. Instead, these items are packed into a table, _FRAMEWK.DBF, found in the HOME( )+ Wizards folder. The Application Wizard unpacks the files when it generates your new application (see Figure 4).
Figure 4. The Application Wizard copies template files from this _FRAMEWK.DBF table in HOME( )+ Wizards folder.
Because the files don’t exist on disk, their template file names are largely irrelevant, except to the Application Wizard. Although we’ll use the template names here, keep in mind that their copies receive new names when the Wizard generates your application.
Menu Templates
Just as the framework identifies “dialog box roles” and supplies sample dialog boxes to fill those roles, it identifies some “menu roles,” and comes equipped with standard menus to meet these requirements. The roles are startup (the main menu for your application) and navigation (a context menu for those forms you identify as needing navigation on the menu).
There are three template startup menus, each corresponding to one of the three application types described by the Application Builder as normal, top form, and module. T_MAIN.MNX, is a standard “replace-style” Visual FoxPro menu. It’s used for normal-style applications, which take over the Visual FoxPro environment and replace _MSYSMENU with their own menu. T_TOP.MNX, for top form applications, looks identical to T_MAIN.MNX, but has some code changes important to a menu in an MDI frame. T_APPEND.MNX is an “append-style” menu, characteristic of modules, which are applications that add to the current environment rather than controlling it.
There is one navigation menu template, T_GO.MNX. Its options correspond to the options available on the standard navigation toolbar (_NavToolbar in _FRAMEWK.VCX).
NoteBecause both T_GO.MNX and T_APPEND.MNX are “append-style” menus, they can exist as part of either _MSYSMENU or your top form menu. The Application Builder synchronizes your copy of T_GO.MNX to work with your normal- or topform-type application. However, if you change your application type manually rather than through the Application Builder, or if you want a module-type application that adds to an application in a top form, you may need to tell these menus which environment will hold them.
You make this change in the General Options dialog box of the Menu Designer (select or clear the Top-Level Form check box). If you prefer, you can adjust the ObjType of the first record in the MNX programmatically, as the Application Builder does. See the UpdateMenu( ) method in HOME( )+ WizardsAPPBLDR.SCX for details.
Like the document and toolbar classes in _FRAMEWK.VCX, the menu templates are not required. They simply provide good examples, and should give you a good start on learning how to use menus in a framework application.
In particular, you’ll notice that the menus do not call procedural code directly, only application object methods. This practice ensures that the code is properly scoped, regardless of whether the MPR is built into an app, or whether the .app or .exe holding the MPR is still in scope when the menu option runs.
Because Visual FoxPro menus are not object-oriented, they can’t easily hold a reference to the application object. To invoke application object methods, the menus use the object’s global public reference. This reference is #DEFINEd as APP_GLOBAL, in an application-specific header file, like this:
#DEFINE APP_GLOBAL goApp
Here is an example menu command using the #DEFINEd constant (the Close option on the File menu):
Each template menu header #INCLUDEs this header file. You can change the #DEFINE and recompile, and your menus will recognize the new application reference.
NoteThe application object can manage this public reference on its own (you don’t need to declare or release it). It knows which variable name to use by consulting its cReference property, which holds this name as a string. You can either assign the value in the program that instantiates your application object (as shown in the default main program) or you can assign this string to the cReference property of your _Application subclass at design time.
The template menus are the only part of the framework using this global reference. If you wish, your forms and other objects can use the reference, too, but there are rarely good reasons to do this. Before you opt to use the global reference, think about ways you might pass and store a reference to the application object in your forms instead. If your forms have mediator objects, they have a built-in method to receive this reference any time they need it.
Metatable Template
_FRAMEWK.DBF contains records for T_META.DBF/FPT/CDX, the table holding information about documents for your application. Records in this table indicate whether a document should be treated as a “form” or “report”—and you can create other document types on your own.
The document type designation is used by the framework dialog boxes descending from _DocumentPicker, to determine which documents are displayed to the user at run time. For example, the _ReportPicker dialog box will not display documents of “form” type, but the _FavoritePicker dialog box displays both forms and reports.
However, document type as specified in the metatable does not dictate file type. A “report” type document might be a PRG, which called a query dialog box and then ran a report based on the results.
The Application Builder creates and edits metatable records when you use the Builder to add forms and documents to the application. If you manually add a form or document to a framework project, the Project Hook object invokes the Builder to ask you for details about this document and fill out the metatable accordingly. Of course, you can also add records to the metatable manually.
The Application Builder and the _FRAMEWK.VCX dialog boxes descending from _DocumentPicker rely on the default structure of this metatable. (You’ll find its structure detailed in**Appendix 2.) The dialog boxes derive from this table the information they need to invoke each type of document, including the options you’ve set in the Application Builder for each document. (Appendix 3 gives you a full list of _DocumentPicker subclasses and their assigned roles.)
Just as you don’t have to use the _DocumentPicker dialog boxes, you don’t have to use the default metatable structure in a framework application. If you like the idea of the table, you could design a different structure and use it with dialog boxes with different logic to call the _Application methods that start forms and reports.
NoteIf you design a metatable with a different structure from the default, the application object can still take care of it for you. On startup, the metatable is validated for availability and appropriate structure. Once the metatable is validated, the application object holds the metatable name and location so this information is available to your application elements later, even though the application object makes no use of the metatable directly.
Edit your _Application subclass’s ValidateMetatable( ) method to reflect your metatable structure if it differs from the default. No other changes to the standard _Application behavior should be necessary to accommodate your metatable strategy.
You can also dispense entirely with a metatable in a framework application. No part of the framework, except the_DocumentPickerdialog boxes, expects the metatable to be present.
For instance, you might have no need for the dialog boxes or data-driven document access in a simple application. In this case, you can eliminate the metatable and invoke all your reports and forms directly from menu options. Simply provide method calls such as APP_GLOBAL.DoForm( ) and APP_GLOBAL.DoReport( ) as menu bar options. Fill out the arguments in these methods directly in the command code for each menu option, according to the requirements of each form and report.
Additional Text Templates
_FRAMEWK.DBF holds copies of some additional text files copied for your application’s use.
T_START.PRG is the template for the program that initializes your application object and shows the splash screen. Its behavior is well documented in comments you’ll find in the application-specific header file, described later. In addition, as just mentioned, it is not necessary. The program that creates your application object does not have to be the main program for your application, nor does it have to do any of the things that T_START.PRG does.
For example, suppose your application is a “module type,” handling a particular type of chore for a larger application. Because it is a module, it does not issue a READ EVENTS line or disturb your larger application’s environment. It may or may not need to use the framework’s user log on capabilities; you may have set up a user logging system in the outer program. The outer application may be a framework application, or it may not. All these things will help you decide what kind of startup code you need for this application object.
Let’s look at some sample code you might want to use for an accounting application. This .exe file is not a framework application, but it has a framework module added to it, which performs supervisor-level actions. Only some users are allowed to have access to this module. When your accounting application starts up, it may have an application manager object of its own, which performs its own login procedures. The method that decides whether to instantiate the framework module might look like this:
IF THIS.UserIsSupervisor( )
THIS.oSupervisorModule = ;
NEWOBJECT(THIS.cMyFrameworkModuleSupervisorClass,;
THIS.cMySupervisorAppClassLib)
IF VARTYPE(THIS.oSupervisorModule) = "O"
* success
ELSE
* failure
ENDIF
ELSE
IF VARTYPE(THIS.oSupervisorModule) = "O"
* previous user was a supervisor
THIS.oSupervisorModule.Release()
ENDIF
ENDIF
This code does not handle the public reference variable, a splash screen, or any of the other items in T_START.PRG.
You may not need the public reference variable at all because, in this example, your framework application is securely scoped to your larger application manager object. However, if your module application has menus that use the global reference to invoke your application object, you might assign the correct variable name to THIS.oSupervisorModule.cReference just above the first ELSE statement in the preceding sample code (where you see the “* success” comment). This is the strategy you see in T_START.PRG.
NoteIf many different outer applications will use this module, you will prefer to assign the appropriate cReference string in the class, rather than in this method (so you only need to do it once). You can assign this value to cReference either in the Properties window or in code during startup procedures for the application object. Either way, an assign method on the cReference property in _Application does the rest.
T_META.H is the template name for the application-specific header file, just mentioned in the section on menu templates. Only the menus and T_START.PRG use this header file, so it is up to you whether you use it, and how you use it. In the preceding example, you might not use it at all, or you might use only its APP_GLOBAL define to set the application object’s global reference.
The framework uses a few more text templates:
T_CONFIG.FPWNot surprisingly, provides a template for the config.fpw generated for your application. The template version gives new Visual FoxPro developers some ideas about what the config.fpw is for (it’s mostly comments); you will almost certainly wish to edit this file to meet your own standards.
T_LOG.TXTProvides a startup file for the “action log” the Project Hook will write during the life of your application to let you know what changes it has made to your application while you worked with the project.
T_HEAD.TXTProvides a standard header that the Application Wizard uses when generating your application-specific copies of framework templates. You might want to revise T_HEAD.TXT to include your own copyright notices, especially after you’ve edited the rest of the templates.
Specifying Your Own Framework Components
If you’ve done any development at all, you’ve undoubtedly experienced moments in which you identify something you wish to abstract from the process of developing a single application. You’ve done it too many times, you know how to do it, and now it’s time you figure out the best way to do it—so you never have to do it again.
In OOP terms, this is the time to develop a superclass to handle this function, so you can reuse its features. In template terms, this is the time to edit the template you copy for each application’s use. In the Visual FoxPro 6.0 application framework’s mixed environment, as you know, we have both types of components.
We’ll quickly review how these components are managed automatically by the Application Wizard and Builder during your development cycle. Then we’ll turn our attention to how you integrate your own superclasses and edited templates into this system.
Framework Components During Your Application Lifecycle
When you choose to create a new framework application, the Application Wizard takes your choices for a location and project name and generates a project file. If you select the Create project directory structure check box, the Application Wizard also creates a directory tree under the project directory. It adds _FRAMEWK.VCX and the required foundation class libraries to this project. It also adds a class library with appropriate application-specific subclasses of _FRAMEWK.VCX.
The Application Wizard then adds template-generated, application-specific versions of all the non-OOP components the application needs. As you probably realize, the Application Wizard copies these files out of the memo fields in _FRAMEWK.DBF.
_FRAMEWK.DBF contains two more records we haven’t mentioned yet: T_META.VCX and T_META.VCT. These records hold straight subclasses of the classes in _FRAMEWK.VCX, and they are copied out to disk to provide your application-specific class library.
NoteT_META.VCX is not a template. It is just a convenient way for the Application Wizard to hold these subclasses, and is not part of your classes’ inheritance tree. Your subclasses descend directly from _FRAMEWK.VCX when the Application Wizard creates them, and thereafter will inherit directly from _FRAMEWK.VCX.
Once your new framework project exists, the Application Wizard builds it for the first time. It also associates this project with a special Project Hook object, designed to invoke the Application Builder. The Application Wizard shows you the new project and invokes the Application Builder.
At this point, the Application Builder takes over. The Application Builder provides an interface you can use to customize the framework aspects of any framework-enabled project, throughout the life of the project.
You can use the Application Builder to customize various cosmetic features of the application object, such as its icon. When you make these choices, the Application Builder stores them in the appropriate properties of your _Application subclass. (In some cases, it also stores them in the matching _Splash subclass properties.)
In addition, the Application Builder gives you a chance to identify data sources, forms, and reports you’d like to associate with this project. It gives you convenient access to the data, form, and report wizards as you work, in case you want to generate new data structures and documents. For inexperienced developers, the Application Builder provides a visual way to associate data structures directly with forms and reports, by providing options to invoke report and form wizards each time you add a new data source.
Whether you choose to generate reports and forms using the wizards or to create your own, the Application Builder and its associated Project Hook object help you make decisions about framework-specific use of these documents. (Should a report show up in the Report Picker dialog box, or is it only for internal use? Should a form have a navigation toolbar?) It stores these decisions in your framework metatable.
As you think about these automated elements of a framework development cycle, you’ll see a clear difference between the changes you can effect if you change the Application Wizard, or generation process, and the changes you can effect by editing the Application Builder and Project Hook. The files provided by the Wizard, in advance of development, represent your standard method of development. The changes made thereafter, through the Builder and Project Hook, represent customization you can do for this single application.
The balance of this article concentrates on enhancing the Wizard to provide the appropriate framework components when you begin a new application. Once you have established how you want to enhance the startup components, you will think of many ways you can change the Builder and the Project Hook, to take advantage of your components’ special features, during the rest of the development cycle.
NoteAn important change in versions after Visual FoxPro 6.0 makes it easy for you to customize the Application Builder to match your style of framework use. Rather than directly invoking the default appbldr.scx, the default Application Builder in later versions is a PRG.
The PRG makes some critical evaluations before it displays a Builder interface. For example, it checks to see whether the project has an associated Project Hook object, and whether this Project Hook object specifies a builder in its cBuilder property. See HOME( )+ WizardsAPPBLDR.PRG for details. You will find it easy to adopt this strategy, or to edit appbldr.prg to meet your own needs for displaying the Builder interface of your choice.
A preview version of appbldr.prg is included with the source for this article. See appbldr.txt for instructions on making this new Application Builder available automatically from the VFP interface, similar to the new wizard components delivered as part of the document.
A Closer Look at the Standard Application Wizard
You’ll find the Visual FoxPro 6.0 Application Wizard files in your HOME( )+ Wizards folder. When you invoke the Application Wizard from the Tools menu, it calls appwiz.prg, which in turn invokes the dialog box in Figure 5, provided by appwiz.scx.
Figure 5. The standard Visual FoxPro 6.0 Application Wizard dialog box provided by appwiz.scx
When you choose a project name and location, appwiz.prg invokes HOME( )+ WizardsWZAPP.APP, the Visual FoxPro 5.0 Application Wizard, with some special parameters.
The older wizard contained in wzapp.app does most of the work of creating your new project files. The Visual FoxPro 5.0 Application Wizard determines that you are in a special automated mode from the object reference it receives as one parameter and does not show its original interface. It evaluates a set of preferences received from this object reference, and proceeds with the generation process.
The standard implementation has a number of constraints:
Your application subclasses descend directly from _FRAMEWK.VCX. This prevents your adding superclass levels with your own enhancements to the framework, and you certainly can’t specify different superclasses when you generate different “styles” of applications.
Your copies of the ancestor classes, in _FRAMEWK.VCX and FFC libraries, are presumed to be in the HOME( )+ Wizards and HOME( )+ FFC directories. Because these ancestor classes are built into your framework applications, and therefore require recompilation during a build, you have to give all team members write privileges to these locations or they can’t use the Application Wizard to start new framework applications. In addition, the fixed locations hamper version control; you may wish to retain versions of ancestor classes specific to older framework applications, even when Microsoft delivers new FFC and Wizards folders.
Your non-OOP components are always generated out of HOME( )+ Wizards_FRAMEWK.DBF. The templates are not easily accessible for editing. The assumed location of _FRAMEWK.DBF prevents you from using different customized template versions for different types of apps, and also presents the same location problems (write privileges and versioning) that affect your use of the framework class libraries. As with your application subclasses, you can’t designate different templates when you generate different types of applications.
You have no opportunity to assign a custom Project Hook to the project.
To allow you to design and deploy customized framework components, a revised Application Wizard should, at minimum, address these points.
You can make the required changes without major adjustment of the current Application Wizard code, but some additional architectural work provides more room for other enhancements later.
A New Application Wizard
If you DO NEWAPPWIZ.PRG, provided in the source code for this article, you will get a dialog box almost identical to Figure 5, and functionally equivalent to the original dialog box. The only difference you’ll notice is a request, on startup, asking you if you wish to register this wizard in your HOME( )+ WizardsWIZARD.DBF table for future use (see Figure 6).
Figure 6. The Newappwiz.prg wizard classes can be registered to HOME( )+ WizardsWIZARD.DBF so you can choose them from the Tools Wizards menu later.
Though your newly instantiated wizard class calls the old Visual FoxPro 5.0 Wizard code just as the original one did, its internal construction allows completely new generation code to replace this approach in a future version.
You can call newappwiz.prg with a great deal of information packed into its second parameter, to indicate what wizard class should instantiate and what this wizard class should do once instantiated.
Why the second parameter, rather than the first? Newappwiz.prg, like appwiz.prg, is designed with the standard wizard.app in mind. wizard.app, the application invoked by the Tools Wizards menu option for all wizard types, uses its registration table, HOME( )+ WizardsWIZARD.DBF to find the appropriate wizard program to run. Wizard.app passes other information in its first parameter to the wizard program (in this case, newappwiz.prg). Wizard.app passes the contents of the Parms field of wizard.dbf, as the second parameter.
If you choose Yes in the dialog box in Figure 6, the NewAppWizBaseBehavior class becomes a new choice in the registration table, and fills out its options in the Parms field. Additional NewAppWizBaseBehavior subclasses will do the same thing, registering their own subclasses as separate entries. Once a class is registered in wizard.dbf, you don’t have to call newappwiz.prg directly again.
If you’ve chosen Yes in the dialog box in Figure 6 and also choose to register the wizard subclass we investigate in the next section, when you next choose the Application Wizard from the Tools menu, you’ll get a choice, as you can see in Figure 7.
Figure 7. Select your Application Wizard du jour from the Tools Wizards option—once you have more than a single Application Wizard listed in your HOME( )+ WizardsWIZARD.DBF table.
An Extended Subclass of the New Wizard: AppWizReinherit
With an enhanced architecture in place, we can address the issues of component-generation we’ve raised.
Run newappwiz.prg again, this time with a second parameter indicating a different wizard subclass to instantiate:
You should get another message box, similar to Figure 6, asking you if you want to register this subclass in the wizard.dbf table. When you’ve dismissed the message box, you see the dialog box in Figure 8.
The first page of this dialog box contains exactly the same options as the standard Application Wizard.
NoteYou’ll find all the visual classes used in the new wizards in newappwiz.vcx, as part of the source code for this article. The container you see on this page of the AppWizFormReinherit class is the same container class used in AppWizFormStandard. You can read more about these dialog box classes in Appendix 4.
Each subsequent page of the dialog box addresses one of our concerns with the way the original Application Wizard delivers components, and includes some information about how it works. (Figure 9 shows you pages 2 and 3.) Each option defaults to the same behavior you’d get from the original Application Wizard—you don’t need to fill out information on all pages.
Figure 9. Pages 2 and 3 of the Re-inherit App Wizard provide a layer of superclasses and the locations of your FFC and _FRAMEWK.VCX libraries for this framework application.
If you change the parent VCX as suggested on the second page of the dialog box, you can have one or more layers of superclasses between your application’s subclasses of _FRAMEWK.VCX. You’ll create team-specific enhancements in these layers.
NoteThis version of the Application Wizard will create the initial classes for you, as subclasses of the components in _FRAMEWK.VCX, if you specify a VCX name that does not exist. Later, you can create more layers of subclasses from the one the Application Wizard derived from _FRAMEWK.VCX, and designate your subclass layer in this dialog box as appropriate. The VCX you designate on the second page of this dialog box should always conform to the following rules:
Be the immediate superclasses (parent classes) of the application-specific VCX for this application.–and–
Include all the required subclasses of _FRAMEWK.VCX, with the same names as the _FRAMEWK ancestor classes.
You may want several different branches of your team-specific class levels, to match different types of framework applications you commonly create. For example, you could have one superclass set with your team’s options for a framework module and another one with your team’s topform custom attributes (including the class and classlibrary for your subclass of _topform to provide the correct frame).
NoteThese branches, or types, are not restricted to the “styles” or options you see represented in the Application Builder. They are just part of the normal process of subclassing and enhancing a class tree.
For example, you may decide to create Active Documents as framework applications. To do so, you’ll need an _Application subclass that is aware of its hosted environment, and makes certain interface decisions accordingly. You’ll also need an ActiveDoc subclass that is aware of the framework’s capabilities and calls application object methods in response to browser-triggered events, just as the menu templates invoke framework behavior.
Now that you can insert class levels between _FRAMEWK.VCX and your application-specific level, you can make the implementation of these features standard across applications.
If you change the locations of the FFC and _FRAMEWK.VCX libraries on the “Ancestors” page, the Application Wizard will place appropriate copies of the required class libraries in your specified locations if they don’t exist. The Application Wizard also ensures that your copy of _FRAMEWK.VCX inherits from the proper version of FFC, and that your parent classes point to the proper version of _FRAMEWK.VCX.
NoteAs mentioned in the section “FoxPro Foundation Generic Classes,” your FFC location can include your own version of _BASE.VCX. Your _BASE.VCX does not have to have the same code or custom properties as the original _BASE.VCX, but like your parent classes, your _BASE must include classes descended from the same Visual FoxPro internal classes, with the same names, as the classes in the original _BASE.
Other FFC libraries, not used in the framework and not described in this article, will not necessarily work with your own _BASE.VCX. For example, if your application uses _GENHTML, the _HTML.VCX library relies on code in the HOME( ) + FFC_BASE.VCX library. If you use other FFC libraries in your framework application, you may have two _BASE.VCXs included in your project—this is perfectly normal.
The Application Wizard then focuses on your template files on the next page of the dialog box. If you set a location for your template files, the Application Wizard will create fresh copies of these files (by copying them from the original _FRAMEWK.DBF), ready for you to edit.
In each case, if the files are already in the locations you supply, the Application Wizard will use the ones you have.
The last page of the dialog box allows you to pick a Project Hook. The original AppHook class in HOME( ) + WizardsAPPHOOK.VCX is the required ancestor class for a Project Hook designed to work with this application framework, but you can add a lot of team-specific features to your Project Hook subclass. The Application Wizard attempts to verify that the class you specify on this page descends from the appropriate AppHook class.
When you generate your application, the Application Wizard will create a new set of straight subclasses from your parent VCX (or _FRAMEWK.VCX, if you haven’t changed the default on the “Parents” page). These subclasses become the new T_META.VCX/VCT records in _FRAMEWK.DBF. The Wizard appends new contents for all the other template records of _FRAMEWK.DBF from the template folder, if you’ve named one.
NoteThe first time you and the Application Wizard perform these tasks, it won’t make much difference to the final results. Once the Wizard gives you editable superclass layers and your own copies of the templates, however, you have all the architecture necessary to customize the framework for subsequent uses of the Application Wizard.
Having replaced _FRAMEWK.DBF records, the Application Wizard proceeds to create your new application much as before, inserting information about your designated Project Hook class at the appropriate time.
All the “enhanced” Wizard actions are tuned to respect the current setting of the lDelegateToOriginalAppWizard switch, which indicates whether the Visual FoxPro 5.0 Application Wizard code is running or if new code is creating the project. For example, because the original code only looks in the HOME( )+ Wizards folder for _FRAMEWK.DBF, if you have indicated a different place for your _FRAMEWK.DBF (on the “Templates” page) this table will be copied to HOME( )+Wizards before wzapp.app runs. (The first time this occurs, the new Wizard copies your original _FRAMEWK.DBF to a backup file in the HOME( ) + Wizards folder.) Presumably, newer code simply uses your templates table wherever you’ve placed it.
When you use this Wizard to generate a framework application it saves information about your preferred parent classes, as well as the locations of your FFC and _FRAMEWK libraries and template files, to special _FRAMEWK.DBF records. You won’t need to enter this information, unless you wish to change it. This release of the Application Wizard doesn’t save information about the custom Project Hook subclass you may have specified. However, the next section will show you how to put this information into the Parms of wizard.dbf for default use.
NoteBecause the Application Wizard reads its stored information out of _FRAMEWK.DBF, it can’t get the location of _FRAMEWK.DBF from a stored record! However, you can put this information into the Parms field of wizard.dbf, as described in the next section, so all your developers use the proper version of _FRAMEWK.DBF without having to look for it.
You may even decide to use a version of this Wizard class, or of its associated dialog box, that only allows some developers to change the “advanced” pages. Other team members can fill out standard information on Page 1, but they’ll still get your improved versions of all the framework components.
Registering Additional Wizard Subclasses and Customized Records
The new Application Wizard provides the opportunity to register each subclass of its superclass separately in the wizard.dbf table. The wizard stores its class name and location in the Parms field of its own wizard.dbf record.
However, you can add more information in the Parms field. You can even store multiple entries in the wizard.dbf for a single subclass, with differently tuned Parms values. The Application Wizard, once instantiated, uses this additional information.
Here’s the full list of nine options you can pass in the second parameter, or place in the Parms field, for use by NewAppWizBaseBehavior and its subclasses. All #DEFINEs mentioned in this list are in the newappwiz.h header file associated with newappwiz.prg:
These three options instantiate the Wizard:
Wizard classMust descend from #DEFINEd APPWIZSUPERCLASS, defaults to NEWAPPWIZSUPERCLASS.
Wizard classlibLibrary containing wizard class, defaults to NEWAPPWIZ.PRG.
.App or .exe file nameOptional file, containing the wizard class library.
These six options are used by the Application Wizard after it instantiates:
Wizard form classMust descend from #DEFINEd APPWIZFORMSUPERCLASS, defaults to #DEFINEd NEWAPPWIZFORMSTANDARD.
Wizard form classlibLibrary containing the form class, defaults to NEWAPPWIZ.VCX.
.App or .exe file nameOptional file containing the wizard form class library.
Project Hook classThe Project Hook class you want to associate with this project, if you don’t want to use the default Project Hook class associated with framework-enabled projects. This class should descend from the AppHook class in HOME( )+ “WizardsAPPHOOK.VXC”, so it includes the default functionality, but can include enhancements required by your team.
Project Hook classlibThe class library containing the Project Hook class you choose to associate with this project.
Template DBFHolding application components, defaults to HOME( )+ Wizards_FRAMEWK.DBF (#DEFINED as APPWIZTEMPLATETABLE).
Store these values delimited by commas or carriage returns in the Parms field of wizard.dbf. Similarly, if you call newappwiz.prg directly, you can pass all this information as the program’s second parameter, as a single string delimited with commas or carriage returns.
After you’ve registered the AppWizReinherit class, the Parms field for this class’ record in wizard.dbf contains the following information:
APPWIZREINHERIT,<fullpath>newappwiz.fxp,,AppWizFormReinherit, <fullpath>NEWAPPWIZ.VCX,,APPHOOK, <fullpath of HOME()+ "Wizards"> APPHOOK.VCX, <fullpath of HOME()+ "Wizards"> _framewk.DBF
You could run the NEWAPPWIZ program, passing the same string as its second parameter, to get AppWizReinherit‘s default behavior.
Using our ActiveDoc example just shown, you could create a wizard.dbf entry that invokes the same Wizard class but defaults to a different parent VCX and different menu templates than the rest of your framework applications.
To accomplish this, you’d edit the information in the ninth value for this row of the wizard.dbf table, which indicates Template DBF, by editing the Parms field.
Your new row in the table contains the same string in the Parms field, except for the section following the last comma, which points to a new template table. Your special ActiveDoc copy of _FRAMEWK.DBF holds your special Active Document menu templates and superclass information.
Next, suppose you decide that your ActiveDocument framework applications need a special Project Hook subclass, not just special superclasses and menu templates. You could specify this hook automatically, in the seventh and eighth sections of the Parms field. You might even subclass the AppWizFormReinherit dialog box, to disable the last page of this dialog box for ActiveDocument-type applications, by changing the fourth and fifth sections of the Parms field. (This way, your team members would always use the right Project Hook class when generating this type of framework application.)
If you made all these changes, this new entry in the wizard.dbf table might have a Parms field that looked like this:
You would also edit the Name field in wizard.dbf for this entry, perhaps to something like “Active Document Framework Application,” to distinguish this entry from your standard values for the AppWizReinherit class.
When one of your team members accessed the Tools Wizards option from the system menu, “Active Document Framework Application” would now appear on the list of available Wizards, as part of the list you saw in Figure 7. The developer could automatically create the right type of framework application, without making any special choices.
A Few Parting Thoughts about Team Practices
You’ll notice a check box in the Reinheritance Wizard‘s dialog box, indicating that you can omit message boxes and generate your new application with no warning dialog boxes or user interaction. Although this is a helpful option once you’ve used this Wizard a few times, please be sure to read all the message boxes, and the information in the edit boxes on the various pages of this dialog box, at least once.
Any developer’s tool, especially one that edits visual class libraries and other metafiles as extensively as this one does, can potentially cause problems if the system is low on resources. The Help text available within this Wizard attempts to point out its potential trouble spots, so you can close other applications as needed, and have a good idea of what to expect at each step. Other caveats, such as incompletely validated options in this preliminary version, are indicated in the Help text as well.
You also see a More Info button, which provides an overview of the issues this class is meant to address, and how you can expect it to behave (see Figure 10).
Figure 10. Wizard documentation under the More Info button
Beyond its stated purpose to enhance the Application Wizard, AppWizReinherit and its dialog box class try to give you a good model for tool documentation, both at design and run time. The dialog box’s NewAppWiz_Documentation( ), GetUserInfo( ), and DisplayDocumentation( ) methods should give you several ideas for implementation of run-time documentation. Newappwiz.prg has a demonstration procedure, BuilderGetDocumentation( ), which shows you how you can apply these ideas to design time documentation for Builders as well. A final demonstration procedure in newappwiz.prg, ReadDocs( ), shows you another aspect of this process.
Each documentation idea demonstrated here is a variation on a theme: Text is held (using various methods) within the VCX, so it travels with the VCX and will not get lost no matter how widely you distribute the library.
Whether you use these particular implementations is not important; in many cases you’ll be just as well off if you create a text file with documentation and use Visual FoxPro’s FileToString( ) method to read this information for display by the tool whenever necessary.
No matter how you decide to implement it, documentation that helps your team better understand the intended use, extension possibilities, and limitations of the tools you build is critical to their adoption and successful use.
A framework is, in itself, a kind of abstraction, a level above daily activities. Enhancements to a framework represent yet another level of abstraction. Your team will benefit from all the extra attention you can give to communicating your goals for this process.
With any framework, you can efficiently prototype applications and build complete lightweight applications. With a framework set up the way your team operates, you can accomplish these goals without sacrificing quality, depth, or your normal habits of development. With a framework set to deliver your standard components and practices automatically, even new developers can make meaningful, rewarding contributions to your team effort.
Appendix 1: The User Option System
The framework employs a user-registration system based on a user table that is created by the application object if not found at run time. The application object uses the cUserTableName property to set the name and location of this table. If no path is supplied in this property, the location will be set by the cAppFolder property.
Note By default, the application object sets cAppFolder to the location of the APP or EXE that instantiated it. If, for some reason, the application object was instantiated outside a compiled APP or EXE container, cAppFolder contains the location of the application object’s VCX.
If necessary, the application object creates this table in the appropriate location, using the following code (excerpted from the CreateUserTable( ) method):
lcIDField = THIS.cUserTableIDField
lcLevelField = THIS.cUserTableLevelField
* names of two generic-requirement fields,
* User ID and level, are specified by
* _Application properties in case you
* wish to match them to some existing system
CREATE TABLE (tcTable) ;
((lcIDField) C(60), ;
(lcLevelField) I, ;
UserPass M NOCPTRANS, ;
UserOpts M NOCPTRANS, ;
UserFave M NOCPTRANS, ;
UserMacro M NOCPTRANS, ;
UserNotes M )
INDEX ON PADR(ALLTR(&lcIDField.),60) TAG ID
* create a case-sensitive, exact word match
INDEX ON PADR(UPPER(ALLTR(&lcIDField.)),60) TAG ID_Upper
* create a case-insensitive, exact word match
INDEX ON DELETED( ) TAG IfDeleted
If you don’t opt to have users log in and identify themselves in this application, this table is still created. In this case it supplies a default record, representing “all users,” so user macros, favorites, and options can still be stored in this table on an application-wide basis.
NoteBecause of their “global” nature in Visual FoxPro, user macro saving and setting features are only available to framework applications that issue READ EVENTS. Module applications are not allowed to edit the macro set.
When a user logs in, his password is evaluated using the user table’s UserPass field. A SetUserPermissions( ) method, abstract in the base, is called at this time so the user’s level can be checked in order to make appropriate changes to the application and menu options as well.
If the login is successful (or when the application starts up assuming no user login for this application), user name and level are stored in the cCurrentUser and iCurrentUserLevel properties.
User macros, favorites, and options are set from the user’s record in the user table. The _Application code handling macros rely on standard Visual FoxPro abilities to SAVE and RESTORE macros to and from the UserMacro memo field. The favorites system uses an easy-to-read ASCII format in the UserFave memofield. However the options system and the UserOptions field deserve more explanation.
The user table stores option information in its UserOptions memo field, by SAVEing the contents of a local array. This local array is RESTOREd and copied into a member array, aCurrentUserOpts, to establish user options when the current user is set.
The array format is fixed, and yet extremely flexible in the types of user options that can be stored. The allowable options include SETs and member properties, and the options should be specified as being “global” to the application or private to a datasession. The array is laid out, to specify these attributes of each option, in four columns, as follows.Expand table
User Option Array Column 1
Column 2
Column 3
Column 4
Item nameFor a SET command, the item you’re setting, same as what you’d pass to the SET( ) function. For an object, the property you wish to set. Can be the Member.Property you wish to set.
Value for this item
Property (.F.) or SET (.T.) ?
Session (.F.) or Global (.T.) ?
Each time a user logs in, the application method ApplyGlobalUserOptions( ) applies SET options and application object property values for all array rows with .T. in the fourth column. The mediator object has the responsibility to call the application method ApplyUserOptionsForSession( ), on your instructions, passing a reference to its parent form. This method applies SET options and form property values for all array rows with .F. in the fourth column.
The _Options dialog box supplied in _FRAMEWK.VCX gives you examples of all the combinations that can be created for a user option using this array, although its contents are merely examples. It shows you how the user options stored in an array can be expressed as a user interface, giving the user a chance to make changes. It also shows how results of a user-option-setting can be “translated” back into the user options array for use during this login, or saved as defaults to the user preference table.
You will note that, when the user options to apply changes to the current settings, the Options dialog box reinvokes ApplyGlobalUserOptions( ) and then iterates through the available forms, giving their mediators a chance to reapply session settings if they’re set to do so.
In many cases, a “global” setting can transferred to forms as well. For example, the _ErrorLogViewer dialog box has a mediator that checks the application’s cTextDisplayFont setting. This is a global user option, because it provides a chance for the user to specify a text font across all the UI of an application. The mediator transfers the value of the cTextDisplayFont to a property of the same name belonging to its parent dialog box. An assign method on this property then applies the fontname value to all members of the dialog box that should reflect the setting.
Appendix 2: The Default Metatable Structure
This table shows you the default structure of the framework’s metatable. Appendix 3 shows you how the default _FRAMEWK.VCX dialog boxes use this information.Expand table
FieldName
Type
Use
Doc_type
C
This field contains a character to distinguish between document types. Currently, “F” is used for “forms” and “R” is used for “reports.” But this designation just determines how the document type is presented in the interface, not necessarily what type of Visual FoxPro source code file underlies the document. See Alt_Exec and Doc_wrap fields, below.More document types may be added. The framework already contains one extra type, “A,” specifically reserved for you to add application information. The framework will not use “A”-type metatable records in any way, so the reservation of this type simply allows you to use metatable records, or perhaps one metatable header record, as a convenient place for system storage. In most cases, you would want to transfer the contents of such a record to application properties on startup.
Doc_descr
C
The “caption” or long description you want to show up in document picker lists.
Doc_exec
M
The name of the file to be run, usually an .scx or .frx file. In the case of a class to be instantiated, this is the .vcx file name.For Form-type documents, the file extension is assumed to be .scx unless this entry is marked “Doc_wrap” (see below) or the Doc_class field is filled out, in which case the extension is assumed to be .vcx.For Report-type documents, the file extension will default to .frx unless this entry is marked “Doc_wrap”. If no .frx file exists by that name, the application object looks for an .lbx file.In all cases, you may also fill out the file extension explicitly.In all cases, if you Include the file to be run in the project, you need not use paths in this field. If you wish to Exclude the file from the project, you may use path information. Assuming your applications install their subsidiary Excluded files to the appropriately located folder, relative pathing should work in the metatable, and is probably the best policy in this case!
Doc_class
M
The class to be instanced, where the Doc_exec is a .vcx file
Doc_new
L
Mark this .T. for a Form-type document you wish to show up in the FileNew list. When the application object instantiates a form from the FileNew list, it sets its own lAddingNewDocument property to .T. This practice gives the form a chance to choose between loading an existing document or a blank document during the form’s initialization procedures.In many cases, the form delegates this process to its mediator object. The mediator object saves this information for later use.If you do not use a mediator, you may wish to save this information to a form property; you can’t expect the application object’s lAddingNewDocument to reflect the status of any particular form except during the initialization process of that form.For a Report-type document, this field denotes an editable report (new report contents, or even a new report from a template). This capability isn’t currently implemented.
Doc_open
L
Mark this .T. for a Form-type document you wish to show up in the FileOpen list.For a Report-type document, this field denotes a runnable report or label and will place the item in the report picker list.
Doc_single
L
Mark this .T. for a Form-type document that is modeless but should only have one instance. The application object will bring it forward, rather than create a second instance, if the user chooses it a second time.
Doc_noshow
L
Mark this .T. for a Form-type document that you wish to .Show( ) yourself after additional manipulation, rather than allowing the DoForm( ) method to perform the .Show( ).NoteYou will have to manipulate the application’s forms collection or the current _SCREEN.Forms( ) contents to get a reference to this form, so you can manipulate the form and then .Show it when you are ready. If you need this reference immediately, the best place to get it is probably the application object’s aForms[] member array. At this moment, the application object’s last-instantiated form is the one for which you want the reference, and the application object’s nFormCount property has just been refreshed. Therefore, .aForms[THIS.nFormCount] gives you the reference you need when you’re in an application object method (in other code, replace THIS with a reference to the application object). You can see an example of this usage in the _Application‘s DoFormNoShow( ) method.You can create Doc_Wrap programs as described in the entry for the next field. Your wrapper program can take advantage of the DoFormNoShow( ) method, receive its return value (a reference to the form or formset object), and proceed to do whatever you want with it.
Doc_wrap
L
If this field is marked .T. indicating a “wrapped” document, the application’s DoProgram( ) method will run instead of its DoReport( )/DoLabel( ) or DoForm( ) method.If you omit the file extension, the DoProgram( ) method uses the standard Visual FoxPro extension hierarchy to figure out what file you wish to run (“.exe .app .fxp .prg”).
Doc_go
L
If this field is marked .T. and the document is “Form”-type, the form uses the framework’s standard Go context menu for navigation. The menu name is configurable using the application object’s cGoMenuFile property. This field is not used for report-type documents.
Doc_nav
L
If this field is marked .T. and the document is “Form”-type, the form uses the framework’s standard navigation toolbar for navigation. The class is configurable using the application object’s cNavToolbarClass and cNavToolbarClassLib properties. This field is not used for report-type documents.
Alt_exec
M
If this field is filled out, it takes precedence over the Doc_exec field just described. When the user makes a document choice, the _DocumentPicker’s ExecDocument( ) method converts the contents of this field into a string and runs that string as a macro.Your Alt_exec statement can be anything you choose, and it can use attributes of the metatable, including the Properties field (below) however you want. For example, you can choose to have the metatable editable (on disk) rather than included in the APP/EXE, and you can place information in the Properties field dynamically at run time. Your document would then be able to be “aware” of this information by examining the current contents of the Properties field.
Properties
M
This memo field is not used by the framework in any way. It’s for developer use, primarily in conjunction with the Alt_exec field.
User_notes
M
This memo field is not used by the framework in any way. It can be used for notes that would be displayed as Help text for a particular form or report, and so on.
Appendix 3: Default Document- Management Elements of the Framework
The framework accesses metatable information through the _DocumentPicker classes. _DocumentPicker is an abstract standard dialog box class, which contains a picklist and a couple of buttons. The working _DocumentPicker subclasses each have their own way of using the information in the metatable to perform two tasks:
Show the documents in the picklist.
Run the appropriate action when the user picks a document.
Each subclass stores the relevant metatable fields into an array, which serves as the data source for the list box in the dialog box. The same array holds the metatable information that will eventually act on the user’s choice.
The _DocumentPicker superclass has an abstract FillDocumentArray( ) method, designed to perform the first service during the dialog box Init( ), and another abstract method called ExecDocument( ), which is triggered whenever/however the user makes a selection from the document list.
The _DocumentPicker class receives a parameter from the application object. Each subclass of _DocumentPicker uses the parameter to determine which of two states it is supposed to be in when it displays its document list and acts on the user’s choice of a document from the list. The _DocumentPicker superclass simply makes note of this logical value, leaving it to the subclasses to interpret it.
The various _DocumentPicker’s FillDocumentArray( ) methods concentrate on different document types, and fill the array with the appropriate information for that type. Their ExecDocument( ) methods call different application object methods depending on their document type and the dialog box’s current state, sending information from the metatable from the array to method arguments as needed.
The first two columns in the table below show you the names of these working classes and the document types that will appear in their lists, courtesy of their FillDocumentArray( ) method. The other columns show the application methods that call them, and the meaning assigned to their two states when ExecDocument( ) is triggered. Each application method listed here takes a logical parameter (defaulting to .F., State 1) to indicate for what purpose the class presents its document list.Expand table
_DocumentPicker Subclass
_Document types
Associated _Application method
State 1 action
State 2 action
_NewOpen
forms
DoNewOpen( )
Edit
Add
_ReportPicker
reports and labels
DoReportPicker( )
Run report/label
Modify/Add not implemented in _Application superclass.
_FavoritePicker
documents and files of any type
DoStartupForm( )
Run document/file
Put document / file on Favorites menu for quick access.
Appendix 4: Using the NEWAPPWIZ Visual Classes
AppWizFormReinherit, the dialog box called by AppWizReinherit, and AppWizFormStandard, the default dialog box with the same interface as the original wizard, both descend from the same superclass, AppWizFormBaseBehavior (see Figure 11).
Figure 11. Newappwiz.vcx in the Class Browser
AppWizFormBaseBehavior is the required superclass for any dialog box provided as the UI of a NewAppWizBaseBehavior or its descendents. The Application Wizard superclass validates your dialog box class when it instantiates the dialog box as descending from this superclass dialog box.
NewAppWizBaseBehavior contains only the very simple required behavior, no visible controls. It has three custom properties to represent required wizard information (project name, location, and whether or not the Wizard should generate project directory structure). It receives this information from an object reference the Wizard passes. It has a Finish( ) method which passes this information back to the Application Wizard.
In your subclass of AppWizFormBaseBehavior, you simply databind the interface controls of your choice to these three custom properties. You create other controls and custom properties to represent your enhanced options. Your dialog box calls the Finish( ) method when you’re ready to generate. (Both AppWizFormReinherit and AppWizFormStandard use the OKButton class you see in Figure 11, which contains the call to its parent form’s Finish( ) method.)
You can augment Finish( ) to pass more options from the dialog box back to your Wizard subclass as necessary.
You’ll find more information in the NewAppWiz_Documentation method of the superclass. The default AppWizFormStandard subclass shows you a simple example of how to make it work
Microsoft Transaction Server for Visual FoxPro Developers
Summary: Discusses using Microsoft® Visual FoxPro® version 6.0 with MTS to develop three-tier applications. (36 printed pages).
Contents
IntroductionWhat Is Microsoft Transaction Server?Why Is MTS Important for Visual FoxPro Developers?Creating Your First MTS ServerSetting Up SecurityThe Basic Features of MTSJust-In-Time ActivationTransactionsProgramming ModelsDeploymentRemote Deployment and AdministrationSecurityShared Property ManagerMTS Support for Internet Information ServerAutomating MTS AdministrationTips and TricksExpand table
Click to copy the sample files associated with this technical article.
Introduction
No doubt you’ve heard all about Microsoft Transaction Server (MTS) and how it will make your life easier to develop three-tier applications. This article offers a good primer on using Visual FoxPro 6.0 with MTS. We cover the basics of using MTS and then extend it to using with Visual FoxPro Component Object Model (COM) Components. This document is intended to be used with the Microsoft PowerPoint® slide show included with the Visual FoxPro sample files.
MTS is a great environment for working with three-tier development. However, one should realize that it is simply not just a matter of dropping your Visual FoxPro servers into an MTS package and expecting miracles. While it is true that much of the work is already done for you, nothing comes for free. Performance and scalability are critical factors that require well-thought-out designs. Good MTS applications are designed with MTS in mind from the start!
This article assumes that you have MTS already installed. It is available in the Microsoft Windows NT® version 4.0 Option Pack, available from the Microsoft Web site at https://www.microsoft.com/windows/downloads/default.asp.
In addition, you should familiarize yourself with the basics of MTS. Information is available in the Help files provided with MTS when you install the Windows NT 4.0 Option Pack.
What Is Microsoft Transaction Server?
MTS is a component-based transaction processing system for building, deploying, and administering robust Internet and intranet server applications. In addition, MTS allows you to deploy and administer your MTS server applications with a rich graphical tool (MTS Explorer). MTS provides the following features:
The MTS run-time environment.
The MTS Explorer, a graphical user interface for deploying and managing application components.
Application programming interfaces (APIs) and resource dispensers for making applications scalable and robust. Resource dispensers are services that manage nondurable shared state on behalf of the application components within a process.
The MTS programming model provides a framework for developing components that encapsulate business logic. The MTS run-time environment is a middle-tier platform for running these components. You can use the MTS Explorer to register and manage components executing in the MTS run-time environment.
The three-tier programming model provides an opportunity for developers and administrators to move beyond the constraints of two-tier client/server applications. You have more flexibility for deploying and managing three-tier applications because:
The three-tier model emphasizes a logical architecture for applications, rather than a physical one. Any service may invoke any other service and may reside anywhere.
These applications are distributed, which means you can run the right components in the right places, benefiting users and optimizing use of network and computer resources.
Why Is MTS Important for Visual FoxPro Developers?
Microsoft is investing a great amount of resources in three-tier development because of a multitude of benefits derived from this architecture. As shown in Figure, Tier 2, the so-called “middle tier,” represents the layer where much of the Application Services/Business Logic is stored. Visual FoxPro COM components are ideally suited for this architecture and will play a key role in this tier for many years to come. This middle tier is also where MTS lives.
Figure 1. Web-enabled three-tier architecture
Future applications will consist of Web based front ends using a combination of HTML/XML. While Visual FoxPro data can be used as your database of choice for Tier 3, your applications should be written to communicate to a generic back end. This should be a test of your application’s extensibility. “How easy is it to swap back ends—let’s say Visual FoxPro database to Microsoft SQL Server™?” There are several options, including Open Database Connectivity (ODBC) and ActiveX® Data Objects (ADO), which provide generic interfaces to data. Remember, your application should be written knowing that any or all of the three tiers can be swapped out independent of each other.
So why is MTS great for Visual FoxPro developers? It should be clear now that the ability to swap out tier components at will makes for a great reusability story. Microsoft has a concept called total cost of ownership (TCO), which means the collective cost of providing and maintaining corporate Information Services. The three-tier model goes a long way toward reducing TCO.
Updating the Presentation layer is very easy because it merely involves one having to refresh his/her browser. Windows front ends consisting of Visual FoxPro/Visual Basic® forms offer more flexibility in user interface, but updating 150 sites can be time-consuming. In addition, one should expect improved UI options available in HTML.
The back-end data is usually the tier that changes the least. Having data managed centrally also reduces costs. Remember that data can be distributed and still managed from one location. It doesn’t have to be stored centrally to be managed centrally.
Finally, we get to Visual FoxPro’s role in the middle tier. Middle-tier components tend to change most often because they represent business rules, which change as the needs of the business changes. Traditional client/server and monolithic applications would often combine the first two layers into one. This was very inefficient because of the distribution costs in updating sites. Today, with browsers, much of this distribution problem goes away. However, business rules are often complex and can contain sensitive/secure information, so it’s not always wise to send these rules back with the HTML to a Web browser. In addition, it can impede performance.
So, we end up with a dilemma. We want to limit the amount of information sent back to the client, but we also want to minimize the number of back and forth trips between client and server, because bandwidth is also a big consideration (more so with the Internet versus an intranet). The best solution is one involving a so-called “Smart Client.” Traditionally, the Web browser is thought of as an unintelligent client whose job is to merely display an entire static Web page. Each time something on the page changes, we need to refresh the entire Web page. With dynamic HTML (DHTML), you no longer need to do this. Only parts of the Web page affected need updating. In addition, some of the business rules can (and should) reside on the client, thus reducing round trips to the server. For example, you may want to have your client have simple data validation rules, such as one to ensure a value is not negative. It would be more efficient to perform these sorts of checks on the client. Most of the rules, especially sensitive ones, will exist on the server away from client eyes. It is also important to realize, however, that client-side business rules are subject to change almost as frequently as those on the server. The ATSWeb application (available at https://msdn.microsoft.com/vfoxpro/ats_alpha/default.htm) offers a great example of business rules being applied to both client and server.
MTS provides an environment for hosting your Visual FoxPro middle-tier objects because it handles many of the common tasks, including resource and thread management, security, deployment, application robustness, and transactions. This leaves you, the developer, with only the responsibility of providing business logic specific to your application.
Creating Your First MTS Server
Let’s jump right in and create an MTS server, because it’s very simple if you already know how to create a Visual FoxPro COM component.
Creating a Visual FoxPro COM Component
Create a new project file called test1.pjx
Create a new program file (PRG) called test1.prg
Add the following code to this program:DEFINE CLASS server1 AS custom OLEPUBLIC PROCEDURE hello RETURN “Hello World” ENDPROC ENDDEFINE
Build the server as a DLL (for example, test1.dll). All MTS components must be created as in-process DLL servers. You now have a server that can be tested directly in Visual FoxPro:x=create(“test1.server1”) ? x.hello()
Adding the Visual FoxPro COM Component to an MTS Package
A package is a collection of components that run in the same process. Packages define the boundaries for a server process running on a server computer. For example, if you group a Sales component and a Purchasing component in two different packages, these two components will run in separate processes with process isolation. Therefore, if one of the server processes terminates unexpectedly (for instance, because of an application fatal error), the other package can continue to execute in its separate process.
This section describes the task of installing the Visual FoxPro server into the MTS environment.
Launch MTS Explorer.
In the left pane, navigate to the Computers item and select My Computer. You are now looking at the MTS environment.
Click the Packages Installed node to view all default packages installed by MTS. You can think of a Package as a set of components that perform related application functions. For example, an Inventory package might consist of two DLLs, each performing a task related to checking product inventory for a customer order.
Let’s create a new package now. Select the Action -> New -> Package menu item.
Click the Create an empty package button. Type in a name for your new package (for example, Foxtest1).
Click the Next button, and then click the Finish button. You should now see your new package added under the Packages Installed node.
Click your new package node (for example, Foxtest1). You should now see two items. The Components folder is where you add new components such as the Visual FoxPro component you just created. The Roles folder is where you set up groups of users (roles) who all share similar access privileges (security). You do not need to add anything to the Roles folder in order to use your Visual FoxPro component with MTS.
Click the Components folder and select the Action -> New -> Component menu item.
Click the Install new component(s) button. This will bring up the Install Components dialog box. Click the Add files button and go to the location where you created your Visual FoxPro server (for example, test1.dll). Select both the .dll and .tlb files. The .tlb file is the type library file containing properties and methods of your server. After selecting these two files, you should see your OLEPUBLIC component listed in the lower panel. Click Finish and you should see your server added to this folder.
At this point, your package is complete and ready to go. Later, we will talk about setting Transaction support. This can be done from the Properties dialog box of your server.
Accessing Your Component
You can now test your new MTS packaged component using a command similar to the one used to test Visual FoxPro after the DLL server was first created.
x=create("test1.server1")
? x.hello()
That’s all you need to do! If you go back into the MTS Explorer, you should see the component represented with a spinning icon. Click the Status View to see details about the state of the object.
Figure 2. New component viewed in MTS Explorer
If you release the object (RELEASE x), MTS releases its reference.
Going Forward
We’ve just discussed the basics of installing your Visual FoxPro server in MTS. Essentially, all we did was wrap the Visual FoxPro component inside an MTS process that manages security, transaction state, fault tolerance, and other common server responsibilities. All Visual FoxPro servers used with MTS are registered this way. The remainder of the article discusses how to take advantage of MTS-specific features such as security and transactions. You can write code in your components that talk directly to the MTS run-time environment. In addition, the above process can be entirely automated, because MTS exposes an administrative Automation interface.
Setting Up Security
So why are we starting out so early with security? Well, sooner or later, you’re going to fiddle with some sort of security switch and suddenly that MTS application of yours will no longer work. It’s important that you follow these instructions and refer to them later when you decide to add security to your applications.
NoteMTS 2.0 security setup is described in the Readme document. If you have MTS installed on Microsoft Windows® 95, you can skip this section.
Setting System Package Identity
Before you do anything in MTS, it is a good idea to configure the system package for administrating security. When installing MTS, set the system package identity before creating any new packages as follows:
Create a new local Windows NT group named “MTS Administrators” and a new local user named “MTS Administrator.”
Add the “MTS Administrator” user to the “MTS Administrators” and “Administrators” groups.
Set the identity of the system package to “MTS Administrator.” If this does not work, try setting this to the Administrator user.
NoteYou cannot set a package’s identity to a group.
Shut down the system package so that it will be restarted with the new identity. You can do this by right-clicking the My Computer icon in MTS Explorer and selecting Shut Down Server Processes.
Adding Security for MTS Packages
You first need to determine whether you want all or just a few components in your Package to have security. Right-click the Package and select Properties. Next, click the Security tab. Then check the Enable authorization checking check box. To enable or disable security at a component level, right-click a component and display the Properties dialog box.
If this is all you do, an “Access is denied” error message is generated when you try to access your component. You MUST associate a valid role with any component marked for security!
Right-click the package’s Roles folder and select New Role. Type in a functional role such as Managers, Accountants, and so on.
The new role is added as a subfolder. Right-click this folder to Add New User (you will get a dialog box to Add Users and Groups to Role). Select the user(s) that you want to add to your role. To finish, select the Role Membership folder under each component that is marked for security and add the new role created in step 3 by right-clicking the folder and selecting New Role.
NoteYou may still experience the “Access is denied” error message when running your components. There are a couple of possible solutions:
Sometimes adding a Group to a role does not work (step 3). You might try adding individual users instead.
The user rights for that user are not properly set. Make sure the user account for the identities of the system package and other MTS packages have the Windows NT “Log on as a service” user right. You can verify this by using the Windows NT User Manager:
From the Policies menu, select User Rights.
Click Show Advanced User Rights.
Tips for Visual FoxPro Users
Much of the security administration can easily be handled by Automation using the MTS Admin objects. You can set up Security administration in the AfterBuild event of a ProjectHook class you have tied to the project that generates your MTS COM DLL server. See the section “Using Visual FoxPro 6.0 Project Hooks” for examples.
The Basic Features of MTS
Before we jump right into using Visual FoxPro with MTS, let’s review some basic concepts that you need to know in order to make effective use of the MTS environment. For more detailed information, see MTS Help.
Activity
An activity is a collection of MTS objects that has a single distributed thread of logical execution. Each MTS object belongs to a single activity. This is a basic concept that describes how the middle-tier functions when confined to the MTS environment. In an MTS package, multiple clients can access objects, but only one object per client is running at a time on a single thread.
Context
Context is state that is implicitly associated with a given MTS object. Context contains information about the object’s execution environment, such as the identity of the object’s creator and, optionally, the transaction encompassing the work of the object. The MTS run-time environment manages a context for each object.
As a developer, think of every Visual FoxPro object that is registered in an MTS package as having an associated Context object that is created every time you instantiate the Visual FoxPro object. So, each time you issue a CreateObject command, two objects are created—your server and its associated Context. In fact, you can return an object reference to this Context object directly in your code, as in the following example:
The Context object has the following properties and methods.Expand table
Count
CreateInstance
DisableCommit
EnableCommit
IsCallerInRole
IsInTransaction
IsSecurityEnabled
Item
Security
SetAbort
SetComplete
As you can see, the properties, events, and methods (PEMs) are used to access information related to the object transaction and security context (see MTS Help for more details on specific syntax for these PEMs). It is important to understand that the Context state is inherited. An object in a package called from another object in the same package will inherit the state of its caller. Because Context is confined within the same process, state, such as security, is trusted. No object in a package needs to explicitly provide its own security. When your object is released, so is its Context.
Package
Packages, as we just described, are the building blocks of MTS. Think of them as mini applications—a set of components that perform related application functions. All components in a package run in the same MTS process.
Remember, “Good MTS applications are designed with MTS in mind from the start.” You should design your Package contents with your entire application in mind. Each package runs in its own process, so try to design packages that don’t attempt to do more than they absolutely need to. There are performance advantages to maintaining many components within in a single package, but there may also be security constraints (roles) that dictate a different architecture.
Packages are also the primary means of deployment. The MTS environment allows one to export the contents of a Package to a nice distributable setup (both client and server). We’ll discuss this in the “Deployment” section.
Role
A role is a symbolic name that defines a class of users for a set of components. Each role defines which users are allowed to invoke interfaces on a component. A role is the primary mechanism to enforce security. Role-based security is handled at the component level. It’s possible that this may be at the method level in a future version of MTS. Security cannot be enforced on the Windows 95 version of MTS.
Roles are stored at the package level. Each component in a package can belong to one of more of the defined roles. For example, an Inventory package might contain a Visual FoxPro server whose responsibility is to handle inventory. There are two roles defined in this package: Managers and Clerks. These two roles are simply collections of Windows NT users/groups with a collective name that you provide. Your server is coded so that Clerks can access inventory data for normal order entries and reporting. Managers have additional power in that they can override inventory levels to make adjustments (for example, quarterly product shrinkage estimates).
You can set up security so that it is automatically handled (for instance, users not in roles are given “Access is denied” error message), or you can manage it programmatically through code. The Context object’s IsCallerInRole method is ideal for this.
Resource Dispensers
A resource dispenser manages nondurable shared state on behalf of the application components within a process. Resource dispensers are similar to resource managers, but without the guarantee of durability. MTS provides two resource dispensers:
The ODBC resource dispenser
The Shared Property Manager
Resources are shared within the same process—same process = same package. In the section “Shared Property Manager,” we discuss programmatically accessing shared properties. This is a really cool thing for Visual FoxPro developers because it allows multiple instances of objects to share state information. For example, you could have a counter that tracks the last ID number used by a database.
ODBC resource dispenser
The ODBC resource dispenser manages pools of database connections for MTS components that use the standard ODBC interfaces, allocating connections to objects quickly and efficiently. Connections are automatically enlisted on an object’s transactions and the resource dispenser can automatically reclaim and reuse connections. The ODBC 3.0 Driver Manager is the ODBC resource dispenser; the Driver Manager DLL is installed with MTS.
Shared Property Manager
The Shared Property Manager provides synchronized access to application-defined, process-wide properties (variables). For example, you can use it to maintain a Web page hit counter or to maintain the shared state for a multiuser game.
Resource Managers
A resource manager is a system service that manages durable data. Server applications use resource managers to maintain the durable state of the application, such as the record of inventory on hand, pending orders, and accounts receivable. Resource managers work in cooperation with the Microsoft Distributed Transaction Coordinator (MS DTC) to guarantee atomicity and isolation to an application. MTS supports resource managers, such as Microsoft SQL Server version 6.5, that implement the OLE Transactions protocol.
The MS DTC is a system service that coordinates transactions. Work can be committed as an atomic transaction even if it spans multiple resource managers, potentially on separate computers. MS DTC was first released as part of SQL Server 6.5 and is included in MTS, providing a low-level infrastructure for transactions. MS DTC implements a two-phase commit protocol to ensure that the transaction outcome (either commit or abort) is consistent across all resource managers involved in a transaction. MS DTC ensures atomicity, regardless of failures.
You might be asking if Visual FoxPro is a resource manager, because it has its own native database. Unfortunately, the answer is no. Visual FoxPro transactions are native to Visual FoxPro and do not go through the MS DTC. Therefore, automatic transaction support within MTS is not supported for Visual FoxPro data. You cannot use the Context object’s SetAbort method to abort a transaction if the data is stored in Visual FoxPro databases/tables. The database must either support OLE Transactions (SQL Server) or be XA-compliant (Oracle).
Base Clients
A base client is simply a client that runs outside of the MTS run-time environment, but instantiates MTS objects. In a three-tier architecture, a base client is typically the presentation layer, such as an application form or Web page. The base client neither knows nor needs to know that MTS is used in the middle tier. It merely creates an instance of an object that exists in an MTS package and awaits a response. The following table describes some of the differences between a base client and an MTS component, such as a Visual FoxPro DLL server.Expand table
Base client
MTS component
Can be EXEs, DLLs.
Must be in-process DLL.
MTS does not manage its process.
Manages server processes that host MTS component.s
MTS does not create or manage threads used by application.
Creates and manages threads.
Does not have implicit Context object.
Each MTS object has own Context object.
Cannot use Resource Dispensers.
Can use Resource Dispensers.
Just-In-Time Activation
Just-in-Time (JIT) activation is the ability to activate an MTS object only as needed for executing requests from a client. Most Visual FoxPro developers are familiar with object instantiation, as in the following code:
A “stateful” object created by this code retains state during the lifetime of the object (until it is released). This means that property values (such as myProperty) are retained between statement execution. When the object is finally released, all object references and state are released.
There is overhead with creating objects from your Visual FoxPro components. Each time you instantiate an object, Visual FoxPro needs to allocate a certain amount of memory. In addition, the first time you create an object, Visual FoxPro takes a little extra time to load its run-time libraries. When the last instance is released, the entire Visual FoxPro run time is also released.
JIT activation addresses many of these memory issues that affect performance. The first thing JIT does is cache the server’s run-time libraries in memory, even though no outstanding object references exist. The first time you instantiate a Visual FoxPro server that’s in an MTS package, the Visual FoxPro run time loads the address space of the MTS process. When you release the object, MTS still keeps the libraries in memory for a specified amount of time. You can change this setting in the package’s property sheet (default = 3 minutes). This saves having to reload the run time when the object count hits 0.
The main thing that JIT activation offers is ability to transform your object from “stateful” to “stateless” mode. In the preceding example, you can interpret a “stateless” object as one having the initial default settings. So, in the example, the value of myProperty would be reset to its original setting. A stateless object is managed by MTS and is very lightweight, so it consumes much less memory. The only thing keeping the stateless object alive is the object reference held onto by the client. Internally, MTS recycles threads consumed by stateful objects when they go stateless. When a method is invoked on that object, it then becomes stateful again on a thread that could be different from the one originally created on.
Putting your objects into a stateless mode is handled easily by the Context object. The following code illustrates putting an object in a stateless mode:
This code is actually called from within a method of your Visual FoxPro server. You can see if your object is stateless by viewing the status of your component in the MTS Explorer. A stateless object appears in the Objects column, but not in the Activated or In Call columns.
Use the SetComplete method to put the object in a stateless mode. Use SetComplete for committing transactions (as we discuss in the next section, “Transactions”). You can also use SetAbort to make an object stateless.
Again, when you change an object to stateless, all property settings revert to their original defaults. When you invoke a method (or property set/get) on this stateless object, the object is activated (goes stateful) and the object’s INIT event is fired. When you call SetComplete, the object DESTROY event is fired.
NoteAny state that exists on the object is lost when the object is deactivated (SetComplete). If you need to save state, you should either persist information to a database or use the MTS Shared Property Manager.
Because your object’s INIT is called whenever your object goes from Stateless to Stateful, you should try to minimize the amount of code in this event.
Here is a simple scenario showing interaction between client and MTS server.
Visual FoxPro server code:
DEFINE CLASS mts2 AS Custom OLEPUBLIC
MyColor = "Green"
PROCEDURE InUsa (tcCustID)
LOCAL llInUSA,oMTX,oContext
oMtx = CreateObject("MTXAS.APPSERVER.1")
oContext = oMtx.GetObjectContext()
llInUSA = .F.
USE CUSTOMER AGAIN SHARED
LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
IF FOUND()
llInUSA = (ATC("USA",country)#0)
ENDIF
oContext.SetComplete()
RETURN llInUSA
ENDPROC
ENDDEFINE
Base client executes following code:
LOCAL oCust,cCust,lUsa
oCust = CreateObject("vfp_mts.mts2")
? oCust.MyColor
Green
oCust.MyColor = "Red"
? oCust.MyColor
Red
cCust = "JONES"
lUsa = oCust.InUsa(cCust) && object goes stateless (deactivated)
? oCust.MyColor && object is activated (stateful)
Green
RELEASE oCust && object is fully released
Notice in the preceding example how the state of oCust is lost after the InUsa method is called. The MyColor property no longer returns Red, but is instead reset to its original value of Green.
Transactions
If you have used Visual FoxPro at all, you are probably aware that Visual FoxPro supports transactions. Changes to your data can be committed or rolled back. Though transactions are critical to MTS, don’t be misled by the name; there is a lot more to it than just transactions. However, the ability to have MTS automatically handle transactions between distributed objects is quite powerful. Transactions are often discussed in terms of the ACID acronym:
Atomicity—ensures that either the entire transaction commits or nothing commits.
Consistency—a transaction is a correct transformation of the system state.
Isolation—protects concurrent transactions from seeing each other’s partial and uncommitted results.
Durability—committed updates to managed resources can survive failures.
As just mentioned, MTS transaction support is not compatible with Visual FoxPro data. It only works with databases supporting OLE transaction or XA protocols. Both SQL Server and Oracle data can be used with MTS in transactional fashion.
You should understand what we mean by a transaction and to what extent things are either committed or rolled back. Consider the following scenario (all done within confines of two components in a single MTS package):
Component A adds a new customer record to the Customer table in SQL Server.
Component A writes out new record to a Visual FoxPro database (audit log).
Component A sends e-mail notification of new customer to some manager.
Component A calls Component B.
Component B edits the Orders table with a new order in SQL Server.
Component B writes out text log file of activity.
Component B completes activity by committing the transaction (SetComplete).
Component A discovers bad credit history with customer and aborts transaction (SetAbort).
When Component B commits in step 7, not a whole lot happens because MTS manages the entire Context within the package in a distributed fashion. Component B actually inherits transaction state from Component A, so it cannot really fully commit the transaction. The real transaction terminates in step 8 when the last object with transaction state aborts. At this point, changes made to both Customer and Orders tables are rolled back because these tables are in SQL Server. Unfortunately, the Visual FoxPro table update, e-mail notification, and text log file activities are not rolled back. When a transaction is aborted/committed, only data managed through the MS DTC is affected. There is no event that is magically triggered. (Check out the MTS SDK for ideas on using Spy).
Remember, good MTS apps are written with MTS in mind from the start. Managing transactions is very important, and while much of it is handled automatically, you will need to provide a fair amount of code to effectively manage all the resources being utilized in a transaction setting.
Transaction support is set at the component level, but transactions can span multiple packages. You can set this option in the MTS Explorer from the component’s Property Sheet (see MTS Help for details on the various options). Again, the object’s Context manages and passes on transaction state for a given component. If the transaction setting of a component is marked as “Requires a transaction,” a transaction is always associated with the component. If another object that calls this component already has a transaction in effect, no new transaction is created. The component merely inherits the current one. A new transaction is only created if one does not already exist in the context.
Figure 3. Setting Transaction support
Let’s return a minute to the SetComplete and SetAbort methods. These methods actually serve two purposes. From their names, they imply functionality related to transactions. However, as already discussed, they also serve to deactivate objects (make them stateless). In fact, these methods can be used simply for JIT activation without any concern for transactional support. Again, SetComplete releases valuable resources/memory used by MTS to allow for improved scalability. The Context object also includes several other methods useful for transactions: EnableCommit, DisableCommit, and IsInTransaction. The following example shows how to handle transactions in Visual FoxPro:
LPARAMETER tcCustID
LOCAL lFound,oMTX,oContext
oMtx = CreateObject("MTXAS.APPSERVER.1")
oContext = oMtx.GetObjectContext()
USE CUSTOMER AGAIN SHARED
LOCATE FOR UPPER(cust_id) == UPPER(tcCustID)
lFound = FOUND()
IF FOUND()
oContext.SetComplete()
ELSE
oContext.SetAbort()
ENDIF
RETURN lFound
In this scenario, we assume that another component already performed an update on another table (for example, Orders). If the customer ID in the preceding code was not found, the entire transaction would be rolled back.
You’re probably wondering how transactions work in the code, which clearly appears to be against Visual FoxPro data. Actually, this example is using Remote Views against SQL Server data. Again, Visual FoxPro tables do not support OLE transactions, so you will not get MTS transaction support if you use DBF tables. However, data updates either to Remote Views or by SQL pass-through work just fine.
**Tip **Make sure that your connection to a remote data source is made without any login dialog box. If you are using a connection stored in a DBC, ensure that the Display ODBC logins prompt is set to Never. For access to remote data through SQL pass-through commands, you can use the SQLSetProp function:
SQLSETPROP(0, 'DispLogin', 3)
Programming Models
MTS supports two programming models. The TransactionContext model is intended primarily for backward compatibility. It essentially lets the base client control the transaction. The assumption is that the COM component has no MTS awareness (that is, the component was written before MTS was available). The second model is called the ObjectContext model and assumes the COM component inside the MTS package has MTS smarts and is aware of its Context object.
TransactionContext
We do not recommend using this model for new three-tier applications, because it has limited access to the full capabilities of MTS. It merely offers a way to provide some transaction support to applications whose middle-tier components were developed without MTS in mind. The burden of transaction handling rests on the base client. With this model, the base client is likely to be a smart client that has scripting capabilities (for example, an application form). The base client is less likely to be a Web page, and it always runs outside of the MTS run-time environment.
The following code snippet in a Visual FoxPro form (base client) shows this model in use. The middle-tier component is a Visual FoxPro server whose ProgID is “vfp_mts.mts1”. The assumption here is that this server knows nothing about MTS, thus requiring the base client to perform all transaction handling:
The code in the middle tier simply does a lookup in a SQL Server table for a customer’s home country. If the record was actually changed, the base client would have the capability to actually commit or roll back the transaction. The TransactionContext object only supports three methods: CreateInstance, Commit, and Abort.
ObjectContext
The ObjectContext model is the only model you should consider for new MTS application development. It relies on component awareness of MTS, but this should be your goal so that you can optimize performance and take advantage of MTS-specific features.
Unlike the TransactionContext object, which uses the following PROGID:
#DEFINE TRANS_CLASS "TxCtx.TransactionContext"
the ObjectContext object can be accessed using the following code:
#DEFINE MTX_CLASS "Mtxas.AppServer.1"
The ObjectContext object, which can be referenced in your Visual FoxPro code, as shown here:
LOCAL oMTX,oContext
oMtx = CreateObject("MTXAS.APPSERVER.1")
oContext = oMtx.GetObjectContext()
contains the following properties, events, and methods (PEMs).Expand table
PEM
Description
Count
Returns the number of Context object properties.
CreateInstance
Instantiates another MTS object.
DisableCommit
Declares that the object hasn’t finished its work and that its transactional updates are in an inconsistent state. The object retains its state across method calls, and any attempts to commit the transaction before the object calls EnableCommit or SetComplete will result in the transaction being aborted.
EnableCommit
Declares that the object’s work isn’t necessarily finished, but its transactional updates are in a consistent state. This method allows the transaction to be committed, but the object retains its state across method calls until it calls SetComplete or SetAbort, or until the transaction is completed.
IsCallerInRole
Indicates whether the object’s direct caller is in a specified role (either directly or as part of a group).
IsInTransaction
Indicates whether the object is executing within a transaction.
IsSecurityEnabled
Indicates whether security is enabled. MTS security is enabled unless the object is running in the client’s process.
Item
Returns a Context object property.
Security
Returns a reference to an object’s SecurityProperty object.
SetAbort
Declares that the object has completed its work and can be deactivated on returning from the currently executing method, but that its transactional updates are in an inconsistent state or that an unrecoverable error occurred. This means that the transaction in which the object was executing must be aborted. If any object executing within a transaction returns to its client after calling SetAbort, the entire transaction is doomed to abort.
SetComplete
Declares that the object has completed its work and can be deactivated on returning from the currently executing method. For objects that are executing within the scope of a transaction, it also indicates that the object’s transactional updates can be committed. When an object that is the root of a transaction calls SetComplete, MTS attempts to commit the transaction on return from the current method.
Deployment
Microsoft Transaction Server offers excellent tools for deploying both client- and server-side setups. Setups are made at the package level, so you should include all components for your application in a particular package. The deployment package contains all the distributed COM (DCOM) configuration settings you need, so you don’t have to fuss with the messy DCOM Configuration dialog box.
To create a setup
Click the package that you want to create setup.
Select Export… from the Action menu. The Export dialog box is displayed.
Figure 4. Exporting a package
**Important **The directions in the Export dialog box are not very clear. You should not simply type in a path as specified. If you do, the Export routine creates a file with a .pak extension in the folder location you specify. Instead, you should always type a full path and file name for the .pak file, as shown in Figure 4.
You can also use the scriptable administration objects to automate deployment and distribution of your MTS packages. See the section “Remote Deployment and Administration” to follow for more details.
The output of the Export operation consists of two setups:
Server Setup
This setup, which is placed in the folder specified in the Export dialog box, contains the .pak file and all COM DLL servers used by the package.
NoteWith Visual FoxPro servers, you will also have .tlb (type library) files included. You can install this package by selecting Install from the Package Wizard in MTS Explorer.
Figure 5. Installing package from the Package Wizard
Client Setup
The Export process creates a separate subfolder named “clients” in the folder specified in the Export Package dialog box. The Clients folder contains a single .exe file that a user can double-click to run.
The Client setup merely installs necessary files and registry keys needed by a client to access (remotely through DCOM) your MTS package and its COM servers.
Remote Deployment and Administration
The MTS Explorer allows you to manage remote components (those installed on a remote machine). The Remote Components folder contains the components that are registered locally on your local computer to run remotely on another computer. Using the Remote Components folder requires that you have MTS installed on the client machines that you want to configure. If you want to configure remote computers manually using the Explorer, add the components that will be accessed by remote computers to the Remote Components folder.
Pushing and Pulling
If both the server and client computer are running MTS, you can distribute a package by “pulling” and “pushing” components between one or more computers. You can “push” components by creating remote component entries on remote computers and “pull” components by adding component entries to your local computer. Once you create the remote component entries, you must add those component entries to your Remote Components folder on your local machine (pull the components).
Before you deploy and administer packages, set your MTS server up by doing the following:
Configure roles and package identity on the system package.
Set up computers to administer.
You must map the System Package Administrator role to the appropriate user in order to safely deploy and manage MTS packages. When MTS is installed, the system package does not have any users mapped to the administrator role. Therefore, security on the system package is disabled, and any user can use the MTS Explorer to modify package configuration on that computer. If you map users to system package roles, MTS will check roles when a user attempts to modify packages in the MTS Explorer.
Roles
By default, the system package has an Administrator role and a Reader role. Users mapped to the Administrator role of the system package can use any MTS Explorer function. Users that are mapped to the Reader role can view all objects in the MTS Explorer hierarchy but cannot install, create, change, or delete any objects, shut down server processes, or export packages. If you map your Windows NT domain user name to the System Package Administrator role, you will be able to add, modify, or delete any package in the MTS Explorer. If MTS is installed on a server whose role is a primary or backup domain controller, a user must be a domain administrator in order to manage packages in the MTS Explorer.
You can also set up new roles for the system package. For example, you can configure a Developer role that allows users to install and run packages, but not delete or export them. The Windows NT user accounts or groups that you map to that role will be able to test installation of packages on that computer without having full administrative privileges over the computer.
In order to work with a remote computer, you first need to add it to the Computers folder in the MTS Explorer:
Click the Computers folder.
Select New -> Computer from the Action menu.
Enter name of the remote computer.
ImportantYou must be mapped to the Administrator role on the remote computer in order to access it from your machine. In addition, you cannot remotely administer MTS on a Windows 95 computer from MTS on a Windows NT server.
You should now see both My Computer and the new remote computer under the Computers folder. At this point, you can push and pull components between the two machines. Think of the Remote Components folder as its own special package. You are merely adding to it components that exist in one or more packages of remote machines.
The following example pulls a component from a remote machine to My Computer.
Click the Remote Components folder of My Computer.
Select New-> Remote Component from the Action menu to display the dialog box shown here.
Figure 6. Adding a component to Remote Components
In this example, we select (and add) a component called test6.foobar2 from a package called aa on the remote machine calvinh5. This package also has another component (Visual FoxPro OLEPUBLIC class) named test6.foobar, which we do not select. When we click OK, a copy of the DLL and the type library are copied to the local machine (My Computer) and stored in a subfolder of your MTS root location (in this case, c: C:Program FilesMtsRemoteaa). In addition, the server is now registered on your machine. Note that while the DLL is copied to your machine, the .dll registered in your registry points to the remote machine.
If you encounter problems after you click OK, you may not have proper access rights to copy the server components. Ensure that the remote machine is configured with proper access privileges for you. At this point, you can go into Visual FoxPro running on the local machine and access the server:
You use MTS Explorer to view the activated object in the remote machine folder under the package it is registered in. You will not see the object activity in the Remote Components folder. See the “Working with Remote MTS Computers” topic in the MTS Help file for more details.
Security
Security in MTS is handled by roles. Roles are established at the package level. Components within that package can set up role memberships. The following MTS Explorer image shows a package called Devcon1, which contains three roles. Only the last two components contain Role Memberships.
Figure 7. Package with roles
If you navigate the Roles folder, you can see all Windows NT users or groups assigned to that particular role.
To create a new role
Click the Roles folder.
Select New-> Role from the Action menu.
Enter a new role name in the dialog box.
You can add new users/groups to a particular role as follows:
To add new users or groups
Click the Users folder of the newly added role.
Select New-> User from the Action menu.
Select users/groups from the dialog box.
MTS handles its security several different ways. The MTS security model consists of declarative security and programmatic security. Developers can build both declarative and programmatic security into their components prior to deploying them on a Windows NT security domain.
You can administer package security using MTS Explorer. This form of declarative security, which does not require any component programming, is based on standard Windows NT security. This can be done by Package- or Component-level security.
Declarative Security
You can manage Declarative security at the package and at the component level through settings available in the Security tab of the Package Properties dialog box.
Package-level security
Each package has its own security access authorization, which can be set in the Package Properties dialog box.
Figure 8. Package properties
By default, the Security check box is not marked, so you need to check this box to enable security. If you do not enable security for the package, MTS will not check roles for the component. If security is enabled, you must also enable security at the component level in order to have roles checked.
Component-level security
Each installed component can also have its own security setting. You set security for a component through the same Enable authorization checking check box on the Property dialog box in MTS Explorer. If you are enabling security at both levels and you do have defined roles, you must include one of the roles in the component’s Role Membership folder. If you do not include a role in the folder, you will get an “Access is denied” error message when you try to access a property or method of the component. Of course, if you do not have any roles, you will get the same error.
Note You can still do aCreateObjecton the component, but that is all.
oContext = CreateObject("vfp_mts.mts1")
oContext.Hello() && will generate an Access is denied error
To restrict access to a specific component within a package, you must understand how components in the package call one another. If a component is directly called by a base client, MTS checks roles for the component. If one component calls another component in the same package, MTS does not check roles because components within the same package are assumed to “trust” one another.
When you change the security settings for a particular package or component, you need to shut down server processes before changes can take place. This option is available from the Action menu when Package is selected.
Programmatic Security
You can put code in your program to check for specific security access rights. The following three properties and methods from the Context object return information regarding security for that package or component.Expand table
Methods
Description
IsCallerInRole
Indicates whether the object’s direct caller is in a specified role (either directly or as part of a group).
IsSecurityEnabled
Indicates whether security is enabled. MTS security is enabled unless the object is running in the client’s process.
Security
Returns a reference to an object’s SecurityProperty object.
The following method checks whether the called object is in a particular role. The IsCallerInRole method is useful when the roles are defined, but if your code is generic and doesn’t know the particular roles associated with a component, you must handle this through your error routine.
PROCEDURE GetRole (tcRole)
LOCAL oMTX,oContext,lSecurity,cRole,lHasRole
IF EMPTY(tcRole)
RETURN "No Role"
ENDIF
oMtx = CREATEOBJECT(MTX_CLASS)
oContext = oMtx.GetObjectContext()
IF oContext.IsSecurityEnabled
THIS.SkipError=.T.
lHasRole = oContext.IsCallerInRole(tcRole)
THIS.SkipError=.F.
DO CASE
CASE THIS.HadError
THIS.HadError = .F.
cRole="Bad Role"
CASE lHasRole
cRole="Yep"
OTHERWISE
cRole="Nope"
ENDCASE
ELSE
cRole="No Security"
ENDIF
oContext.SetComplete()
RETURN cRole
ENDPROC
Advanced users can access the SecurityProperty object to obtain more details on the user for handling security. The Security object offers the following additional methods.Expand table
Method
Description
GetDirectCallerName
Retrieves the user name associated with the external process that called the currently executing method.
GetDirectCreatorName
Retrieves the user name associated with the external process that directly created the current object.
GetOriginalCallerName
Retrieves the user name associated with the base process that initiated the call sequence from which the current method was called.
GetOriginalCreatorName
Retrieves the user name associated with the base process that initiated the activity in which the current object is executing.
What type of security should you use? Programmatic security offers more power in terms of structuring specific functionality for particular roles. You can use Case statements, as in the previous example, which perform different tasks, depending on the role. Declarative security, on the other hand, can only control access at the component level (not method or lower).
Changes to Programmatic security, however, require a new build of the component, which may not always be convenient or realistic. Controlling Component-level security for users and roles by using MTS Explorer to turn security on or off gives an administrator greater control. The optimal solution is one with utilizes both declarative and programmatic securities in the most efficient manner.
Shared Property Manager
The Shared Property Manager (SPM) MTS resource dispenser allows you to create and share properties across components. Because it is a resource dispenser, all other components in the same package can share information, but information cannot be shared across different packages. For example, if you want to keep a counter to use for generating unique IDs for objects in a package, you could create a Counter property to hold the latest unique ID value. This property would be preserved while the package was active (regardless of object state).
The SPM also represents an excellent way for an object to preserve its state before being deactivated in a stateless mode (SetComplete). Just-In-Time activation does not affect or reset the state of SPM.
The following example shows how to use the SPM with Visual FoxPro servers:
#DEFINE MTX_CLASS "MTXAS.APPSERVER.1"
#DEFINE MTX_SHAREDPROPGRPMGR "MTxSpm.SharedPropertyGroupManager.1"
PROCEDURE GetCount (lReset)
LOCAL oCount
LOCAL oMTX,oContext
LOCAL nIsolationMode,nReleaseMode,lExists
oMtx = CREATEOBJECT(MTX_CLASS)
oContext = oMtx.GetObjectContext()
oSGM = oContext.CreateInstance(MTX_SHAREDPROPGRPMGR)
nIsolationMode = 0
nReleaseMode = 1
* Get group reference in which property is contained
oSG = oSGM.CreatePropertyGroup("CounterGroup", nIsolationMode,;
nReleaseMode, @lExists)
* Get object reference to shared property
oCount = oSG.CreateProperty("nCount", @lExists)
* check if property already exists otherwise reset
IF lReset OR !lExists
oCount.Value = 1
ELSE
oCount.Value = oCount.Value + 1
ENDIF
RETURN oCount.Value
ENDPROC
The following settings are available for Isolation and Release modes.
Isolation mode
LockSetGet 0 (default)—Locks a property during a Value call, assuring that every get or set operation on a shared property is atomic. This ensures that two clients can’t read or write to the same property at the same time, but doesn’t prevent other clients from concurrently accessing other properties in the same group.
LockMethod 1—Locks all of the properties in the shared property group for exclusive use by the caller as long as the caller’s current method is executing. This is the appropriate mode to use when there are interdependencies among properties or in cases where a client may have to update a property immediately after reading it before it can be accessed again.
Release mode
Standard 0 (default)—When all clients have released their references on the property group, the property group is automatically destroyed.
Process 1—The property group isn’t destroyed until the process in which it was created has terminated. You must still release all SharedPropertyGroup objects by setting them to Nothing.
MTS Support for Internet Information Server
MTS includes several special system packages for use with Microsoft Internet Information Server (IIS). The Windows NT Options Pack 4.0 integrates MTS and IIS more closely. In the future, you can expect even better integration to play a more central role in your Web applications.
IIS Support
Transactional Active Server Pages—You can now run Scripts in Active Server Pages (ASP) within an MTS-managed transaction. This extends the benefits of MTS transaction protection to the entire Web application.
Crash Protection for IIS Applications—IIS Web applications can now run within their own MTS package, providing process isolation and crash protection for Web applications.
Transactional Events—You can embed commands in scripts on ASP pages, enabling you to customize Web application response based on transaction results.
Object Context for IIS Built-In Objects—The MTS object context mechanism, which masks the complexity of tracking user state information from the application developer, now tracks state information managed by IIS built-in objects. This extends the simplicity of the MTS programming model to Web developers.
Common Installation and Management—MTS and IIS now share common installation and a common management console, lowering the complexity of deploying and managing business applications on the Web.
IIS System Packages
If you use MTS with Internet Information Server version 4.0, the Packages Installed folder contains the following IIS-specific system packages.
IIS in-process applications
The IIS In-Process Applications folder contains the components for each Internet Information Server application running in the IIS process. An IIS application can run in the IIS process or in a separate application process. If an IIS application is running in the IIS process, the IIS application will appear as a component in the IIS In-Process Applications folder. If the IIS application is running in an individual application process, the IIS application will appear as a separate package in the MTS Explorer hierarchy.
IIS utilities
The IIS Utilities Folder contains the ObjectContext component required to enable transactions in ASP pages. For more information about transactional ASP pages, refer to the Internet Information Server documentation.
Automating MTS Administration
Microsoft Transaction Server contains Automation objects that you can use to program administrative and deployment procedures, including:
Installing a prebuilt package.
Creating a new package and installing components.
Enumerating through installed packages to update properties.
Enumerating through installed packages to delete a package.
Enumerating through installed components to delete a component.
Accessing related collection names.
Accessing property information.
Configuring a role.
Exporting a package.
Configuring a client to use Remote Components.
You can use the following Admin objects in your Visual FoxPro code.Expand table
Object
Description
Catalog
The Catalog object enables you to connect to MTS Catalog and Access collections.
CatalogObject
The CatalogObject object allows you to get and set object properties.
CatalogCollection
Use the CatalogCollection object to enumerate, add, delete, and modify Catalog objects and to access related collections.
PackageUtil
The PackageUtil object enables installing and exporting a package. Instantiate this object by calling GetUtilInterface on a Packages collection.
ComponentUtil
Call the ComponentUtil object to install a component in a specific collection and import components registered as in-process servers. Create this object by calling GetUtilInterface on a ComponentsInPackage collection.
RemoteComponentUtil
Using the RemoteComponentUtil object, you can program your application to pull remote components from a package on a remote server. Instantiate this object by calling GetUtilInterface on a RemoteComponents collection.
RoleAssociationUtil
Call methods on the RoleAssociationUtil object to associate roles with a component or interface. Create this object by calling the GetUtilInterface method on a RolesForPackageComponent or RolesForPackageComponentInterface collection.
In addition, the following collections are also supported.Expand table
Collection
LocalComputer
ComputerList
Packages
ComponentsInPackage
RemoteComponents
InterfacesForComponent
InterfacesForRemoteComponent
RolesForPackageComponent
RolesForPackageComponentInterface
MethodsForInterface
RolesInPackage
UsersInRole
ErrorInfo
PropertyInfo
RelatedCollectionInfo
If you want to get a reference to a particular collection, use the GetCollection method. The following example shows, first, getting the collection of packages and, second, getting a collection of all components in the first package:
NoteThe GetCollection method merely returns an object reference to an empty collection. You need to explicitly call the Populate method to fill the collection.
Collections are case sensitive, as in the following example code:
Visual FoxPro 6.0 is ideally suited for using MTS Automation because of the new Project Manager and Application Builder hooks support.
Using Visual FoxPro 6.0 Project Hooks
The MTS samples posted along with this document contain a special Project Hook class designed specially for MTS. This class automatically shuts down and refreshes MTS registered servers contained in that project. One of the issues that developers must consider when coding and testing servers under MTS is repeatedly opening the MTS Explorer to manually shut down processes so that servers can be rebuilt and overwritten. Using a Project Hook nicely automates this process. Here is sample code from the BeforeBuild event, which iterates through the Packages collection shutting-down processes.
* BeforeBuild event
LPARAMETERS cOutputName, nBuildAction, lRebuildAll, lShowErrors, lBuildNewGuids
#DEFINE MTS_CATALOG "MTSAdmin.Catalog.1"
#DEFINE MSG_MTSCHECK_LOC "Shutting down MTS servers...."
LOCAL oCatalog,oPackages,oUtil,i,j,oComps
LOCAL oProject,lnServers,laProgIds,lcSaveExact
THIS.lBuildNewGuids = lBuildNewGuids
oProject = _VFP.ActiveProject
lnServers = oProject.servers.count
DIMENSION THIS.aServerInfo[1]
STORE "" TO THIS.aServerInfo
IF lnServers = 0 OR nBuildAction # 4
RETURN
ENDIF
WAIT WINDOW MSG_MTSCHECK_LOC NOWAIT
DIMENSION laProgIds[lnServers,3]
FOR i = 1 TO lnServers
laProgIds[m.i,1] = oProject.servers[m.i].progID
laProgIds[m.i,2] = oProject.servers[m.i].CLSID
laProgIds[m.i,3] = THIS.GetLocalServer(laProgIds[m.i,2])
ENDFOR
ACOPY(laProgIds,THIS.aServerInfo)
* Shutdown servers
oCatalog = CreateObject(MTS_CATALOG)
oPackages = oCatalog.GetCollection("Packages")
oUtil = oPackages.GetUtilInterface
oPackages.Populate()
lcSaveExact = SET("EXACT")
SET EXACT ON
FOR i = 0 TO oPackages.Count - 1
oComps = oPackages.GetCollection("ComponentsInPackage",;
oPackages.Item(m.i).Key)
oComps.Populate()
FOR j = 0 TO oComps.Count-1
IF ASCAN(laProgIds,oComps.Item(m.j).Value("ProgID")) # 0
oUtil.ShutdownPackage(oPackages.Item(m.i).Value("ID"))
EXIT
ENDIF
ENDFOR
ENDFOR
WAIT CLEAR
SET EXACT &lcSaveExact
* User is building new GUIDs, so packages
* need to be reinstalled manually
IF lBuildNewGuids
RETURN
ENDIF
This is only one of the many possibilities provided by a Visual FoxPro Project Hook. The MTS Admin objects can save a great deal of time you normally would spend manually setting options in the MTS Explorer.
Using Visual FoxPro 6.0 Application Builders
As with the Project Hooks, you might also want to create an Application (Project) Builder that handles registration of Visual FoxPro Servers in MTS packages. The Visual FoxPro MTS samples include such a builder. (See the Readme file in the mtsvfpsample sample application for more details on setup and usage of these files.)
This Builder simply enumerates through all the servers in your Visual FoxPro project and all the available MTS packages. You can then select (or create) a particular package and registered server to install in that package. Additionally, you can set the Transaction property for each component. The Visual FoxPro code called when the user clicks OK is as follows:
#DEFINE MTS_CATALOG "MTSAdmin.Catalog.1"
#DEFINE ERR_NOACTION_LOC "No action taken."
LOCAL oCatalog,oPackages,oUtil,i,j,oComps,nPoslcPackage
LOCAL lPackageExists,oCompRef
LOCAL oProject,lnServers,laProgIds,lcSaveExact,oPackageRef,lctrans
lcPackage = ALLTRIM(THIS.cboPackages.DisplayValue)
lPackageExists = .f.
SELECT mtssvrs
LOCATE FOR include
IF !FOUND() OR EMPTY(lcPackage)
MESSAGEBOX(ERR_NOACTION_LOC)
RETURN
ENDIF
THIS.Hide
oCatalog = CreateObject(MTS_CATALOG)
oPackages = oCatalog.GetCollection("Packages")
oPackages.Populate()
FOR i = 0 TO oPackages.Count-1
IF UPPER(oPackages.Item(m.i).Name) == UPPER(lcPackage)
oPackageRef = oPackages.Item(m.i)
lPackageExists=.T.
EXIT
ENDIF
ENDFOR
IF !lPackageExists &&creating new package
oPackageRef = oPackages.Add
oPackageRef.Value("Name") = lcPackage
oPackages.SaveChanges
ENDIF
oComps = oPackages.GetCollection("ComponentsInPackage",;
oPackageRef.Key)
oUtil = oComps.GetUtilInterface
SCAN FOR include
oUtil.ImportComponentByName(ALLTRIM(progid))
ENDSCAN
oPackages.SaveChanges()
oComps.Populate()
SCAN FOR include
DO CASE
CASE trans = 1
lctrans = "Supported"
CASE trans = 2
lctrans = "Required"
CASE trans = 3
lctrans = "Requires New"
OTHERWISE
lctrans = "Not Supported"
ENDCASE
FOR j = 0 TO oComps.Count-1
IF oComps.Item(m.j).Value("ProgID")=ALLTRIM(progid)
oCompRef = oComps.Item(m.j)
oCompRef.Value("Transaction") = lctrans
oCompRef.Value("SecurityEnabled") = ;
IIF(THIS.chkSecurity.Value,"Y","N")
ENDIF
ENDFOR
ENDSCAN
oComps.SaveChanges()
oPackages.SaveChanges()
Tips and Tricks
Hopefully, this article offers enough insight into creating Visual FoxPro components that work well with your three-tier MTS applications. Here are a few final tips to consider:
Design your components with MTS in mind from the start.
Components must be in-process DLLs. Do not use Visual FoxPro EXE servers.
When adding Visual FoxPro components, make sure to select both .dll and .tlb files.
In the Project Info dialog box of Visual FoxPro DLL servers, set Instancing to MultiUse.
Don’t be afraid to mix with other components (for example, Visual Basic servers).
You must have DTC running for transaction support.
Call SetComplete regardless of whether you’re using transactions, because it places objects in stateless mode.
Your MTS object has an associated Context object. Do not place this code in the base client.
Connections must have DispLogin set to Never; for SQL pass-through, use SQLSetProp(0).
Minimize the number of PEMs on an object (protect your PEMs).
Because of page locking issues, limit the length of time you leave SQL Server 6.5 transactions uncommitted.
To use security, you must have a valid role associated with the component.
Avoid using CreateInstance on non-MTS components.
Do not pass object references of the Context object outside of the object itself.
Consider using disconnected ADO recordsets to move data between tiers.
You can pass Visual FoxPro data in strings, arrays, or ADO recordsets.
Passing Parameters:
Be careful when passing parameters.
Always use SafeArray when passing object references.
Passing by value:- Fastest and most efficient- Copies the parameters into a buffer- Sends all values at once
Passing by reference:- Sends a reference, but leaves the object back in the client.- Accessing the parameter scampers back to the client machine.
Always read the Late Breaking News! It contains important information such as Security configuration details.
By default, MTS will create a maximum of 100 apartment threads for client work (per package). In Windows NT 4.0 Service Pack 4 (and later), you can tune the MTS activity thread pool. This will not affect the number of objects than can be created. It will simply configure the number that can be simultaneously in call. To tune the MTS activity thread pool:
Open your Windows Registry using RegEdit and go to the package key:HKLM/Software/Microsoft/Transaction Server/Package/{your package GUID}
Add a REG_DWORD named value:ThreadPoolMax
Enter a value for ThreadPoolMax. Valid values are:0 to 0x7FFFFFFF
Summary: Discusses the new Microsoft Visual FoxPro Component Gallery. Covers the open architecture and programmable hooks that the Gallery and the companion Class Browser expose for customization. (17 printed pages)
This white paper discusses the new Microsoft® Visual FoxPro® Component Gallery. Together with the Visual FoxPro Class Browser, the Gallery provides useful ways to accomplish common development tasks. This document also discusses the open architecture and programmable hooks that these tools expose for customization. While this white paper presents the Gallery, an accompanying paper, The Visual FoxPro 6.0 Class Browser, presents the Class Browser, providing details specific to that development tool.
Update
The Visual FoxPro 6.0 Component Gallery and other related components have been updated. You can download this update from https://msdn.microsoft.com/vfoxpro/downloads/updates.asp. To ensure that you are using the latest version, you should download and install the update.
The Visual FoxPro Component Gallery
The Component Gallery is the Class Browser’s companion. Both share the same display surface, and you can toggle between them with a handy toolbar button. The Component Gallery can be used to categorize and display almost anything (not just Visual FoxPro components), and its strength is in grouping the various artifacts of software development.
The Component Gallery is a flexible and programmable shortcut manager and explorer. Since it shows nothing but shortcuts, nothing you do directly in the Component Gallery affects the underlying files. You can create new files through the Gallery, for example, but the element in the Gallery remains a shortcut to the new file. Delete the shortcut and the underlying file is not deleted. (You could extend the Gallery to delete the underlying file when you delete the shortcut, but writing, implementing, and living with this extension is up to you.)
Visual FoxPro 6.0 uses a system memory variable (named GALLERY) to identify the Component Gallery application. In Visual FoxPro 6.0, the default is GALLERY.APP in your HOME( ) directory. By changing the value of GALLERY, you can wrap and substitute the Component Gallery application just as you do many other Visual FoxPro tools. Visual FoxPro thus continues to provide user-definable extensibility. GALLERY.APP is a wrapper for BROWSER.APP. Running GALLERY.APP is the same as running BROWSER.APP and passing the sixth parameter as true (.T.). You can run the Component Gallery programmatically by using the following code:
DO (_GALLERY)
The full syntax respected by the Component Gallery includes the following parameters:
First parameter (cFileName): File Name or file list (catalog(s) separated by commas).
Second parameter (cDefaultItem): Text of Catalog, Folder, or Item to be selected when the Component Gallery is started.
Third parameter (nWindowState): Window state of Component Gallery form when started (0=normal, 1=minimized, or 2=maximized).
Sixth parameter (lGallery): Must be true (.T.) to run in Component Gallery mode. False (.F.) is for Class Browser mode; refer to The Visual FoxPro 6.0 Class Browser.
Seventh parameter (lNoShow): Default false (.F.). If set to true (.T.), the Component Gallery is not activated or shown. You can then use the public memory variable, _oBrowser, to access the Component Gallery object model. (Refer to Visual FoxPro Help for documentation on the Class Browser methods, properties, and events.
Because of its intimate partnership with the Class Browser, the Component Gallery supports all the add-in mechanisms found in the browser. The Component Gallery, however, adds metadata-driven display and behavior of items to its Class Browser functionality.
Let’s define a few terms that will help us understand the Component Gallery. After that, we’ll look at how you can use the Component Gallery, then we’ll examine the metadata that makes it all work.
Component Gallery Terms
Here are some terms that will serve us well in subsequent discussion.
Catalog: The highest-level container in the Component Gallery and its unit of file storage. A catalog is a .DBF or .FPT table whose records define shortcuts to software resources. For example, you could have a catalog named Office Pool.DBF*,* which contained folders and shortcuts to tables, programs, documents, hyperlinks, and anything else needed to manage friendly office wagers. More than likely, you’ll also create project catalogs to organize all the artifacts of your software projects as they are created. Displayed in the Component Gallery, a catalog is a folder with no parent folder.
Folder: Like a subdirectory, a logical package of items. Catalogs can contain zero or more folders. Folders are either static or dynamic. A static folder contains predefined shortcuts to items. A dynamic folder determines its contents each time the Component Gallery is refreshed. A dynamic folder can be defined as a directory such as “C:Projects*.*,” a Visual FoxPro project (.PJX) file, a class library (.VCX) file, a database (.DBC) file, or any URL or file that is valid in the Internet Explorer Web Browser control pane mode.
Item: A shortcut to a particular artifact.
Item Type: A category that defines the behavior of the items in the catalog. The default item types are stored in the home( )+”GalleryVfpGlry.VCX” class library, and are configurable for each catalog. (For each catalog, see the Properties dialog box, available on the Shortcut menu). The root catalog, always named “Catalogs,” contains the default item types that all catalogs inherit.
The Component Gallery Interface
By now you should be comfortable with explorer-type interfaces. If so then the basic features of the Component Gallery work pretty much as you expect.
The Gallery is divided into two panes. The Catalog pane, on the left, lists the hierarchy of currently open catalogs. The Items pane, on the right, shows the items in the current catalog hierarchy. Both panes provide item-sensitive context menus for doing the usual useful things: cut, copy, paste, rename, and so on. You can also invoke item-sensitive property dialog boxes for selections in the left or right panes. Moreover, the entire Component Gallery is enabled for both regular and OLE drag-and-drop operations.
In Web view mode, the Items pane is automatically hidden by an Internet Explorer Web browser control. When the selected folder is a dynamic folder that is a URL, the Component Gallery displays in Web view mode. In the Web view mode, the four display control buttons become Back, Forward, Stop, and Refresh buttons.
As in the Class Browser, you can use the Item icon to drag the currently selected item to the desktop, design surface, or project. Right-clicking Item invokes a GetPict( ) dialog box to change the icon. A nice touch here is that when you select Cancel in the GetPict( ) dialog box, you get an option to reset the icon to the item default. Setting the icon using the Item icon is the same as setting the item picture in the Properties dialog box for that item.
The View control box contains default and user-defined views of the Component Gallery. Views are queries of specific items in and across catalogs. For example, selecting Internet filters the catalogs to display Internet items only.
To create your own custom views, see the Dynamic Views tab in the Component Gallery Options dialog box. If Advanced Editing mode is enabled, you can create custom item-type views.
To create custom item-type views:
Select an item and invoke its properties.
Click the Views tab.
In the EditBox, type a line item such as the following text.My Stuff=Test Files
Press F5 to refresh the Component Gallery. The My Stuff view appears in the Views control box.
Select this view, the Test Files folder appears.
Select this folder and a shortcut for that item appears in the right pane.
This process allows you to create custom item-type views, and any item can be in any view. Refer to the existing item type settings in the shortcut items included in the Visual FoxPro Catalog items.
The Go to Browser button toggles the Component Gallery window to the standard Class Browser. Hint: right-clicking the button displays a long list of the previously opened folders.
The Open button is for opening new catalogs. The Open dialog box is a little unconventional and merits explanation. In that discussion, we’ll take our first look at the Component Gallery internals.
This isn’t your garden-variety Open dialog box. The Catalog box displays the catalogs currently registered on your system. The catalog names are kept in the BROWSER.DBF table, and the detail records for each catalog are stored in the GALLERYVFPGLRY.DBF table. The Add catalog checkbox adds the contents of the catalog to the current view (the default is “replace”). In addition you can use the Browse button to select an existing catalog that is not listed in the catalog dropdown.
When you click Options in the Component Gallery window, a three-tabbed dialog box appears wherein you can set certain Component Gallery properties.
The Standard tab displays the general defaults for the Component Gallery itself; some of these are self-explanatory. Note, however, the Advanced editing enabled checkbox, which enables you to access advanced features of Component Gallery options and property dialogs.
Use the Catalogs tab to maintain the catalogs that appear in the Catalog box in the Open dialog box. Click New to load a new catalog in the Catalogs pane. A Global catalog is visible in the Catalogs pane regardless of which catalog is selected for display. I’ve made my Favorites catalog a global catalog so I always have access to my favorites. A Default catalog opens whenever you invoke the Component Gallery. Note that when you invoke the Component Gallery from the Class Browser, it always comes up empty. The Component Gallery initially appears populated only when the original invocation is performed with the DO (_Gallery) command.
The Dynamic Views tab can be used to create your own custom dynamic views of your catalogs. In the figures that follow, I’ve created a new dynamic view named “Excel Spreadsheets” that displays all items of type “file” that contain “.XLS” in their names.
In the following example, I’ve created a dynamic view of “UseCase” documents by both creating and assigning such keywords as “Actor” and “Extends.” The keywords displayed in this list are stored in a table named Keywords.DBF.
The Component Gallery Find button is a nice surprise—it works just like dynamic views! In effect, when you use Find you are creating a new persistent view like the one defined on the Dynamic Views tab. I’m not so sure I’m crazy about this. After all, cluttering my own dynamic views every time I search through a file might be a bit much. On the other hand, dynamic views are easy enough to purge in the Dynamic Views tab of the Component Gallery Options dialog box.
Understanding Item Types
The behavior of a Gallery item, as in what happens when you click it or drag it, is defined in its item type. The class library, GalleryVfpGlry.VCX, stores the item types supplied by Microsoft, and you can modify, subclass, or simply copy these classes to create your own types. If you develop your own custom item types, it’s probably a good idea to store them in some other .VCX file, such as My_VfpGlry.VCX. This allows you to later update the Component Gallery class libraries without fear of clobbering your work.
Here is the hierarchy of the Component Gallery item types supplied by Microsoft. _item and _folder are abstract root classes that are defined in _Gallery.VCX. All the others are in VfpGlry.VCX.
When creating your own item types, the most flexible prototype is the _fileitem. In fact, _fileitem should serve most of your needs, since it invokes Windows file associations to run or modify the item. Moreover, the _fileitem type can redirect popular file extensions to other file types. We’ll talk more about redirection shortly.
Item types can be tied to particular catalogs. The root catalog, which is always named “Catalog,” serves as the basis for all catalogs. If you select the Item Types tab on the Folder Properties of the root catalog, you’ll see something like this dialog box.
Note the following points about the Item Types tab in this dialog box.
The list of item types matches the item types you see in the New Item shortcut menu. To modify the New Item shortcut menu, simply edit this list.
Each item type can be associated with display text, a class, and a class library. In this case the display text is “ActiveX,” the class is _ActivexItem, and the class library is VfpGlry.VCX.
The lines in the properties edit box specify what’s displayed in the Class Item tab of the Item Properties dialog box. For example, the Properties box of the ActiveX item in the Component Gallery example above show the following:
File name: (and, within braces, “.ocx, .exe, and .dll”), cFileName—Specifies that the Class Item tab of this class of item will provide a label, textbox, and command button. This information is stored in the object cFileName property.
Remote path: (and, within braces, “.ocx, .exe, and .dll”), cRemoteField—Specifies that the Class Item tab of this class of item will provide a label, textbox, and command button. This information is stored in the object cRemoteField property.
ActiveX ProgID: cProgID—Specifies that the Class Item tab of this class of item will provide only a label and testbox for ActiveX ProgID. This information is stored in the object ProgID property.
NoteThe entries within braces create a command button with an ellipsis (…) that will, when clicked, display the Open dialog box that defaults to browse for the listed file extensions.
The other lines of the ActiveX item type are out of view; they include entries for Class, Class library, Source project, and Associated file, and these all work the same as those already described.
NotePlacing an asterisk (*) before the property name marks that property to be displayed as read-only text in the Properties dialog box. For example, the following entry in Class Item displays the textbox as read-only.
*Base class:,cBaseClass
If you create your own item types, you can make them available by clicking Add and specifying the new item type.
Item Redirection
The Properties page of the _FileItem item type is worth a look because, in addition to showing custom properties, it shows an example of item redirection. See the Redirect box in the following dialog box.
This is the full list of redirections:Expand table
APP=_sampleitem
AVI=_videoitem
BMP=_imageitem
DBF=_dataitem
FRX=_reportitem
GIF=_imageitem
H=_programitem.
HTM=_urlitem
HTML=_urlitem
ICO=_imageitem
JPG=_imageitem
LBX=_reportitem
LOG=_programitem
MNX=_menuitem
OCX=_activexitem
PJX=_ProjectItem
PRG=_programitem
RMI=_sounditem
SCX=_formitem
TXT=_programitem
WAV=_sounditem
You can probably guess how redirections work: when an item with any of those file extensions is created, the designated item type is created instead. For example, if you try to add a .PRG file as an item, the Component Gallery uses the PRG redirection to create a _programitem instead of a _fileitem. This is why the fileitem item type is so flexible; it has the ability to properly redirect new items to the correct item type.
The BROWSER.DBF Structure
The Class Browser stores all its metadata in a table named BROWSER.DBF in your HOME( ) directory. The Component Gallery also uses BROWSER.DBF to store its catalog-related information. Here’s a field-by-field description of important elements in BROWSER.DBF that pertain to the Gallery.Expand table
Field
Description
PLATFORM
“WINDOWS” except for records of type “ADDIN” in which the field value is blank.
TYPE
“PREFW” records store browser and gallery preferences. “ADDIN” records store add-in information.
ID
“FORMINFO” records are used by the Class Browser to store form preferences and by the Component Gallery to store information about your catalogs. The only way to tell the difference is that Component Gallery records contain the string “.dbf” in the Name field.”BROWSER” records contain default settings for the Class Browser. See the Properties field for this record to see these default properties.“METHOD” records store Class Browser add-ins that are tied to a particular Class Browser event or method.“MENU” records store Class Browser add-ins that are not tied to a particular Class Browser event or method, and are therefore available on the add-in shortcut menu.
DEFAULT
Logical true (.T.) for the default Component Gallery catalog when the Component Gallery is started with an unspecified first parameter of GALLERY.APP.
GLOBAL
Applies to Component Gallery catalog records. Logical true (.T.) if the catalog is global. By default, new catalogs are not global. To specify a catalog to global, select the Catalogs tab in the Component Gallery Options dialog box.
BACKUP
Specifies, when true (.T.), that the Class Browser or Component Gallery check for duplicate files in the backup subfolder.When a catalog or a VCX is opened by browser/gallery, this field in the associated BROWSER.DBF record is queried. If the backup file doesn’t exist, one is automatically created (including a subfolder named Backup if needed). Then the Backup field is set to false (.F.) You can set this field programmatically to force the browser or gallery to automatically back up that file or table the next time that file is opened, and only the next time.You can set this field via add-in hooks or just at any time with a program that opens and updates browser.dbf.This feature is used internally in one special case. When browser.dbf is first created after VFP is installed, a new browser.dbf, containing the default catalogs (around 5 or so), is created. Because Visual FoxPro does not install the associated backup catalog tables, the Backup field is initially set to true (.T.) so that each catalog is backed up the very first time it is opened. Beyond that special function, its functionality is available to developers for their own purposes.
NAME
Specifies the file name related to this record. For a Class Browser record, the file type could be, among other things, .VCX, .PJX, .SCX, .OCX, .DLL, .EXE, or APP.For Component Gallery records, the file type is .DBF.In the case of Class Browser and Component Gallery add-ins, the name field stores the name of the add-in. This is what will appear in the add-in shortcut menu if the add-in is not tied to an event or method.
DESC
Provides a description of the catalog referred to in the Name field. Used only by the Component Gallery.
METHOD
Stores the name of the method to which a Class Browser or Component Gallery add-in is tied. If the method field content equals “*” then the add-in will run for all methods.
SCRIPT
Internal Gallery use only
PROGRAM
Used by the Class Browser and the Component Gallery to specify the name of the program to run by .PRG-based add-in.
CLASSLIB
Used by the Class Browser and the Component Gallery to specify the name of the class library in the case of a .VCX-based add-in.
CLASSNAME
Specifies the name of the class to run in the case of a .VCX-based add-in. Used by the Class Browser and the Component Gallery.
FILEFILTER
Specifies file masks for which the add-in applies. The FileFilter is specified in the fourth parameter of the Add-in method.
TOP
Specifies the top coordinate for the browser/gallery form.
LEFT
Specifies the left coordinate for the browser/gallery form.
HEIGHT
Specifies the height of the browser/gallery form.
WIDTH
Specifies the width of the browser/gallery form.
HEIGHT1
Specifies the height of the class and member description panes in the Class Browser.
HEIGHT2
Specifies the height of the item description pane in the Component Gallery.
WINDOWSTAT
Specifies the display size of the Component Gallery or Class Browser.0 – Window is zoomed normal 1 – Window is minimized 2 – Window is maximized
DESCBOXES
Specifies, if true (.T.), that the description panels are to be displayed. Used by the Class Browser and the Component Gallery.
AUTOEXPAND
Specifies, if true (.T.), that the hierarchical items are automatically to be displayed expanded in the left-hand side pane. Used by the Class Browser and the Component Gallery.
PUSHPIN
Specifies, if true (.T.), that the display is always on top. Used by the Class Browser and the Component Gallery.
VIEWMODE
Gallery listview mode (1 – 4).
FONTINFO
Specifies the Class Browser and the Component Gallery display font preference.
FORMCOUNT
Number of instances running for file.
UPDATED
The date and time this record was last updated.
COMMENT
Unused.
User1….4
Unused.
The Catalog Table Structure
This is a very brief overview of Component Gallery-specific metadata. The Component Gallery distributes its metadata to several locations.
Like the Class Browser, the Component Gallery keeps some of its metadata on a table named BROWSER.DBF, which is found in your HOME( ) directory. The data therein stores the references to the available catalogs, as well as some of their properties such as whether the catalog is a global (auto-open) or default (in the default view). See the BROWSER.DBF metadata description.
If you delete a Component Gallery catalog record from BROWSER.DBF, it won’t appear in the Component Gallery Open dialog box. The Component Gallery catalog records in BROWSER.DBF contain “.dbf” in the Name field. Since this field is of type memo, you can’t easily identify Component Gallery records in a simple browse of BROWSER.DBF.
The rest of the Component Gallery metadata is stored in VFPGLRY.DBF, which installs in the Visual FoxPro Gallery subdirectory. This table stores catalog item type metadata. It is here that the behavior of the various item types is defined. When you look at the Component Gallery, you are looking at catalogs whose items are defined in the particular catalog tables, but whose behavior emanates from the items defined here.
To illustrate some of the functionality of VFPGLRY.DBF, let’s examine fields in a representative record, the one with ID=”fileitem”.Expand table
Field
Value
Comment
Type
“CLASS”
Metadata class specification. Catalog items can “inherit” from one another. There are thus many different variants of “fileitem” elsewhere in the metadata, and they may override or augment the things defined in this record.The type field can be any value. The values that are reserved and used by the Component Gallery are:“FOLDER”—Folder item (catalog, if parent field is empty).“ITEM”— Item for the right pane, must have a valid parent field setting.“CLASS”—Specifies an item type setting for that specific catalog, beyond the default item types of vfpglry.vcx.“VIEW”—Specifies a custom view used when the catalog is open.“SCRIPT”—Used to specify a special record that contains code in the Script field, and can be called by having a property setting like cDblClick=<MyScript>.“OBJECT”—Used to specify a custom class to be instantiated when the catalog is refreshed. The ItemClass field is used to specify the class name, and the ClassLib field is used to specify the VCX of the class.
ID
“fileitem”
The unique identifier for this type of item.
Text
“File”
The item display text.
Typedesc
“Item”
Specifies the type of element. This is not a folder, but an item.
Desc
The text that appears in the item description pane.
Properties
File name:{},cFileName Parameters:{},cParams
Specification for input fields that appear in the Properties dialog box for items of this type. Values inside the braces are used as the parameter in GetFile( ) dialogs.
Classlib
Vfpglry.vcx
The class library in which the item’s class is stored.
Classname
_fileitem
The default class that embodies this catalog item.
Alternate classes to embody file items of these particular types. Newly created items with these extensions are remapped to the designated item types.
Other records may use different fields and different values, but this representative record is enough to get you started in working with the Component Gallery.
Catalog tables contain records that reference actual catalog items. The main native catalog is named “Visual FoxPro Catalog,” and it is found in VFP_Catalog.DBF. All the Visual FoxPro 6.0 foundation classes, for example, are cataloged there.
The structure of catalog tables is the same as that of VFPGLRY.DBF, so that much of what we’ve already seen also applies here. This is a good opportunity to look at a few other metadata fields and how they work. This example uses the record with ID=”clireg” in Activex_Catalog.Dbf. This item allows you to register a custom Visual FoxPro automation server remotely, using its generated .VBR file.Expand table
Field
Value
Comment
Type
“ITEM”
ID
“clireg”
This item’s ID.
Parent
“actxtools”
The ID of the parent catalog record, which refers to a folder named “Tools.”
Desc
“This tool allows you to register a custom VFP automation server remotely using the generated VBR file.”
The description window text.
Properties
cDblClick=<>
You can override the events (keypress, click, dblclick, and rightclick) by setting the [cEventName] property. If it’s something like cDblClick=DO foo.prg, then it will run that line. If you set cDblClick=<testscript>, then it will run the code in the Script memo field of the record with ID= “testscript”. If you set cDblClick=<>, then it will run the code in the Script memo field of that record.Thus this DblClick runs the code found in the script field.You can use a record with Type=”SCRIPT” to provide, in the catalog table, a reusable Script memo field that can be called when you run the browser object and reference the .RunScript( ) method.
Filename
(HOME(6)+”CLIREGCLIREG32.EXE”)
The name, stored in oTHIS.cFileName, of the file to run. See the Script field below. Note that the whole behavior of this item is defined by the filename field and, in this case, the Script field. The ClassName and ClassLib fields are blank in this record.
Script
cVBRFile = GETFILE(“VBR”) cCliReg = oTHIS.cFIleName IF !FILE(m.cCliReg) RETURN .F. ENDIF IF EMPTY(m.cVBRFile) OR UPPER(JUSTEXT(m.cVBRFile))#”VBR” RETURN .F. ENDIF oTHIS.Runcode([RUN /N &cCliReg. “&cVBRFile.” -NOLOGO])
The Script field provides code for the SCRIPT type item with the ID specified by [cEventName] in the properties field. This Visual FoxPro code will run in a code block upon DblClick.Note that in this version there is no script equivalent of DODEFAULT( ), so if you script an event, the default behavior for this event will not execute.If you need a behavior like DODEFAULT( ), just manually make the direct call in the custom script like oTHIS.DblClick
Steven Black specializes in developing multilingual, multisite, and other challenging software situations, including project turnarounds and cleanups. He is the creator of Steven Black’s INTL Toolkit, a multilingual framework for FoxPro and Visual FoxPro. He’s a regular speaker at Visual FoxPro conferences, and his contributions occasionally darken the pages of FoxPro books and magazines.
Microsoft Visual FoxPro 6.0 and Visual Studio Installer Tutorial
Summary: This article lists the basic steps involved in creating, configuring, and building a Microsoft Windows Installer package (.msi) file with Microsoft Visual Studio Installer. (14 printed pages)Expand table
Click to download the VFP_VSI.exe sample file.
Microsoft® Visual Studio® Installer is a graphical tool that simplifies the creation of application setup programs for distribution to single user or enterprise-wide desktops. Setups created with the Visual Studio Installer provide advanced capabilities such as centralized distribution for maintenance and updates, application self-repair, and powerful installation rollback facilities.
Visual Studio Installer setups are based on the new Microsoft Windows® installer technology. The Windows installer reduces the total cost of ownership (TCO) for customers by enabling them to efficiently install and configure applications. The Windows installer is part of the Windows 2000 and Zero Administration Windows (ZAW) efforts to reduce the overall cost of deploying, using, and managing desktop computers.
This tutorial lists the basic steps involved in creating, configuring, and building a Microsoft Windows Installer package (.msi) file with Microsoft Visual Studio Installer. An .msi file is a storage file containing the instructions and data required to install an application.
This tutorial will show how to author an .msi file to configure the installation of a Visual FoxPro® application. It will also show how to launch the .msi file and install the application.
To author and launch an .msi file with Visual Studio Installer, complete these tasks:
Open Visual Studio Installer and create an installer project as part of a Visual Studio solution.
Add files to the installer project and configure file properties.
If desired, configure the project properties.
If desired, establish how to modify the target machine system registry when your product is installed and configure registry properties.
If desired, establish how the target machine operating system will handle your installed document types, MIME types, COM objects, and type libraries, and configure properties for each of these objects.
If desired, control and customize the installation dialogs presented when your users run the installer package file to install, repair, or uninstall your product.
Add merge modules to the project.
Build the installer package file.
Test the installer package file.
Distribute the application.
Create an Application to Distribute
For the purposes of this demo, the Visual FoxPro Application Wizard was used to create an application called VFPVSIDemo. The application was then built into an EXE, called VFPVSIDemo.exe.
The data used by the application is in a folder named Data. This folder is a subfolder of the main application folder.
Step 1: Open Visual Studio Installer
Click Start, and select Programs.
From the Programs menu, select Microsoft Visual Studio 6.0, and then select Microsoft Visual Studio 6.0 Enterprise Tools.
From the Microsoft Visual Studio 6.0 Enterprise Tools menu, click Visual Studio Installer.
The Microsoft Development Environment launches, and you can create a new installer project from the New tab in the New Project dialog box.
Create an empty installer project by highlighting the Empty Installer icon.
Enter VFPVSIDemo as the name of the project.Make a note of the directory in the Location textbox. This is where the application installer file you create will be located. You can change the location for your project if you like.
Choose Open.
Figure 1. Creating a new Visual Studio Installer project
Visual Studio Installer creates your installer project. The Project Explorer displays your installer project hierarchy. You can expand the Target Machine node to start setting up the configuration of your installed product on the target machine.
Step 2a: Add Application Files to the Installer Project
The File System editor in Visual Studio Installer gives you a way to configure your application files on the target machine while you add them to the installer project.
In the Project Explorer, expand the Target Machine node.
Double-click File System in the Target Machine node.
In the File System editor, right-click Application Folder.
Select Add File(s) from the context menu.
In the Browse for Files dialog box, navigate to the directory that contains the application. Select the files you want to add. In this case, choose the file VFPVSIDemo.exe.
Click Open.The File System editor displays the file(s) you added in the folder you selected. The files are also listed in the installer project Files node in the Project Explorer.Figure 3. Files added to the installer project
In the File System editor, select User’s Start Menu.
In the Name column, right-click and select Create Shortcut.
In the Shortcut Properties dialog, select VFPVSIDemo.exe and choose OK.
Right-click the shortcut and choose Rename. Rename the shortcut VFPVSIDemo.
This places a shortcut to the file VFPVSIDemo.exe on the user’s Start menu.
See the following topics for more detailed information about working with files in an installer project:Expand table
In the previous step, you added the application files to the installer project. In this step, you will add the data files, which reside in a different directory.
In the File System editor, right-click Application Folder.
Select AddFolder from the context menu.
Change the name of the new folder to Data.
In the File System editor, right-click Data.
Select Add File(s) from the context menu.
In the Browse for Files dialog box, navigate to the directory that contains the application. Select the files you want to add. In this case, choose each of the data files.
Click Open.Figure 4. Data files added to the installer project
Step 3: (Optional) Configure Project Properties
Select the VFPVSIDemo project in the Project Explorer window.
At the end of the Project menu, select the VFPVSIDemoProperties option.
The Project Properties dialog box appears. You can view or change the project properties in the Project Properties dialog box.
Step 5: (Optional) Establish Document and MIME Type and COM Object Associations
With the Visual Studio Installer Associations editor, you can specify how the target machine operating system will install and register your document types, MIME types, COM objects, and type libraries.
In the Project Explorer, expand the Target Machine node under your installer project.
Double-click Associations in the Target Machine node.
The Associations editor appears.
See the following topics for more detailed information about working in the Associations editor:Expand table
For information about:
See:
Working with document types, extensions, verbs, MIME types, COM objects, and type libraries
Step 6: (Optional) Customize the Installation Run-Time Dialog Boxes
With the Visual Studio Installer User Interface editor, you can customize the installation run-time display. Specifically, you can specify and customize dialogs that are displayed during the installation process.
Open the solution containing your Visual Studio Installer project.
In the Project Explorer, expand the Target Machine node under your installer project.
Double-click User Interface in the Target Machine node.
The User Interface editor appears.
See the following topics for more detailed information about installation user interface dialogs:Expand table
A merge module (.msm file) is a single package that includes all files, resources, registry entries, and setup logic to install a shared component. Visual FoxPro applications should always include the following merge modules:
VFP6RUN.MSM
MSVCRT.MSM
OLEAUT32.MSM
The files MSVCRT.MSM and OLEAUT32.MSM ship with Visual Studio Installer. You can find these and other merge modules in the directory c:Program FilesMicrosoft Visual StudioCommonToolsVSInstBuildRes.
Note The files contained in MSVCRT.MSM and OLEAUT32.MSM are automatically installed by Windows 2000. Therefore you do not need to add these merge modules to the Installer project if you know the application will only be installed on Windows 2000.
Save the file VFP6RUN.MSM (available from the sample download at the top of this article) to the directory with the other merge modules.
Choose Add Merge Module(s) from the Project menu.
In the Browse for Merge Module dialog highlight the file VFP6RUN.MSM and choose Open.
After you configure all elements of an application’s installation in your installer project, you must build the project into an installer package (.msi) file. You can then distribute the .msi file to users who want to install your application.
In the Project Explorer, select your installer project.
Make sure the Build type project property (on the Build tab of the Project Properties dialog box) is set to either:
With the installer project selected in the Project Explorer, select Build from the Build menu.
You should see the message Solution Update Succeeded in the Status Bar if the project built successfully. If errors occurred, you should see them in the Task List.
For development and debugging purposes, the best way to launch your installer package (.msi) file is from within the Microsoft development environment.
In the Project Explorer window, right-click the VFPVSIDemo project.
Select Launch Installer from the context menu.
Select Next on the opening screen of the VFPVSIDemo Setup Wizard.Figure 5. Opening screen of VFPVSIDemo Setup Wizard
In the Select Installation Folder step, you can choose to install the application in the default directory or change the directory.Figure 6. Select Installation Folder step in VFPVSIDemo Setup Wizard
In the Confirm Installation step, select Next to begin the installation.
When the installation is complete, select Close to exit the VFPVSIDemo Setup Wizard.
Choose VFPVSIDemo from the Start menu to launch the application.
For more information about these Windows installer requirements and launching an installer package file, see Launching an Installer Package File.
Note If you set the Build Type as Installer with Windows Installer Loader in the previous step, you should run SETUP.EXE file to test your setup.
Run the application to confirm the installation succeeded. If you accepted the defaults, the application is installed in the directory C:Program FilesVFPVSIDemo and the data is installed in C:Program FilesVFPVSIDemoData.
Step 10: Distribute the Application
Your application is now ready for distribution. The file VFPVSIDEMO.MSI contains the application and the files in the VFP6RUN.MSM merge module.
Locate the VFPVSIDemo.msi file. If you accepted the default Location when you created the project, it will be in a directory such as Visual Studio ProjectsVFPVSIDemoOutputDISK_1.
To launch the installer, double-click the file VFPVSIDemo.msi.
Open the VFPVSIDemo Setup Wizard. Choose RepairVFPVSIDemo to reinstall the application. Choose RemoveVFPVSIDemo to uninstall the application. Then choose Finish.Figure 7. Repair or Remove in VFPVSIDemo Setup Wizard
Using Microsoft Visual Studio Installer for Distributing Visual FoxPro 6.0 Applications
Summary: This article is a supplement to assist Microsoft Visual FoxPro 6.0 developers in using the Visual Studio Installer as an alternative to the Visual FoxPro 6.0 Setup Wizard. (13 printed pages)Expand table
Click to download the VFP_VSI.exe sample file.
Introduction
The Microsoft® Visual Studio® Installer is a great new tool you can use to create customized setups for your Visual FoxPro® distributed applications. It is based on the new Microsoft Windows® installer, which reduces the total cost of ownership (TCO) for your customers by enabling them to efficiently install and configure your products and applications. The new Windows installer is part of the Windows 2000 and Zero Administration Windows (ZAW) efforts to reduce the overall cost of deploying, using, and managing desktop computers.
This article is not meant as a replacement for the Visual Studio Installer (VSI) documentation, which you should read first. It is merely a supplement to assist Visual FoxPro developers in using VSI as an alternative to the Visual FoxPro 6.0 Setup Wizard. We highly recommend you reread the Visual Studio Installer Best Practices section on how to package your application components, available in the VSI documentation.
This article is broken down into sections based on typical distributed application scenarios. At the end, you will find a reference guide to assist you in creating VSI setup scripts.
Visual FoxPro Distribution Scenarios
Simple Executable Application
Many typical Visual FoxPro applications today are built entirely with Visual FoxPro and use native Fox data (that is, .dbc and .dbf files). The following steps are general guidelines to follow for creating a Visual Studio Installer setup for your Visual FoxPro distributed application.
Open a new project. Launch the Visual Studio Installer and select a new project of type Empty Installer.
Add application files. You can add files from the Project menu’s Add File(s) item or drag and drop them from the Windows Explorer to either the Project Explorer or File System window. (You cannot drag and drop an entire folder, only files from within.)
Set application file locations. This step ensures that your application files are installed in the proper target location. Use the File System window to place files in the location where you want them installed.NoteIf you manually drag and drop files from the Windows Explorer, the application folder structure is not preserved. You need to manually add subfolders to the Application Folder in the File System window to preserve the folder structure.
Set file-specific settings. Open the Properties window to set individual file install settings for any file in your VSI project.
Add a shortcut to application. You can select the User’s Desktop or User’s Start Menu folder in File System window as a place to create a shortcut to your main application. After selecting the desired location, right-click on the right pane and select Create Shortcut. Pick the name of your application from the dialog. If you want, you can add additional shortcuts to other files your application uses.
Add required merge modules. Merge modules are packages of files and install information for common shared components. With Visual FoxPro applications, you should include the following merge modules:
VFP6RUN.MSM
MSVCRT.MSM
OLEAUT32.MSM
Merge modules that ship with VSI are installed in the following location:C:Program FilesMicrosoft Visual StudioCommonToolsVSInstBuildRes
Copy
This location includes MSVCRT.MSM and OLEAUT32.MSM. Merge modules can be added to your project in a way that is similar to how you add a file. The **Project** menu’s **Add Merge Module(s)** item allows you to do this.
> **Note**` `Windows 2000 installs files in these last two merge modules under System File Protection. If you are only distributing your application to customers running Windows 2000, you do not need to include these modules.
Set Project Options. You do this through the Project menu’s myproject Properties item (see VSI documentation).ImportantChoose the appropriate Build Type option based on your target customer. Picking the Installer with Windows Installer Loader option will add an extra 2.6 megabytes (MB) to the entire setup, but it is required for customers who do not have the Windows Installer loaded on their machines.
Set additional VSI installer options (see VSI documentation).
Build your .msi installer package file by selecting Build from the Build menu.
The output of your project is a Microsoft Installer package file (.msi), which any user can double-click to run. See the section Distributing Your Application below for more details.
Executable Application with ActiveX Controls
A common element of many Visual FoxPro applications is ActiveX® Controls. You can include ActiveX Controls with your VSI setups by following these steps:
Follow steps in the Simple Executable Application scenario above.
Include the COMCAT.MSM merge module. Note that Windows 2000 also installs files in this merge module.
Follow additional steps below based on specific ActiveX Controls being installed:Common ActiveX Controls—these are the common controls, which ship with Visual FoxPro 6.0 and Visual Studio 6.0. VSI ships with merge modules for most of these controls. Simply add the appropriate merge module for that control (see the Reference Guide below). For example, if your application uses the Treeview control, you should include the MSCOMCTL.MSM merge module.VBCCE Controls—Visual Basic® 6.0 allows developers to create custom ActiveX Controls. You will need to manually add this control to the project and set certain properties (for example, install location, registration). You can set the Register property for the ActiveX Control file to vsifrSelfReg (1) to register the file (see Important below for more details). The install location for this control can be the same as the application if the control is not likely to be shared with other applications. In addition to the actual control, you will also need to include the Visual Basic run-time merge module (MSVBVM60.MSM).MFC Controls—some of the older controls, such as the Calendar control, use the MFC libraries. You should include the MFC42.MSM merge module if this is the case. As with VBCEE controls, you will need to manually add the control to the project and set various settings.Third-Party Controls—refer to documentation provided by the vendor on how and where to install the control. Make sure you register the control. The documentation should also provide information on any dependency files needed (for example, MFC, Visual Basic run time). You may also have to add specific Registry keys (use VSI Registry window) for any necessary licensing requirements.
ImportantTo ensure the Microsoft Windows installer knows about your installed files to roll back or advertise them, you must install the files in a manner compliant with Windows Installer requirements. Self-registering your files is not compliant with Windows installer requirements. The Associations Editor in Visual Studio Installer makes it possible for you to install Windows installer-compliant COM objects. For more information, see the Visual Studio Installer documentation.
If you choose to register controls by setting the Register property to vsifrSelfReg, then you should also set the SharedLegacyFile property so that the application can be properly reference counted. This is essential if that control is shared by multiple applications.
NoteThe Common ActiveX Controls included in the VSI merge modules contain registration information that is Windows installer-compliant.
Applications with HTML Help
The VFP6RUN.MSM merge module includes both FOXHHELP.EXE and FOXHHELPPS.DLL files needed to support context HTML Help within your Visual FoxPro 6.0 applications. Besides your application specific .chm file, you will need to include the core HTML Help viewer files. The HTML Help viewer files are available as a redistributable called HHUPD.EXE, which can be downloaded from the MSDN Web site https://msdn.microsoft.com/library/tools/htmlhelp/wkshp/download.htm. Users will need to run this HHUPD.EXE installer after they run the .msi installer.
Applications using MDAC Components
If your applications use any of the following data components, you will want to include the Microsoft Data Access Components merge module (MDAC.MSM).
ODBC Drivers
OLE DB Providers
ADO
RDS
ImportantThe MDAC merge module does not actually contain any MDAC files. It simply provides a check for the installer. If MDAC is not installed on the user’s system, then a message dialog is displayed indicating that the user needs to also install these components. You will still need to include the actual MDAC redistribution setup with your application. This setup (MDAC_TYP.EXE) is included at the following Visual FoxPro 6.0 SP3 location:
<vfproot>Distrib.srcSystem
You can also obtain MDAC_TYP.EXE from the Microsoft web site. Users will need to run the MDAC_TYP.EXE installer manually after they run the .msi installer.
Shared Components
Often, applications consist of components that are shared by multiple applications. Visual FoxPro frameworks, foundation classes, and other shared files and class libraries are examples of these types of components. As described in the Visual Studio Installer documentation, it is recommended that these types of files be combined into merge modules, which can be included later with any application installer package. This ensures that files are always installed in a consistent manner.
The Visual Studio Installer also lets you combine files (as well as shortcuts, registry keys, and so on) into a common component. However, the Visual Studio Installer best practice recommendation is to make each file included in your installer project a component. One of the limitations of combining multiple files into a single component is that all files must be installed in the same directory on the target machine. A good example of when you might combine multiple files into a single component is Visual FoxPro binary files (for example, dbf/fpt, scx/sct, frx/frt, lbx/lbt).
COM Servers
A specific example of components that could be packaged into merge modules is a Visual FoxPro COM Server (both Local .exe and In-Proc .dll servers). You can then add your COM Server merge module to any VSI installer project.
Correctly installing and registering COM objects is necessary to take advantage of Windows installer rollback and advertising features. As explained in the VSI documentation, two powerful features of the Windows installer are the abilities to:
Roll back an unsuccessful installation, returning the target machine to its preinstallation state.
Advertise installed products or even individual elements of a product, such as COM objects. Advertising makes a product or COM object available to the user or target machine (by placing a shortcut in the appropriate place, such as the Start menu or registry) without installing the product until the user or another machine function specifically calls the advertised element.
To support rolling back component installation and registration if your product installation fails and component advertisement on the target machine, you must register installed COM objects by establishing the necessary associations. You can choose not to do so by self-registering your installed COM objects, but this sacrifices the enhanced Windows installer rollback and advertising functionality.
With traditional scripted setup programs, self-registration was the accepted method for installing COM objects, and it is still a viable method. However, the Windows installer cannot perform rollback installations and registration of self-registered COM objects, and it cannot advertise those objects. This is because self-registered COM objects do not pass their installation and registration information to the Windows installer.
To ensure that the Windows installer knows enough about your installed COM objects to perform a rollback on or advertise them, you must install those COM objects in a manner compliant with Windows installer requirements. With the Associations editor in Visual Studio Installer, you can install Windows installer-compliant COM objects.
Additionally, type library information for the COM Server must also be captured because it is also registered in the Registry. With Visual FoxPro, a type library can be bound inside of the .exe or .dll server file, or exist separately as a .tlb file.
When authoring a VSI setup to include a Visual FoxPro COM Server, you have two options for handling server registration:
Option 1: You can set the Register property for the COM Server file to vsifrSelfReg (1). This option performs the older style self-registration. However, you lose many of the Windows installer capabilities just mentioned. It is the easiest option for authoring your VSI setup.
Option 2: The recommended approach for registering COM servers requires a little more work. You will need to manually add Registry keys and COM Object associations to the VSI setup. Let’s walk through an example (note that all the sample files are included with the sample download at the top of this article):
First, add your COM server file(s) to the setup. This is either the .exe or .dll file you created. If you built this file using Windows NT® under Visual FoxPro 6.0 Service Pack 3 (SP3), you do not need to include the .tlb type library file because it is bound into the server file. Otherwise, you need to include this file. After adding these files to the project, make sure to set the Register property to 0 so that they are not self-registered during installation.You can optionally add the server’s .vbr file, which is used by CLIREG32.EXE to register a COM server remotely.NoteThe Visual Studio Installer does not support post-install actions as the Visual FoxPro Setup Wizard does, so you need to run CLIREG32.EXE separately after the setup.
Open up the File System window from Target Machine node and select an install location for your server components. The Visual FoxPro 6.0 Setup Wizard typically installs COM servers in the WindowsOleSrv folder. You can choose to install to this location if you feel that your component may be shared by multiple applications. If you want to isolate your component specific for a single application, you can install it in the same location as the application.
Author the Registry keys necessary to register the COM server. You can obtain the information to do this by opening up the .vbr file for that server. This file contains all the Registry keys written out when one self-registers a COM server (for example, REGSVR32 foxdemo1.dll). The following .vbr file contents are from a sample COM server called FOXDEMO1.DLL (note that this sample has just one OLEPUBLIC server and is specific for a multithreaded .dll server):VB5SERVERINFO VERSION=1.0.0
If you take a close look at the .vbr file, you will see that there are four groups of Registry keys that we need to add. These are keys for PROGID, CLSID, INTERFACE, and TYPELIB. All of the keys fall under the HKEY_CLASSES_ROOT Registry hive. It is a good idea to open up the Registry using REGEDIT to see how the keys should appear. You can later run your .msi setup to see if the Registry keys are being written out properly.
PROGID—the PROGID keys are used by COM when you create an instance of the COM server. For example, in the above server, one would call the following:oServer = CreateObject(“foxdemo1.foxsvr1”) The PROGID keys are simply added to your VSI project through the Registry window. Right-click the appropriate node to add new Keys and String Values. You can copy and paste values from the .vbr file to save time.
CLSID—the CLSID keys (HKEY_CLASSES_ROOTCLSID) require a little extra work because you will want to associate your COM server with these keys. You will first add most of the keys similar to the PROGID keys above using the Registry window.NoteA few Registry keys written out during a self-registration are not included in the .vbr file (Implemented Categories and Programmable). The sample includes these. You can also see them in the Registry using REGEDIT.Once you have entered all of the Registry keys except the one with the reference to the actual server file, you need to add a COM Object association for this file using the Associations window. The VSI documentation explains this. Make sure you enter the proper CLSID value.NoteThe Windows installer actually adds an extra string value to this Registry key during install. This value may look a little weird, but it is needed by the Windows installer for features such as Advertising.
INTERFACE—the Interface keys are entered just like the PROGID keys using the Registry window.
TYPELIB—the last set of keys to add are the TYPELIB keys. Again, you will use the Registry window to do this (do not use the VSI Type Libraries option under the Associations window). The one tricky part is entering the name of the file under the WIN32 key because you don’t always know exactly where the file will be installed. The Windows Installer allows you to place custom properties or wildcards in the Value property. For example, you could set the value of the WIN32 key to the following:[TARGETDIR]foxdemo1.dll During the install process, the wildcard placeholder is replaced with the actual value of the folder specific to the target machine. In addition, you will need to use a Windows Installer property for the HELPDIR key. The Appendices below have some of the common Windows installer properties. Refer to the Windows 2000 Platform SDK for more details.
You are now done and simply need to build your .msi installer package or .msm merge module. It is always a good idea to thoroughly test any setup such as this where you have manually entered Registry keys.
**Tip **If you are installing your COM Server onto a Windows 2000 or Windows 98 (Second Edition or later) machine, you should seriously consider installing the component directly to the application directory so that it is isolated from other applications. In order to do this, you must use Option 2 to register your component. The one difference to make in your setup is not to use the Associations window for handling the CLSID registry keys. Instead, you should simply add an entry in the Registry window with the name of your COM server but without a path. If no path is included, COM will first look in the application folder for that component. For more details on isolating components, go to http://search.microsoft.com/us/dev/default.asp and search on “DLL Hell.”
Localized Applications
The VFP6RUN.MSM file includes the standard language-neutral resource file (VFP6RENU.DLL), which is used for all English (US) shipping applications. If you want to include support for another localized resource file (VFP6Rxxx.DLL), simply drop that file into the project and install it in the Windows System folder. It does not need to be registered. You should set the File’s DoNotUninstall property to True. For example, include VFP6RDEU.DLL for the German run-time resource file.Expand table
Language
Resource File
German
VFP6RDEU.DLL
French
VFP6RFRA.DLL
Spanish
VFP6RESP.DLL
Simplified Chinese
VFP6RCHS.DLL
Traditional Chinese
VFP6RCHT.DLL
Unsupported Scenarios
The Visual FoxPro 6.0 Setup Wizard should still be used for the following scenarios:
Applications requiring post-executable actions.
Applications requiring Microsoft Graph run-time files.
COM Servers requiring DCOM and/or remote automation support (this can be done by manually adding Registry keys, but is not as flexible as Visual FoxPro Setup Wizard).
Distributing Your Application
When you distribute your application, you should still follow many of the same guidelines mentioned in the Visual FoxPro documentation. VSI setups (.msi files) use the new Windows Installer technology, which is available on certain platforms. The following steps are general installation instructions when distributing your application to customers. There are two basic scenarios to consider:
Installer package built with the Installer with Windows Installer Loader Build Type option. In the near future, this is likely to be the type of installation package you will want to create because many of your customers will still be running on older Windows operating systems. The user simply needs to run the SETUP.EXE file to install the entire application. This bootstrap loader file first checks for and, if necessary, installs the Windows Installer, then it installs your .msi package setup.—or—
Installer package not built with the Installer with Windows Installer Loader Build Type option. With these setups, the user already has Windows Installer installed on his or her machine. The user simply needs to run (double-click) the .msi package file to install it.
Install any additional required setups (users will need to run these separately):
MDAC_TYP.EXE—if your application uses any MDAC components.
HHUPD.EXE—if your application uses HTML Help.
NoteBuilding an installer with the Windows installer bootstrap loader creates these distinct files as part of your installer package. You must include all of these files on the media that you choose to distribute your application:
Your .msi file.
SETUP.EXE—the file that determines whether or not the Windows installer resides on the target machine and installs the Windows installer if necessary.
SETUP.INI—the file that tells SETUP.EXE the name of your .msi file to install.
INSTMSIW.EXE—the Windows installer for Windows NT machines. (Windows NT 3.51 is not supported.)
INSTMSIA.EXE—the Windows installer for Windows 95 and Windows 98 machines.
Appendices
VFP6RUN Merge Module
The VFP6RUN.MSM merge module properly installs the necessary files to support your Visual FoxPro distributed applications, including COM servers, Active Documents, and normal Windows executables. As with the Visual FoxPro 6.0 SP3 Setup Wizard, run-time files are installed and registered in the Windows System directory. Because the VFP6RUN.MSM merge module is properly authored for Windows installer file installation and registration, it can take advantage of rollback and advertising features. The following files are included in the VFP6RUN merge module:Expand table
File
VFP6R.DLL
VFP6T.DLL
VFP6RENU.DLL
VFP6RUN.EXE
FOXHHELP.EXE
FOXHHELPPS.DLL
Reference Guide to VSI Merge Modules
Expand table
Core Components
Merge Module
OLE Automation Support Files
OLEAUT32.MSM
Microsoft Visual C Run-Time Libraries
MSVCRT.MSM
Microsoft Component Category Manager Library
COMCAT.MSM
Microsoft Foundation Classes
MFC42.MSM
Visual Basic 6.0 Run-Time Library
MSVBVM60.MSM
Microsoft Data Access Components 2.1
MDAC.MSM
Expand table
ActiveX Controls
Merge Module
Microsoft Animation Control (v5.0)Microsoft UpDown Control (v5.0)
COMCT232.MSM
Microsoft Coolbar Control (v6.0)
COMCT332.MSM
Microsoft TabStrip Control (v5.0)Microsoft Toolbar Control (v5.0)Microsoft StatusBar Control (v5.0)Microsoft ProgressBar Control (v5.0)Microsoft TreeView Control (v5.0)Microsoft ListView Control (v50)Microsoft ImageList Control (v5.0)Microsoft Slider Control (v5.0)
COMCTL32.MSM
Microsoft Common Dialog Control (v6.0)
COMDLG32.MSM
Microsoft Data Bound Grid Control (v5.0)
DBGRID32.MSM
Microsoft DBList Control (v6.0)Microsoft DBCombo Control (v6.0)
DBLIST32.MSM
Microsoft Multimedia Control (v6.0)
MCI32.MSM
Microsoft Chart Control (v6.0) (OLE DB)
MSCHRT20.MSM
Microsoft Animation Control (v6.0)Microsoft UpDown Control (v6.0)Microsoft MonthView Control (v6.0)Microsoft Date and Time Picker Control (v6.0)Microsoft Flat ScrollBar Control (v6.0)
MSCOMCT2.MSM
Microsoft TabStrip Control (v6.0)Microsoft Toolbar Control (v6.0)Microsoft StatusBar Control (v6.0)Microsoft ProgressBar Control (v6.0)Microsoft TreeView Control (v6.0)Microsoft ListView Control (v6.0)Microsoft ImageList Control (v6.0)Microsoft Slider Control (v6.0)Microsoft ImageComboBox Control (v6.0)
MSCOMCTL.MSM
Microsoft Communications Control (v6.0)
MSCOMM32.MSM
Microsoft FlexGrid Control (v6.0)
MSFLXGRD.MSM
Microsoft Hierarchical FlexGrid Control (v6.0)
MSHFLXGD.MSM
Microsoft Internet Transfer Control (v6.0)
MSINET.MSM
Microsoft MAPI Session Control (v6.0)Microsoft MAPI Message Control (v6.0)
MSMAPI32.MSM
Microsoft Masked Edit Control (v6.0)
MSMASK32.MSM
Microsoft Winsock Control (v6.0)
MSWINSCK.MSM
Microsoft Picture Clip Control (v6.0)
PICCLP32.MSM
Microsoft SysInfo Control (v6.0)
SYSINFO.MSM
Microsoft Tabbed Dialog Control (v6.0)
TABCTL32.MSM
Common Windows Installer Properties
Expand table
Property name
Brief description of property
SourceDir
Root directory containing the source files.
TARGETDIR
Location into which the installation package is copied during an administrative installation.
AppDataFolder
Full path to the Application Data folder for the current user.
CommonFilesFolder
Full path to the Common Files folder for the current user.
DesktopFolder
Full path to the Desktop folder.
FavoritesFolder
Full path to the Favorites folder for the current user.
FontsFolder
Full path to the Fonts folder.
NetHoodFolder
Full path to the NetHood folder for the current user.
PersonalFolder
Full path to the Personal folder for the current user.
PrintHoodFolder
Full path to the PrintHood folder for the current user.
ProgramFilesFolder
Full path to the Program Files folder.
ProgramMenuFolder
Full path to the Program Menu folder.
RecentFolder
Full path to the Recent folder for the current user.
SendToFolder
Full path to the SendTo folder for the current user.
StartMenuFolder
Full path to the Start menu folder.
StartupFolder
Full path to the Startup folder.
System16Folder
Full path to folder for 16-bit system DLLs.
SystemFolder
Full path to the System folder.
TempFolder
Full path to the Temp folder.
TemplateFolder
Full path to the Template folder for the current user.
Summary: This article guides Visual FoxPro® developers through successfully writing Visual FoxPro code to access the Microsoft® Messaging Queue (MSMQ) COM objects directly. Many tips and tricks specific to Visual FoxPro are scattered throughout the code samples. (37 printed pages)Expand table
Click to copy the MSMQwVFP6 sample file.
Contents
IntroductionAll About MSMQFeatures of MSMQTerminology OverviewProgramming MSMQ with VFPBasic Queue OperationsBasic Message OperationsMessage AcknowledgmentsResponse MessagesAdvanced VFP Programming for MSMQWorking with MSMQ TransactionsGoing Forward
Introduction
Microsoft® Messaging Queue (MSMQ) is an exciting technology that every Visual FoxPro® developer can employ in his or her application. This document will guide you through successfully writing Visual FoxPro code to access the MSMQ COM objects directly. Many tips and tricks specific to Visual FoxPro are scattered throughout the code samples. Many of the code samples contained in this document are included with the Visual FoxPro MSMQ web pack.
The first part of this document describes general MSMQ concepts that you need to know. If you are already familiar with MSMQ, you can go directly to the programming sections.
In preparation for reading this article, you may want to first install MSMQ and read through the documentation. The MSMQ SDK documentation is more detailed and recommended for developers. This article repeats sections from topics in the MSMQ documentation. If you have installed Microsoft Windows® 2000, the Component Services documentation contains expanded detail on messaging services:
NoteThere are differences between MSMQ 1.0 and 2.0 features not documented here, which appear in the Windows 2000 docs.
MSMQ 1.0 is included as part of the Windows NT4 Options Pack, which is a free download from the Microsoft MSMQ Web site:
MSMQ 1.0 is not installed by default, but rather as an option available in the Custom setup. MSMQ 2.0 will be incorporated directly with future versions of Windows. This document describes MSMQ in a version-independent manner, however, differences between versions, will be noted where applicable.
Throughout this document, you will see references to e-mail. E-mail and Messaging are often compared and confused. In fact, many people often distinguish between the two with following analogy: E-mail is to People as Messaging is to Applications.
All About MSMQ
With the trend toward distributed computing in enterprise environments, it is important to have flexible and reliable communication among applications. Businesses often require independent applications running on different systems to communicate with each other and to exchange messages even though the applications may not be running at the same time.
MSMQ is a “fast store-and-forward” service for Windows NT Server Enterprise Edition (Windows NT Server/E), that enables applications running at different times to communicate across heterogeneous networks and systems that may be temporarily offline. Applications send messages to MSMQ, and MSMQ uses queues of messages to ensure that the messages eventually reach their destination. MSMQ provides guaranteed message delivery, efficient routing, security, and priority-based messaging.
Message queuing is like e-mail (asynchronous) versus the telephone (synchronous). See the example in Figure 1:
Figure 1. Asynchronous versus synchronous communication
Features of MSMQ
MSMQ version 1.0 supports the following features:
Asynchronous communication
Message-Oriented Middleware (MOM) Connectionless messaging. With store-and-forward message queuing, applications aren’t affected by network fluctuations and do not have to establish sessions. Because MSMQ uses a sessionless model at the application level, the sender and receiver don’t need to support the same protocol. MSMQ supports Internet Protocol (IP) and Internet Packet eXchange (IPX).
Network traffic prioritization. Message prioritization allows urgent or important traffic to preempt less-important traffic so you can guarantee adequate response time for critical applications at the expense of less important applications.
Guaranteed delivery. Messages can be logged to a disk-based queue to provide guaranteed delivery.
Transactions. The MSMQ transaction flag can be used to implement transaction-based applications, ensure messages are delivered in order, ensure messages are delivered no more than once, and confirm messages reached or were retrieved from the destination queue.
Dynamic queues. Queue information resides in a dynamic/replicated database so administrators can change queue properties without affecting messaging applications. Using MSMQ Explorer, administrators can make these changes from any computer running MSMQ Explorer.
Routing. MSMQ supports smart routing, based on the physical topology of the network, session concentration, and transport connectivity. Session concentration allows efficient usage of slow links.
Security. MSMQ supports privacy and security through access control, auditing, encryption, and authentication. Access control is implemented using Windows NT security and digital signatures. Auditing is implemented through the Windows NT event logging service. Encryption and authentication (using digital signatures) are supported using public and private keys.
Disparate system integration. MSMQ-based applications can be implemented across a wide variety of hardware platforms using MSMQ connectivity products provided by Level 8 Systems.
Dynamic queues, integrated security, manageable scalability, and smart routing differentiate MSMQ from other middleware implementations available today.
MSMQ differs from Remote Procedure Calls (RPC), where applications are required to maintain sessions, and from Windows Sockets, and messaging API (MAPI). Although Windows Sockets provides low-level functions for writing applications, Windows Sockets does not allow applications to run at different times in the way that MSMQ does and MSMQ uses a more general-purpose message queuing model than MAPI.
Terminology Overview
MSMQ applications communicate between computers using a unit of information (text or binary data) called a message. Transactional messages, those which can be discarded if the transaction is aborted, can be used to pair the sending or receiving of any message with an action in another operation. Using transactional messages ensures that the unit of work is carried out as an atomic operation, that is, the operation succeeds or fails as a whole. Transactional messages can also be used to ensure that a message is delivered only once and that all messages sent from one computer to another are delivered in order. Positive and negative acknowledgements can be used to confirm messages reached or were retrieved from the destination queue. See Figure 2.
Figure 2. Transactional messages
MSMQ supports two delivery methods: express and recoverable. Choosing between express and recoverable delivery is a matter of trading performance and resource use for reliability and failure recovery. Express messages use fewer resources and are faster than recoverable messages, but cannot be recovered if the computer storing the memory-mapped message fails. Recoverable messages use more resources and are slower than express messages, but can be recovered no matter which computer fails.
MSMQ uses public and private queues to store and forward messages. All MSMQ queues, regardless of their function, can be manipulated with the same MSMQ functions. This includes the special journal, dead letter, transactional dead letter, administration, system, and report queues. Each of the queues is simply a standard MSMQ queue used for a specific purpose. For more information on the MSMQ API, see the Microsoft Message Queuing Services Software Development Kit (MSMQ SDK).
MSMQ routes and delivers messages based on a combination of queue priority and message priority. Messages are routed and delivered by queue priority first, and message priority second.
MSMQ supports dependent clients, independent clients, and servers. Independent clients and servers run the MSMQ Services and can communicate asynchronously, while MSMQ-dependent clients require synchronous access to an MSMQ Service.
Some components of MSMQ Services hold copies of the MSMQ information store (MQIS) database. The MQIS is a distributed database that holds enterprise topology, enterprise settings, computer information, and queue information. MSMQ-based applications can query the MQIS to find queues and get queue properties.
NoteWith MSMQ 2.0, the MSMQ information store can also be Microsoft Active Directory™ services.
All computers operate within one MSMQ enterprise, divided into sites, and connected through site links. Site link costs define the cost of sending messages between sites, making communication between any two computers fast and inexpensive. Computers running in MSMQ communicate over connected networks (CNs), a collection of computers where any two computers can communicate directly. MSMQ Services designated as in routing servers (InRSs), out routing servers (OutRSs), and site gates can be used to control the flow of messages and provide session concentration. MSMQ Services take all these factors into account when routing messages within your MSMQ enterprise.
NoteMany of the MSMQ topology concepts have changed with MSMQ 2.0. PECs (Primary Enterprise Controllers) are now Domain Controllers, and the concept of connected networks no longer exists. Make sure to read through these documents carefully.
Programming MSMQ with VFP
This section of this article focuses on Visual FoxPro-specific programming practices for integrating MSMQ into your applications.
Using the MSMQ ActiveX® objects, you can program all common messaging needs including:
Creating a message queue
Getting a List of Available Queues
Opening and deleting queues
Sending messages
Reading messages
Sending messages with acknowledgments
Responding to an event triggered when message arrives in queue
There are various ActiveX objects (COM servers) available for accessing MSMQ, including those for handling queues, messages and events. This article is not a language reference, so not all the available objects, properties and methods are mentioned below. The focus is on specific scenarios with emphasis on real-world sample code. For information on all the MSMQ objects, refer to the Microsoft Message Queuing Services Administrator’s Guide:
MSMQQuery
MSMQQueueInfos
MSMQQueueInfo
MSMQQueue
MSMQMessage
MSMQEvent
MSMQApplication
MSMQCoordinatedTransactionDispenser
MSMQTransaction
MSMQTransactionDispenser
You may not have a need for all of these objects (especially some of the transaction ones), but it’s a good idea to be somewhat familiar with them.
In addition to the core set of objects, MSMQ also provides a rich set of automation components that support composing and parsing the body of MSMQ mail messages, which are used to communicate with e-mail based applications through the MSMQ mail services. These include objects such as MSMQMailEMail, MSMQMailFormData, and MSMQMailRecipient. See the MSMQ SDK docs for more details.
Before you begin using VFP and MSMQ, read some of the comments in source code. They contain valuable tips and idiosyncrasies with using the MSMQ objects.
ImportantIt is highly recommended that you use Visual FoxPro 6.0 SP3 or higher when programming against MSMQ. There are API functions that require this version.
Listed below are a few examples of why you should incorporate MSMQ into your applications.
Applications that are often disconnected – many Fox developers today are creating distributed multi-tier applications. With these types of applications, it’s sometimes the case where client machines are taken offline from the host server machine(s). This can apply to both Web or LAN based situations. One of the big advantages of MSMQ is that it handles both offline and online scenarios transparently, saving you from writing separate offline and online application logic.Think of this being similar to your e-mail client. You can write e-mail messages both offline and online without any behavioral distinction.
Asynchronous messaging – have you ever written a Visual FoxPro application where one of the application operations takes a long time to perform (e.g. printing a report, re-indexing a large database, etc.)? Visual FoxPro does not return control to the user until the operation is completed. This can be very annoying to the end user who is trying to be productive with your application. Many Visual FoxPro developers employ kludges to handle these situations such as Timers and DO WHILE loops which continuously poll some resource (e.g., file or table) for a change (semaphore) made by the application. These types of coding habits are not efficient and needlessly waste valuable system resources and processor time. With asynchronous messages, you can setup a VFP object that sits in limbo until a message arrives in a queue. Once this event happens, a method on your VFP object is called. You can call a VFP COM server, which runs in its own process, to handle the application request and allow the user to continue working. The MSMQ Message can contain all the information being passed from your application to your VFP COM “helper” server.
Workflow type applications – typical office settings today require efficient passing of information such as documents between various members of the organization. Along the way, this information is often manipulated and refined. With MSMQ, you can keep a nice audit trail and information can be passed in a secured fashion with guaranteed delivery. Because messages can be sent in a transaction, they will be protected from various risks to the system. See the Response Messages section below for more details.
Basic Queue Operations
Creating a Message Queue
One of the first tasks you will want to do with MSMQ is to create a queue so that you can store messages. The simplest way to do this is by using the MSMQ explorer.
In the MSMQ Explorer, right-click a computer node and select New -> Queue.
In the dialog, enter a name for your queue. You will need to decide whether to make it transactional.
Programmatically, you can create a queue with just a few lines of code. Here is the basic code you need to create a public message queue.
The important Pathname property controls the name and location of the queue. Because it is based on UNC naming conventions, you can include the computer name, or for local queues, use the “.” qualifier. Alternately, you can use the FormatName property instead of Pathname. The FormatName property is the recommended strategy because it is better suited for offline work. You should be getting a reference to the QueueInfo object. There is a QueueInfos object that represents a collection of QueueInfo objects. Think of a QueueInfo as simply an object containing specific information about the Queue itself such as location. However, there is one important distinction, a QueueInfo does not contain any information about the contents of the queue. The information about the contents is stored in the Queue object. The Queue Query object, which is used to locate a specific queue, returns a QueueInfos object, distinguishing it from possible multiple queues with that same name existing within the enterprise.
**Important**To create a public message queue, you must be online (attached to a valid MSMQ Service (e.g., primary site controller). Independent clients cannot create public queues while offline.
Before creating a public queue, check to see if it already exists by using the following syntax.
lcQueueName = “myqueue1”
* Try to locate queue first
oQuery = create(“msmq.msmqquery”)
* Lookup queue to see if it exists
* Important - queue names are case-sensitive
oQueueInfos = oQuery.LookupQueue(,,lcQueueName)
* Move to first record in queue set
oQueueInfo = oQueueInfos.Next()
IF ISNULL(oQueueInfo) &&queue not yet created so create it
oQueueInfo = CreateObject(“msmq.msmqqueueinfo”)
oQueueInfo.Pathname = “.”+lcQueueName
oQueueInfo.Label = lcQueueName
oQueueInfo.Create
ENDIF
You may need to create a private queue for a variety of reasons, including performance and offline usage. A private queue is named similarly to that of a public queue except it has a “PRIVATE$” qualifier immediately preceding the name of the queue.
oQueueInfo.Pathname = “.PRIVATE$”+lcQueueName &&must be unique
Deleting a Message Queue
While not necessarily a common operation, you may need to delete a particular queue. The following code snippet has this operation.
The QueueInfo’s PUBLIC format name is a unique identified key such as following:
PUBLIC=179446c5-0001-11d3-8234-00c04f984590
Getting a reference to a specific queue involves just two lines of code. This is an important concept to remember, that is, obtaining a reference to a specific queue whether it be for deleting, opening it up to send a message, opening it up to read a message, etc.
Queues are like tables with the messages being similar to records. Think of them also like cursors in that you can have many instances of the same queue open. The QueueInfo is 1:1 relation and points to the Queue object itself. A queue can be opened to either send messages, read (peek) messages, or retrieve (receive) messages. A queue can not be opened to both send and read a message with the same Open() call. However, you can open multiple instances of the same queue in different modes.
To open a queue, you use the QueueInfo Open() method:
oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
IF EMPTY(oSendQueue.IsOpen)
RETURN
ENDIF
You are probably wondering about the checking here with IsOpen. You should always check to see if your Open operation succeeded. The queue may be unavailable or exclusively in use. The EMPTY() function is used because some versions of MSMQ return logical while others return 0/1.
The source code includes an MSMQ.H file which is the #INCLUDE file for all the MSMQ constants. These constants are stored in the MSMQ type library. The samples include this file, but can alternately be recreated using the following code (you will need to have VB installed for the special COM server library that reads type libraries).
* GETCONSTANTS.PRG
LOCAL lctlbfile, oTLB, oTInfos, lcNewfile, i, j, lnTmpValue
* MSMQ specific files
lctlbfile = “c:winntsystem32mqoa.dll”
lcNewFile = HOME()+”msmq.h”
oTLB=create(“tli.tliapplication”)
oTInfos=oTlb.TypeLibInfoFromFile(lctlbfile)
SET TEXTMERGE ON NOSHOW TO (lcNewFile)
\* Constants for type library: <<lctlbfile>>
* <<oTInfos.HelpString>>
FOR i = 1 TO oTInfos.Constants.Count
* <<oTInfos.Constants[m.i].Name>> Enum
* <<oTInfos.Constants[m.i].HelpString>>
FOR j = 1 TO oTInfos.Constants[m.i].Members.Count
#DEFINE
\<<oTInfos.Constants[m.i].Members[m.j].Name>>
lnTmpValue = oTInfos.Constants[m.i].Members[m.j].Value
\<<lnTmpValue>>
ENDFOR
ENDFOR
SET TEXTMERGE TO
SET TEXTMERGE OFF
MODIFY FILE (lcNewFile) NOWAIT
Getting a List of Available Public Queues
You may have a need to get a list of all public queues available (independent clients need be online). The following code snippet populates an array aQueues with pathnames of public queues available.
* Create/locate queue
LOCAL oQuery,oQueueInfo,oQueueInfos,aQueues
DIMENSION aQueues[1]
oQuery = create(“msmq.msmqquery”)
oQueueInfos = oQuery.LookupQueue()
* Move to first record in queue set
oQueueInfo = oQueueInfos.Reset
oQueueInfo = oQueueInfos.Next
DO WHILE !ISNULL(oQueueInfo)
IF !EMPTY(aQueues[ALEN(aQueues)])
DIMENSION aQueues[ALEN(aQueues)+1]
ENDIF
aQueues[ALEN(aQueues)] = oQueueInfo.pathname
oQueueInfo = oQueueInfos.Next
ENDDO
Getting List of Available Private Queues
Obtaining a list of private queues is a little less intuitive, and the objects themselves do not support specific language to do this. The following work-around seems to handle this fine with MSMQ 1.0 and 2.0 (Microsoft does not support this strategy so use it at your risk).
#DEFINE MSMQ_KEY SOFTWAREMicrosoftMSMQSetup”
#DEFINE HKEY_LOCAL_MACHINE 2147483646
LOCAL oQuery, QueueInfo, QueueInfos, aQueues, i, j, aKeys
LOCAL aFiles,lcQueue,lcMSMQPath, oReg, oIni
DIMENSION aQueues[1]
DIMENSION akeys[1,1]
DIMENSION aFiles[1]
oReg = NewObject(“registry”,”ffcregistry.vcx”)
oIni = NewObject(“oldinireg”,”ffcregistry.vcx”)
IF oReg.EnumOptions(@akeys, MSMQ_KEY, HKEY_LOCAL_MACHINE, .F.) = 0
FOR i = 1 TO ALEN(akeys,1)
IF ATC(“Directory”,aKeys[m.i,1])#0
lcMSMQPath = akeys[m.i,2]+”StorageLQS”
ADIR(aFiles, lcMSMQPath + “*.*”)
FOR j = 1 TO ALEN(aFiles)
lcQueue = ““
oIni.GetIniEntry(@lcQueue,”Properties”,;
“QueueName”,lcMSMQPath+aFiles[m.j])
IF ATC(“private$”,lcQueue)#0
IF !EMPTY(aQueues[ALEN(aQueues)])
DIMENSION aQueues[ALEN(aQueues)+1]
ENDIF
aQueues[ALEN(aQueues)] = “.”+lcQueue
ENDIF
ENDFOR
EXIT
ENDIF
ENDFOR
ENDIF
Basic Message Operations
Once you have your queue created, you are going to want to create or read messages in it. By far, these are the most common things you do with messaging, just as you would do with e-mail.
Creating/Sending a Message
We now have our queue open specifically for sending a message. Remember, each object reference returned by the Open method can either be used for sending, peeking or receiving a message. However, you can have multiple concurrent object references, each performing a different function. Let’s go ahead and create a simple message.
oMsg = create(“msmq.msmqmessage”)
oMsg.Label = “My First Message”
oMsg.Body = “This is body for my first message.”
oMsg.Send(oSendQueue)
In this example, we compose a message that is not dependent of any queue. The body of the message is text we want to post. Later, we will look at alternative content for the message body. When we are finished composing the message, we send it to a queue that we already have opened for sending messages. Sending messages is made simple using this core set of code, but there are lots of options/attributes that can be used.
Sending Offline Messages
If you are running an Independent Client machine that is temporarily offline, or the machine with the destination queue unavailable (e.g., offline), then you will need to handle opening a queue a little differently than the typical way using Pathname:
Earlier in this document, the FormatName was described as an alternative approach to using Pathname. If you are sending a message to a public queue while offline (even to your own machine), you must use the FormatName when getting a reference to the queue.
NoteFor private queues, it is fine to use Pathname.
The reason for using FormatName is that MSMQ needs to access the MSMQ Services when getting information about a public queue to route the message (in MSMQ 1.0, this routing info is stored in a Microsoft SQL Server™ database called MQIS). In offline mode, this information is unavailable, so the queue cannot be opened.
This concept is similar to working with e-mail offline. If you have your Microsoft Outlook® client opened offline and send a message, the message sits in limbo until you connect to your network where the e-mail server is available to resolve and provide proper routing information. The process of sending a message, when all information for proper routine is unavailable, is known in messaging lingo as Store and Forward.
MSMQ provides this same service if your MSMQ Service is unavailable to route the message to a public queue. Instead of using the Pathname for queue specification, you use the FormatName. MSMQ supports several different types of Format Names for use. My preference is the Direct Format Name type (see MSMQ reference for details on other types).
If using the Direct Format Name, MSMQ will not try to access queue information from the MQIS database. The message is in essence sent blindly to an address that may or may not exist. Until the machine is back online, that address cannot fully be resolved. This strategy can also improve performance. MSMQ will periodically check to see if you are online so that it can process the outgoing queues.
Checking if you are Offline
It’s a good idea if you are working with Independent Clients to have a check in your code to see if MSMQ is online/offline. Again, MSMQ does not support specific language for this, so you can try the following code:
* This program checks to see if MSMQ is offline so
* demos will use Private queues
LOCAL x
x = create(“checkoffline”)
RETURN x.IsOffline()
DEFINE CLASS checkoffline AS custom
PROCEDURE IsOffline
LOCAL lcQueueName,oQuery,oQueueInfos,oQueueInfo
lcQueueName = ““
oQuery = create(“msmq.msmqquery”)
oQueueInfos = oQuery.LookupQueue
oQueueInfo = oQueueInfos.Reset
oQueueInfo = oQueueInfos.Next
RETURN VARTYPE(oQueueInfo)#”O”
PROCEDURE Error(p1,p2,p3)
ENDDEFINE
You should make sure you have a routine like this to determine if you can open a queue using Pathname or Formatname.
Reading a Message
Once your message queue starts to fill up with messages, you’ll probably want to read (handle) them. It is important that you understand the difference between reading (peeking) at messages vs. retrieving (receiving) them.
MSMQ offers 2 options for viewing messages, the primary difference being the resulting action after processing of the message. If you Peek at a message, it is left in the queue. If you Receive a message, it is removed from the queue. The idea behind the later is that you are processing the message once and only once. These concepts are important because MSMQ guarantees delivery and is transactional. Acting on the same message twice can cause a redundant transaction. Again, you need to open a queue specifically for either of these types of message viewing.
Let’s first look at message peeking. When you first open your queue for peeking, you need to see if any messages exist in the queue. The following code snippet shows this.
oMsg = oPeekQueue.Peek(,,100)
IF ISNULL(oMsg)
? “Queue is empty”
ENDIF
There are three Peek functions you can use (Peek, PeekCurrent, and PeekNext).
NoteThe documentation for these three functions for MSMQ 1.0 is incorrect in terms of the ordering of the parameters. The nReceiveTimeout parameter is actually the last parameter.
The default for the nReceiveTimeout parameter is infinity, causing your machine to appear hung if it is trying to find a message in an empty queue. This documentation bug is also prevalent with the Receive and ReceiveCurrent functions. You can use the following code to iterate through all of the messages in your queue.
oMsg = oPeekQueue.PeekCurrent(,,100)
DO WHILE !ISNULL(oMsg)
? oMsg.Body,oMsg.Class
oMsg = oPeekQueue.PeekNext(,,100)
ENDDO
oPeekQueue.Close
Again, since we are only peeking at messages, none of the messages will be deleted from the queue. You can see them in the MSMQ Explorer. The next example shows actually Receiving messages for processing.
nMessages = 0
oMsg = oRecQueue.ReceiveCurrent(,,,100)
DO WHILE !ISNULL(oMsg)
nMessages = nMessages+1
oMsg = oRecQueue.ReceiveCurrent(,,,100)
ENDDO
? “Total messages:”,nMessages
oRecQueue.Close
If you run this code on your message queue, you will notice that the messages no longer appear in the Queue under MSMQ Explorer. Again, this is because Receiving messages causes their removal from the queue.
Using a Journal
You’re probably thinking that having messages removed from the queue is a bad thing since there is no way to trace their activity. And it’s likely that you want some sort of activity (audit) log for your application. MSMQ makes it simple to do this. When you create your public/private queue, you can automatically have an associated Journal queue created at the same time (or later on).
oQueueInfo.Journal = 1
A Journal is simply an associated queue for processed messages. When a message is Received (via Receive or ReceiveCurrent), it is automatically processed and moved to the Journal queue. You can view the Queue’s Journal queue in the MSMQ Explorer by expanding the Queue’s tree node.
In fact, you can also programmatically read through the messages in a particular Journal:
* cFormatName is derived from original Queue (see above)
oQueueInfo = create(“msmq.msmqqueueinfo”)
oQueueInfo.FormatName = cFormatName+”;JOURNAL”
oJournalQueue = oQueueInfo.Open(MQ_PEEK_ACCESS,MQ_DENY_NONE)
oMsg = oJournalQueue.PeekCurrent(,,100)
DO WHILE !ISNULL(oMsg)
oMsg = oJournalQueue.PeekNext(,,100)
? oMsg.Body,oMsg.Class
ENDDO
oJournalQueue.Close
Message Acknowledgments
Sending a Message with Acknowledgment
We’ve now added some simple messages to a queue and even found a way to read/process them. No doubt you’ve sent e-mail to a friend in the past and never received an acknowledgment. While this might be fine for simple e-mail, often with your application, you are going to want some sort of acknowledgment that the message was received.
MSMQ offers two types of acknowledgments.
When message reached queue (MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE)
When message was actually read (MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE)
As with most operations involving MSMQ, acknowledgments are handled via sending a new message to a queue. The only difference with an Ack message is that there is no attached body. A special queue is setup for the acknowledgment message. This is referred to as an Admin Queue. An Admin Queue is actually just a normal queue (you can have a Journal associated with it), and Ack messages which you process (Receive) are removed (and optionally forwarded to associated Journal). An Admin Queue is created in the normal fashion like any other queue (see above).
The primary difference with message acknowledgments is not in the Admin Queue, but rather in the composition of the message itself. Here is an example of a message being sent with instructions for acknowledgment.
* Create new messages
oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
IF EMPTY(oSendQueue.IsOpen)
RETURN
ENDIF
oMsg = create(“msmq.msmqmessage”)
oMsg.Label = “Test Message”
oMsg.Body = “This is a sample message.”
oMsg.Ack = MQMSG_ACKNOWLEDGMENT_FULL_RECEIVE
*oMsg.Ack = MQMSG_ACKNOWLEDGMENT_FULL_REACH_QUEUE
oMsg.AdminQueueInfo = oAdminQueueInfo
oMsg.Send(oSendQueue)
oSendQueue.Close
The only difference with a normal message is setting of the Ack and AdminQueueInfo properties.
NoteSetting of AdminQueueInfo property requires Visual FoxPro 6.0 SP3 or higher.
Reading Message Acknowledgments
You are going to want to check to see whether all of the messages reached their target queue, were received within a timeout period, etc. This is all handled by the Class property of the message.
oRecQueue = oAdminQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
oMsg = oRecQueue.PeekCurrent(,,100)
IF !ISNULL(oMsg)
DO CASE
CASE oMsg.Class = MQMSG_CLASS_ACK_REACH_QUEUE
MESSAGEBOX(“The message reached the queue.”)
CASE oMsg.Class = MQMSG_CLASS_ACK_RECEIVE
MESSAGEBOX(“The message was acknowledged as received.”)
CASE oMsg.Class = MQMSG_CLASS_NACK_RECEIVE_TIMEOUT
MESSAGEBOX(“Message was not removed from the queue in time.”)
OTHERWISE
MESSAGEBOX(“The message did not reach the queue.”)
ENDCASE
oMsg = oRecQueue.Receive(,,,100) &&remove from queue
ENDIF
oRecQueue.Close
Message Timeouts
If you are receiving Ack messages based on when the target message queue actually processes (Receive, ReceiveCurrent) the message, you can also impose a timeout. This is handled by the message’s MaxTimeToReceieve property.
If the time-to-be-received timer expires before the message is removed from the queue, MSMQ discards the message, sending it to the dead letter queue if the message’s Journal property is set to MQMSG_DEADLETTER.
MSMQ can also send a negative acknowledgment message back to the sending application if the message is not retrieved before the timer expires.
There is also a similar MaxTimeToReachQueue property.
NoteIf the queue is local, the message always reaches the queue.
These two Acknowledgment operations are facilitated by MSMQ’s built-in Message Timers.
More About Message Timers
MSMQ provides two timers to help you maintain better control of your messages: a time-to-be-received and a time-to-reach-queue timer.
The time-to-be-received timer determines how long a message remains in the system, starting from the time the message is sent, to the time it is removed from the target queue.
The time-to-reach-queue timer determines how long a message has until it reaches the target Queue Manager of the target queue. Typically, this timer is set to a value less than the time-to-be-received setting.
When both timers are used, and if the time-to-be-received timer is set to a shorter time interval than the time-to-reach-queue timer, it takes precedence over the time-to-reach-queue timer. MSMQ does not allow messages to remain in the system longer than the time allowed by their time-to-be-received timer.
When either timer expires, MSMQ discards the message. However, MSMQ can also send a copy of the message to a dead letter queue or an acknowledgment message to an administration queue. If the message’s acknowledgment property specifies full or negative acknowledgments, MSMQ sends the appropriate negative acknowledgment message to the administration queue specified by the message. If the message’s journal property specifies a dead letter queue, a copy of the message is sent to one of two places. The copies of non-transactional messages are sent to the dead letter queue on the computer where the timer expired. Copies of transactional messages are copied to the transactional dead letter queue on the source machine.
Response Messages
Message acknowledgments are convenient for many application needs, especially those involving workflows. You might also want to consider a more detailed approach using an actual response message. Similar to the Admin Queue used for Ack messages, you will need to set up a special Response Queue for Response messages. A major difference with Ack messages is that the Response message contains a body. In reality, the Response Queue is nothing fancy and could easily be handled without the built-in functionality.
Sending Message with Response
Sending a message that asks for a response requires use of the ResponseQueueInfo property of your outgoing message.
* Create new messages
oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
oMsg = create(“msmq.msmqmessage”)
oMsg.Label = “Test Message”
oMsg.Body = “This is a sample message.”
oMsg.Delivery = MQMSG_DELIVERY_RECOVERABLE
oMsg.ResponseQueueInfo = oResponseQueueInfo
oMsg.Send(oSendQueue)
oSendQueue.Close
Reading Message from Response Queue
Messages can be read from the Response Queue. When a message arrives at its target queue, you can query the ResponseQueueInfo property to get an object reference to the Response Queue. Once you have this, you simply create a new message (with body) to send.
* Read message from target queue
oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
oMsg = oRecQueue.Receive(,,,100)
IF ISNULL(oMsg)
RETURN
ENDIF
* See is a reponse queue is setup and send message if so
IF !ISNULL(oMsg.ResponseQueueInfo)
oRespQueue = oMsg.ResponseQueueInfo.Open(MQ_SEND_ACCESS,;
MQ_DENY_NONE)
oMsg2 = create(“msmq.msmqmessage”)
oMsg2.Label = “Response Message”
oMsg2.Body = “This is a response message”
oID = oMsg.id
cTmpStr=““
FOR i = 1 TO ALEN(oID)
cTmpStr = cTmpStr+CHR(oID[m.i])
ENDFOR
oMsg2.CorrelationId = CreateBinary(cTmpStr)
oMsg2.Send(oRespQueue)
MESSAGEBOX(“A response message was returned.”)
oRespQueue.Close
ENDIF
Take a close note at the code for the CorrelationID property. This property is a 20-element array of bytes. You will need to first convert it to a string and then run it through Visual FoxPro’s CreateBinary function.
Advanced VFP Programming for MSMQ
In this section, we will explore a number of advanced topics that will be of value in your MSMQ application.
Sending Private Messages
MSMQ provides a secured channel for sending private, encrypted messages throughout your MSMQ enterprise. MSMQ ensures that the body of private messages are kept encrypted from the moment they leave the source Queue Manager to the moment they reach their target Queue Manager.
With encryption and decryption provided by MSMQ Queue Managers, applications do not have to encrypt messages when they are sent, or decrypt received messages. When a private message is sent, the source Queue Manager encrypts the body of the message, then sends the message on to the target Queue Manager. When the target Queue Manager receives the message, it decodes the body of the message and passes the clear message on to the queue. The receiving application can then read the message from the queue without ever knowing it was encrypted.
NoteThe receiving application sees the message as clear text. However, it can look at the message’s privacy level to determine whether the message was sent encrypted, or look at the encryption algorithm used when the message was sent.
Sending private messages is easy:
Optional. Verify that the queue can receive private messages. The MSMQQueueInfo object’s PrivLevel must be set to MQ_PRIV_LEVEL_BODY or MQ_PRIV_LEVEL_OPTIONAL. If set to MQ_PRIV_LEVEL_BODY, the queue can only accept private messages. Non-private messages will be ignored.oQueueInfo.PrivLevel = MQ_PRIV_LEVEL_BODY
Open the queue for sending messages.
Set the MSMQMessage object’s PrivLevel property to MQMSG_PRIV_LEVEL_BODY.oMsg.PrivLevel = MQMSG_PRIV_LEVEL_BODY
Optional. Set the encryption algorithm used to encrypt the message.oMsg.EncryptAlgorithm = MQMSG_CALG_RC4
Send the message.
Here is a sample VFP snippet putting it all together:
An important concept you should understand when sending messages is the various Delivery options.
MQMSG_DELIVERY_RECOVERABLE – In every hop along its route, the message is forwarded to the next hop or stored locally in a backup file until delivered. This guarantees delivery even in the case of a machine crash.
MQMSG_DELIVERY_EXPRESS – The default. The message stays in memory until it can be delivered. (In-memory message store and forward.)
When the message’s delivery mechanism is set to MQMSG_DELIVERY_EXPRESS, the message has faster throughput. When set to MQMSG_DELIVERY_RECOVERABLE, throughput may be slower. However, MSMQ guarantees that the message will be delivered, even if a computer crashes while the message is en-route to the queue.
Handling this is in your code is simply a matter of setting a single message property.
NoteMessages must be sent in recoverable mode if the offline client computer is turned off. Messages sent in express mode are held in RAM and will be lost when the computer is turned off.
Binding Excel Objects to Message Bodies
One of the truly powerful features of MSMQ is the ability to attach COM objects to the bodies of messages. This works for quite a few common objects, including ADO recordsets and Excel spreadsheets. In this section, we will look at spreadsheets. Before you think about trying to attach a Visual FoxPro object, be aware of the following rule for message body content.
The body of an MSMQ message can be a string, an array of bytes, or any persistent COM object that supports IDispatch and IPersist (IPersistStream or IPersistStorage).
NoteVisual FoxPro 6.0 objects do not support the ability to persist themselves via IPersist.
There are several ways to attach Excel data to your message. The following example demonstrates creating a message and attaching an existing Excel file to the message. You can use Visual FoxPro’s GETOBJECT() function to get an object reference to the file and set it directly to the Body property.
Before reading the contents of your Excel spreadsheet object, first check the type of object you are dealing with. The following code uses the TYPE() function to check the object type, and shows accessing the Active Sheet object reference to obtain specific spreadsheet content. There is also code to iterate through the contents of the spreadsheet, however, you can use whatever automation calls you prefer.
NoteThe message body contains a reference to the application object.
* Read in the Excel object
oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
oMsg = oRecQueue.PeekCurrent(,,100)
oObj = oMsg.Body
DO WHILE !ISNULL(oObj)
IF TYPE(“oObj.Application.Name”)=“C” AND;
ATC(“Excel”,oObj.Application.Name)#0
oMsg = oRecQueue.ReceiveCurrent(,,,100)
oObj = oMsg.Body
oSheet = oObj.ActiveSheet
? “Excel contents:”
FOR i = 1 TO 100
IF EMPTY(oSheet.Rows(m.i).Columns(1).Text)
EXIT
ENDIF
?
FOR j = 1 TO 100
lcText = oSheet.Rows(m.i).Columns(m.j).Text
IF EMPTY(lcText)
EXIT
ENDIF
?? lcText+”,”
ENDFOR
ENDFOR
EXIT
ENDIF
oMsg = oRecQueue.PeekNext(,,100)
oObj = oMsg.Body
ENDDO
oRecQueue.Close
Binding ADO Recordset Objects to Message Bodies
ADO recordsets represent a valuable medium for passing data via MSMQ messages. Rather than passing a long string representing a record from your FoxPro table, which you would later need to parse, you can pass a more efficient object. Additionally, the data is stored in an encrypted format.
Using the new VFPCOM utility available from the Visual FoxPro Web site, (https://msdn.microsoft.com/vfoxpro/downloads/updates.asp), you now have an excellent mechanism for passing Visual FoxPro data. VFPCOM contains methods for converting VFP data to/from ADO recordsets. The following sample shows VFPCOM used to convert a VFP cursor to a recordset, and then attach that recordset to a message.
Reading and detaching the ADO recordset is easy as demonstrated in the following code, but don’t forget to first check for a valid ADO object. Use the TYPE() function with a common known member of the object.
* Receive the messages
oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS, MQ_DENY_NONE)
* Get the ADO object
oMsg = oRecQueue.PeekCurrent(,,100)
oObj = oMsg.Body
DO WHILE !ISNULL(oObj)
IF TYPE(“oObj.eof”)=“L”
oMsg = oRecQueue.ReceiveCurrent(,,,100)
oObj = oMsg.Body
oObj.MoveFirst
DO WHILE !oObj.eof
? oObj.Fields[1].Value
oObj.MoveNext
ENDDO
EXIT
ENDIF
oMsg = oRecQueue.PeekNext(,,100)
oObj = oMsg.Body
ENDDO
oRecQueue.Close
Using XML with Messages
One of the more exciting uses for messages is passing of XML data. XML data can be used for a variety of purposes, including storage of relational data and other structured information. MSMQ messages are nicely suited for passing XML data, because the message body can persist this structured data.
We’ve already discussed passing ADO recordsets and Microsoft Office objects such as Excel spreadsheets. You’re probably wishing that it was easy to attach VFP cursors and objects to messages as well. XML is a wonderful medium for doing just this.
Rick Strahl’s Web site (www.west-wind.com/) contains an excellent set of XML classes called wwXML, which you can use with your applications. The following table contains some of the wwXML methods of interest.Expand table
Method
Description
CursorToXML
Converts a cursor into an XML representation.
XMLToCursor
Converts an XML document created with CursorToXML back into a cursor.
ObjectToXML
Converts a live reference of an object to XML. All variables are converted to text and stored. Optionally can walk nested objects.
XMLToObject
Creates an object from an XML structure.
Here is an example using wwXML to persist a VFP cursor.
#DEFINE WWXML_PATH “C:VFPWWXML”
LOCAL lcQueueName,oQueueInfo,oSendQueue, oXML,oMSG
SET CLASSLIB TO (WWXML_PATH + “wwXML.vcx”) ADDITIVE
SET PROCEDURE TO (WWXML_PATH + “wwUtils.prg”) ADDITIVE
lcQueueName = “myXMLqueue”
oQueueInfo = CreateObject(“msmq.msmqqueueinfo”)
oQueueInfo.Formatname = “DIRECT=OS:.”+lcQueueName
oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
USE _samples+”datacustomer” AGAIN SHARED
oXML = NewObject(“wwXML”)
oXML.lCreateDataStructure = .T.
oMsg = create(“msmq.msmqmessage”)
oMsg.Label = “Persisted VFP Cursor - “ + TRANS(DATETIME())
oMsg.Body = oXML.CursorToXML()
oMsg.Send(oSendQueue)
oSendQueue.Close
One of the considerations when using XML with MSMQ 1.0, is that the XML will be stored as simple text, which typically is not encrypted. You may need to take some precautions and use security. There is also an EncryptAlgorithm message property that can be used to encrypt the message, however, this only applies for private messages.
Working with MSMQ Events and Asynchronous Message Reading
Applications such as Visual Basic® allow you to bind to events of COM objects. Visual FoxPro 6.0 supports this with ActiveX controls, but not with COM objects such as an ADO Recordset. This means you will not have the ability to bind a VFP object to a COM object and have user code executed when an event of the COM object is triggered.
The new VFPCOM utility (see above) addresses this critical need. You can now have VFP code executed when a message arrives in a particular queue. MSMQ triggers an event that VFP can respond to. The advantage here is that your application can now handle reading messages in the queue asynchronously. Instead of having some object using up lots of processor time consumed in an endless DO WHILE loop, you can have your dormant objects awaken only when a message arrival event occurs.
The process of setting up a queue for asynchronous reading of messages is as follows:
Create your VFP MSMQ events class to handle reading of messages as they arrive in queues. This class needs to contain Arrived and ArrivedError methods to handle the message arrival events.
Use VFPCOM to bind an instance of your VFP MSMQ events class to an instance of an MSMQEvent object. This is done with VFPCOM’s BindEvents method.
Open the queue and call its EnableNotification method passing the MSMQEvent object you just setup with BindEvents.
NoteYou must leave the queue open and the BindEvents call intact, otherwise your event connection loses scope and will fail.
The following class definition is a sample template that can be used for your event binding. Notice that you can use the AppSpecific property to special case handling for certain messages.
DEFINE CLASS foxevent AS custom
Procedure Arrived(oQueue,Cursor)
oMsgRec = oQueue.PeekCurrent(,,0)
? “Message Arrived Event: “+TRANS(oMsgRec.AppSpecific)
IF oMsgRec.AppSpecific = 33 &&get only this one
oMsgRec = oQueue.ReceiveCurrent(,,,0)
oMsgRec = oQueue.PeekCurrent(,,0)
oQueue.EnableNotification(oMSMQEvent,MQMSG_CURRENT,1000)
ELSE
oQueue.EnableNotification(oMSMQEvent,MQMSG_NEXT,1000)
ENDIF
ENDPROC
Procedure ArrivedError(Queue,ErrorCode,Cursor)
? “Message Arrived Error”
ENDPROC
ENDDEFINE
Once you have your class defined, you can set things up for asynchronous reading of messages. The remainder of the code below shows how to set up the event handler. This is also a simple way to test the event handling by adding a bunch of messages.
* Setup up event handler to receive the messages
oMSMQEvent = create(“msmq.msmqevent”)
oComUtil = create(“vfpcom.comutil”)
oFoxEvents = create(“foxevent”)
oComUtil.BindEvents(oMSMQEvent,oFoxEvents)
oRecQueue = oQueueInfo.Open(MQ_RECEIVE_ACCESS,MQ_DENY_NONE)
oRecQueue.EnableNotification(oMSMQEvent,MQMSG_CURRENT,1000)
* Add a bunch of new message
oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
oMsg = create(“msmq.msmqmessage”)
oMsg.AppSpecific = 11
oMsg.Send(oSendQueue)
oMsg.AppSpecific = 22
oMsg.Send(oSendQueue)
oMsg.AppSpecific = 33
oMsg.Send(oSendQueue)
oMsg.AppSpecific = 44
oMsg.Send(oSendQueue)
oMsg.AppSpecific = 55
oMsg.Label = “Message55”
oMsg.Body = “This is body of message 55.”
oMsg.Send(oSendQueue)
oSendQueue.Close
Working with MSMQ Transactions
MSMQ transactions are used by the sending and receiving application. In this model, MSMQ uses two transactions, one to send messages to the queue and the other to retrieve messages from the queue. The sending transaction can commit to sending the messages to the queue and the receiving application can commit to retrieving the messages; MSMQ provides its own confirmation process to notify the sending application that either the messages were retrieved from the queue or why the receiving application failed to retrieve them. MSMQ provides both implicit and explicit mechanisms for incorporating transactions. There are two types of implicit transactions, MTS Transactions and XA-Compliant Transactions. Let’s quickly look at the two explicit ones (MSMQ Internal Transactions and MS DTC External Transactions) and then come back to the MTS Transactions.
MSMQ Internal Transactions
MSMQ Internal Transactions provide better performance for transactions that only send or receive MSMQ messages. Unlike MS DTC external transactions, MSMQ internal transactions cannot be passed to another resource manager. It is the cost of coordinating between several resource managers that make MSMQ internal transaction less expensive in terms of memory than MS DTC external transactions.
NoteWhen sending a single message, MSMQ provides a single-message send operation that uses an MSMQ internal transaction. This mode of sending a message provides the best performance of all transaction types. When using this mode, MQBeginTransaction and Commit are implied.
When setting up your queue for handling internal transactions, you must ensure that the queue is created to allow for transactions. This is handled in the Create() method shown below.
oQueueInfo = create(“msmq.msmqqueueinfo”)
oQueueInfo.pathname = “.”+lcQueueName
oQueueInfo.Label = lcQueueName
oQueueInfo.Journal = 1
* First parameter determines if queue is transactional
oQueueInfo.Create(.T.)
Sending Messages with Transactions
Transactions can be included at the sending and receiving end of messages. The process for setting up messages for transactions is quite simple. You create an object instance of the MSMQ Transaction Dispenser object, to start a transaction. This object has a single method called BeginTransaction, which returns an MSMQ Transaction object. This object is then used to control Commit/Abort operations on sending/receiving messages. The following code demonstrates how this works for sending messages.
* Setup Transaction and Transaction Dispenser objects
oTransDisp = create(“msmq.msmqtransactiondispenser”)
oTrans = oTransDisp.BeginTransaction
* Create new message
oMsg = create(“msmq.msmqmessage”)
oMsg.Label = “My Trans Message”
oMsg.Body = “This is a trans message.”
oMsg.Send(oSendQueue, oTrans)
oSendQueue.Close
If your queue is not set up to handle transactions, you will get an error during the Send method call.
At this point, we have a message that we sent to our queue. The message does not appear in the queue in the MSMQ Explorer because the Send transaction was not yet committed. If you commit the operation by calling the Transaction object’s commit method, then the message is posted to the queue. If you abort, then it is discarded.
IF MESSAGEBOX(“Do you want to commit sending message(Yes) ;
or abort(No)?”,36)=6
oTrans.Commit
ELSE
oTrans.Abort && causes message to be disgarded
MESSAGEBOX(“Queue should be empty since message was disgarded.”)
RETURN
ENDIF
NoteIn the example I was actually able to commit/abort the transaction even after I closed the queue.
Receiving Messages with Transactions
A transaction can be associated with receiving message, just as it was with sending messages.
oTransDisp = create(“msmq.msmqtransactiondispenser”)
oTrans = oTransDisp.BeginTransaction
oMsg = oRecQueue.ReceiveCurrent(oTrans,,,100)
? oMsg.Label
? oMsg.Body
oRecQueue.Close
IF MESSAGEBOX(“Do you want to commit receiving message(Yes) ;
or abort(No)?”,36)=6
oTrans.Commit
MESSAGEBOX(“Queue should be empty with message moved to Journal.”)
ELSE
oTrans.Abort
MESSAGEBOX(“Queue should contain unread message.”)
ENDIF
The Abort operation does not actually discard the message. It instead treats it as unread.
NoteWhen message are not processed, this can be used as an alternate approach to using the Peek functions to read messages.
MS DTC External Transactions
MS DTC External Transactions are used when the transaction includes more actions than simply sending or retrieving MSMQ messages (more than one resource manager is used). In this case, the application must ask MS DTC (Microsoft® Distributed Transaction Coordinator) for a transaction object and explicitly reference that object each time it sends a message, retrieves a message, or executes an action of another resource manager.
When an application is performing a MS DTC transaction, MSMQ is acting as part of a transaction processing system that includes a transaction manager and any number of resource managers.
Programming to use explicit external transactions is not much different from the examples shown above with internal transactions. In fact, the only difference is the Transaction Dispenser being used.
If you get an error message after your BeginTransaction call, you probably don’t have DTC running. You can turn it on via the SQL Server Service Manager (note: many people set their system to automatically turn on DTC during startup).
One interesting difference between the DTC External and Internal Transactions is that when you abort Receiving a message with a DTC External Transaction, the message is left intact in the queue. With Internal Transactions, the message is purged and moved to the Xact Dead Letter queue.
MTS Transactions
One of the nice features of MSMQ is its implicit coordination with Microsoft® Transaction Server to handle transactions. As you may know, MTS is a runtime environment used to handle robust distributed transactions. Visual FoxPro works very well with MTS, and with Visual FoxPro 6.0 SP3, you can achieve highly scalable applications using new multi-threaded DLL servers. When the application is running in the MTS environment, MSMQ can use the current MTS transaction if one is available.
Programming for MTS Transactions requires using an MTS context object to obtain and make transactional calls. Again, you can control transactions in MSMQ for both sending and receiving messages. Unlike the explicit types shown above, the Send and Receive calls do not take a Transaction object as parameter, but rather a constant (MQ_MTS_TRANSACTION).
In your MTS server component, you can control Commit/Abort operations by using the SetAbort and SetComplete methods of the MTS context object. Below is pseudo-sample of how your MTS server might be structured to handle an MSMQ message in a transaction.
* Your MTS server
oMTX = Create(“MTXAS.APPSERVER.1”)
oContext = oMTX.GetObjectContext()
* Create new message and send it to queue
oSendQueue = oQueueInfo.Open(MQ_SEND_ACCESS, MQ_DENY_NONE)
oMsg = create(“msmq.msmqmessage”)
oMsg.Label = “My MTS Message”
oMsg.Body = “This is a MTS message.”
oMsg.Send(oSendQueue, MQ_MTS_TRANSACTION)
oSendQueue.Close
* Do some other stuff here which may not cause abort action
IF lHadSomeError
oContext.SetAbort
ELSE
oContext.SetComplete
ENDIF
Transaction Programming Considerations
If some operations in a transaction fail, it is the application’s responsibility to decide whether to terminate the entire transaction (by calling the Transaction object’s abort member function) or commit the transaction anyway (if the failures are such that the transaction is still viable). If the application does commit to a transaction where some operations have failed, the failed operations will not be part of the transaction.
There is no limit to the number of messages sent, the number of messages retrieved, or the number of queues used in a single transaction. However, an application cannot send a message to a queue and then try to retrieve it during the same transaction.
Calling MQSendMessage does not actually send the message within the transaction. The actual sending is done at some time after MS DTC commits the transaction. When MS DTC returns a successful commit return value, the sending application is guaranteed that the message will be sent. If a transaction is aborted, all MSMQ transaction operations are rolled back: no messages are sent, and all retrieved messages are returned to their original place in the queue.
MSMQ guarantees exactly-once-delivery. This means that all messages sent to a queue will arrive once and only once. MSMQ takes special measures to prevent any message duplication or loss.
MSMQ guarantees that all messages sent to a specific transaction queue will arrive in the order they were sent by the transaction. This means that if transaction T1 sends messages M1 and M2 to queue Q1, M1 will arrive before M2. However, there is no guarantee if two transactions are sending messages to the same queue. If transaction T1 sends messages M1 and M2 to Q1, and a second transaction T2 sends messages M3 and M4 to Q1, MSMQ only guarantees that M1 will arrive before M2, and that M3 will arrive before M4. In order to guarantee that M1 and M2 will arrive before M3 and M4, the application must commit to T2 only after getting a successful return code from T1.MSMQ does not guarantee order of delivery to different queues, nor does it guarantee order of delivery from different computers.
Message Authentication
Message authentication allows the receiving application to verify the source of a message and that the message was not modified on its way to the queue. This is done by attaching a digital signature to the message when it is sent, then verifying the digital signature when the message reaches the queue. The receiving MSMQ Queue Manager uses the digital signature to verify the sender and that the message was not modified.
To digitally sign a message, the sending application uses a public and private signing key pair to create the digital signature. MSMQ provides the key pair when an internal security certificate is used or when an external security certificate is used. External certificates are obtained from a certificate authority (CA).
When an internal security certificate is used, the private signing key is registered the first time that the MSMQ Control Panel application is run. The public signing key is provided within the internal certificate.
Internal certificates are used when the receiving application needs to validate the sender identifier attached to a message. When using an internal certificate, only the sender identifier is guaranteed correct.
External certificates are used when you want to use the information in the certificate (not just the sender identifier sent with the message) to verify the source of a message. The information in the external certificate is guaranteed by the certificate authority that created the certificate.
MSMQ does not validate an external certificate. The receiving application must validate the certificate before using an authenticated message. MSMQ generates the digital signature of a message when it is sent and verifies the digital signature when the message is received, but does not validate the certificate itself.
NoteExternal certificates are required when communicating with operating environments other than Windows NT® where the sender identifier is meaningless.
Going Forward
As you no doubt have seen throughout the article, MSMQ offers endless opportunities for Visual FoxPro developers. MSMQ is a wonderful technology for use with Windows DNA applications where Visual FoxPro COM components can play an essential role in the middle tier.
The samples included with this article contain valuable code you can use in your own applications. You might consider creating reusable classes for common operations such as opening queues, sending and receiving messages. Additionally, it is also useful to have a handy set of tools for working with MSMQ. The following tools included with the samples are invaluable in helping to develop and debug your MSMQ applications:
MSGDISPATCH.SCX – lets you send a message to a queue. The form allows you to send normal text messages, but also lets you drag and drop Office documents (e.g., DOC, XLS and PPT files) onto the body area to have them attached to the message. Messages can be sent to both private and public queues. An admin queue can also be set up for message acknowledgments.
MSGREADER.SCX – while the MSMQ Explorer does not allow you to send messages to a queue, you can view them. Unfortunately, the body contents displayed in the dialog is not very usable, especially with non-text bodies. This form lets you view all sorts of message bodies including plain text, Office documents, ADO recordsets and XML from Rick Strahl’s wwwXML classes.
EVENTHANDLER.SCX – asynchronous events . The EventHandler uses the new VFPCOM utility to bind a Visual FoxPro object, which implements the same interface as the MSMQEvent class, to an MSMQ queue so that you can have Fox code executed when a message arrives in that queue.
All of the tools and samples are “smart” in detecting whether you are online or offline. If you are offline, then the tools will display Private queues to select.
Finally, more information on using MSMQ can be found at the following:
Microsoft Systems Journal: www.microsoft.com/msj/default.asp – a number of useful MSMQ articles have appeared in MSJ over the past few years. Many of the samples are written in Visual Basic, which can easily be converted to Visual FoxPro syntax.
Books – although slow in release, good books are starting to appear on the shelves of your local bookstore. You can search on some of the online book web sites for keywords including MSMQ, MTS, and Distributed Applications.
Summary: Describes the Microsoft® Visual FoxPro® version 6.0 Class Browser and how, combined with the Component Gallery, it can be an effective tool for a variety of development purposes. (9 printed pages)
Contents
IntroductionWhat Is the Visual FoxPro Class Browser?What’s New in Visual FoxPro 6.0The Browser InterfaceClass ManagementWrite Your Own Add-InsAppendix: Browser.dbf StructureAbout the Author
Introduction
This article discusses the Visual FoxPro Class Browser. Together with the new Component Gallery, the Class Browser provides many useful ways to accomplish common development tasks. This document also discusses the open architecture and programmable hooks that these tools expose for customization purposes.
What Is the Visual FoxPro Class Browser?
The Class Browser is useful for a variety of development purposes, such as:
Managing classes and class libraries, including adding, deleting, renaming, and redefining classes.
Browsing all the classes and class libraries used by a project or application.
Generating equivalent code for visual classes.
Browsing the interfaces of Microsoft ActiveX® controls and Component Object Model (COM) components.
Creating running instances at design time.
What’s New in Visual FoxPro 6.0
Most changes to the Visual FoxPro 6.0 Class Browser enhance its usability.
A Mode button (cmdBrowser) has been added to the toolbar to toggle between the Class Browser and the new Component Gallery.
The Type drop-down combo box is now on the same line as the toolbar, resulting in more efficient use of display real estate.
You can size the individual panes in the Class Browser because the boundaries between panes now behave like splitter controls.
The Class Browser Shortcut menu provides new options. You can now toggle the visibility of the Parent Class toolbar, open a new instance of the Class Browser, open a new instance of the Gallery, and force the Class Browser display to refresh.
In the methods and properties pane you’ll notice the methods and properties now display icons to show the status (public, protected, and hidden) of these members. These icons are consistent with those used in Visual Modeler.
The Redefine dialog box has been enhanced to use a conventional class specification dialog box, rather than requiring you to type the class name, and optionally use GetFile( ) to specify its .vcx file.
If you open .tlb, .dll, .olb, or .ocx files in the Class Browser, you’ll notice the typelib information now includes much more detail, including properties, methods, and their parameters.Note Because much useful information appears in the lower-right description pane, make sure you use the Class Browser Shortcut menu to toggle the description panes on.
If the .tlb, .dll, .olb, or .ocx file displayed in the Class Browser has an associated Help file, pressing the space bar will bring up help for the highlighted item.
In the Class Browser, you can export code to HTML. With a class highlighted in the left pane, right-click the View Code button. This invokes your Web browser and opens the browser window over the right pane of the Class Browser.
The View Code window is now modeless.
Right-click the Gallery/Browser Mode button to see a history of previously selected items and files.
With a class selected, Ctrl+ right-click the Class icon image to generate a new object instance created in the Command window.
Users of low-resolution displays will note that you can now resize the Class Browser to a smaller size than was possible before. The browser’s minimum dimensions in Visual FoxPro 5.0 are 123×252 pixels with two lines of display, and in Visual FoxPro 6.0 they are 131×155 pixels with five lines of display, so the browser in Visual FoxPro 6.0 can display a third less real estate in tight situations.
Of course, the big change in the Class Browser is integration with the new Component Gallery.
The Browser Interface
Figure 1. The Class Browser interface
Here are a few things you should know about the Class Browser to increase your productivity with this tool.Expand table
Click this button to see the class code. If the output contains nested classes, the code won’t execute. Right-click this button and see the class code in HTML format. The window that appears is actually an instance of your browser, wherein you can use the Shortcut menu to view or print the HTML for displaying the source.
Use this button to create a new class. This new class can be a subclass of the currently selected class, a subclass of any other class, or a subclass of a Visual FoxPro base class.
You can redefine classes in the Class Browser with this icon. In Visual FoxPro 6.0, you can redefine a class to that of a different base class. You are warned that you will lose some intrinsic methods and properties in the process.
When you open a Method Code window, this button (which floats in its own toolbar independent of the Class Browser) allows you to view the code hierarchy in parent class methods.
View more than class libraries
In the Class Browser Open dialog box, note the different types of files that are supported. Figure 2 shows the Open dialog box pointing to the Component Gallery directory, with the drop-down list expanded to show the types of files you can show in the Class Browser.
Figure 2. The Class Browser Open dialog box
You can open Visual FoxPro forms in the Class Browser, and many of the features, such as code generation, work as you might expect. This is very handy for sharing your code with other users via e-mail.
If you open a Visual FoxPro project file, all the class libraries in that project will be visible in a single view. This is great if your project contains hundreds of classes and you have no idea in which library a particular class belongs.
In the Class Browser you can open any .exe or COM files (for example, .ocx, .dll, or .tlb) and display their public interfaces. If Help is available, pressing the spacebar invokes it.
To add controls to a form or class
In the Class Browser, open the .vcx (class library) file containing the class of the object you want to add to the form, select the class, and then use the drag-and-drop operation to move the Class icon to the design surface.
Invoke the class of a selected item on a form
In the form or Class Designer, select a control. If you then invoke the Class Browser, the class and its class library will be displayed with the class for the selected control highlighted. This works with running forms too, except that it uses the control that has focus.
Class Management
Bewildered by many of the class management activities that you can do with the Class Browser? Then see the article, “Managing Classes with Visual FoxPro.”
Write Your Own Add-Ins
Class Browser add-ins are a built-in way you can extend the Class Browser. Add-ins are useful because of the endless variety of things you can do with them. For example, you can write an add-in to recursively call the Class Browser’s ExportCode( ) method to generate all the code for a particular class library. You can also create an add-in to modify how ExportCode( ) works, like run beautify.app as a post process to format the exported code just as you please.
All Class Browser events and methods contain hooks to accept add-ins, and add-ins can also be run independently of Class Browser events and methods. See “Creating Add-Ins for the Visual FoxPro Class Browser” for more information.
There you have it: a quick cafeteria-style tour of some of the neat things in the Visual FoxPro 6.0 Class Browser. Of course, you should also read the Visual FoxPro online Help for more details. These are great tools, and if you use Visual FoxPro every day it’s likely that you could make much better use of them.
Appendix: Browser.dbf Structure
The Class Browser stores all its metadata in a table named Browser.dbf found in your HOME() directory. The Component Gallery also uses Browser.dbf to store its catalog-related information. Table 1 provides a field-by-field description of important elements in Browser.dbf.
Table 1. Browser.dbf MetadataExpand table
Field
Values
Description
B
G
PLATFORM
WINDOWS <EMPTY>
Applies to records NOT of TYPE=”ADDIN.” Applies only to records of TYPE=”ADDIN.”
•
•
TYPE
PREFW
ADDIN
Specifies that the record stores browser and gallery preferences. Specifies that the record stores add-in information.
•
•
ID
FORMINFO
BROWSER
METHOD
MENU
Specifies that a Class Browser record stores form preferences; specifies that Gallery record stores catalog information. Specifies that record contains default settings for the Class Browser. See the PROPERTIES field. Specifies that the record contains a Class Browser add-in that is tied to a particular event or method. Specifies that the record stores a Class Browser add-in that is NOT tied to a particular event or method, and is thus available on the Add-in menu.
• • • •
• • •
DEFAULT
.T. .F.
Specifies a Component Gallery catalog. Specifies a Class Browser record. The default is false (.F.).
•
GLOBAL
.T. .F.
Specifies that a gallery catalog is global. The default is false (.F.)
•
BACKUP
.F. .T.
Specifies whether a backup is attempted the next time the file is opened. The default is false (.F.).When you open a catalog or a .vcx file in the Class Browser or Component Gallery, this field in the associated browser.dbf record is queried. If the value is logical .T. (true), a search is made for a file of the same name in the backup subfolder. If the backup file doesn’t exist, one is automatically created in a subfolder called Backup. Then the BACKUP field is set to .F.Through add-in hooks or with any program that opens and updates browser.dbf, you can set this field to force the Class Browser or Component Gallery to automatically back up a file or table the next time that file is opened, and only the next time.This feature is used internally in one special case; when browser.dbf is first created after Visual FoxPro is installed, a new browser.dbf is created with the default catalogs (around 5 or so). The BACKUP field is set to .T. so that each catalog gets backed up the first time it is opened because Visual FoxPro does not install the associated backup catalog tables. Beyond that special function, it can be used at will by developers for their own purpose.
•
•
NAME
cFilename
A memo field that specifies the file name that relates to the current record. This value appears in the add-ins Shortcut menu if the add-in is NOT tied to an event or method.In Class Browser records the file extension can be .vcx, .pjx, .scx, .ovx, .dll, .exe, .app, or others. In Component Gallery records the file extension is .dbf.
•
•
DESC
cDescription
A memo field that contains the description of the catalog referred to in the NAME field.
•
METHOD
cMethodName
A memo field that specifies the method to which a Class Browser or Component Gallery add-in is tied. If the method field is equal to “*” the add-in will execute for all methods.
•
•
PROPERTIES
memo
A memo field that specifies the default settings.
•
SCRIPT
Internal Gallery use only.
•
PROGRAM
cPRGFilename
A memo field that contains the name of the program to be run by a .prg-based add-in.
•
•
CLASSLIB
cClasslibName
A memo field that contains the name of the class library to be used by a .vcx-based add-in.
•
•
CLASSNAME
cClassName
A memo field that contains the name of the class to be used by a .vcx-based add-in.
•
•
DISPMODE
<n>
Specifies the display mode of the class library. 1 = hierarchical 2 = alphabetic
•
TOP
<nnn>
A numeric field that specifies the stored top (y) coordinate for the Class Browser/Component Gallery form.
•
•
LEFT
<nnn>
A numeric field that specifies the stored left (x) coordinate for the Class Browser/Component Gallery form.
•
•
HEIGHT
<nnn>
A numeric field that specifies the stored height of the Class Browser/Component Gallery form.
•
•
WIDTH
<nnn>
A numeric field that specifies the stored width of the Class Browser/Component Gallery form.
•
•
HEIGHT1
<nnn>
A numeric field that specifies the stored height of the class and member description panes in the Class Browser.
•
HEIGHT2
<nnn>
A numeric field that specifies the stored height of the item description pane in the Component Gallery.
•
WINDOWSTAT
<n>
A numeric field that specifies the characteristics of the Class Browser or Component Gallery window. 0 = Normal window 1 = Minimized window 2 = Maximized window
•
•
PROTECTED
.F. .T.
A logical field that specifies whether protected members are displayed. The default is false (.F.).
•
EMPTY
.F. .T.
A logical field that specifies whether empty methods are to be displayed.
•
HIDDEN
.F. .T.
A logical field that specifies whether hidden members are to be displayed.
•
DESCBOXES
.F. .T.
A logical field that specifies whether description panes are to be displayed.
•
AUTOEXPAND
.F. .T.
A logical field that specifies whether hierarchical items in the treeview are to be automatically displayed expanded in the left-hand side pane.
•
PUSHPIN
.F. .T.
A logical field that specifies whether the display is always on top.
•
PCBROWSER
.F. .T.
A parent class toolbar flag. A logical field that specifies whether the toolbar is on for that file.
VIEWMODE
<n>
A numeric field that specifies the mode of the Gallery listview. 1. Large (standard) Icons 2. Small Icons 3. List 4. Report
•
FONTINFO
cFontPref
A memo field that contains the current display font preference.
•
•
FORMCOUNT
<n>
A numeric field that specifies the number of Class Browser instances running for the .vcx file.
•
•
UPDATED
<DateTime>
A datetime field that specifies when this record was last updated.
•
•
COMMENT
Unused
•
•
User1….4
Unused
•
•
About the Author
Steven specializes in developing multilingual, multisite, and other challenging software situations, including project turnarounds and cleanups. He is the creator of Steven Black’s INTL Toolkit, a multilingual framework for FoxPro and Visual FoxPro. He’s a regular speaker at Visual FoxPro conferences, and his contributions occasionally darken the pages of FoxPro books and magazines.
Supported Visual FoxPro SET Commands
Unsupported Visual FoxPro Commands and Functions
Symbols & Command
&& Command
Command
NAME?
? | ?? Command
??? Command
@ … BOX Command
@ … CLASS Command
@ … CLEAR Command
@ … EDIT – Edit Boxes Command
@ … FILL Command
@ … GET – Check Boxes Command
@ … GET – Combo Boxes Command
@ … GET – Command Buttons Command
@ … GET – List Boxes Command
@ … GET – Option Buttons Command
@ … GET – Spinners Command
@ … GET – Text Boxes Command
@ … GET – Transparent Buttons Command
@ … MENU Command
@ … PROMPT Command
@ … SAY – Pictures & OLE Objects Command
@ … SAY Command
@ … SCROLL Command
@ … TO Command
| Command
A ACCEPT Command
ACTIVATE MENU Command
ACTIVATE POPUP Command
ACTIVATE SCREEN Command
ACTIVATE WINDOW Command
ADD CLASS Command
ADD TABLE Command
ALTER TABLE – SQL Command
APPEND Command
APPEND FROM ARRAY Command
APPEND FROM Command
APPEND GENERAL Command
APPEND MEMO Command
APPEND PROCEDURES Command
ASSERT Command
ASSIST Command
AVERAGE Command
B BEGIN TRANSACTION Command
BLANK Command
BROWSE Command
BUILD APP Command
BUILD DLL Command
BUILD EXE Command
BUILD MTDLL Command
BUILD PROJECT Command
C CALCULATE Command
CALL Command
CANCEL Command
CD | CHDIR Command
CHANGE Command
CLEAR Commands
CLOSE Commands
CLOSE MEMO Command
COMPILE Command
COMPILE DATABASE Command
COMPILE FORM Command
CONTINUE Command
COPY FILE Command
COPY INDEXES Command
COPY MEMO Command
COPY PROCEDURES Command
COPY STRUCTURE Command
COPY STRUCTURE EXTENDED Command
COPY TAG Command
COPY TO ARRAY Command
COPY TO Command
COUNT Command
CREATE CLASS Command
CREATE CLASSLIB Command
CREATE COLOR SET Command
CREATE Command
CREATE CONNECTION Command
CREATE CURSOR – SQL Command
CREATE DATABASE Command
CREATE FORM Command
CREATE FROM Command
CREATE LABEL Command
CREATE MENU Command
CREATE PROJECT Command
CREATE QUERY Command
CREATE REPORT – Quick Report Command
CREATE REPORT Command
CREATE SCREEN – Quick Screen Command
CREATE SCREEN Command
CREATE SQL VIEW Command
CREATE TABLE – SQL Command
CREATE TRIGGER Command
CREATE VIEW Command
D DEACTIVATE MENU Command
DEACTIVATE POPUP Command
DEACTIVATE WINDOW Command
DEBUG Command
DEBUGOUT Command
DECLARE – DLL Command
DECLARE Command
DEFINE BAR Command
DEFINE BOX Command
DEFINE CLASS Command
DEFINE MENU Command
DEFINE PAD Command
DEFINE POPUP Command
DEFINE WINDOW Command
DELETE – SQL Command
DELETE Command
DELETE CONNECTION Command
DELETE DATABASE Command
DELETE FILE Command
DELETE TAG Command
DELETE TRIGGER Command
DELETE VIEW Command
DIMENSION Command
DIR or DIRECTORY Command
DISPLAY Command
DISPLAY CONNECTIONS Command
DISPLAY DATABASE Command
DISPLAY DLLS Command
DISPLAY FILES Command
DISPLAY MEMORY Command
DISPLAY OBJECTS Command
DISPLAY PROCEDURES Command
DISPLAY STATUS Command
DISPLAY STRUCTURE Command
DISPLAY TABLES Command
DISPLAY VIEWS Command
DO CASE … ENDCASE Command
DO Command
DO FORM Command
DO WHILE … ENDDO Command
DOEVENTS Command
DROP TABLE Command
DROP VIEW Command
E EDIT Command
EJECT Command
EJECT PAGE Command
END TRANSACTION Command
ERASE Command
ERROR Command
EXIT Command
EXPORT Command
EXTERNAL Command
F FIND Command
FLUSH Command
FOR EACH … ENDFOR Command
FOR … ENDFOR Command
FREE TABLE Command
FUNCTION Command
G GATHER Command
GETEXPR Command
GO | GOTO Command
H HELP Command
HIDE MENU Command
HIDE POPUP Command
HIDE WINDOW Command
I IF … ENDIF Command
IMPORT Command
INDEX Command
INPUT Command
INSERT – SQL Command
INSERT Command
J JOIN Command
K KEYBOARD Command
L LABEL Command
LIST Commands
LIST CONNECTIONS Command
LIST DATABASE Command
LIST DLLS Command
LIST OBJECTS Command
LIST PROCEDURES Command
LIST TABLES Command
LIST VIEWS Command
LOAD Command
LOCAL Command
LOCATE Command
LOOP Command
LPARAMETERS Command
M MD | MKDIR Command
MENU Command
MENU TO Command
MODIFY CLASS Command
MODIFY COMMAND Command
MODIFY CONNECTION Command
MODIFY DATABASE Command
MODIFY FILE Command
MODIFY FORM Command
MODIFY GENERAL Command
MODIFY LABEL Command
MODIFY MEMO Command
MODIFY MENU Command
MODIFY PROCEDURE Command
MODIFY PROJECT Command
MODIFY QUERY Command
MODIFY REPORT Command
MODIFY SCREEN Command
MODIFY STRUCTURE Command
MODIFY VIEW Command
MODIFY WINDOW Command
MOUSE Command
MOVE POPUP Command
MOVE WINDOW Command
N NOTE Command
O ON BAR Command
ON ERROR Command
ON ESCAPE Command
ON EXIT BAR Command
ON EXIT MENU Command
ON EXIT PAD Command
ON EXIT POPUP Command
ON KEY = Command
ON KEY Command
ON KEY LABEL Command
ON PAD Command
ON PAGE Command
ON READERROR Command
ON SELECTION BAR Command
ON SELECTION MENU Command
ON SELECTION PAD Command
ON SELECTION POPUP Command
ON SHUTDOWN Command
OPEN DATABASE Command
P PACK Command
PACK DATABASE Command
PARAMETERS Command
PLAY MACRO Command
POP KEY Command
POP MENU Command
POP POPUP Command
PRINTJOB … ENDPRINTJOB Command
PRIVATE Command
PROCEDURE Command
PUBLIC Command
PUSH KEY Command
PUSH MENU Command
PUSH POPUP Command
Q QUIT Command
R RD | RMDIR Command
READ Command
READ EVENTS Command
READ MENU Command
RECALL Command
REGIONAL Command
REINDEX Command
RELEASE BAR Command
RELEASE CLASSLIB Command
RELEASE Command
RELEASE LIBRARY Command
RELEASE MENUS Command
RELEASE PAD Command
RELEASE POPUPS Command
RELEASE PROCEDURE Command
RELEASE WINDOWS Command
REMOVE CLASS Command
REMOVE TABLE Command
RENAME CLASS Command
RENAME Command
RENAME CONNECTION Command
RENAME TABLE Command
RENAME VIEW Command
REPLACE Command
REPLACE FROM ARRAY Command
REPORT Command
RESTORE FROM Command
RESTORE MACROS Command
RESTORE SCREEN Command
RESTORE WINDOW Command
RESUME Command
RETRY Command
RETURN Command
ROLLBACK Command
RUN | Command
S SAVE MACROS Command
SAVE SCREEN Command
SAVE TO Command
SAVE WINDOWS Command
SCAN … ENDSCAN Command
SCATTER Command
SCROLL Command
SEEK Command
SELECT – SQL Command
SELECT Command
SET ALTERNATE Command
SET ANSI Command
SET ASSERTS Command
SET AUTOSAVE Command
SET BELL Command
SET BLOCKSIZE Command
SET BORDER Command
SET BROWSEIME Command
SET BRSTATUS Command
SET CARRY Command
SET CENTURY Command
SET CLASSLIB Command
SET CLEAR Command
SET CLOCK Command
SET COLLATE Command
SET COLOR OF Command
SET COLOR OF SCHEME Command
SET COLOR SET Command
SET COLOR TO Command
SET Command
SET COMPATIBLE Command
SET CONFIRM Command
SET CONSOLE Command
SET COVERAGE Command
SET CPCOMPILE Command
SET CPDIALOG Command
SET CURRENCY Command
SET CURSOR Command
SET DATABASE Command
SET DATASESSION Command
SET DATE Command
SET DEBUG Command
SET DEBUGOUT Command
SET DECIMALS Command
SET DEFAULT Command
SET DELETED Command
SET DELIMITERS Command
SET DEVELOPMENT Command
SET DEVICE Command
SET DISPLAY Command
SET DOHISTORY Command
SET ECHO Command
SET ESCAPE Command
SET EVENTLIST Command
SET EVENTTRACKING Command
SET EXACT Command
SET EXCLUSIVE Command
SET FDOW Command
SET FIELDS Command
SET FILTER Command
SET FIXED Command
SET FORMAT Command
SET FULLPATH Command
SET FUNCTION Command
SET FWEEK Command
SET HEADINGS Command
SET HELP Command
SET HELPFILTER Command
SET HOURS Command
SET INDEX Command
SET INTENSITY Command
SET KEY Command
SET KEYCOMP Command
SET LIBRARY Command
SET LOCK Command
SET LOGERRORS Command
SET MACKEY Command
SET MARGIN Command
SET MARK OF Command
SET MARK TO Command
SET MEMOWIDTH Command
SET MESSAGE Command
SET MULTILOCKS Command
SET NEAR Command
SET NOCPTRANS Command
SET NOTIFY Command
SET NULL Command
SET NULLDISPLAY Command
SET ODOMETER Command
SET OLEOBJECT Command
SET OPTIMIZE Command
SET ORDER Command
SET PALETTE Command
SET PATH Command
SET PDSETUP Command
SET POINT Command
SET PRINTER Command
SET PROCEDURE Command
SET READBORDER Command
SET REFRESH Command
SET RELATION Command
SET RELATION OFF Command
SET REPROCESS Command
SET RESOURCE Command
SET SAFETY Command
SET SECONDS Command
SET SEPARATOR Command
SET SKIP Command
SET SKIP OF Command
SET SPACE Command
SET STATUS BAR Command
SET STATUS Command
SET STEP Command
SET STRICTDATE Command
SET SYSFORMATS Command
SET SYSMENU Command
SET TALK Command
SET TEXTMERGE Command
SET TEXTMERGE DELIMITERS Command
SET TOPIC Command
SET TOPIC ID Command
SET TRBETWEEN Command
SET TYPEAHEAD Command
SET UDFPARMS Command
SET UNIQUE Command
SET VIEW Command
SET VOLUME Command
SET WINDOW OF MEMO Command
SHOW GET Command
SHOW GETS Command
SHOW MENU Command
SHOW OBJECT Command
SHOW POPUP Command
SHOW WINDOW Command
SIZE POPUP Command
SIZE WINDOW Command
SKIP Command
SORT Command
STORE Command
SUM Command
SUSPEND Command
SYS(2001) – SET … Command Status
T TEXT … ENDTEXT Command
TOTAL Command
TYPE Command
U UNLOCK Command
UPDATE – SQL Command
UPDATE Command
USE Command
V VALIDATE DATABASE Command
W WAIT Command
WITH … ENDWITH Command
X Y Z ZAP Command
ZOOM WINDOW Command
Visual FoxPro IDE Enhancements
This version of Visual FoxPro includes many enhancements to the Interactive Development Environment (IDE), including changes to the window behavior, Options dialog box, and system menus.
Docking Windows Visual FoxPro provides docking functionality to its core IDE windows. When you drag a dockable window to a Visual FoxPro window boundary, it reconfigures against the boundary you choose. There are three types of docking behavior supported. For details, see Docking Windows.
Normal Docking Windows dock to a boundary of the main Visual FoxPro window. Linked Docking Windows dock to each other and simultaneously share a dockable window container. Tabbed Docking Windows dock to each other and share the full window through tabs. The following windows are dockable:
Command window Document View window Data Session window Properties window Debugger window Watch window Trace window Output window Locals window Call Stack window Document View Window The new Document View window makes it possible for you to view and navigate to any procedure, function, #DEFINE definition or preprocessor directive in your program or class. The Document View window is a more flexible modeless window that replaces the Procedures/Functions model dialog box that was available in previous versions of Visual FoxPro. For details, see Document View Window.
Properties Windows The following enhancements have been added to the Properties window. For details, see Properties Window.
The Properties window can now be activated without the form/class designer opened. Properties of the desktop (_SCREEN) are displayed even if the form/class designer is not active. ActiveX control properties, events, and methods are now displayed in a different color to distinguish them from native ones. Methods and events that have code in parent classes now show inherited class information. The Properties window is now available from the Windows menu and the Standard toolbar. Command Window The contents of the Command window are saved to a file, _command.prg. You can clear the window by selecting Clear from the Shortcut menu. The file is read only at startup, so it can be used by multiple instances of Visual FoxPro. For details, see Command Window.
Standard Toolbar The Standard toolbar now provides easy access to common Visual FoxPro tools, including the Properties window, Document View window, Class Browser, and Object Browser.
Report Designer The Report Designer is much more accessible through the keyboard and now includes the following functionality. For details, see Report Designer.
Bands menu option Makes it possible for you to access the dialog boxes for the properties of individual bands. Insert Control menu option Makes it possible for you to select controls to place in your report. Control of Foreground and Background Colors Makes it possible for you to manage foreground and background colors through additions to the Format menu, when the Report Designer is open. The Report Designer also includes new keyboard navigation:
CTRL+TAB to toggle in and out of Tab mode. TAB and SHIFT+TAB to move between report objects. CTRL+E to toggle into and ESC to toggle out of Label editing. Options Dialog Box A number of enhancements have been added to the Options dialog box to offer improved use of the IDE. For details, see Options Dialog Box.
View Tab You can now control the number of Most Recently Used files that are displayed in the File menu and in IntelliSense. File Locations Tab The File Locations tab makes it possible for you to specify the paths of the following additional Visual FoxPro items: File Type Description FoxCode Table Specifies the location of FoxCode.dbf for IntelliSense records. For more information, see _FoxCode System Variable and Customizing IntelliSense Using FoxCode. FoxTask Table Specifies the location of the table that retains current TaskList shortcut records. For details, see _FoxTask System Variable. IntelliSense Manager Specifies the location of the program assigned to the _Codesense System Variable. Task List Specifies the location of the program assigned to the _TASKLIST System Variable. Object Browser Specifies the location of the program assigned to the _ObjectBrowser System Variable.
IDE Tab The IDE tab makes it possible for you to specify formatting, Save, appearance, and behavior settings for Visual FoxPro file types. The Edit, Format, Tools, and Window menus incorporate changes to add editor and other functionality to the IDE. Miscellaneous IDE Enhancements In addition to the above-mentioned improvements, Visual FoxPro now contains a wealth of other improvements, including:
Your Foxuser.dbf resource file is now opened as shared so that you can run multiple instances or allow multiple users to access it simultaneously. The Windows menu has a new Cascading menu item to allow you to have your windows reorganized on your desktop in a cascading fashion. The form/class designer allows you to more easily drilldown into container classes such as pageframes. By holding down the CTRL or CTRL+SHIFT keys, you can now click directly on a control within a container to select it. In the form/class designer, you can hold down the CTRL or CTRL+SHIFT keys, and use the arrow keys to move or resize a control by the amount of your grid scale associated with the Snap-to-Grid setting. Additional information and value tips have been added to a number of design surfaces. Extended information is displayed for fields in the database and view designers and the data environment. The Method editor displays value tips for events and methods in the procedures drop-down list. The Watch and Locals debug windows have value tips for expressions that exceed the width of the column displaying the value. WAIT WINDOW and Visual FoxPro System dialog boxes (for example, error) now inherit their font settings from your Windows Appearance setting in the Display control panel. Additionally, you can use CTRL+C to copy the contents of a System dialog box to the Clipboard. See Also Visual FoxPro Interactive Development Environment | Task List Manager | IntelliSense Overview | Visual FoxPro Editor Enhancements | Editor Tab, Tools Options Dialog Box | File Locations Tab, Tools Options Dialog Box | IDE Tab, Tools Options Dialog Box | Report Designer | Docking Windows | Command Window | Document View Window | Data Session Window | Properties Window | Debugger Window | Watch Window | Trace Window | Debug Output Window | Locals Window | Call Stack Window
Visual FoxPro Editor Enhancements
The Visual FoxPro Editor includes many enhancements to make it easier to edit and examine code including additional functionality and redistribution of settings and controls.
The following IDE changes support some of the added functionality of the Visual FoxPro Editor.
Bookmarks and Shortcuts
The Visual FoxPro Editor now displays a selection margin on the left side of the window so you can highlight a line and specify breakpoints, bookmarks or shortcuts. For details, see Creating Bookmarks and Shortcuts.
Bookmarks reference a specific line of source code you might want to return to for further editing or viewing. You can add a shortcut from the selection margin or Editor menu. Bookmarks are not saved between sessions of Visual FoxPro.
Similar to Bookmarks, Shortcuts are persistent code markers that are stored in the Foxtask table and can be accessed using the Task List Manager.
Find Dialog Box
The Find dialog box has new support for performing searches using wildcard pattern matching. Additionally, you can perform searches using several new keyboard shortcuts without opening up the Find dialog box.
Embedded Hyperlinks
Visual FoxPro Editor supports embedding and enabling hyperlinks. When you include any valid hyperlink protocol trigger, Visual FoxPro applies hyperlink attributes to the entered text. For details, see “Embedding Hyperlinks” in Dynamic Information Sharing.
Options Dialog Box
The Options dialog box has the following enhancements: Editor Tab The Editor tab of the Options dialog box provides the functionality of the Syntax coloring tab, as well as additional options related to the Visual FoxPro Editor. IDE Tab The IDE tab makes it possible for you to specify settings for various types of editors used by Visual FoxPro, including programs, methods, stored procedures, text files and memos. These settings are the defaults used for a new document of a specific type. Once a document is created, its specific settings are stored in the Foxuser resource file, so they can be restored when that document is opened again.
An Override check box provides the ability to globally override a specific document’s settings stored in the Foxuser resource file.
If you use Visual FoxPro editors in your distributed applications, you can control their functionality by limiting access to the IntelliSense functions and disabling the availability of hyperlinks.
Miscellaneous Editor Enhancements
In addition to the previously mentioned improvements, this version of Visual FoxPro contains a wealth of other improvements including:
In this version of Visual FoxPro, the editor opens files as DENY WRITE rather than DENY READ, so a file can be read or copied even while it is open in the Visual FoxPro Editor.
You can swiftly selections of text to upper or lower case from the Format or Editor shortcut menus.
The Format menu has new options to toggle word wrap and viewing white space (tab, space and paragraph marks).
A dirty file indicator mark (asterisk) is now displayed next to the file name in the title bar of the editor window if the document contains any unsaved changes.
You can now control whether indentation inserts tabs or spaces. The amount of the indentation can also be set.
The characters used for comment strings when you select Comment from the Format or Editor shortcut menus can be set in the Options dialog box.
While editing source code, the editor now highlights the parameter contents of a function, when you type a close parenthesis character. The duration of the highlighting can be set in the Options dialog box.
A number of new keyboard shortcuts have been added to the editor to improve developer productivity. See Keyboard Shortcuts topic for more details.
IntelliSense in Visual FoxPro
IntelliSense displays information in windows and lists that assists you with statement and function completion syntax and displays the available variables, objects, properties, methods, and events of objects.
In Visual FoxPro, although IntelliSense always is available for native commands and functions, strong typing allows full IntelliSense support in editor windows for all user-defined code elements.
The following are some features of IntelliSense in Visual FoxPro:
When you type a table-related keyword in the Visual FoxPro Editor or in the Command window, the Auto Table/Field drop-down list displays the valid members in the current context.
When you type one of the listed commands in the Command window, Visual FoxPro displays a list of the appropriate MRU files.
You can access IntelliSense functionality automatically for containers and controls in Visual FoxPro visual designers, such as the Form designer.
IntelliSense functionality is activated as you type in the Visual FoxPro Editor, in code editor windows for methods, and in the Command window.
Use the Visual FoxPro IntelliSense Manager Window to modify the appearance and behavior of the IntelliSense functionality.
You can modify or add to the functionality of IntelliSense in Visual FoxPro by modifying the contents of FoxCode.dbf.
You can enable or disable IntelliSense functionality programmatically by setting the _VFP.EditorOptions property value. For more information, see EditorOptions Property.
Task List Manager
The Visual FoxPro Task List Manager makes it possible for you to keep track of Shortcuts to locations in your code that you might want to return to for further editing. Additionally, the Task List Manager makes it possible for you organize your personal and business to-do lists in a manner similar to Microsoft Outlook.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
Several news articles, press releases, and opinion pieces cover diverse topics relevant to Saudi Arabia. The collection discusses economic matters like inflation rates, private sector growth, and tourism trends in the Kingdom. It also highlights various social initiatives such as community development programs, awards for social investment, and efforts to combat childhood cancer. Furthermore, there are articles on sports, including the Saudi Forum for Media and Formula E racing, and international relations, notably concerning the war in Ukraine and Saudi Arabia’s role in global energy markets. The articles demonstrate a wide array of the region’s current events and concerns, from local cultural topics to coverage of global events with regional influence.
Source Material Review & Study Guide
I. Quiz: Short Answer Questions
Answer the following questions in 2-3 sentences each.
According to the provided text, how does the Kingdom maintain a lower inflation rate compared to other G20 countries?
What economic sectors are expected to contribute to Saudi Arabia’s non-oil sector growth in the coming years?
What is the purpose of the King Salman Humanitarian Aid and Relief Center? Give at least one example of the center’s work in 2025, according to the text.
What health issue was the focus of a campaign on February 15, 2025, as highlighted in the text? What are the most common types of this health issue in children?
What was the focus of the 8th International Conference on Data Science and Machine Learning (CDMA2025) at Prince Sultan University?
What is the King Abdullah bin Abdulaziz International Centre for Interreligious and Intercultural Dialogue (KAICIID) doing to promote the Saudi national identity?
According to the text, how is Saudi Arabia working to create an attractive investment environment?
What is the “Saudi Media Forum 2025” and what is its purpose?
According to the text, what are some challenges of the “Generative AI” adoption among Saudi organizations?
According to the text, what was the United States’ involvement in Ukrainian matters?
II. Quiz Answer Key
The Kingdom maintains a lower inflation rate by controlling prices of housing, water, electricity, fuel, and gas, and providing direct subsidies, according to the text. This is despite increases in some consumer prices, such as food and transportation.
The non-oil sector is expected to grow due to efficiency, flexibility, and innovation, supported by public and private investments in strategic sectors.
The center aims to alleviate the suffering of people in need and those affected by crises and disasters. In 2025, they distributed over 3,000 tons of dates in Yemen and food baskets and health kits in Syria.
The campaign focused on childhood cancer, raising awareness about early detection, diagnosis, and treatment. The most common types of cancer in children are leukemia, brain cancer, lymphoma, neuroblastoma, and soft tissue sarcomas.
The conference focused on the transformative impact of artificial intelligence, advanced systems, robotics, and software revolution in various sectors.
KAICIID fosters dialogue and builds bridges to promote a cohesive society and strengthen civic engagement.
Saudi Arabia is providing attractive investment opportunities for the private sector, both domestic and international, including offering investment opportunities worth $1.8 trillion to global companies and incentives for the local private sector.
The Saudi Media Forum 2025 is an event aimed at developing the media sector. It includes sessions focused on developing talent, modernizing the sector, and defining new roles for media in various fields, showcasing the Kingdom’s achievements and developmental progress.
Challenges include skills gaps and difficulties in scaling generative AI workloads from development to production. Data security and privacy are also concerns.
The text mentions that Russian and American officials discussed settling the conflict in Ukraine and ensuring security guarantees for the country’s strength and prosperity.
III. Essay Questions
Address each of the following essay prompts in a well-structured essay of approximately 500-750 words.
Analyze Saudi Arabia’s economic diversification efforts as depicted in the provided text. What are the key strategies and sectors targeted, and what challenges and opportunities are presented by these efforts?
Examine the role of humanitarian aid and international cooperation in Saudi Arabia’s foreign policy. How does the King Salman Humanitarian Aid and Relief Center contribute to the Kingdom’s global image and influence, and what are the potential limitations of this approach?
Discuss the significance of cultural initiatives and events, such as the Saudi Media Forum and art exhibitions, in promoting Saudi Arabia’s national identity and soft power. How effective are these initiatives in shaping international perceptions of the Kingdom, and what are the potential areas for improvement?
Evaluate the impact of technological advancements, particularly in artificial intelligence and digital transformation, on Saudi Arabia’s economy and society. What opportunities and challenges do these technologies present, and what measures are being taken to ensure their responsible and beneficial implementation?
Analyze the relationship between Saudi Arabia and the United States as it is represented in the provided texts. What are the key areas of cooperation and contention between the two countries, and how might these dynamics evolve in the coming years?
IV. Glossary of Key Terms
Inflation: A general increase in prices and fall in the purchasing value of money.
G20: The Group of Twenty, an intergovernmental forum comprising 19 countries plus the European Union. It works to address major issues related to the global economy.
Non-Oil Sector: Sectors of the economy that do not involve the production or sale of petroleum products.
Economic Diversification: The process of shifting an economy away from a single income source toward multiple revenue streams from a wider array of sectors.
Strategic Sectors: Key industries identified as crucial for long-term economic growth and national development.
Public Investment Fund (PIF): A sovereign wealth fund owned by Saudi Arabia, playing a key role in economic diversification and investment projects.
Humanitarian Aid: Assistance provided to people in distress or need, often in response to natural disasters or conflicts.
Soft Power: The ability to influence others through attraction rather than coercion or payment.
Artificial Intelligence (AI): The theory and development of computer systems able to perform tasks that normally require human intelligence.
Digital Transformation: The integration of digital technology into all areas of a business, fundamentally changing how it operates and delivers value.
Generative AI: A type of artificial intelligence that can generate new content, such as text, images, or audio.
Sustainable Tourism: Tourism that takes full account of its current and future economic, social, and environmental impacts, addressing the needs of visitors, the industry, the environment, and host communities.
Saudi Vision 2030: A strategic framework to reduce Saudi Arabia’s dependence on oil, diversify its economy, and develop public service sectors such as health, education, infrastructure, recreation, and tourism.
National Identity: A sense of belonging to a nation and a feeling of cohesion among its people, often based on shared culture, values, and history.
Saudi Arabia in 2025: Growth, Innovation, and Global Role
Okay, here’s a briefing document summarizing the key themes and ideas from the provided text excerpts.
Briefing Document: Analysis of “20692.pdf” Excerpts
Overview: The excerpts from “20692.pdf” present a snapshot of Saudi Arabia in early 2025, covering diverse topics ranging from economic indicators and investment strategies to cultural events, healthcare initiatives, and international relations. The overall tone is optimistic, highlighting progress, innovation, and the Kingdom’s growing role on the global stage.
Key Themes & Ideas:
Economic Growth and Diversification: A major theme is the Kingdom’s efforts to diversify its economy away from oil dependence and attract foreign investment.
Quote: ” (The Minister of Economy and Planning praised the efficiency and flexibility of the Saudi private sector, expecting the non-oil sector to grow by 4.8% in 2025 and 6.2% in 2026, supported by innovation). This quote demonstrates the expected growth in the non-oil sector.
Quote: “.” (…that the Kingdom needs investments worth a trillion dollars in infrastructure over the next seven to ten years…). This highlights the scale of investment needed.
Low Inflation: Saudi Arabia recorded the lowest inflation rate among the G20 countries, with a rate of 2.0% in January 2025 compared to January of the previous year.
Investment in Strategic Sectors: The Kingdom is actively investing in strategic sectors and infrastructure projects.
Quote: (The importance of cooperation between the governmental and private sectors, which the Kingdom is witnessing, and the role of the Public Investment Fund in this context). This emphasizes the importance of public-private partnerships.
The Public Investment Fund (PIF) is a central player, offering investment opportunities to the private sector worth 40 billion riyals.
Technological Advancement and Digital Transformation: There’s a strong emphasis on embracing digital technologies and fostering innovation.
Quote: “جامعة سلطان و»الحكومة الرقمية« تعززان التحول الرقمي” (Sultan University and the “Digital Government” enhance digital transformation). This illustrates efforts to promote digital transformation.
Launch of AI initiatives: King Saud University launched five initiatives to support digital transformation in the field of artificial intelligence, including establishing an AI office, creating AI labs, and launching AI-related student clubs.
The “Saudi Forum for Artificial Intelligence” highlights the transformative impact of AI and robotics across various sectors.
Humanitarian Efforts and Global Role: The Kingdom is actively engaged in humanitarian aid and plays a significant role in international affairs. (King Salman Center… for Relief and Humanitarian Works implements its humanitarian projects to alleviate the suffering of humanity in a number of countries in need and affected by crises and disasters). This quote shows the Kingdom’s focus on humanitarian aid.
The King Salman Center for Relief and Humanitarian Aid is a key player, implementing projects in Yemen, Syria, and Sudan.
The “Ita’am 4” initiative aims to distribute 390,109 food baskets in 27 countries during Ramadan.
Cultural Events and Promotion of National Identity: The excerpts highlight cultural events and initiatives aimed at promoting Saudi heritage and identity.
The “Saudi Forum for Media 2025” aims to develop the media sector and showcase the Kingdom’s achievements.
Exhibition on Al-Sharqiya doors: King Abdulaziz Center for World Culture (“Ithra”) showcased traditional doors from the Eastern region as a cultural heritage.
Focus on Social Issues: There’s attention given to important social issues such as child cancer awareness.
February 15th marks the International Childhood Cancer Day, with the Kingdom participating in awareness campaigns.
Prince Sita Award: Honoring winners of the Prince Sita Award, highlighting the importance of building relationships and promoting values of cooperation and compassion.
Tourism: There is a growing demand for rental housing in Mecca due to the increasing seasons and development movement.
The Grand Mosque and the Prophet’s Mosque in Mecca and Medina were captured in photographs, providing an opportunity for the public to discover the beauty of the holy sites and the relationship of pilgrims and worshippers to the Islamic sites.
Points of Interest:
Low Inflation Amidst Global Concerns: The Kingdom’s ability to maintain low inflation, as stated, is noteworthy given global inflationary pressures.
AI and its Adoption: AI will be a challenge for organizations due to skills gaps and experience.
Regional and International Involvement: Reports of Saudi Arabia’s involvement in international affairs, for example the meeting with other countries to discuss the war on Ukraine.
Potential Implications:
The economic diversification efforts, if successful, could significantly strengthen the Kingdom’s long-term stability and reduce its vulnerability to oil price fluctuations.
Investment in technology and digital transformation could lead to greater innovation and competitiveness.
Continued humanitarian efforts could enhance Saudi Arabia’s reputation and influence in the international community.
Overall, the excerpts portray a dynamic and ambitious Saudi Arabia actively pursuing economic development, technological advancement, and a prominent role in global affairs.
Saudi Arabia: Recent Developments and Initiatives FAQ
FAQ about Recent Developments and Initiatives in Saudi Arabia
Here are some frequently asked questions based on the provided news excerpts, covering various aspects of Saudi Arabian society, economy, and international relations:
1. What is the status of inflation in Saudi Arabia compared to other G20 countries?
Saudi Arabia has maintained one of the lowest inflation rates among G20 countries. In January 2025, the inflation rate was recorded at 2.0%, driven by increased prices in housing, water, electricity, gas, and fuel, but offset by decreases in transportation costs. The Saudi Central Bank’s policies and the government’s measures, including direct support and capping fuel prices, have been effective in curbing inflation.
2. How is Saudi Arabia promoting private sector involvement in its economic transformation?
The Saudi government acknowledges that it cannot solely finance its ambitious infrastructure projects. Minister of Economy and Planning emphasized the efficiency, flexibility, and innovation of the Saudi private sector, which is projected to drive non-oil sector growth. The Public Investment Fund (PIF) is playing a key role in this effort, with initiatives to offer investment opportunities worth billions of riyals to the private sector, aiming to boost local content and stimulate the economy.
3. What steps is Saudi Arabia taking to advance digital transformation and artificial intelligence (AI)?
Saudi Arabia is heavily investing in digital transformation, with initiatives like the “KSU Smartest” event at King Saud University showcasing AI applications. King Saud University is launching a new AI office and establishing AI and learning labs across all colleges. There are also programs to develop skills in AI and robotics, aimed at aligning with the Kingdom’s Vision 2030 goals.
4. How is Saudi Arabia addressing humanitarian needs both domestically and internationally?
The King Salman Humanitarian Aid and Relief Centre (KSRelief) is actively involved in providing aid to various countries, including Yemen, Syria, and Sudan. They distribute food baskets, medical supplies, and other forms of assistance to those affected by crises and disasters. Domestically, initiatives like the “Itaam 4” campaign aim to distribute food packages during Ramadan to needy families across Saudi Arabia.
5. What efforts are being made to enhance tourism and cultural experiences in Saudi Arabia?
Developments include a focus on the Red Sea as a tourist destination, as well as promoting cultural events. There’s also an effort to improve the experiences for visitors to holy sites like Mecca and Medina, and to promote Saudi Arabia’s cultural heritage through events such as the “Expo Juffer” photography exhibition.
6. How is Saudi Arabia emphasizing sustainability in tourism and travel?
A growing number of travelers (over 80%) consider the environmental impact when planning their trips, surpassing the regional average. While 71% confirmed sustainable practices in their accommodations during their last vacation, only 40% actively sought these options before booking, highlighting an area for improvement.
7. What role is the Saudi Arabian government playing in the development of the real estate sector?
The Saudi Arabian real estate sector is experiencing significant growth, driven by rising demand for housing in major cities. The government is issuing numerous building permits and working to transfer knowledge and expertise to the private sector to support this growth and align with Vision 2030.
8. What are some of the challenges and opportunities facing the Saudi media landscape?
The Saudi media landscape is undergoing a rapid transformation, driven by digital technologies and AI. The Saudi Media Forum serves as a platform to discuss these changes and identify ways to enhance Saudi Arabia’s position as a leading media force. There is a focus on correcting stereotypes, promoting Saudi culture, and leveraging media for diplomatic and cultural exchange. Some challenges include navigating issues of media integrity and adapting to new technologies.
Saudi Arabia: Transformation and Global Role
Saudi Arabia is a country undergoing significant transformation and playing an increasingly important role on the global stage, as evidenced by a variety of initiatives and events.
Here’s a breakdown of key aspects:
Economic Development and Investment: Saudi Arabia is projected to become one of the most attractive investment destinations globally, drawing investors from around the world. The Kingdom is undertaking massive infrastructure projects valued at over a trillion dollars to boost the national economy and attract foreign investment. These projects are not merely temporary boosts but are designed to create a long-term economic foundation. The country aims to develop its infrastructure to meet current needs and ensure its position in the future global market.
Global Dialogue Facilitation: Saudi Arabia is emerging as a preferred location for international dialogue among world leaders. The Kingdom has expressed its willingness to host summits and promote dialogue between various nations, such as Russia and Ukraine, to achieve lasting peace. This reflects the country’s commitment to finding political solutions to global crises and collaborating with international partners to address pressing global issues.
Low Inflation Rate: Saudi Arabia has maintained one of the lowest inflation rates among the G20 countries. In January 2025, the inflation rate was recorded at 2.0% compared to the previous year.
Support for Regional Stability: The Kingdom is a proponent of regional stability, offering its support to Lebanon in response to attempts to undermine the safety of Lebanese citizens.
Technological Advancement: Saudi Arabia is actively promoting digital transformation through initiatives such as the CDMA2025 conference and collaborations between universities and digital government entities. These efforts aim to enhance research, knowledge transfer, and skill development in digital governance and internet governance.
Cultural and Heritage Preservation: The Kingdom is investing in the preservation and promotion of its cultural heritage through various initiatives, such as supporting environmental tourism, highlighting natural resources, and organizing festivals.
Philanthropic Activities: Saudi Arabia is recognizing and celebrating contributions to humanitarian and national achievements through awards like the Princess Sita Award for Development.
Arts and Culture Promotion: Saudi Arabia is actively involved in promoting arts and culture through initiatives like the King Abdulaziz Center for World Culture’s (Ithra) award for Cultural Communication, exhibitions, and collaborations with organizations like the Red Sea Film Foundation.
Space Program: The Kingdom is investing in its space program and plans to launch the Saudi Space Agency’s first center to study the future of space, reflecting the country’s ambition to be at the forefront of space exploration and technology.
Saudi Arabia Inflation Rate, January 2025
Here’s what the sources say about inflation rates:
Low Inflation Rate Saudi Arabia has maintained one of the lowest inflation rates among the G20 countries.
In January 2025, the inflation rate was recorded at 2.0% compared to January of the previous year.
The increase in inflation was driven by a rise in the prices of the housing, water, electricity, and gas section by 8.0%. Additionally, the cost of food and beverages increased by 0.8%, the prices of personal goods and services went up by 3.3%.
There was a decline in the prices of the transport section by 1.9%.
Experts confirm that Saudi Arabia continues to control inflation rates at the lowest levels due to its appropriate financial policies and measures. These policies enable it to maintain those low levels compared to other economies, in addition to continuously stimulating activities to increase investment, which supports the labor market, improves domestic demand, and stimulates consumption.
January 2025 statistics indicated that the rent prices for housing increased by 9.7% in January 2025, due to the increase in villa rents by 7.7%. The housing section is influential in the continuation of the annual inflation rate for December 2024, given its weight of 25.5%.
Food and beverage prices showed an increase of 0.8%, driven by a rise in vegetable prices of 5.6%. The prices of miscellaneous goods and services also increased by 3.3%, affected by the increase in the prices of jewelry, watches, and precious artifacts. Restaurant and hotel prices recorded an increase of 0.8%, driven by an increase in hotel service prices of 3.1%, while furnished apartments recorded an increase of 0.6%, affected by an increase in tuition fees for pre-primary and primary education by 0.6%.
The bulletin indicated a decrease in the prices of the furnishing and household equipment section by 2.4%, due to a decrease in the prices of furniture, carpets, and floor coverings by 4.0%. The clothing and footwear section decreased by 1.5%, affected by a decrease in the prices of ready-made clothes by 3.3%.
Car purchases prices decreased by 1.9%.
On a monthly basis, the consumer price index recorded a slight increase of 0.3% in January 2025 compared to December 2024. The housing section increased by 0.3%, driven by an increase in actual housing rent prices. The food and beverage section also witnessed an increase of 0.3%, and the prices of fish and seafood increased by 1.7%.
The prices of personal goods and services increased by 0.5%.
There was no change in the prices of restaurants and hotels.
The advisor Dr. Abdulrahman Biba, mentioned that the continuation of the inflation rate at this low level compared to the inflation rates recorded by the economies of the world confirms the success and effectiveness of the financial policies and measures followed by the Saudi Central Bank, as well as the governmental efforts, including direct support and setting a maximum price ceiling for fuel.
The consumer price index (CPI) reflects changes in the prices paid by consumers for a fixed basket of goods and services consisting of 490 items. This basket is based on the results of the household expenditure and income survey conducted in 2018, and the relevant prices are collected through field visits to points of sale.
Social Investment Initiatives in Saudi Arabia
Here’s what the sources say about social investment in Saudi Arabia:
Princess Sita Award for Development This award recognizes and celebrates contributions to humanitarian and national achievements.
Investment in Organizational Culture Investing in developing a strong organizational culture is a sustainable approach to achieving the desired goals of the company, its profitability, environment, and reputation. A positive work culture ensures employee commitment, which in turn translates into innovation and productivity.
Initiatives for Social Responsibility The Saudi government supports social responsibility through various initiatives and programs. For example, the Al Watania Company for Industries was awarded for its efforts in social work, and NEOM was recognized for its social responsibility initiatives and programs within NEOM.
Support for Charitable Organizations Charitable organizations and associations play an important role in serving the community. They provide programs for orphans and aim to deliver the best services to them.
Community Partnerships Partnerships between governmental entities and the private sector contribute to achieving sustainable development. For example, a collaboration in the Al-Qassim region aims to increase green spaces, improve the environment, and develop operational efficiency.
Initiatives to Improve the Urban Landscape The Riyadh region is undertaking initiatives to improve the urban landscape and foster a sense of belonging among residents. For example, there are initiatives to name gardens, enhance local identity, and promote community engagement in decision-making related to public facilities.
Support for Families of Patients Some organizations focus on providing support and care for patients and their families. For instance, the “Friends of Patients” association exemplifies efforts to meet patient needs.
Global Oil Market: Production, Demand, and Geopolitics
Here’s what the sources say about oil:
Oil Prices and Production: There is discussion regarding Saudi Arabia potentially increasing oil production to utilize its full capacity, depending on the long-term strategy and requirements for economic growth.
Global Oil Market Dynamics: Some countries should consider building new oil pipelines after tariffs revealed weaknesses in infrastructure. The US President threatened trade wars.
Russian Oil in Asia: An Indian oil minister stated that refineries in South Asia would only purchase Russian oil if supplied by non-sanctioned foreign companies.
China’s Fuel Demand: The International Energy Agency (IEA) cautioned that China’s demand for transportation fuel may have peaked, with overall fuel consumption potentially reaching 8.1 million barrels per day by the end of 2024.
US-Canada Relations: Canada sees some parts of the trade relationship with the United States as unsustainable.
Houthi Attacks on Oil Facilities: There’s mention of Houthi attacks on oil facilities, particularly affecting areas like Bab al-Mandab, with concerns about their impact on maritime navigation and global trade.
Hedge Funds and Oil Prices: There is mention of hedge funds reducing their bullish bets on oil prices, which could influence market dynamics.
Saudi Aramco’s Production and Projects: Saudi Aramco is moving forward with projects related to unconventional gas and liquids, which could impact future oil and gas production capacities.
Saudi Arabia’s Inflation Rate and Economic Strategies in January 2025
Saudi Arabia maintained a low inflation rate in January 2025 through several key strategies and policies. The inflation rate was recorded at 2.0% in January 2025, compared to January of the previous year.
Key factors that contributed to keeping inflation under control include:
Financial Policies and Measures: Implementing appropriate financial policies and measures enabled the Kingdom to maintain low inflation levels compared to other economies.
Continuous Investment Stimulation: Continuously stimulating activities to increase investment supported the labor market, improved domestic demand, and stimulated consumption.
Government Support: The government provided direct support and set a maximum price ceiling for fuel.
Housing Market Influence: The housing section significantly influenced the annual inflation rate, with rent prices for housing increasing by 9.7% in January 2025 due to a 7.7% increase in villa rents.
Consumer Price Index (CPI): The CPI, which reflects changes in prices paid by consumers for a fixed basket of 490 items, is monitored to understand and manage inflation.
Two Holy Mosques: Spiritual Depth Through Photography
The objective of the “اك�سبوجر” 2025 photography exhibition is to showcase the depth of the spiritual aspect of the Two Holy Mosques by transforming ordinary scenes into artistic paintings that reflect the Islamic arts and architecture in these two mosques.
Key goals of the exhibition are:
To offer an exploration of the human experience using photography as a tool for visual storytelling.
To capture moments of worship and reverence of pilgrims and worshippers.
To provide an opportunity to appreciate the creative talent of Saudi photographers in portraying the holiest Islamic sites.
To highlight the harmony and spiritual depth within the scenes of Muslim worshippers circling the Kaaba.
Saudi Forum for Media 2025: Strategic Dimensions
The Saudi Forum for Media 2025 has several strategic dimensions aimed at enhancing the Kingdom’s role and influence in the global media landscape. These dimensions include:
Adapting to Global Changes: The forum aims to help Saudi media keep pace with global transformations and to enhance the Kingdom’s position as an effective soft power.
Exploring Opportunities and Challenges: It will address challenges and opportunities related to digital developments that can be leveraged to boost Saudi media’s influence internationally.
Geopolitical Role and Vision 2030: The forum seeks to leverage Saudi Arabia’s developing geopolitical role and Vision 2030 to aid in its media-related goals.
Journalism Innovation: It focuses on the latest developments in the media sector, including humanistic and smart journalism.
Economic Transformation in Media: The forum explores the economic shifts in the media landscape, such as digital platforms like Netflix and Sony Pictures, and seeks to develop sustainable economic models for Saudi media.
Addressing Key Issues: Discussions will cover how Saudi media can elevate the Kingdom’s global media presence and correct stereotypical images of the Arab and Islamic world.
Cultural Diplomacy: It will also address how media content can enhance the Kingdom’s cultural diplomacy strategies.
High-Level Participation: The forum will act as a strategic platform by gathering political figures, major companies, and media personalities to discuss vital issues like artificial intelligence and sustainability.
Shaping the Future of Media: The forum aims to shape the future of media in Saudi Arabia and the Arab world, with a focus on turning discussions into tangible outcomes.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
The February full moon, historically known as the Snow Moon, blazes with extraordinary intensity this year as it illuminates the zodiac sign of Leo. Unlike its tranquil namesake, this lunation crackles with fiery energy, amplified by dynamic planetary alignments. While all zodiac signs may sense its electrifying undercurrents, three in particular—Taurus, Leo, and Aquarius—are poised to experience profound shifts. These individuals will navigate challenges that demand introspection, resilience, and a bold embrace of authenticity, ultimately unlocking pathways to personal evolution.
Taurus: Embracing the Unfamiliar Under this lunation, Taurus finds itself at a cosmic crossroads as celestial tensions disrupt its innate preference for stability. The alignment of the Sun, Moon, and Mercury with Uranus in Taurus creates a push-pull dynamic, urging those born under this earth sign to venture beyond their emotional and psychological safe zones. Sudden revelations or external changes may force Taureans to reevaluate long-held habits, particularly in how they project their identity. This period demands adaptability, challenging their natural inclination to cling to routine, and instead inviting them to explore uncharted facets of their persona.
The full moon’s energy also illuminates the necessity of inner transformation as a foundation for future aspirations. Taureans are encouraged to confront fears tied to impermanence, recognizing that growth requires releasing outdated versions of themselves. While the pressure to evolve may feel daunting, this lunation rewards courage. By channeling their legendary determination into self-reinvention rather than resistance, Taurus individuals can dismantle emotional barriers, paving the way for a future aligned with their truest ambitions.
Leo: The Spotlight on Authenticity As the full moon radiates in their own sign, Leos are thrust into a moment of self-reflection, where their goals and desires come under intense scrutiny. This lunation amplifies their natural magnetism but also highlights obstacles—external criticisms, career upheavals, or lingering self-doubt—that threaten to derail their progress. The key lies in distinguishing between genuine ambition and the weight of others’ expectations. Leos must confront whether their pursuits stem from authentic passion or a desire to meet societal or relational standards, a process that demands brutal honesty.
Yet, this lunar phase also empowers Leos to reclaim their narrative. By leaning into their innate confidence and creativity, they can transmute challenges into opportunities for clarity. The fiery energy of the moon fuels their courage to shed superficial distractions and focus on what truly matters. For Leos, success under this lumination isn’t about brute force but about refining their vision, trusting that staying true to their core values will guide them through turbulence. The path forward may be rugged, but their unwavering self-belief becomes the compass.
Aquarius: Navigating Emotional Depths For Aquarius, typically the zodiac’s detached analyst, this full moon illuminates the intricate dance of relationships. Positioned in their sector of partnerships, the lunation urges a shift from intellectual detachment to emotional engagement. Aquarians may find themselves unexpectedly confronting buried sentiments within close bonds, whether romantic, familial, or platonic. Memories or unresolved tensions could surface, challenging their tendency to rationalize feelings rather than experience them. This phase invites a delicate balance: maintaining their signature objectivity while allowing space for vulnerability.
The lunation’s call to soften resonates deeply here. Aquarians are reminded that connection requires courage—not just to love others, but to trust that opening their heart won’t dilute their individuality. Embracing vulnerability becomes an act of strength, proving that authenticity isn’t diminished by emotional exposure. By acknowledging their sensitivities, Aquarius individuals can forge deeper, more meaningful connections without sacrificing their unique perspective. This lunar moment is less about losing oneself in others and more about discovering how intimacy enriches their visionary spirit.
Conclusion: A Catalyst for Renewal February’s full moon serves as a celestial mirror, reflecting areas of life where growth is both urgent and inevitable. For Taurus, Leo, and Aquarius, the lunation’s blaze illuminates distinct challenges—whether reinventing identity, honing ambition, or embracing vulnerability. Yet, each sign is equipped with innate strengths to navigate these trials. By leaning into courage, authenticity, and emotional honesty, this fiery lunation becomes less a source of drama and more a catalyst for profound, lasting transformation.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
This comprehensive YouTube transcript outlines a structured masterclass designed to transition beginners into professional cryptocurrency traders. The curriculum follows a seven-step roadmap, beginning with financial prerequisites like understanding cash flow and economic structures before moving into technical foundations. Students are introduced to the mechanics of blockchain technology, the history of Bitcoin, and the functional differences between various altcoins and stablecoins. A significant portion of the guide emphasizes practical execution, detailing how to navigate the Binance exchange for both spot and future trading. Beyond technical skills, the source focuses on developing a disciplined mindset, teaching essential strategies for risk management, psychological stability, and independent project research. Through a combination of theoretical lessons and practical assignments, the course aims to equip learners with the “wisdom” needed to identify market opportunities and secure long-term profitability.
The Architect’s Blueprint for Cryptocurrency Trading
Crypto trading is essentially the act of buying digital assets at a lower price and selling them at a higher price to generate a profit. While it can be a way to build wealth, the sources emphasize that it is a “continuous study game” that requires a deep understanding of several foundational concepts.
1. Core Concepts and Terminology
Blockchain: This is the underlying technology of cryptocurrency. It is a decentralized digital ledger (or database) where transaction records are stored in “blocks” linked together in a chain. This system is considered transparent and non-hackable because it is distributed across multiple computers (nodes) worldwide rather than being controlled by a single central authority.
Bitcoin vs. Altcoins:Bitcoin was the first decentralized cryptocurrency, created with a limited supply of 21 million coins to combat inflation. Altcoins (Alternative Coins) refer to any cryptocurrency other than Bitcoin, such as Ethereum (ETH), Solana (SOL), and BNB.
Stablecoins: These are digital assets pegged to a stable reserve, most commonly the US Dollar (e.g., USDT or USDC). They allow traders to move into a stable position without exiting the crypto ecosystem entirely.
2. Primary Trading Methods
Spot Trading: This is the most basic form of trading where you buy a coin and own it. The goal is to hold the asset until the price increases to sell for a profit. It is generally considered safer and “halal” by some because it does not involve interest or gambling-like bets.
Future Trading: In this method, you do not own the asset but instead predict its price movement. You can bet on the price going up (Long) or down (Short). This often involves leverage, which can lead to high profits but also carries the risk of losing your entire investment (liquidation).
3. Methods of Analysis
To make informed decisions, traders use two main types of research:
Technical Analysis (TA): This involves studying historical price data and charts to predict future movements. Key concepts include identifying Support and Resistance levels (where price typically bounces or drops) and Supply and Demand zones.
Fundamental Analysis (FA): This focuses on the “why” behind a project’s value. It involves researching the project’s team, roadmap, tokenomics (supply and demand dynamics), and news events like partnerships or regulatory changes.
4. Risk Management and Psychology
The sources highlight that psychology is just as important as technical skill.
Protect Your Capital: Never risk your principal investment on high-risk trades; instead, grow it slowly using “Blue Chip” cryptos like Bitcoin or Ethereum.
Set Goals and Exit Plans: Decide on a profit target (e.g., 5%) and stick to it. Greed often leads traders to wait too long, only to see the market crash.
Use Stop-Losses: Always have a plan for when to exit a trade if it goes against you to minimize losses.
DYOR (Do Your Own Research): Never rely solely on others’ advice. Use tools like CoinMarketCap for data and TradingView for chart analysis to verify information yourself.
5. Practical First Steps
To begin, you typically need to:
Choose an Exchange: The sources recommend Binance as a user-friendly and secure platform.
Complete KYC: Verify your identity to ensure account security.
Deposit Funds: Use methods like P2P (Peer-to-Peer) trading to buy stablecoins (USDT) using local currency.
Start Small: It is suggested to start with a small amount, such as $10 or $11, to practice and learn the interface before committing larger capital.
The Architecture of Decentralized Ledgers
Blockchain technology serves as the foundational infrastructure for cryptocurrencies like Bitcoin. It is essentially a decentralized digital ledger or database where transaction records are stored and distributed across a network of computers. Unlike traditional centralized systems controlled by a single bank or government, a blockchain is managed by a global network of nodes, making it transparent and highly secure.
1. Structural Components
The technology gets its name from how data is organized and linked:
Blocks: Data is stored in distinct “blocks”. Each block typically contains three things: relevant data (such as transaction details), a hash, and the previous hash.
The Hash: Often described as a “digital fingerprint,” a hash is a unique identifier generated by an algorithm (like SHA-256). If even a tiny piece of data within the block is changed, the hash changes completely.
The Chain: Blocks are linked because each new block contains the hash of the one before it. This sequence begins with the Genesis Block, which is the very first block in the chain and has a previous hash of “00”.
2. Security and Immutability
Blockchain is considered non-hackable and tamper-proof due to its decentralized nature and consensus rules.
Tamper Resistance: Because each block points to the previous one, changing data in an old block would break the entire chain, as all subsequent hashes would no longer match.
Consensus Mechanisms: To make a change or add a block, the network must agree through specific protocols. The sources highlight two main types:
Proof of Work (PoW): Requires miners to use computational power to solve complex puzzles, a process that takes a specific amount of time (e.g., 10 minutes for a Bitcoin block).
Consensus/Voting: Some chains use a voting system where a change is only accepted if 51% of the network agrees.
Smart Contracts: These are digital, self-executing agreements with the terms of the contract directly written into code. Once deployed on the blockchain, they follow strict rules that even the creator cannot easily bypass.
3. The Role of Nodes and Mining
Nodes: These are the individual computers connected to the blockchain network. Every node typically holds a copy of the entire ledger, ensuring that if one computer fails or is compromised, the rest of the network remains intact.
Mining: This is the process of verifying transactions and adding them to the blockchain. “Miners” provide their computer’s processing power and electricity to the network in exchange for rewards, such as newly minted coins (e.g., Bitcoin) or transaction fees (gas fees).
4. Evolution and Web 3.0
Blockchain is a key component of the transition from Web 2.0 (centralized platforms like Facebook) to Web 3.0. In the Web 3.0 ecosystem, blockchain allows for a decentralized business model where individuals have more control over their data and can earn rewards (tokens) for their participation in the network. While primarily used for finance today, its real-world use cases are expanding into areas like cross-border payments and augmented reality.
The Science of Spot Trading: A Systematic Framework
The Spot Trading process is described as a comprehensive “science” rather than a simple act of buying and selling. It involves a systematic approach that includes preparation, research, and technical execution.
1. Preparation and Goal Setting
Before entering any trade, the sources emphasize the importance of having a clear Goal Set.
Decide Your Target: You must decide what you want to achieve with a specific trade or investment. For example, if you aim for a 5% profit, you should stick to that plan and exit once the target is reached.
Have an Exit Plan: You should have an exit strategy ready even before you buy an asset.
Follow a System: Successful traders follow a disciplined system (Plan A, B, C) rather than relying on emotional “common sense”.
2. Risk Management and Psychology
Managing your capital and your mind is a critical part of the process.
Protect Principal Capital: You should avoid taking maximum risks with your main investment amount. A safer strategy is to grow your principal slowly using stable assets like Ethereum or Solana, and only use the generated profits for high-risk trades.
Manage Emotions: Psychology plays a massive role; for instance, the fear of losing money is often stronger than the joy of making it. Losing your capital quickly (e.g., in one week) prevents you from learning the market over a longer, more productive period.
3. Research (DYOR Framework)
The “Do Your Own Research” (DYOR) framework is the backbone of the spot trading process. It consists of two main types of analysis:
Fundamental Analysis (FA): This involves checking the “why” behind a project. You should examine Tokenomics (total supply and demand), the Team (who is leading the project), the Roadmap, and White Papers to understand the project’s long-term vision.
Technical Analysis (TA): This is the study of historical price data and charts to predict future movements. It helps you identify specific Support and Resistance levels or Supply and Demand zones where it is best to buy or sell.
4. Execution on an Exchange
Once the research is complete, the physical trading happens on an exchange like Binance.
Depositing Funds: You typically start by depositing stablecoins (like USDT) into your account, often through P2P (Peer-to-Peer) trading.
Selecting a Pair: You choose a trading pair, such as XRP/USDT, which means you are using USDT to buy XRP.
Types of Orders:Market Order: Buying or selling immediately at the current market price.
Limit Order: Setting a specific price at which you want to buy or sell. The trade only executes if the market hits that price, allowing for automated exits.
Using Indicators: Traders often use technical indicators like RSI (Relative Strength Index) to see if a coin is oversold or Volume to check the strength of the market movement before executing.
5. Profit Realization
The final step is to “lock in” your profits. The sources warn against greed, noting that waiting for a 15% gain when your plan was 5% often leads to missing the exit entirely if the market suddenly crashes. Success in spot trading comes from taking your profit and moving on to the next opportunity.
The Crypto Investor’s Guide to Fundamental Analysis
Fundamental Analysis (FA) in the cryptocurrency market is the systematic study of a project’s intrinsic value to understand the “why” behind its potential for growth or decline. Unlike technical analysis, which focuses on historical price charts, FA examines the project’s underlying technology, team, economic model, and the real-world problems it aims to solve.
1. Core Pillars of Project Research
To perform FA, traders utilize a framework often called DYOR (Do Your Own Research), which focuses on several key areas:
The Team and Leadership: Researching who is behind a project is vital; a team with a proven track record of scaling previous startups or deep technical expertise is a strong fundamental signal.
The White Paper and Roadmap: The white paper serves as the project’s foundational document, outlining its technical goals and the specific problem it intends to fix. The roadmap provides a timeline for future developments, allowing traders to judge if the project is meeting its milestones.
Competition: It is essential to analyze a project’s competitors to see if its solution is unique or if it is likely to be outperformed by established “juggernauts” in the space.
Real-World Utility: Projects with clear use cases, such as XRP for cross-border payments or Ethereum as a platform for other decentralized applications, generally have stronger fundamentals.
2. Tokenomics and Economic Health
Tokenomics refers to the supply and demand dynamics of a cryptocurrency. This includes:
Supply Metrics: Analyzing the Total Supply, Circulating Supply, and Market Cap. A limited supply (like Bitcoin’s 21 million limit) can drive prices up if demand increases.
Inflationary vs. Deflationary Models: Some coins are inflationary (supply increases over time), while others are deflationary. For example, Ethereum shifted toward a deflationary model by implementing a “burning” mechanism where more coins are removed from circulation than are newly minted, which is a positive sign for long-term investors.
Token Unlocks: Monitoring when large portions of locked tokens are released to the community or team is crucial, as massive unlocks can lead to increased selling pressure.
3. Market Sentiment and External Factors
Sentiment Analysis is considered a sub-domain of FA that monitors the “mood” of the market through external events:
News and Regulations: Major legal events, such as the SEC’s regulatory actions, can cause massive shifts in a coin’s fundamentals overnight.
Macroeconomic Data: Global financial indicators like CPI (Consumer Price Index) data or changes in Bank Interest Rates significantly impact investor behavior. For instance, if interest rates rise, investors might pull capital out of risky markets like crypto, causing prices to drop.
Partnerships and Adoptions: High-profile collaborations or the adoption of a coin by major companies (e.g., Tesla or BlackRock) serve as massive fundamental endorsements.
4. Execution: Combining FA with Technical Analysis
Successful traders use FA to determine which asset to buy, while using Technical Analysis (TA) to decide when to buy it. By combining these, a trader might identify a fundamentally strong project (like Ethereum) and then wait for it to hit a specific “demand zone” on a price chart before executing a trade. This combined approach, supported by tools like CoinMarketCap and TradingView, helps minimize risk and maximize the potential for profit.
The Disciplined Mind: Psychology of Risk and Market Success
Risk management psychology is described in the sources as a “continuous study game” where discipline and emotional control are just as vital as technical skill. Understanding how your mind reacts to market movements is the key to preserving capital and ensuring long-term success.
1. The Psychology of Fear and Loss Aversion
One of the most significant psychological hurdles is the human tendency to value a loss of $100 more than a gain of $100.
Fear-Based Decisions: The fear of losing money often triggers “panic exits,” causing traders to abandon their analysis right before a market recovery.
Logic-Based Training: To counter this, traders must build a logic-based psychological framework by understanding the risk of an asset before entering a trade. When you have a predefined plan for how much you are willing to lose, you are less likely to act on emotion.
2. Controlling Greed and FOMO
The sources emphasize that greed is the primary reason traders miss profitable exits.
Sticking to the Target: If your plan is to take a 5% profit, you must exit at 5%. Traders often wait for a 15% gain only to see the market crash back to their entry point.
Avoiding FOMO (Fear of Missing Out): Social media often creates a “bubble” where traders feel they must invest in a project or be “left on the road”. The sources suggest that the best way to kill FOMO is to test the impulse with a small, affordable amount of capital to prove to yourself that “easy money” schemes rarely work.
3. The Discipline of the “System”
Success comes from following a rigid system (Plan A, B, and C) rather than relying on “common sense” or intuition.
Protecting Principal Capital: You should never take maximum risks with your initial investment (principal). Instead, use stable “Blue Chip” assets to grow the principal slowly and only use generated profits for high-risk trades.
Risk-Reward Ratios: A disciplined trader might set a rule to only take trades with a 1:2 risk-to-reward ratio (e.g., risking 1% to make 2%). This ensures that even if only half of your trades are successful, you remain profitable overall.
4. Decision-Making and Self-Reflection
Traders are encouraged to refine their psychology through active self-reflection.
The One-Hour Rule: Spend one hour daily away from your phone and charts to reflect on your decisions. Analyze why you made a specific trade, whether it was emotional, and what the outcome was.
Consulting Mentors: Important financial decisions should be discussed with experienced mentors or teachers rather than uninformed sources (like a random person on the street) to ensure you are receiving specialized feedback.
5. Managing the “Continuous Game”
The sources warn that losing your capital in a single week prevents you from learning the market’s cycles.
Time in Market: It is better to stretch a $100 investment over six months than to lose it in one week. Staying active in the market over a longer period allows your “muscle memory” and psychological resilience to develop.
Avoid Revenge Trading: After a loss, the impulse to immediately “win it back” can lead to further mistakes. Maintaining a steady routine and following your rules regardless of a single win or loss is essential for a professional mindset.
When it comes to achieving flawless skin, beauty sleep isn’t just a catchy phrase—it’s a science-backed truth. The magic happens while we rest, as our skin enters repair mode, rebuilding collagen and rejuvenating cells. But what if you could amplify these natural processes and wake up to even more radiant skin? By incorporating a few simple beauty secrets into your nightly routine, you can supercharge the benefits of sleep, making sure your skin stays hydrated, firm, and glowing.
From silk pillowcases to nourishing night creams, there are plenty of easy-to-implement practices that will have you waking up to visibly healthier skin. These habits are designed to help your skin recover from the day’s stress, leaving it well-hydrated, rejuvenated, and youthful-looking. In this post, we’ll dive into 20 overnight beauty secrets that can transform your nighttime skincare regimen, helping you achieve your best skin yet.
The key to enhancing your nighttime skincare routine lies in understanding how your skin works overnight. While you sleep, your skin goes into repair mode, actively replenishing moisture and creating new cells. By adopting the right habits, you can maximize these natural processes, ensuring that your skin is always in tip-top shape by morning. So, let’s explore the beauty secrets that will bring out your skin’s natural glow and keep it looking fresh day after day.
Switching to a silk pillowcase is one of the easiest yet most effective ways to improve your skin’s appearance overnight. Unlike cotton, which can cause friction and tug at the skin, silk is much gentler, reducing irritation and preventing the formation of sleep lines that can eventually lead to wrinkles. This smooth surface creates less drag, allowing your skin to glide naturally while you sleep, which can result in fewer creases and less redness upon waking.
In addition to reducing skin irritation, silk pillowcases have moisture-retaining properties that can help keep your skin hydrated. Unlike cotton, which can absorb moisture from your skin, silk helps retain the natural oils produced during the night. As a result, your skin remains nourished and hydrated, helping to combat dryness and maintain a youthful glow. For the best results, opt for high-quality mulberry silk, which is known for its superior smoothness and hypoallergenic benefits.
Night creams are a cornerstone of any effective overnight skincare routine. These rich, thicker formulations are designed to provide intense hydration while you sleep, ensuring that your skin wakes up refreshed and glowing. Unlike day moisturizers, which are typically lighter and formulated to protect against environmental stressors, night creams work with your skin’s natural repair cycle. Ingredients such as hyaluronic acid, ceramides, and peptides are particularly beneficial, as they help to lock in moisture and stimulate collagen production while you rest.
Using a hydrating night cream can also help restore your skin’s natural barrier function, which is crucial for maintaining a healthy complexion. Over time, exposure to harsh elements like sun, wind, and pollution can dehydrate the skin, causing it to appear dull and tired. A good night cream replenishes moisture, reduces fine lines, and ensures your skin stays plump and nourished. If you’re unsure which product is best for your skin type, opt for one that’s suitable for your specific concerns—whether that’s dryness, aging, or sensitivity.
Keywords: night cream, hydrating cream, skin hydration, hyaluronic acid, ceramides, peptides, skin repair, collagen production
One of the simplest ways to prevent wrinkles and promote better skin health is to sleep on your back. When you sleep on your side or stomach, your face is pressed against the pillow, causing unnecessary pressure on your skin. This can lead to the formation of sleep lines, which over time can turn into permanent wrinkles. Sleeping on your back keeps your skin free from this pressure, allowing it to maintain its natural shape and reducing the risk of premature aging.
In addition to helping with wrinkle prevention, sleeping on your back promotes better circulation and reduces the likelihood of puffiness or dark circles under the eyes. By keeping your head elevated, you allow fluids to drain more effectively, which can alleviate morning swelling. If you find it challenging to sleep on your back, try using a pillow that supports your neck and head, helping to maintain the right posture throughout the night.
Keywords: sleep position, sleeping on your back, wrinkle prevention, facial pressure, puffiness, dark circles, skin circulation
These three simple but effective tips can have a lasting impact on the health and appearance of your skin. By incorporating a silk pillowcase, using a hydrating night cream, and sleeping on your back, you’re setting yourself up for smoother, healthier, and more youthful skin every morning. It’s the small, consistent changes that can lead to the biggest results, so make these habits a part of your nightly routine for maximum beauty sleep benefits.
Adding a nourishing sleep mask to your nightly routine is an excellent way to boost your skin’s hydration and enhance its overall texture. Sleep masks are designed to deliver intense moisture while you rest, providing a deep layer of nourishment that can help rejuvenate the skin. Many sleep masks contain beneficial ingredients like antioxidants, peptides, and vitamins that work throughout the night to repair and restore your skin’s natural balance. For optimal results, look for a mask that aligns with your skin type and addresses your specific concerns, such as dryness, uneven tone, or dullness.
The best time to apply a sleep mask is right before bed after finishing your other skincare steps. This allows the mask to lock in moisture and all the beneficial ingredients, ensuring your skin stays hydrated and revitalized until morning. Over time, incorporating a sleep mask into your routine can result in smoother, more radiant skin with improved elasticity. If you’re looking to brighten your complexion, choose a mask with vitamin C, or for a soothing effect, try one that contains calming ingredients like aloe vera or chamomile.
Facial oils are an essential addition to any skincare routine, especially when used overnight. These oils, rich in nourishing ingredients like argan, rosehip, and jojoba, provide deep hydration and help seal in moisture. Unlike heavier creams, facial oils are lightweight yet effective, ensuring your skin absorbs them fully without clogging pores. They are rich in essential fatty acids and antioxidants, which not only hydrate the skin but also help repair damage caused by environmental stressors. Regular use of facial oils can lead to a more radiant, plump complexion with a healthy glow.
In addition to hydration, facial oils also help restore the skin’s natural barrier, keeping it protected from harmful external factors. As you sleep, the oil works to reduce inflammation and redness, promoting a balanced complexion. Over time, the oils help minimize the appearance of fine lines and improve skin elasticity, leaving your skin looking fresher and more youthful. For best results, apply facial oil as the last step in your evening routine to lock in moisture and provide an extra layer of nourishment while you rest.
A proper evening cleanse is essential for maintaining healthy skin, especially before applying any hydrating products. Gently removing makeup, dirt, and environmental pollutants is the first step in your nighttime skincare routine. Using a gentle cleanser ensures that your skin isn’t stripped of its natural oils, which can lead to dryness and irritation. A thorough cleanse helps your skin absorb the beneficial ingredients from your subsequent skincare products, such as serums, oils, or moisturizers, ensuring they work at their best.
When choosing a cleanser, look for one that suits your skin type and concerns. For sensitive skin, opt for a mild, fragrance-free formula that won’t cause irritation. For oily or acne-prone skin, a foaming cleanser with salicylic acid can help keep pores clear. Regardless of your skin type, always double cleanse if you’ve worn makeup during the day to ensure every trace of impurities is removed. By starting with a clean base, your skin will be better prepared to take full advantage of the overnight healing and moisturizing products you apply next.
By incorporating a sleep mask, facial oils, and a gentle nighttime cleanse into your skincare regimen, you are setting yourself up for optimal hydration and skin rejuvenation. These steps not only help your skin retain moisture but also work to repair and renew it while you sleep. The right combination of products can enhance your skin’s texture, tone, and overall appearance, leaving you with smooth, glowing skin every morning. When you prioritize your skin’s health overnight, you’re ensuring it looks and feels its best for the long term.
The delicate skin around your eyes is particularly vulnerable to signs of aging, puffiness, and dark circles, which is why it requires special attention. Using a targeted eye cream at night ensures that this area receives the extra hydration and care it needs. Eye creams often contain powerful ingredients like retinol, peptides, and hyaluronic acid, which work to smooth fine lines, reduce puffiness, and brighten dark circles. Retinol, for instance, stimulates collagen production, which can help thicken the skin around the eyes and reduce the appearance of crow’s feet.
By adding an eye cream to your evening routine, you can also improve the overall texture and tone of the under-eye area, making it appear fresher and more youthful. As the skin around your eyes is thinner than the rest of your face, it is more susceptible to environmental damage and dehydration, so applying a nourishing cream helps restore its moisture balance. For best results, gently tap the product into your skin using your ring finger, which applies the least pressure, to avoid tugging or irritating this sensitive area.
Keywords: eye cream, dark circles, puffiness, retinol, peptides, fine lines, hydration, under-eye care
Chapped, dry lips can ruin your morning routine, leaving you with uncomfortable or cracked skin. To avoid this, make lip care an essential part of your nighttime routine by applying a nourishing lip balm before bed. Choose one with hydrating ingredients like shea butter, beeswax, or vitamin E to provide long-lasting moisture while you sleep. These ingredients not only keep your lips soft but also help repair any damage caused by dryness or environmental stressors, ensuring you wake up with smooth, plump lips.
Lip balm acts as a barrier to lock in moisture overnight, preventing your lips from becoming dry and cracked. Regularly moisturizing your lips while you sleep will also help to maintain their natural softness and elasticity. You can even use a lip mask occasionally to treat your lips to a more intensive hydrating treatment. By making lip care a nightly habit, you’ll wake up with hydrated, kissable lips, ready for your day ahead.
Keywords: lip balm, chapped lips, hydration, shea butter, beeswax, vitamin E, lip care, smooth lips
The dry indoor air, particularly during colder months, can strip your skin of moisture, leaving it feeling tight and dehydrated by morning. Using a humidifier while you sleep helps maintain the moisture levels in the air, preventing your skin from becoming dry overnight. By adding moisture to the air, a humidifier supports your skin’s natural ability to retain hydration, ensuring it stays smooth and soft. This is especially helpful for individuals living in dry climates or those who use heating systems that tend to dry out the air during winter.
In addition to benefiting your skin, a humidifier can also help with other skin-related issues like dryness in the nose and throat, which can occur in arid conditions. The added moisture ensures that your skin can absorb and retain more hydration from your nighttime skincare routine, leading to healthier, more radiant skin. For best results, keep the humidifier close to your bed but not directly near your face to prevent any irritation. A well-maintained humidifier can be a game-changer for skin health, particularly for those with dry or sensitive skin.
Incorporating an eye cream, lip balm, and humidifier into your skincare routine can significantly enhance the quality of your skin’s hydration and overall appearance. These small but impactful additions provide your skin with the nourishment it needs to stay smooth, soft, and youthful-looking. As your skin works to repair itself overnight, these products ensure that it has the hydration and support it needs to stay balanced and rejuvenated. By prioritizing these key steps in your nighttime routine, you’ll wake up with healthier, more radiant skin, ready to face the day with confidence.
Keywords: skin hydration, nighttime routine, youthful skin, eye care, lip care, humidifier benefits, skin rejuvenation
Maintaining a cool room temperature while you sleep can significantly benefit your skin and overall well-being. When you sleep in a cooler environment, your body’s temperature regulation improves, which helps you rest more deeply and wake up feeling refreshed. A good night’s sleep is essential for skin regeneration, and by keeping your room cool, you ensure that your body isn’t overheating, which could lead to dehydration or skin imbalances. Excess heat can cause your skin to lose moisture, potentially leading to dryness, irritation, and breakouts.
A cooler sleep environment can also help balance oil production, reducing the risk of acne flare-ups that are often triggered by excessive heat. The optimal sleeping temperature is usually between 60 and 67°F (15-19°C), which promotes restorative sleep cycles and keeps your skin hydrated. For added comfort, try using breathable sheets or pajamas to help maintain the ideal temperature while you sleep. By making this simple adjustment, you not only improve your skin’s health but also your overall quality of sleep.
Keywords: cool room, skin hydration, body temperature regulation, deep sleep, breakouts, oil production, acne prevention
Retinol, a form of Vitamin A, is one of the most effective ingredients for promoting skin renewal and reducing signs of aging. When used at night, retinol accelerates cell turnover, meaning that it helps to shed dead skin cells and stimulate the production of new, healthy skin cells. This process not only helps to reduce fine lines and wrinkles but also promotes a more even skin tone by addressing hyperpigmentation and dark spots. Retinol works best in the evening because it can make the skin more sensitive to sunlight, so using it at night allows the ingredient to work its magic while you sleep.
Incorporating retinol into your nightly skincare routine can also boost collagen production, which is key to maintaining skin elasticity and preventing sagging. While retinol can be potent, starting with a lower concentration and gradually increasing the dose allows your skin to adjust without irritation. It’s important to pair retinol with a good moisturizer to avoid dryness and peeling. Over time, retinol can transform your skin, leaving it smoother, brighter, and more youthful.
Keywords: retinol, skin renewal, cell turnover, wrinkles, fine lines, hyperpigmentation, collagen production
One of the simplest ways to ensure your skin stays plump and hydrated overnight is by drinking water before bed. Hydrating before sleep replenishes the moisture your skin needs for repair and rejuvenation during the night. When you’re well-hydrated, your skin retains moisture more effectively, helping to maintain its elasticity and overall health. Drinking a glass or two of water in the evening allows your body to process and distribute the hydration it needs to keep your skin looking fresh and radiant by morning.
In addition to supporting skin hydration, staying hydrated helps flush out toxins from your body, contributing to a clearer complexion. While it’s essential to stay hydrated throughout the day, drinking water before bed ensures your skin has the resources it needs to repair itself overnight. However, be mindful not to drink too much too close to bedtime, as it might disrupt your sleep with frequent trips to the bathroom. A balanced amount of water before bed will help your skin stay glowing without interrupting your rest.
Keywords: hydration, water before bed, skin repair, skin elasticity, clear complexion, overnight hydration
Incorporating a cool room, retinol, and proper hydration into your nighttime routine can greatly enhance your skin’s health and appearance. A cooler environment promotes better sleep, while retinol accelerates cell turnover and combats aging, and proper hydration helps replenish your skin’s moisture levels. These simple yet powerful habits work together to give your skin the tools it needs for overnight repair and rejuvenation. By making these steps a consistent part of your routine, you can wake up with smoother, healthier skin and enjoy long-term skincare benefits.
Taking care of your scalp is just as important as looking after your skin, as it plays a crucial role in hair health. A nourishing hair serum can be a game-changer in your nighttime routine, especially when it contains ingredients like biotin, vitamin E, and other essential nutrients. These ingredients promote healthy hair growth by strengthening hair follicles and improving circulation to the scalp. Vitamin E, in particular, is known for its antioxidant properties that protect the scalp from damage while keeping it moisturized throughout the night.
Applying a hair serum before bed also helps combat dryness, flakiness, and irritation that can occur on your scalp. Many serums also contain hydrating elements that prevent scalp dehydration, ensuring that your hair remains soft, shiny, and healthy-looking. For optimal results, massage the serum gently into your scalp and hair ends, and allow it to work overnight. Over time, using a nourishing hair serum can lead to stronger, thicker hair and a balanced scalp, both of which contribute to an overall healthier appearance.
Keywords: nourishing hair serum, biotin, vitamin E, hair growth, scalp care, healthy hair, scalp hydration
One of the most important steps in maintaining clear, healthy skin is to sleep with a clean face. Leaving makeup, dirt, or oils on your skin overnight can clog pores, prevent regeneration, and lead to breakouts or irritation. Cleansing your skin thoroughly before bed removes any buildup from the day, allowing your skin to breathe and regenerate without obstruction. By washing away impurities, your skin is better able to absorb the nourishing ingredients from your serums and moisturizers, maximizing their effectiveness.
A proper nighttime cleanse also helps maintain balanced skin, preventing excess oil production and minimizing the risk of acne or dullness. Opt for a gentle cleanser that suits your skin type—whether you have dry, oily, or sensitive skin. Double cleansing, especially after wearing makeup, is highly effective in ensuring all traces of makeup and impurities are removed. With a clean face, your skin can repair itself more efficiently overnight, leading to a brighter, clearer complexion by morning.
As we age, our skin’s natural collagen production decreases, which can lead to sagging and fine lines. A collagen-boosting night cream can help counteract these effects by supporting your skin’s firmness and elasticity. Look for night creams that contain collagen, peptides, or other skin-strengthening ingredients, as these help enhance the structure of your skin while you sleep. Peptides are particularly effective in stimulating collagen production, which is key to maintaining youthful, plump skin.
When applied before bed, these creams provide your skin with the necessary ingredients to support its natural repair processes. Collagen-boosting formulas also work to improve the skin’s texture, making it smoother and more resilient. With consistent use, you can see a noticeable improvement in skin firmness, reduced fine lines, and a more youthful complexion. The best results come when these creams are paired with other nighttime skincare steps, such as proper hydration and sun protection during the day, to maintain a balanced skincare routine.
Incorporating a nourishing hair serum, cleansing your face thoroughly, and using a collagen-boosting night cream into your evening skincare routine can significantly enhance your skin and hair health. These simple yet effective steps work together to promote smoother, firmer skin, healthier hair, and a well-maintained scalp. By taking the time to care for your skin and hair overnight, you allow them to regenerate and repair, ensuring that you wake up looking refreshed and rejuvenated. These habits are crucial in achieving long-term skin and hair health, leaving you with a radiant, youthful glow each morning.
Exfoliating your scalp is a crucial step in promoting healthy hair growth and maintaining a clean, balanced scalp. Just as your skin benefits from regular exfoliation to remove dead cells and unclog pores, your scalp can also benefit from a gentle scrub to clear away buildup, excess oils, and dead skin cells. Using a scalp scrub once a week helps ensure that your hair follicles remain unclogged, which is essential for healthy hair growth. A clean, exfoliated scalp allows your hair to grow more freely and thrive, preventing common scalp issues like dandruff or product buildup.
The right scalp scrub should contain exfoliating ingredients such as sea salt or sugar, which slough off dead skin without causing irritation. Massaging the scrub into your scalp also increases blood circulation, which can further support hair growth. After use, follow with a nourishing hair mask or conditioner to keep your scalp hydrated and promote hair strength. By incorporating a scalp scrub into your weekly routine, you’ll maintain a healthier scalp environment, which can lead to stronger, shinier hair over time.
Taking a warm bath before bed is a time-honored way to prepare both your mind and body for a restful night of sleep. The warmth of the water helps to relax your muscles, calm your nervous system, and release tension accumulated throughout the day. This relaxation encourages the production of serotonin, a hormone that promotes feelings of calm and well-being. By the time you step out of the bath, your body will be ready to enter a deep, rejuvenating sleep, which is essential for healthy, glowing skin.
Incorporating essential oils into your bath, such as lavender or chamomile, can enhance the benefits of this soothing ritual. Lavender, in particular, is known for its calming and skin-soothing properties, making it a perfect addition to your nighttime routine. A warm bath can also benefit your skin by opening your pores, allowing for better absorption of your moisturizers or body oils afterward. Whether it’s a quick soak or a long, relaxing bath, this self-care practice can improve both your sleep quality and skin health.
If you often wake up with dry or dehydrated skin, keeping a hydrating face mist by your bedside can be a simple yet effective solution. Spritzing your face with a moisturizing mist before bed helps to lock in hydration, ensuring your skin stays soft and refreshed while you sleep. Choose a mist that contains ingredients like hyaluronic acid, aloe vera, or rose water, as these are known for their hydrating and soothing properties. These ingredients work overnight to help your skin retain moisture, which is essential for maintaining a youthful, plump appearance.
In addition to its hydrating benefits, a face mist can also provide a calming sensation that promotes relaxation, especially if it contains soothing fragrances like lavender or chamomile. When you wake up, your skin will feel refreshed and nourished, with a dewy glow. A quick spritz of a hydrating mist before bed and first thing in the morning can also help revitalize your skin throughout the day, providing that extra boost of moisture it needs. This simple addition to your routine can make a noticeable difference in how hydrated and refreshed your skin looks.
Keywords: hydrating face mist, moisture retention, hyaluronic acid, aloe vera, rose water, skin hydration, dewy skin
By integrating a scalp scrub, warm bath, and hydrating face mist into your nightly routine, you enhance both your skin and hair health. A gentle exfoliation of the scalp promotes better hair growth, while a warm bath provides relaxation and prepares your body for a deep, rejuvenating sleep. Additionally, keeping a face mist nearby helps to maintain hydration throughout the night, ensuring that your skin stays nourished and refreshed. These practices are not only beneficial for your beauty routine but also contribute to your overall well-being, leaving you feeling and looking your best each morning.
Properly removing your makeup at the end of the day is one of the most essential steps for maintaining clear and healthy skin. Sleeping with makeup on can cause pores to become clogged with dirt, oil, and product residue, leading to acne, irritation, and dull skin. The skin needs time to breathe and repair overnight, and makeup acts as a barrier that prevents this from happening. A thorough cleanse before bed ensures your skin is free from makeup, dirt, and pollutants, which allows your skin to regenerate and absorb nourishing ingredients more effectively.
To make sure you’re removing every trace of makeup, consider double cleansing. Start with an oil-based cleanser to break down makeup and sunscreen, followed by a gentle water-based cleanser to remove any remaining impurities. This two-step process helps to prevent clogged pores and leaves your skin feeling fresh, clean, and ready for the next step in your skincare routine. Removing makeup is not just about preventing breakouts—it’s about allowing your skin to rejuvenate and maintain its natural glow overnight.
The skin on your neck and décolletage is often overlooked in skincare routines, but it is just as delicate as the skin on your face. Over time, the neck area can show signs of aging, such as sagging and wrinkles, due to its thinner skin and constant movement. To address this, incorporating a neck cream into your nighttime routine can help to firm and tighten the skin in this area. Look for neck creams that contain peptides, collagen-boosting ingredients, and antioxidants, as they help to restore elasticity and reduce the appearance of fine lines.
Applying a neck cream at night helps to nourish and hydrate the skin while you sleep, which is the perfect time for skin repair. Gently massage the cream into your neck and décolletage in upward motions to promote circulation and support skin firmness. Just as you care for the skin on your face, it’s important to extend this attention to your neck, which can also show the first signs of aging. By adding a neck cream to your nighttime routine, you can help prevent sagging and maintain a youthful appearance for years to come.
Incorporating the removal of makeup and the use of a neck cream into your nighttime skincare routine completes the essential steps for maintaining healthy, youthful skin. Properly cleansing your skin ensures that it can rejuvenate and repair itself without the hindrance of makeup, while a dedicated neck cream targets the delicate skin on your neck and décolletage, preventing sagging and wrinkles. Together, these simple but effective habits support your skin’s health and vitality, leaving you with a glowing complexion and youthful appearance every morning.
Neiman, D. (2019). The Skin Type Solution: A Revolutionary Guide to Your Best Skin Ever. Hachette Books. This book offers insights into how skin responds to various conditions, including sleep, and the importance of nighttime skincare routines for optimal skin health.
Cohen, P. R. (2016). Cosmetic Dermatology: Principles and Practice. McGraw-Hill Education. This textbook is a comprehensive guide to dermatological practices, covering the latest treatments and ingredients that can help improve skin health during sleep.
Green, D. M., & O’Rourke, L. (2017). The Beauty of Dirty Skin: The Surprising Science of Healthy, Glowing Skin. HarperOne. Green and O’Rourke discuss the importance of skin care at night and how our bodies repair and regenerate skin while we sleep.
Farris, P. K. (2017). The Anti-Aging Skin Care Kit. HarperCollins Publishers. This guide provides an in-depth look at how to create an effective skincare routine, including nighttime habits like using creams and serums to combat signs of aging.
Kobren, L. (2021). Skin Rules: Trade Secrets from a Top NYC Dermatologist. Penguin Books. In this book, the author explores the science behind common skincare myths and provides actionable tips for maximizing skin health, especially overnight.
Lodén, M. (2018). Dermatology and Dermatologic Surgery: Principles and Practice. Springer. This book discusses the skin’s natural repair processes during sleep and how skincare products like night creams and hydrating treatments can enhance these processes.
Sperling, L. (2019). Essential Oils in Skin Care. Chelsea Green Publishing. Sperling offers a deep dive into how essential oils, including lavender and chamomile, can benefit the skin when used in nighttime skincare routines.
Kerr, D. (2020). The Beauty Sleep Bible: How to Wake Up with Radiant Skin. Ten Speed Press. A dedicated exploration of beauty sleep and how incorporating specific nighttime habits, such as using sleep masks and hydrating face mists, can rejuvenate skin overnight.
Dr. David Bank (2020). The Beauty Diet: Unlock the Power of Food to Look and Feel Your Best. HarperOne. While focused on diet, this book also provides insights into how internal health, such as hydration, can improve the effectiveness of your nighttime skincare routine.
Wolf, R. (2020). Skin Aging Handbook: How to Prevent and Treat Premature Aging and Wrinkles. Wiley. Wolf provides scientific insights into how various skincare treatments and nighttime regimens can address aging skin, including the use of retinol and collagen-boosting products.
These resources offer a thorough understanding of skin health, specifically the processes that occur during sleep and how to optimize them with proper skincare practices.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
Are you really the partner you think you are? Relationships can often feel like smooth sailing, but underneath the surface, cracks might be forming. Sometimes, without realizing it, you might be slipping into behaviors that harm your relationship. Ignoring these patterns could lead to heartbreak, misunderstandings, and, ultimately, a failed connection.
No one enters a relationship wanting to be the “bad partner,” yet many unknowingly exhibit signs that suggest they’re falling short. These behaviors might seem harmless or even normal, but over time, they can corrode trust, intimacy, and mutual respect. Self-awareness is key, and recognizing these red flags can be the first step toward positive change.
If you’re starting to question whether you’re being the best version of yourself in your relationship, that’s a good thing. It shows you’re willing to reflect and improve. So, let’s dive into 12 signs that you might not be the ideal boyfriend and learn how to course-correct before it’s too late.
Keywords: bad partner behavior, relationship mistakes, red flags in relationships, relationship self-awareness, ideal boyfriend traits
One of the most subtle yet damaging behaviors in a relationship is taking your partner for granted. It’s easy to fall into a routine where you assume their love and efforts are guaranteed. But relationships need nurturing. If your partner continually supports you, whether emotionally, physically, or in your daily life, and you fail to acknowledge it, resentment can build. Simple acts like expressing gratitude or recognizing their contributions can make a world of difference.
When you neglect to show appreciation, your partner can feel invisible or undervalued. Over time, this can diminish their affection and commitment. As relationship expert Dr. John Gottman highlights, “Small things often” are the key to lasting relationships. Regular gestures of thankfulness, whether through words, actions, or small surprises, demonstrate that you see and value their efforts.
Keywords: taking partner for granted, lack of appreciation, relationship gratitude, relationship neglect, relationship expert advice
Emotional distance can create an invisible wall between you and your partner. In healthy relationships, emotional vulnerability is essential. If you’re consistently shielding your feelings or avoiding meaningful conversations, you’re withholding a vital part of yourself. This lack of openness can make your partner feel isolated and frustrated, leading to misunderstandings or a sense of rejection.
Psychologist Dr. Brené Brown, renowned for her work on vulnerability, emphasizes, “Staying vulnerable is a risk we have to take if we want to experience connection.” Your willingness to share your fears, joys, and struggles allows your relationship to grow deeper. By opening up, you’re not only building trust but also showing that you care enough to let your partner into your inner world.
Keywords: emotional distance in relationships, vulnerability in relationships, emotional intimacy, relationship connection, communication in relationships
Spending time with friends and family is healthy, but constantly prioritizing others over your partner signals a lack of commitment. If your partner feels like they’re always second to your social circle, it can breed insecurity and dissatisfaction. Your relationship should be a priority, and consistent neglect can lead your partner to question their importance in your life.
Balancing your social life and relationship doesn’t mean abandoning your friendships; it means setting boundaries and ensuring your partner feels valued. Relationship therapist Esther Perel explains, “It’s not about being together all the time, but about making the time you spend together count.” Show your partner that they’re a significant part of your world by dedicating quality time to them.
Keywords: neglecting partner, prioritizing friends over partner, relationship commitment, balancing social life, relationship satisfaction
Recognizing these red flags is crucial for maintaining a healthy, thriving relationship. Taking your partner for granted, being emotionally distant, or prioritizing others can seem minor, but these behaviors chip away at the foundation of your bond. Awareness and willingness to change can help you become a more considerate, connected, and committed partner.
As Dr. Gary Chapman, author of The 5 Love Languages, suggests, “Love is something you do for someone else, not something you do for yourself.” By paying attention to these signs and making conscious efforts to improve, you can nurture a relationship that is fulfilling for both you and your partner.
Keywords: relationship improvement, healthy relationships, self-awareness in relationships, commitment, nurturing love
Criticism is a double-edged sword. While offering constructive feedback can be helpful, constant and harsh criticism can damage your partner’s self-esteem. If you frequently point out flaws in their appearance, behavior, or habits, you risk making them feel inadequate and unloved. Over time, this pattern of negativity can erode their confidence and create a toxic environment.
Renowned psychologist Dr. Harriet Lerner warns, “Harsh criticism corrodes the very foundation of a relationship.” Instead of criticizing, focus on open, respectful communication. If there’s something that bothers you, express it as a concern rather than an attack. Remember, support and encouragement foster growth, while constant criticism breeds resentment.
Blame can be a relationship killer. If you’re always shifting responsibility onto your partner and refusing to own up to your mistakes, you’re fostering a dynamic of mistrust and frustration. Blaming your partner repeatedly can make them feel like they’re walking on eggshells, fearful of being accused of something they didn’t do.
Dr. Carol Dweck, author of Mindset: The New Psychology of Success, emphasizes the importance of accountability: “Owning your mistakes is a sign of growth and maturity.” Accepting responsibility shows your partner that you respect them and the relationship. Instead of pointing fingers, approach problems as a team, working together to find solutions rather than assigning blame.
Keywords: blame in relationships, lack of accountability, relationship trust, mature relationships, teamwork in relationships
Inattention might seem harmless, but it can be deeply hurtful. When you fail to listen or notice your partner’s needs and feelings, it signals that they’re not a priority. Small moments of inattention can accumulate, making your partner feel invisible and unloved. Active listening and attentiveness are fundamental to any healthy relationship.
Author and therapist Dr. Gary Chapman notes, “When we give someone our attention, we are affirming their importance.” By paying attention to your partner’s words and actions, you show that you care. Simple gestures, like remembering their preferences or responding thoughtfully, can reinforce your commitment and deepen your connection.
Keywords: lack of attention, relationship neglect, active listening, partner’s needs, healthy communication
Criticizing, blaming, and neglecting your partner’s needs are harmful behaviors that can jeopardize your relationship. These red flags often stem from a lack of self-awareness and emotional maturity. Recognizing these patterns and making an effort to change can lead to healthier, more fulfilling connections.
As author Stephen Covey writes in The 7 Habits of Highly Effective People, “Seek first to understand, then to be understood.” By fostering respect, accountability, and attentiveness, you create a foundation of trust and love that can withstand challenges and strengthen your bond.
Keywords: relationship red flags, self-awareness, emotional maturity, building trust, fulfilling relationships
Excuses are the enemy of accountability. Constantly justifying your mistakes — whether it’s forgetting important dates, failing to follow through on promises, or showing up late — signals to your partner that you’re unwilling to take responsibility. When you’re always blaming exhaustion, stress, or your busy schedule, it shows a lack of effort and sincerity. Over time, this pattern will cause your partner to feel that you don’t value the relationship enough to try.
Dr. Jordan Peterson, author of 12 Rules for Life, notes, “If you avoid responsibility, you avoid growth.” A healthy relationship requires maturity and a willingness to admit when you’re wrong. Instead of making excuses, own your actions, and make an effort to improve. Showing genuine responsibility and following through with your commitments can rebuild trust and show your partner that they matter to you.
Keywords: making excuses in relationships, lack of accountability, responsibility in relationships, relationship growth, rebuilding trust
A relationship thrives on balance and mutual give-and-take. If you’re always insisting on your way and refusing to compromise, your partner will feel undervalued and powerless. Constantly pushing your needs above theirs creates resentment and an unhealthy power dynamic. True compromise means sometimes putting your partner’s needs ahead of your own and finding middle ground where both partners feel respected.
Renowned therapist Dr. John Gottman says, “Compromise is not about losing. It’s about deciding that the relationship is more important than any single argument.” A willingness to meet your partner halfway shows that you’re committed to their happiness and the longevity of the relationship. Compromising doesn’t mean sacrificing your identity; it means building a partnership where both of you thrive.
Humor and teasing can be fun, but if your jokes consistently target your partner’s insecurities, it crosses a line. Constantly putting down their appearance, ambitions, or choices damages their self-esteem and erodes trust. Even if you don’t intend to hurt them, repeated negative remarks can make your partner feel unworthy and unloved. Respect is the cornerstone of any healthy relationship, and degrading comments undermine that respect.
Dr. John Amodeo, author of Dancing with Fire, says, “A loving relationship is a safe haven where each person can thrive.” Instead of tearing your partner down, uplift them. Compliment their achievements, support their dreams, and be their cheerleader. Respectful communication and positive reinforcement nurture a relationship where both partners feel valued and loved.
Making excuses, refusing to compromise, and putting your partner down are behaviors that can slowly poison a relationship. These actions stem from a lack of accountability, empathy, and respect. Recognizing and addressing these red flags is vital for nurturing a healthy, lasting connection. A strong relationship is built on mutual respect, shared responsibility, and genuine support.
As Dr. Gary Chapman, author of The 5 Love Languages, advises, “Love is a choice you make every day.” By choosing to be accountable, flexible, and respectful, you demonstrate a commitment to your partner and the relationship. These small yet significant changes can turn a troubled relationship into a thriving, loving partnership.
Comparison is a silent relationship destroyer. When you consistently measure your partner against others — whether it’s a friend’s boyfriend, a coworker, or even an ex — you’re communicating that they’re not enough. Such comparisons make your partner feel undervalued and insecure. Even if you think your intentions are harmless, the cumulative effect can diminish their confidence and breed resentment.
Psychologist Dr. Linda Sapadin explains, “Comparing your partner to others erodes their self-worth and damages the emotional connection.” Instead of focusing on what your partner lacks, celebrate their unique qualities. A healthy relationship thrives on mutual appreciation and respect. Recognize your partner for who they are, and nurture their individuality.
Stonewalling — shutting down during important discussions — is one of the most harmful communication patterns in a relationship. Whether you avoid answering questions, dismiss your partner’s concerns, or give them the silent treatment, it creates frustration and emotional distance. Stonewalling communicates that your partner’s feelings are not worth your time or attention.
Dr. John Gottman identifies stonewalling as one of the “Four Horsemen of the Apocalypse” for relationships. He suggests, “When one partner stonewalls, the other often feels rejected or abandoned.” Instead of shutting down, try active listening and honest engagement. Even if you need a break to collect your thoughts, communicate that respectfully and return to the conversation when you’re ready.
A supportive partner is essential for personal growth and fulfillment. If you belittle or mock your partner’s ambitions, you undermine their confidence and trust. Even if their goals seem unrealistic to you, dismissing them shows a lack of empathy and respect. Everyone needs encouragement, especially from their significant other.
As motivational author Napoleon Hill wrote, “Cherish your visions and dreams, as they are the children of your soul.” A thriving relationship encourages each partner to pursue their dreams. Instead of taunting their ambitions, ask how you can support them. Be their biggest cheerleader, and they’ll feel valued and understood, strengthening your bond.
Keywords: taunting ambitions, lack of support, partner’s goals, encouraging partner, building confidence
Comparing your partner to others, stonewalling serious conversations, and mocking their ambitions are toxic behaviors that weaken the foundation of any relationship. These patterns communicate disrespect, insecurity, and emotional detachment. Recognizing these harmful habits is the first step toward positive change.
As Dr. Brené Brown says, “Connection is why we’re here; it’s what gives purpose and meaning to our lives.” By replacing these negative behaviors with support, open communication, and appreciation, you foster a relationship built on trust, respect, and genuine connection.
Disregarding your partner’s feelings can create an emotional void in your relationship. If you frequently dismiss their concerns, minimize their emotions, or mock their reactions, you’re sending the message that their feelings don’t matter. This lack of empathy can lead to feelings of alienation and deep emotional wounds, ultimately weakening the connection between you.
Renowned relationship expert Dr. Sue Johnson, author of Hold Me Tight, asserts, “Empathy is the key to secure bonds.” A strong relationship thrives on understanding and validating each other’s experiences. Instead of dismissing your partner’s emotions, actively listen and offer support. By acknowledging their feelings, you build trust, intimacy, and a sense of safety in your relationship.
Secrecy can erode the foundation of trust in a relationship. When you keep things hidden — whether big or small — you create distance and suspicion. Secrets, even seemingly harmless ones, can lead your partner to question your honesty and intentions. Transparency is essential for maintaining trust and emotional closeness.
Dr. Shirley Glass, in her book Not Just Friends, explains, “Trust is built through openness and shattered by secrecy.” If you find yourself hiding aspects of your life, reflect on why. Open communication about your thoughts, actions, and feelings helps create a bond where both partners feel secure and valued. Honesty might feel risky, but it’s the bedrock of a healthy relationship.
Keywords: keeping secrets, relationship trust, transparency in relationships, emotional closeness, honesty in relationships
Healthy relationships thrive on mutual respect, and that includes honoring boundaries. If you frequently push your partner’s limits, dismiss their need for personal space, or pressure them into things they’re uncomfortable with, you’re showing a lack of respect. Disregarding boundaries can create resentment and cause your partner to feel trapped or disrespected.
As Dr. Henry Cloud and Dr. John Townsend explain in their book Boundaries, “Respecting boundaries shows that you value your partner’s autonomy and individuality.” Recognize and honor your partner’s limits, whether emotional, physical, or social. This respect fosters trust, safety, and a healthier dynamic where both partners feel valued and secure.
Disregarding feelings, keeping secrets, and disrespecting boundaries are significant red flags that can harm even the strongest relationships. These behaviors signal a lack of empathy, trust, and respect — core elements needed for a thriving partnership. Recognizing these patterns and addressing them is essential for growth and connection.
As Dr. Sue Johnson wisely says, “We are never so vulnerable as when we trust someone — but paradoxically, if we cannot trust, neither can we find joy.” By fostering empathy, transparency, and respect, you create a relationship where both partners feel seen, heard, and valued. This sets the stage for long-lasting love and mutual happiness.
Keywords: relationship red flags, trust and respect, emotional connection, healthy partnerships, relationship growth
Chapman, Gary.The 5 Love Languages: The Secret to Love That Lasts. Chicago: Northfield Publishing, 2015. A classic resource on understanding and expressing love effectively through different love languages.
Covey, Stephen R.The 7 Habits of Highly Effective People: Powerful Lessons in Personal Change. New York: Free Press, 1989. A guide to personal growth that includes principles for fostering healthy communication and understanding in relationships.
Dweck, Carol S.Mindset: The New Psychology of Success. New York: Random House, 2006. A book on the power of a growth mindset and how accountability fosters healthy personal and relational growth.
Glass, Shirley.Not Just Friends: Rebuilding Trust and Recovering Your Sanity After Infidelity. New York: Free Press, 2003. Offers insights into rebuilding trust and maintaining transparency in relationships.
Gottman, John M., and Nan Silver.The Seven Principles for Making Marriage Work. New York: Harmony Books, 1999. Research-based advice on building strong relationships through communication, appreciation, and conflict resolution.
Johnson, Dr. Sue.Hold Me Tight: Seven Conversations for a Lifetime of Love. New York: Little, Brown and Company, 2008. A guide to building emotional intimacy through open conversations and vulnerability.
Lerner, Harriet.The Dance of Anger: A Woman’s Guide to Changing the Patterns of Intimate Relationships. New York: Harper Perennial, 1985. An exploration of how anger affects relationships and how to foster healthier communication patterns.
Perel, Esther.Mating in Captivity: Unlocking Erotic Intelligence. New York: HarperCollins, 2006. Insights on balancing intimacy, independence, and passion in long-term relationships.
Brown, Brené.Daring Greatly: How the Courage to Be Vulnerable Transforms the Way We Live, Love, Parent, and Lead. New York: Gotham Books, 2012. Examines the importance of vulnerability and trust in building strong connections.
Cloud, Henry, and John Townsend.Boundaries: When to Say Yes, How to Say No to Take Control of Your Life. Grand Rapids: Zondervan, 1992. A foundational work on understanding and setting boundaries in relationships.
This bibliography covers a range of topics, including trust, communication, vulnerability, emotional intimacy, and boundaries, offering comprehensive resources for further study on building healthy relationships.
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
Words can heal, connect, and inspire—or they can alienate, offend, and destroy relationships. The way we speak in difficult moments often holds the power to strengthen bonds or drive a wedge between people. Miscommunication isn’t just about misunderstandings; sometimes, it’s the choice of words that pushes others away. When we rely on dismissive or defensive phrases, we risk damaging relationships without even realizing it.
The truth is, phrases that seem harmless can signal deeper issues: a lack of empathy, resistance to change, or an unwillingness to collaborate. When people feel unheard or undervalued, trust erodes, and frustration builds. Communication is a two-way street, and it’s essential to remain aware of how your words land on others.
Fortunately, improving communication is a skill that anyone can develop. By recognizing and avoiding phrases that shut down dialogue, we can foster healthier and more productive relationships—whether at work, home, or within our social circles. The first step is awareness. Let’s explore some common phrases difficult people say and the damage they can cause.
This phrase signals disinterest and detachment faster than any action can. When you say, “It’s not my job,” you immediately send a message that you’re unwilling to be part of the team’s success. Team dynamics thrive on cooperation and adaptability, not rigid adherence to predefined roles. Even if the request is outside your direct duties, a refusal to engage comes across as apathetic, eroding team morale and productivity.
Collaboration often means stepping up when challenges arise. Studies by Gallup reveal that organizations with highly engaged employees achieve 21% greater profitability compared to those with disengaged teams. Instead of shutting down with “It’s not my job,” a better approach would be to say, “I’d like to help within my capabilities. What do you need?” This shows you are willing to contribute while setting healthy boundaries.
Keywords: Teamwork, employee engagement, collaboration, workplace productivity, communication boundaries
Few phrases are more effective at stifling innovation than “That’s not how we do it here.” This statement signals a fear of change and a preference for the status quo. When people hear this, they feel discouraged from sharing new ideas or exploring creative solutions. In a world where progress depends on adaptability, clinging to old ways can leave an organization stagnant and uncompetitive.
John P. Kotter, a leading authority on change management, notes that leaders must create an environment where people feel encouraged to challenge existing processes. Instead of dismissing new ideas, try saying, “We’ve always done it this way, but I’d love to hear your thoughts on how we can improve.” This approach fosters innovation while respecting past methods and encouraging dialogue.
Memory lapses are normal, yet this phrase turns forgetfulness into a weapon of guilt. When you express disbelief at someone’s inability to remember something, it comes across as accusatory and demeaning. Instead of solving the issue, it creates unnecessary tension and resentment. The reality is that forgetting is part of being human—studies show that people forget 50% of new information within an hour and up to 90% within a week.
Rather than blame someone for not remembering, a more effective and compassionate approach is to repeat the information calmly or provide a reminder. Empathy and understanding go a long way in maintaining healthy communication. As Brené Brown advises, “Empathy is a choice, and it’s a vulnerable choice.” Choose patience over frustration, and you’ll build stronger relationships.
Keywords: Memory lapses, empathy, communication breakdown, patience, understanding
The phrases we choose during challenging interactions can either build bridges or burn them. Dismissive statements like “It’s not my job,” “That’s not how we do it here,” and “I can’t believe you don’t remember” might seem trivial, but they convey deeper issues of apathy, resistance, and judgment. By shifting these responses to more empathetic and open-minded alternatives, we can foster a culture of collaboration, innovation, and understanding.
Words have power—and with that power comes responsibility. Mindful communication can transform not only your personal relationships but also your professional environment. The next time you find yourself tempted to use a phrase that shuts someone down, pause and choose words that open the door to connection. In doing so, you create space for growth, trust, and mutual respect.
Keywords: Mindful communication, collaboration, empathy, professional growth, mutual respect
Few phrases are more grating than “I told you so.” When you use this phrase, it immediately creates a sense of superiority, making the other person feel belittled. While it might feel momentarily satisfying to assert that you were right, this phrase damages relationships and discourages open communication. No one likes to be reminded of their mistakes in a way that diminishes their self-worth.
Instead of claiming victory, take a more gracious approach. Phrases like, “I’m glad we figured that out,” maintain dignity on both sides and emphasize teamwork over ego. This small shift in language helps preserve trust and encourages continued collaboration. Remember, true leadership is about lifting others up, not putting them down.
The phrase “I’ll do it myself” often stems from a desire for control and efficiency. While it may seem productive in the short term, this approach can undermine trust in others’ abilities and damage team morale. When you take on everything yourself, you deny others the opportunity to contribute and grow, creating a culture of dependence rather than collaboration.
A more balanced approach is to invite collaboration. Instead of dismissing others, say, “I’d like to work on this together—I trust your input.” This fosters a sense of mutual respect and shared responsibility. Effective teamwork relies on trust, and showing faith in your colleagues helps build a more cohesive and empowered team.
Saying “I don’t care” is a quick way to alienate others. This phrase signals disinterest and dismissiveness, making people feel that their concerns are unimportant. While you may feel disengaged for personal reasons, outright apathy shuts down emotional connection and fosters resentment. This behavior can stem from insecurity, a need for control, or emotional self-protection.
A better approach is to communicate your feelings honestly. Instead of saying “I don’t care,” try explaining why you feel disengaged. For example, “I’m feeling overwhelmed right now—can we revisit this later?” shows vulnerability and invites understanding. This approach maintains empathy and keeps lines of communication open.
Phrases like “I told you so,” “I’ll do it myself,” and “I don’t care” can seem harmless but often reflect deeper issues of control, superiority, and apathy. These expressions damage relationships, erode trust, and hinder collaboration. By replacing them with more constructive language, you create an atmosphere of respect, teamwork, and empathy.
Mindful communication is essential for building healthy interactions. Choosing words that uplift rather than diminish others can transform both personal and professional relationships. The next time you face a challenging interaction, pause and choose words that foster connection and understanding. In doing so, you build a foundation of trust and mutual respect.
Comparing someone to another person is a surefire way to damage their confidence and self-worth. When you say, “Why can’t you be more like…,” you’re signaling that the individual isn’t good enough as they are. This not only fosters resentment but also creates a culture of insecurity. People need to feel valued for their unique strengths, not measured against someone else’s achievements.
Instead of comparisons, highlight the person’s abilities and potential for growth. A more supportive approach would be, “I appreciate the way you handle things, and I believe you can achieve even more.” This builds confidence and encourages personal development. As Theodore Roosevelt famously said, “Comparison is the thief of joy.” Recognize individual progress and celebrate it.
Keywords: Comparisons, insecurity, self-worth, individuality, personal growth
Telling someone, “You’re too sensitive,” is a form of emotional invalidation. It implies their feelings are excessive or unjustified, which can lead to self-doubt and suppressed emotions. This phrase is often used to deflect responsibility and shift blame, making the person feel guilty for expressing their feelings. It’s a tactic frequently used in toxic communication patterns.
Instead, practice empathy and acknowledge the other person’s emotions. Phrases like, “I’m sorry if that hurt you—can you help me understand how you feel?” invite constructive dialogue and show that you care. Emotional sensitivity is not a flaw; it’s a sign of humanity. Recognizing and respecting others’ emotions can deepen trust and connection.
When someone says, “That’s just the way I am,” they’re resisting accountability and self-improvement. This phrase signals an unwillingness to change, effectively telling others to tolerate bad behavior. Clinical psychologist Dr. Ramani Durvasula explains that this statement is a defense mechanism to avoid growth. It creates frustration and resentment in relationships.
A better approach is to show openness to change. Instead of dismissing concerns, say, “I’m working on improving that—thank you for your patience.” Growth is a lifelong process, and showing a willingness to adapt fosters healthier interactions. As the Greek philosopher Heraclitus said, “The only constant in life is change.” Embrace it.
Keywords: Self-improvement, accountability, growth mindset, adaptability, resistance to change
Phrases like “Why can’t you be more like…,” “You’re too sensitive,” and “That’s just the way I am” may seem harmless but carry damaging implications. These statements undermine self-worth, invalidate emotions, and resist growth. Over time, they erode trust and create a hostile environment, whether in personal relationships or professional settings.
Replacing these phrases with empathetic and growth-oriented language can transform interactions. By recognizing individuality, validating emotions, and embracing self-improvement, you foster healthier, more respectful relationships. Remember, mindful communication is not about perfection but about progress. Every time you choose words that build up rather than tear down, you strengthen connections and create a culture of respect and understanding.
Dismissing someone’s feelings as “dramatic” is a quick way to undermine their emotional experience. This phrase invalidates the person’s perspective and implies that their reaction is exaggerated or unreasonable. When people hear this, they often feel embarrassed, misunderstood, or frustrated. Instead of resolving conflict, this statement intensifies it and shuts down productive communication.
Furthermore, labeling someone as “dramatic” can be a subtle form of gaslighting, making them doubt their own reality. Recognizing that everyone processes emotions differently is key to maintaining healthy communication. A better approach is to say, “I want to understand why this is affecting you so much—can you explain?” This fosters empathy and helps bridge the emotional gap.
Keywords: Emotional invalidation, gaslighting, empathy, communication breakdown, conflict resolution
At face value, “It’s fine” might seem like a harmless response, but it often masks deeper dissatisfaction or passive-aggressiveness. When people use this phrase to avoid discussing their real feelings, it creates confusion and tension. Instead of addressing issues head-on, the phrase leaves the other person guessing what went wrong, which can lead to misunderstandings and frustration.
Open communication is crucial for resolving conflicts. Rather than saying “It’s fine,” try expressing your feelings directly: “I feel upset about this, but I want to talk it through.” This approach avoids ambiguity and promotes honesty. Clear communication not only resolves issues faster but also strengthens trust and mutual understanding.
Keywords: Passive-aggressiveness, open communication, conflict resolution, honesty, trust
Prefacing a statement with “I’m not racist, but…” almost guarantees that what follows will be offensive or discriminatory. This phrase attempts to shield the speaker from accountability while expressing a biased or prejudiced view. It’s a form of microaggression that perpetuates negative stereotypes and can damage relationships or workplace culture.
Instead of justifying a potentially harmful statement, consider whether it needs to be said at all. Reflect on your intentions and how your words might affect others. As diversity expert Dr. Beverly Daniel Tatum advises, “Recognizing bias is the first step toward change.” Choosing words that promote understanding and inclusivity is essential for fostering respectful relationships.
Phrases like “You’re so dramatic,” “It’s fine,” and “I’m not racist, but…” may seem small, but their impact is profound. These statements invalidate feelings, mask true intentions, and perpetuate harmful biases. Over time, they erode trust and sow the seeds of resentment, both personally and professionally. Mindful communication involves being aware of how your words affect others and choosing language that fosters respect and empathy.
By replacing dismissive phrases with honest, empathetic, and inclusive communication, you create healthier and more productive interactions. As Maya Angelou wisely said, “Do the best you can until you know better. Then when you know better, do better.” Every word you choose has the power to build or break relationships—so choose wisely.
Saying, “That’s not my problem” or the snarkier, “That sounds like a you problem,” immediately signals indifference and lack of empathy. In both personal and professional settings, relationships thrive on mutual care and shared accountability. This phrase not only damages trust but also undermines cooperation and teamwork.
Even if a problem isn’t directly yours to solve, showing empathy goes a long way. A better approach is to say, “I understand that’s difficult—how can I help support you?” or “I may not have the solution, but let’s figure out who can help.” Offering understanding or guidance maintains a spirit of collaboration and keeps lines of communication open.
Keywords: Empathy, teamwork, accountability, collaboration, support
While honesty is important, saying “I’m just being honest” can often mask unkindness or unnecessary criticism. As author Bruce Kasanoff explains, “The truth can be used in many ways: to help others, to solve a problem, or… to intentionally hurt someone.” This phrase often deflects responsibility for being hurtful by framing cruelty as honesty.
Constructive feedback is about expressing truth with kindness. Instead of using this phrase, ask, “May I offer some feedback?” or “I want to be honest, but I also want to be helpful.” This approach softens the message and shows respect for the other person’s feelings. Honesty, when delivered with empathy, strengthens relationships rather than tearing them down.
Keywords: Honesty, constructive feedback, kindness, empathy, communication
Using absolute phrases like “You always” or “You never” exaggerates situations and often escalates arguments. This language makes the other person feel attacked and defensive, which hinders resolution. According to Dr. John Gottman, a renowned relationship expert, these kinds of blanket statements are a form of criticism that erodes trust and intimacy.
Instead of making sweeping generalizations, be specific and express your feelings. Try saying, “Sometimes, it feels like…” or “I’ve noticed this happening occasionally.” This approach addresses the issue without assigning blame. It keeps communication open, encourages dialogue, and paves the way for constructive solutions.
Phrases like “That’s not my problem,” “I’m just being honest,” and “You always/never” may seem minor, but they can have lasting negative effects on relationships. These expressions can foster indifference, defensiveness, and resentment. By choosing more considerate and empathetic language, you can nurture trust, encourage collaboration, and promote healthy interactions.
Mindful communication is key to maintaining strong personal and professional relationships. Taking the time to choose words that uplift and respect others helps build a foundation of mutual understanding. Remember, every interaction is an opportunity to connect and grow—so choose your words wisely.
This phrase initially served as a way to calm others down or defuse tense situations. However, it’s increasingly used to invalidate someone’s feelings or concerns, trivializing emotions that may genuinely matter to the other person. By dismissing a topic with “It’s not that deep,” you’re signaling that their perspective isn’t worth engaging with, which can lead to frustration and disconnection.
Instead of shutting someone down, try showing empathy. Say, “I see this is important to you—what’s on your mind?” This approach invites open dialogue and demonstrates that you care about their viewpoint. A little understanding goes a long way in maintaining trust and healthy communication.
Keywords: Emotional invalidation, empathy, emotional connection, active listening, understanding
While the intention behind “I’m just trying to help” may be positive, unsolicited advice can feel controlling or patronizing, especially when it’s given without permission. As Dr. Brené Brown highlights, genuine support involves respecting others’ autonomy and distinguishing between helping and controlling.
Before offering help, ask first. A simple “Would you like my input?” or “Is there any way I can support you?” shows respect for their boundaries and invites consent. This approach fosters trust and ensures your help is genuinely welcomed.
Keywords: Unsolicited advice, consent, boundaries, empathy, genuine support
Ultimatums like “If you don’t like it, leave” create a hostile atmosphere and shut down productive discussions. Marriage and family therapist Megan Harrison, LMFT, explains that ultimatums sow insecurity, leaving people unsure if they’re accepted because of genuine care or out of fear of consequences. This phrase damages trust and makes relationships feel conditional.
A healthier approach is to address issues collaboratively. Try saying, “I’d like us to work through this together—what can we do to make this better?” This keeps the conversation open, promotes understanding, and allows for mutual problem-solving rather than driving people away.
Dismissive phrases like “It’s not that deep,” “I’m just trying to help,” and “If you don’t like it, leave” might seem minor, but they often carry deeper implications of invalidation, control, and hostility. These expressions can erode trust and damage relationships over time. By replacing them with empathetic and collaborative alternatives, you create space for genuine understanding and connection.
Words have the power to build bridges or burn them. Mindful communication, rooted in empathy and respect, can transform interactions and foster healthier relationships. The next time you’re tempted to use a dismissive phrase, pause and choose words that open doors rather than close them. In doing so, you nurture trust, understanding, and collaboration.
This phrase signals frustration and a breakdown in communication. It can come across as dismissive and condescending, suggesting that the other person is incapable of understanding. More often than not, it’s used to assert intellectual superiority rather than resolve the misunderstanding. Instead of fostering clarity, it creates defensiveness and shuts down the conversation.
A better approach is to show a willingness to clarify. Instead of saying, “You just don’t get it,” try asking, “How can I explain this better?” This invites dialogue, acknowledges the potential for misunderstanding on both sides, and keeps the conversation open. Effective communication is about clarity, not blame.
Keywords: Communication breakdown, frustration, intellectual superiority, clarity, dialogue
Time is a precious commodity, but saying, “I’m too busy for this” dismisses the importance of the other person’s needs or concerns. It conveys that your time is more valuable than theirs, leaving them feeling unimportant or brushed aside. Even more frustrating is when it’s clear you’re not genuinely busy but simply avoiding the issue.
Rather than dismissing someone outright, set boundaries with mutual respect. Try, “Can we reschedule when I can give this my full attention?” This communicates that their concerns matter and you want to address them properly, without making them feel insignificant. Respectful time management strengthens relationships and builds trust.
Keywords: Time management, boundaries, mutual respect, communication, prioritization
Phrases like “You just don’t get it” and “I’m too busy for this” might seem like minor expressions of frustration, but they signal deeper issues of condescension and dismissiveness. These statements erode trust, shut down dialogue, and create emotional distance. Replacing them with more thoughtful alternatives like “How can I explain this better?” or “Can we reschedule when I can give this my full attention?” fosters mutual understanding and respect.
Effective communication is about clarity, empathy, and respect. When you choose words that invite dialogue instead of shutting it down, you nurture healthier, more collaborative relationships—whether at work, home, or beyond.
Keywords: Clarity, mutual respect, empathy, communication skills, relationship-building
Brown, Brené.Dare to Lead: Brave Work. Tough Conversations. Whole Hearts. Random House, 2018. A book exploring vulnerability, leadership, and empathy in communication.
Gottman, John, and Nan Silver.The Seven Principles for Making Marriage Work. Harmony, 2015. Offers insights into communication patterns, such as criticism and defensiveness, and their effects on relationships.
Kasanoff, Bruce. “The Truth About Being Brutally Honest.” Forbes, 2016. Discusses the distinction between honesty and cruelty in communication.
Kotter, John P.Leading Change. Harvard Business Review Press, 2012. A seminal work on managing change in organizations and encouraging innovative thinking.
Durvasula, Ramani.Don’t You Know Who I Am? How to Stay Sane in an Era of Narcissism, Entitlement, and Incivility. Post Hill Press, 2019. Explores defensive communication, gaslighting, and toxic interpersonal behaviors.
Harrison, Megan. “Why Ultimatums Can Harm Relationships.” Marriage Counseling Blog, 2020. Analyzes how ultimatums create insecurity and hinder healthy dialogue.
Heraclitus.Fragments. Translated by Brooks Haxton, Penguin Classics, 2001. Contains the quote: “The only constant in life is change,” a key philosophical insight on adaptability.
Gallup.State of the Global Workplace Report. Gallup, 2022. Offers statistics on employee engagement and its impact on workplace productivity.
Brown, Brené.The Gifts of Imperfection. Hazelden Publishing, 2010. Focuses on the importance of empathy, patience, and vulnerability in communication.
Mehrabian, Albert.Silent Messages: Implicit Communication of Emotions and Attitudes. Wadsworth, 1971. Examines the role of non-verbal communication and how messages are interpreted.
Nagoski, Emily, and Amelia Nagoski.Burnout: The Secret to Unlocking the Stress Cycle. Ballantine Books, 2019. Offers guidance on managing emotional exhaustion and communication stressors.
Chapman, Gary.The 5 Love Languages: The Secret to Love That Lasts. Northfield Publishing, 2015. Explains how communication styles can enhance or damage emotional connections.
This bibliography includes both psychological insights and practical communication strategies to support your writing. Let me know if you’d like additional resources!
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!
The text is a religious discourse interpreting a Hadith (a saying of the Prophet Muhammad) concerning the prohibition of certain actions, such as drinking alcohol or wearing silk. The speaker argues against a literal interpretation, emphasizing the Hadith’s context and the need for considering societal changes when applying religious laws. He stresses that the Hadith addresses the misuse of these things, not their inherent prohibition, and advocates for a nuanced understanding guided by Islamic principles. The speaker provides examples of how modern interpretations can address issues mentioned in the Hadith. The overall goal is to explain how to reconcile traditional religious texts with modern challenges.
Frequently Asked Questions about Interpreting Islamic Texts
How should we approach the interpretation of Hadith (Prophetic traditions), especially those that seem to contradict common understandings?
It is crucial not to take Hadith in isolation or at face value. Instead, we must interpret them within the broader context of the Quran and other established Islamic principles. This involves looking at the original Arabic, considering the historical context, and understanding the intended purpose. For instance, if a Hadith speaks of resolving issues related to private parts, it does not imply legitimizing previously forbidden acts, but rather addressing them within the bounds of Islamic law and ethics. The understanding of the audience, the customs of the time, and the broader Islamic legal framework must always be considered. The intent of the prophet was to address these issues in a way that guides people towards righteousness, not towards breaking the bounds of faith.
The text discusses solving issues related to “shame” (private parts). What does this refer to, and what does it not mean?
The text addresses the idea that some people will come and solve issues related to shame. This does not mean making acts of adultery permissible or creating new laws. Rather it points to the fact that there are rules and limits within Islamic law that are meant to protect and guide behavior, not abolish it entirely. The solution discussed refers to resolving issues within those bounds. For example, marriage is a legitimate way to address the “shame” associated with sexual desires while maintaining fidelity and chastity. It refers to issues that some may be experiencing in their personal lives, but that can be solved within the bounds of Islamic law.
What is the proper way to understand Hadith about items like silk and gold?
Hadith concerning clothing, such as silk and gold, must be understood in their historical and cultural context. In the Prophet’s time, these items were often associated with extravagance and pride. Therefore, a ban on men wearing them was to avoid this and promote humility. However, the text points out that these are now used as general items by many, including women, and their meaning in a modern context is different. The principles behind such prohibitions were to avoid takabbur (arrogance) and excessive indulgence. Therefore, the spirit of the law should be observed and the reasons for the rules should be kept in mind.
How does this text explain the Hadith on solving issues of music and wine?
The Hadith about resolving issues related to wine and music are not meant to encourage the use of those items, but to recognize the existence of issues that people have with these items. They need to be addressed by the community. The emphasis is on addressing these issues with wisdom and within the bounds of Islamic law. The focus is on avoiding the problems associated with them. Just as with other issues, a blanket ban on all forms of music or a rejection of any alcohol consumption in every context would be incorrect. Rather it is about understanding the problems surrounding these things in their historical and modern context and finding ways to address them in a responsible manner.
What is the meaning behind the text’s repeated emphasis on understanding context when interpreting religious texts?
The text emphasizes that understanding context is essential because it is easy to misinterpret religious texts if they are taken out of their historical, cultural, and linguistic contexts. Actions of the prophet should be understood within his specific situation. The goal should always be to understand the spirit behind the texts and not apply literal interpretations that may not be applicable across different times and cultures. The purpose is to guide behavior towards righteousness and not lead to new forms of sinful behavior.
How does this text view the use of modern technologies and how should they be used, given their ability to spread good and bad?
This text understands that modern technologies can be either beneficial or harmful depending on how they are used. They should be used with caution and an awareness of their ability to create good and bad in society. The emphasis is on maintaining a sense of responsibility and not ignoring religious principles, particularly when using things such as radio, television, or any medium that can spread ideas, morals, or values.
The text mentions the need for discussion and debate. Why is this important in understanding religious issues?
Discussion and debate are crucial for a deeper understanding of religious issues, because it allows different viewpoints to be expressed, considered, and contextualized. Through dialogue, it is possible to identify any misunderstandings and develop an informed interpretation that is aligned with the spirit and intent of the religious text. This is particularly important in addressing modern problems and issues where multiple solutions and approaches might be possible.
What does the text mean by “solving” problems, and how does this approach differ from merely prohibiting things?
The idea of “solving” problems within this context is not about changing or abolishing Islamic law. Rather, it refers to understanding the issues faced by the people within a community, and coming up with strategies that address these issues without going against fundamental religious principles. It is a practical, nuanced approach that recognizes human nature and needs, rather than a rigid, literal approach focused on prohibition. The goal is to uphold faith while providing guidance in how to live in the modern world.
Analyzing Hadith: A Study Guide
Quiz
Instructions: Answer each question in 2-3 complete sentences.
According to the text, what is the common, incorrect interpretation of the hadith about people solving issues of shame, wine, and music?
How does the text argue that the concept of “solving” these issues should be understood in a more nuanced way than simply permitting them?
What role does the concept of Hudood (limits set by God) play in understanding how these issues are to be approached?
How does the text suggest that the use of things like silk or music should be understood in light of their social context and potential for Takambur (arrogance)?
How does the text explain that the permissibility of certain actions can change based on circumstance and broader Islamic principles?
According to the text, how should one address the misuse of something that has both permissible and impermissible applications?
What example does the text provide to illustrate how an incorrect understanding of a Hadith can lead to extreme actions?
How does the author frame the issue of women and silk clothing in relation to the hadith being discussed?
In the context of the discussion, what is Mannu as it is defined in the text?
What does the author mean when stating they do not want anything for those who do Sitabir?
Answer Key
The common, incorrect interpretation is that the hadith means that these things (shameful acts, wine, and music) are simply made permissible. It’s assumed that “solving” the issues means they are no longer forbidden.
The text argues that “solving” issues should be understood within the context of Hudood, the limits set by God, and that these issues need to be addressed by finding solutions within the boundaries that already exist. It is not about simply permitting actions that were previously forbidden.
Hudood provides the framework for how to understand the permissibility of things. The discussion needs to be within the limits established by Islamic law, even when a matter may seem to be “solved,” meaning finding ways of living that still respect God’s boundaries.
The text indicates that if something like silk is used in a way that promotes arrogance (Takambur), then it becomes impermissible. The social context and the intent behind the usage are important considerations.
The text explains that things can change based on the understanding of a situation and in light of broader Islamic principles. Something that may be permissible in one context or time, may be impermissible in another time or place, because the context matters.
The text suggests that the correct use of weapons that have been used excessively for the wrong purposes should be addressed keeping the situation in mind. These weapons are used to combat an error.
The text gives an example of a person misinterpreting the hadith regarding silk and telling their wife to burn all her silk clothes because it was forbidden. It illustrates how a simple reading of the Hadith can lead to drastic actions without understanding context.
The author argues that the hadith should not be used to restrict women from wearing silk clothing in the present day, as the social context has changed. What is important is not the physical clothing item itself, but the potential for it to cause arrogance in individuals or society.
In the context of the discussion, Mannu is defined as something that goes against a person’s personality, life, honor or property as well as rebelling without a lock. Mannu can be brought about through the speech of others or the status of one’s words.
The author means they do not seek support or recognition for those whose actions in relation to the text, are wrong in their interpretation. That those who do wrong interpretations will not get any benefit from his analysis.
Essay Questions
Instructions: These are essay questions for further reflection. Choose one or more to develop into a longer essay.
Analyze the text’s argument regarding the interpretation of hadith, focusing on its call for nuanced understanding and the importance of context. How does this approach challenge literalist interpretations?
Explore the text’s discussion of Takambur (arrogance) and its relevance to material possessions and practices. How can this concept be used to evaluate contemporary social behaviors and choices?
Discuss the text’s approach to the concept of Hudood (divine limits). How does it balance the need for boundaries with the need for flexibility and critical thought?
How does the text use the specific examples of shame, wine, and music to illustrate the broader principles it is trying to convey about the understanding and application of hadith?
Evaluate the text’s methodology for analyzing and understanding religious texts. How can its approach be applied to other religious or ethical questions?
Glossary of Key Terms
Hadith: A record of the sayings and actions of the Prophet Muhammad, considered a crucial source of guidance in Islamic law and practice.
Ummah: The global community of Muslims, united by faith.
Asar: A narration from companions of the prophet Muhammad.
Fatwa: A legal opinion or ruling issued by a qualified religious scholar on a point of Islamic law.
Bukhari: A collection of hadith compiled by Imam Bukhari, and is considered by Sunni Muslims to be the most authentic collection of hadith.
Quran: The holy book of Islam, believed by Muslims to be the word of God as revealed to Prophet Muhammad.
Majeed: A term often used to refer to the Quran with reverence.
Hudood: The limits or boundaries set by God in Islamic law, defining what is permissible and forbidden.
Takambur: Arrogance or haughtiness; a negative character trait in Islam that can lead to sins and bad behavior.
Mutrafin: Those who live luxurious lifestyles.
Shohat: A reference to things that are considered to be the dress of urine.
Sitabir: One who does or makes the use of something incorrectly.
Mannu: Rebelling without a lock and goes against a person’s personality, life, honor or property. Can come about from someone’s speech or status.
Detailed Table of Contents: Analysis
I. Introduction: Understanding the Core Arguments
A. Addressing Misinterpretations of Tradition: The text opens by emphasizing the importance of proper interpretation of religious traditions (specifically, a hadith from Bukhari). It warns against simplistic readings that lead to flawed conclusions.
B. The Core Argument: This text aims to provide a nuanced understanding of religious principles related to sensitive topics (sexual conduct, adornment, music). The goal is to interpret these topics in a way that avoids broad prohibitions and instead applies nuanced, contextual solutions.
II. Analysis of Specific Issues & Their Solutions
A. The Issue of ‘Sharmgahs’ (Private Parts):This section discusses a hadith that mentions people will solve the issues of shame, addressing the notion that it means that sexual relations are always forbidden. The author posits that the hadith does not mean that sexual relations themselves are inherently bad.
The author contends that the hadith actually refers to addressing situations of sexual shame or misconduct through proper religious guidelines, specifically looking at the Quran and Hadith to develop and apply these rules in a contextual way.
The author claims the hadith is not speaking about marriages or sexual relations with wives (which are permissible), rather, it speaks to solving issues with “private parts” in a way that respects religious boundaries by addressing harmful actions.
B. The Issue of Silk and Adornment:The text addresses the use of silk (historically considered a luxurious garment), and the hadith that discusses the prohibition of silk for men. The author challenges a simplistic understanding of this rule, arguing that it was not intended as an absolute, never-changing ban.
The author argues that the prohibition during the Prophet’s time was tied to the social context where silk was a symbol of arrogance. They suggest it isn’t the material itself, but the way it is used and what it symbolizes in a given society that matters and is therefore relative.
The author claims that this type of approach means that the ban on silk is relative to changing cultural contexts and societal symbols, and should not be a basis for blanket rules.
C. The Issue of “Wine” and Music:The text addresses the hadith’s pronouncements about music and alcohol, again challenging interpretations that consider them inherently forbidden.
The author discusses how the hadith was speaking about correcting situations and contexts where music and alcohol were used to incite harmful behaviors, not that they are inherently forbidden.
The author discusses how the “essence of the day” (ie. religious law) must be followed and that those interpretations are found in the Quran, which allows for their moderate use, but warns against their abuse.
The author concludes that the hadith is speaking to using music and similar forms of entertainment responsibly, in accordance with religious principles and proper understanding of context.
III. The Methodology for Understanding Tradition
A. Contextual Reading: The author strongly advocates for understanding the historical and social context of religious texts, avoiding literal interpretations that could lead to misapplication of religious principles.
B. Holistic Interpretation: The importance of considering the Quran and other hadiths and religious texts in their broader context is stressed, moving beyond the isolated reading of one text alone, which the author sees as insufficient.
C. Intention and Effects: The author emphasizes looking at the intention behind religious rules and their potential effects in society. The intention of these rules is to uphold morality and justice and it must be understood that those intentions cannot be undermined by applying rules broadly and without contextual consideration.
IV. Applying Principles in Modern Contexts
A. Addressing Misuse and Excess: The author notes that while the text calls for careful use of seemingly prohibited things, it is also meant to guide Muslims away from misuse and excess. It is not calling for an abolition of all rules.
B. The Need for Discernment: This section calls for the use of religious interpretation as a method of discernment when deciding what actions and behaviors are consistent with religious principles.
C. The Importance of ‘Manners’ (Adab): The text concludes by re-emphasizing the importance of maintaining proper religious and cultural conduct as essential to these religious guidelines.
V. Conclusion: The Ongoing Application of Islamic Principles
A. The Importance of Interpretation: The text underscores the importance of the process of religious interpretation and the careful application of rules.
B. The Dynamism of Islamic Law: The author posits that Islamic principles are meant to be applied flexibly and responsibly as societies change, rather than imposing strict, unyielding legalism.
C. Continuing Discussion: The author concludes by emphasizing how these interpretations are a basis for on-going discussions, meant to engage with real-world challenges.
Okay, here is a briefing document analyzing the provided text:
Briefing Document: Analysis of “Pasted Text”
Date: October 26, 2023
Subject: In-depth review of a discourse on Islamic tradition, interpretation, and contemporary issues.
Introduction:
This document provides an in-depth analysis of a complex discourse concerning Islamic traditions (“hadith”), particularly focusing on interpretations related to issues such as modesty, the permissibility of certain arts and practices (like music and silk), and the broader application of religious texts in modern contexts. The speaker emphasizes the importance of contextual understanding and the dangers of literal interpretations of religious texts.
Key Themes and Ideas:
The Importance of Contextual Interpretation:
The speaker consistently argues against literal, surface-level readings of hadith. He stresses the need to understand the context in which a tradition was narrated and its purpose within the broader Islamic framework.
He emphasizes the need to consult other sources, especially the Quran, before deriving a ruling (“fatwa”). “So this means that as soon as we read the words, the first thing we have to see is this. Will have to review and see if it is done in Majid.”
He critiques those who “as soon as he reads this, will it be justified for him to start using it, and say that mummy is done, now closeness with the private parts cannot be adopted under any circumstances”.
He uses the example of the “private parts” to say that one cannot ban all intimacy simply because the tradition mentioned resolving shame around the topic.
Reinterpreting a Specific Hadith:
A central point of discussion revolves around a hadith which mentions that, within the Islamic community, some people will “solve the problems of shame, wine, and music.”
The speaker argues that this doesn’t mean all forms of these things will be universally “resolved” (i.e., made permissible). Instead, he states it means that, they would provide guidance for issues, not change the fundamentals.
He cautions against automatically interpreting “solving” as meaning that things are made completely permissible by highlighting, “You will express this relation with the woman, you will do something for her, you will tell the master that from today I am keeping this woman under my sight to reduce this relation and the woman will announce that she is taking the man under her sight When you declare this, then a life long happiness will be earned. Now if you are ashamed, there is a solution for you. This is exactly the reason.”
Modesty and Sexual Ethics:
The speaker addresses the concept of shame (“sharamgah”) in the context of sexual ethics and intimate relationships.
He emphasizes that Islam provides guidelines for appropriate behavior, not an outright prohibition of all forms of intimacy.
He makes reference to Islamic principles that provide solutions to relationship issues and shame. “Now if you are ashamed, there is a solution for you.”
The Status of Silk and Fashion:
The speaker delves into the issue of silk wearing, traditionally prohibited for men in some Islamic interpretations.
He argues that the prohibition should be viewed within the historical context when it was seen as a sign of extravagance and arrogance.
He notes that because “many other things have come in its place, whose status in the world is the same”, that rules should be re-evaluated with consideration of the time.
He asserts that the intention is not to impose an absolute prohibition on men wearing silk, but to ensure that clothing choices are not contributing to arrogance and societal inequality.
Music, Entertainment, and Moral Guidance:
The speaker discusses music and entertainment (“what happens in night clubs here, what happens in films here, what about the arrangements that have been made here at present”), emphasizing the importance of avoiding elements that are sexually suggestive and promote base desires.
He emphasizes that the solution was “not the sentence that due to their release, many things are created in the hearts and minds of people, is n’t it a statement that as a result of this, the thought of Jinsitar is created in the people, you and I know all these usages,”.
He highlights that when “we will see them in the hall as well, we will see them in the library as well and will get them attention, it means that we will solve it in the situation as well” there is a need for critical evaluation and a measured response.
The Purpose of Religious Texts:
The speaker argues that the purpose of religious texts is to guide individuals towards ethical conduct and a proper relationship with God (“Allah Taala”).
He cautions against weaponizing the text, noting “These types of weapons are used when you feel that the excessive use of something is wrong or people are making mistakes in that matter.”
He asserts they are not merely a set of rules to be followed literally without understanding the underlying moral and spiritual principles. He notes, “the main intention is that we will solve it even in those situations when he will be in a state of near remembrance because he will be in a state of trust when the doubt itself will be blocked from his mind when he Virat must have been given a rock by the issue, we will solve it in time too.”
“Haram” and Contextual Usage:
The speaker notes that “apart from the prohibitions of Khuron, He has only Five things They are haram i.e. promiscuous talkies against life and property and honour, farewell to caste and lion”.
He highlights that not everything is “haram”, and it’s the context and use that matters.
He emphasizes that some things that have been deemed “haram” have been misinterpreted. “shame has gone is not an aslam matter, shame has gone aslam is not haram, rather Allah Taala has prescribed some rules after which it is resolved”.
The Role of the Prophet and Scholars:
The speaker references the Prophet Muhammad’s (Sallallahu Alaihi Wasallam) actions and statements as the basis of Islamic practice, but also underscores that the scholars and followers also need to apply critical thinking, to “move ahead immediately… if this soul was used by SM in this way, then here a complaint has been made from the negative side that it would be misused.”
Quotes of Significance:
“in my Ummah, some such people will definitely be born who are modest and can solve the problems of surah, wine and music.” (This is the hadith being interpreted)
“So this means that as soon as we read the words, the first thing we have to see is this. Will have to review and see if it is done in Majid.”
“Now if you are ashamed, there is a solution for you. This is exactly the reason.”
“the private part is never forbidden in the day of God, the car that has been given is not always solved and will remain solved, this thing has to be known first”
“These types of weapons are used when you feel that the excessive use of something is wrong or people are making mistakes in that matter.”
“the main intention is that we will solve it even in those situations when he will be in a state of near remembrance because he will be in a state of trust when the doubt itself will be blocked from his mind when he Virat must have been given a rock by the issue, we will solve it in time too.”
Conclusion:
This discourse offers a sophisticated analysis of Islamic tradition, advocating for nuanced and contextually-aware interpretations. The speaker cautions against a simplistic, legalistic understanding of religious texts, instead urging a focus on their ethical and moral purposes. This perspective is particularly relevant to contemporary discussions on Islam and its application in diverse cultural contexts. The speaker emphasizes that traditions around modesty, music, and fashion should be understood as guidelines to prevent abuse and arrogance, not as outright prohibitions.
Allama Javed Ghamdi interprets Islamic modesty by focusing on the underlying principles and intentions behind the rules, rather than just the literal interpretations of religious texts [1, 2]. He emphasizes understanding the context and purpose of religious guidelines [2, 3].
Here’s a breakdown of his approach based on the provided sources:
Modesty as a Solution, Not Just a Restriction: Ghamdi views modesty (sharmgah) not just as a set of restrictions but as a solution to potential problems [2]. He suggests that Islamic teachings provide ways to address issues of shame and sexual relations in a responsible manner [2].
Contextual Understanding of Texts: Ghamdi argues against a simplistic reading of religious texts. He states that one must consider the broader context, principles, and the intent of Allah when interpreting religious texts [1, 2]. For example, when interpreting a hadith about resolving issues of “shame,” he does not believe it means that anything related to private parts should become permissible [1, 4]. Instead, he states that it refers to resolving issues of shame in a way that Allah has prescribed [1, 4].
Focus on the Spirit of the Law: He believes that the core intention behind Islamic rulings should be given importance [3]. He references the idea that some things, such as silk clothing for men, were considered inappropriate due to their association with extravagance and arrogance during the Prophet’s time [3, 5]. These rules were not meant to be permanent or absolute but to address specific societal issues [3].
Application of Principles in Modern Times: Ghamdi suggests that the principles of modesty should be applied with an understanding of current social norms and contexts [3, 6]. For example, he explains that while silk clothing for men was not permissible in the past, it is not necessarily so in the present [3, 5]. The underlying principle is to avoid things that promote arrogance or are inappropriate given the context of the society [3].
Addressing Misinterpretations: Ghamdi addresses potential misinterpretations of hadith [1]. He emphasizes that understanding the ‘day’ (deen) requires a deeper analysis beyond the literal wording of texts [2]. He uses the example of silk and says people should not go home and burn all their silk because the prophet forbade it; rather one should understand that it was forbidden in the context of the time for specific reasons [5, 7].
Modesty in Different Aspects of Life: He states that modesty is not limited to sexual matters; it also applies to clothing, speech, behavior and social interactions [3, 6]. He says that modesty is the foundation of morality [8].
Solutions and Discussion: Ghamdi argues that Islamic teachings encourage open discussion and finding solutions to problems, rather than simply imposing rigid restrictions [1, 9]. He states that people will solve issues through discussions [9]. He uses the example of wine, and states that while alcohol has been forbidden, a literal reading would suggest that the blessings of Allah are only accepted when offering namaz [3, 9]. Instead, we know that alcohol is not allowed and that is the meaning that we must follow [9].
In summary, Allama Javed Ghamdi’s interpretation of Islamic modesty emphasizes understanding the purpose, context, and underlying principles of religious teachings, rather than a purely literal adherence to the texts [1-3]. He encourages a flexible application of these principles in contemporary life [3, 6].
Ghamdi’s perspective on alcohol consumption, as described in the sources, is nuanced and contextual. Here’s a breakdown:
Not inherently forbidden: The sources suggest that the mere mention of alcohol in religious texts does not automatically mean it is forbidden [1]. There is a recognition that the context and purpose behind such mentions need to be understood [1].
Context matters: The sources argue that if a religious text mentions alcohol, it’s important to consider the broader context and intent, rather than taking a literal, isolated interpretation [1].
Beyond literal interpretation: When the text mentions resolving the issues related to wine, it does not mean that weapons will be forbidden [2].
Issue of Normality: If the normality of liquor was reduced, it is considered wrong in every situation [3].
Intention is key: It is stated that the intention is not to state the law of honor or courage, but to make people aware of the situation [1]. The focus is on how these things are being used and whether the purpose is aligned with religious principles [1, 4].
Modern context: Ghamdi’s view acknowledges that the meaning of things changes over time. What was relevant during the time of the Prophet may not be the same now [1]. He looks at how alcohol is being used now, including in night clubs and films, and considers its impact on society [3].
Addressing the “mischief”: The mention of alcohol is made in the context of addressing the “mischief” that may arise from its use [1]. The focus is on preventing these negative consequences.
No blanket prohibition: The sources indicate that simply because alcohol is mentioned in a religious text does not mean that it is completely forbidden [1].
The intent of a message: A person should not go by the words in the scripture, but rather should see the purpose, reason and methods of use [1].
Focus on solutions: The sources suggest a focus on finding solutions to problems, including those related to alcohol consumption, rather than focusing on rigid rules [2, 3].
Guidance through discussion: Issues related to alcohol use are intended to be solved through discussions, and not through strict prohibition [3].
In summary, Ghamdi does not appear to take a simplistic approach to the issue of alcohol consumption, but instead focuses on understanding the context, intent, and practical implications. The emphasis seems to be on a thoughtful approach that considers societal impact and the use of alcohol in modern situations, rather than a rigid, literal interpretation of religious texts.
The sources discuss the permissibility of music within an Islamic framework, particularly in relation to a hadith that mentions people who will solve the issues of shame, wine, and music [1]. The discussion revolves around how to interpret such religious texts in the context of modern society [1, 2].
Here’s a breakdown of how the sources suggest Ghamdi reconciles Islamic views on music with modern society:
Contextual Interpretation: The sources emphasize that interpreting religious texts requires understanding the context in which they were revealed, rather than taking the words at face value [1, 2]. For example, when the hadith speaks of solving issues related to shame, it doesn’t mean that all forms of intimacy become permissible [3]. It refers to resolving issues within the bounds of what is already permitted by God, such as the relationship between husband and wife [2, 3].
Focus on Intent: The intention behind actions is important. The sources suggest that the focus should not be on the mere act of listening to music, but also on the impact it has on the individual and the society. If music leads to immoral behavior or promotes harmful values, then it is not acceptable [4-6]. However, if music does not lead to these negative outcomes, it may be permissible [6].
Addressing Modern Issues: The sources recognize that many modern forms of entertainment like movies and music can lead to negative consequences if not used responsibly [4-6]. Instead of simply deeming them as forbidden, the sources suggest that the focus should be on addressing the harmful ways in which these things are used [5, 6].
Avoiding Extremism: The sources warn against misinterpreting religious texts to justify extremes. For instance, it would be wrong to conclude that all silk clothing is forbidden for women or that all music is prohibited simply based on one hadith [7]. The sources emphasize the need to consider the overall spirit of Islamic teachings [7, 8].
Emphasis on Ethical Conduct: The sources say the real focus should be on ethical conduct, maintaining decency, and avoiding arrogance. Actions such as wearing silk or listening to music are acceptable as long as they don’t lead to negative character traits such as pride, vanity, and immodesty [4, 7]. If those actions contribute to a corruption of values or behavior, then they are not acceptable [4, 5, 7].
In summary, the sources suggest that Ghamdi’s approach involves interpreting religious texts with an understanding of their context, focusing on the intent and ethical impact, addressing the actual problems caused by certain modern practices, and promoting ethical conduct in accordance with the spirit of Islam [1-8]. It’s about understanding that the goal is not to create a list of forbidden things, but to create a society where everyone behaves responsibly within the boundaries set by God [5, 6].
The sources discuss several issues considered “haram” (forbidden) in Islam, and how these issues are understood and addressed in a modern context. Here’s a breakdown:
Shame (private parts):
The sources discuss a hadith about people who will solve the problems of shame, wine, and music [1]. This doesn’t mean that everything related to the private parts becomes permissible [1, 2].
Rather, the hadith refers to resolving issues within the bounds of what is already permitted by God [3]. For example, it allows for intimacy within a marriage [2].
The sources emphasize that the focus is on addressing the misuse or misunderstanding of these matters rather than imposing blanket prohibitions [2, 3].
The sources highlight that the private parts are not always forbidden in the day of God [4].
Silk and Gold:
The sources discuss how some have misinterpreted religious texts to forbid silk and gold for men [4].
The sources say that during the time of the Prophet, wearing silk and gold was considered a sign of arrogance and luxury [5].
However, the sources argue against a literal interpretation, suggesting that the focus should be on the underlying principles and intentions [5, 6].
It is important to consider whether the wearing of silk is for pride or arrogance, and not just whether silk is worn or not [5].
If these things cause corruption of values or behavior, they are not acceptable [5].
The sources suggest that the focus should be on ethical conduct, maintaining decency, and avoiding arrogance [5].
Alcohol (wine):
The sources indicate that the mere mention of alcohol in religious texts does not automatically make it forbidden [6].
The context and purpose behind such mentions need to be understood [6].
The sources say that the focus should be on how these things are being used, and the problems that arise because of them [7].
If the normality of liquor was reduced, it is considered wrong in every situation [7].
The intention is not to state a law of honor or courage, but to make people aware of the situation. The focus is on how these things are being used and whether the purpose is aligned with religious principles [7].
Music:
The sources say that music, like the other issues, can be a source of harm if used incorrectly, and the focus should be on addressing the harmful ways in which music is used [8].
The sources say that instead of deeming music forbidden, the focus should be on its impact on the individual and society [8].
The sources say that if music leads to immoral behavior or promotes harmful values, then it is not acceptable, but if music does not lead to these negative outcomes, it may be permissible [8].
Other Prohibitions:
The sources mention that some actions are explicitly forbidden, such as “promiscuous talkies” and acts against life, property, and honor [8].
The sources also note that rebellion without a lock and casteism are wrong [4, 8].
The sources emphasize that it is not simply about listing prohibitions but also ensuring ethical conduct, and maintaining decency and humility [8, 9].
General Principles:
Contextual Interpretation: Religious texts should be understood in the context they were revealed, not literally [3, 9].
Intention: The intention behind an action is more important than the act itself [3, 5].
Ethical Conduct: The emphasis is on ethical conduct, maintaining decency, and avoiding arrogance [5, 8].
Addressing Harm: The focus is on identifying and addressing the “mischief” caused by certain practices [6].
Solutions over rigid rules: The sources show a preference for finding solutions through discussion and understanding rather than imposing rigid rules [1, 7].
Modern Relevance: The sources show an awareness of how cultural practices and technology may change and impact the understanding of what is considered haram [6, 10].
In summary, the sources show that Ghamdi’s approach to “haram” issues involves a focus on understanding the intent and context of religious texts, addressing the harm caused by certain practices, promoting ethical behavior and emphasizing solutions and discussions rather than simply adhering to a list of prohibitions.
The sources discuss Islamic traditions, particularly focusing on how they should be interpreted and applied in modern contexts, rather than providing a comprehensive overview of all traditions [1-10]. Here’s a breakdown of key points regarding the discussion of Islamic traditions in the sources:
Contextual Interpretation is Key: The sources emphasize that Islamic traditions, such as Hadiths, must be understood within their specific historical and cultural contexts [1, 2, 4-8]. Literal interpretations without considering the context can lead to misinterpretations and incorrect conclusions [1-3, 5, 6].
Focus on Intent and Ethical Impact: The sources suggest that the intention behind actions is crucial [2, 4, 6, 8, 10]. The ethical impact of a practice, rather than the action itself, should be the focus [5-7]. For instance, wearing silk might be permissible if it doesn’t lead to arrogance or immodesty, while listening to music might be permissible if it does not promote immoral behavior or harmful values [4-6, 8, 10].
Addressing “Mischief”: The sources talk about focusing on the “mischief” that may arise from the use of certain things [2, 3, 6]. This means that the focus should be on identifying and addressing the harmful consequences of certain practices rather than simply deeming them forbidden [2, 3, 6, 7].
Emphasis on Solutions Over Rigid Rules: The sources seem to favor solutions through discussion and understanding rather than imposing rigid rules [1-3, 6, 7, 9]. The goal is to resolve issues of misunderstanding or misuse [1-3, 6, 7]. For example, issues related to shame, wine, and music should be addressed in a way that allows for responsible behavior within the bounds of Islamic teachings [1-3, 6, 7].
Avoiding Extremism: The sources warn against misinterpreting traditions to justify extreme or narrow views [1-3, 5-8]. For example, it’s wrong to conclude that all silk is forbidden for women or all music is prohibited simply based on a literal interpretation of one hadith [1-3, 5-8].
Modern Relevance: The sources show an awareness of how cultural practices and technology may change and impact the understanding of Islamic traditions [6-10]. For example, modern forms of entertainment like movies and music should be addressed in terms of their impact, and not merely be deemed as forbidden [6-8].
Examples of Interpreted Traditions: The sources provide examples of how Islamic traditions related to shame, silk, wine, and music should be understood [1-8]. The tradition stating that some will solve the problems of shame, wine, and music, doesn’t mean these things are permissible without boundaries [1-3, 6, 7]. It means that Muslims should seek solutions within the bounds of Islamic teachings, and in a way that promotes ethical conduct [1-3, 6, 7].
Traditions about clothing, conduct and speech: The sources discuss traditions related to clothing and modesty, such as the use of silk and gold, in order to highlight the importance of humility and not arrogance [3-6, 8]. These traditions must be understood in their proper context, and not misused to create rigid rules [4-6, 8].
The sources also emphasize that the way one speaks and behaves is part of ethical conduct, and not just the rituals and acts of worship [4, 6-8].
Application of Traditions in Daily Life: The traditions should not just be about ritual observance, but should inform the way people behave with each other, and the values they embrace [3-6, 8]. The goal is to promote a society that is ethical, just, and moral [3-6, 8].
In summary, the sources present a view of Islamic traditions that prioritizes contextual understanding, ethical behavior, and addressing harm over rigid adherence to rules or literal interpretations [1-10]. The approach is intended to make Islamic traditions relevant in modern society, by promoting responsible behavior, and by addressing the actual problems that people face in the world today [1-10].
The sources discuss religious interpretations, particularly within an Islamic context, focusing on how to understand and apply religious texts and traditions in a way that is relevant and ethical in modern times. Here’s a breakdown of the key aspects of religious interpretation, as discussed in the sources:
Contextual Interpretation: The sources emphasize that religious texts must be understood within their specific historical, cultural, and social contexts [1-3]. This means that a literal reading of the text is insufficient and can lead to misinterpretations [2]. For instance, when the sources discuss the hadith about people who will solve the issues of shame, wine, and music, they highlight that it does not mean that everything related to those topics becomes permissible without boundaries [1]. Instead, the hadith must be interpreted within the context of Islamic teachings and ethical principles [2].
Focus on Intent and Ethical Impact: The intention behind an action and its ethical impact are considered more important than the action itself [2, 3]. For example, the sources explain that wearing silk or listening to music can be acceptable if it does not lead to arrogance or immoral behavior [4, 5]. The focus should be on maintaining decency and avoiding arrogance [5].
Addressing “Mischief”: The sources emphasize identifying and addressing the “mischief” or harm caused by certain practices or behaviors [1, 2]. Rather than simply deeming something forbidden, the focus should be on addressing the negative consequences it might produce [6, 7]. For instance, the sources discuss how modern forms of entertainment like movies and music can lead to negative consequences if not used responsibly [6, 8].
Solutions Over Rigid Rules: The sources indicate a preference for finding solutions through discussion and understanding, rather than imposing rigid rules [1, 7]. The goal is to resolve issues of misunderstanding or misuse [1, 2]. For example, the sources say that issues related to shame, wine, and music should be addressed in a way that allows for responsible behavior within the bounds of Islamic teachings, and that does not simply create a list of things that are forbidden [1, 7].
Avoiding Extremism: The sources warn against misinterpreting religious texts and traditions to justify extreme or narrow views [2, 3, 5]. It is not appropriate to conclude that all silk is forbidden for women, or all music is prohibited based solely on a literal interpretation of one hadith [2, 3, 5]. Such interpretations can lead to the neglect of the true spirit of Islam, and may even make the religion seem out of touch with modern society [5, 8].
Modern Relevance: The sources demonstrate an awareness of how cultural practices and technology can change and impact the understanding of religious interpretations [3, 5]. Therefore, modern forms of entertainment, like music, should be addressed in terms of their impact on ethical behavior and morality [5, 6, 8].
Application to Daily Life: Religious interpretations are not meant to be confined to ritual observance [4]. They should also inform the way people behave with each other and the values they embrace [5]. The goal is to promote a society that is ethical, just, and moral [5]. The sources also note that speech, conduct and personal appearance are all part of ethical conduct [4-6].
Examples of Interpreted Issues: The sources offer specific examples of how Islamic texts should be interpreted, including discussions on:
Shame (private parts): The hadith about solving the problems of shame doesn’t mean that all forms of intimacy are permitted, but that issues related to private matters should be resolved within the bounds of what is permitted by God [2, 3].
Silk and Gold: The prohibition against men wearing silk and gold should be understood in the context of avoiding arrogance and luxury, rather than a literal prohibition on wearing these items [4, 5].
Alcohol (wine): The mere mention of alcohol in religious texts does not automatically make it forbidden, and the focus should be on the context and intent of its use [6, 7].
Music: Music, like other issues, can be harmful if used incorrectly, but that the focus should be on addressing the harmful ways in which music is used rather than forbidding music in general [6-8].
In summary, the sources present a view of religious interpretation that emphasizes contextual understanding, the intent behind actions, the ethical impact of actions, addressing the harm caused by certain practices, and promoting discussion and solutions over rigid adherence to rules. The approach aims to make religious texts and traditions relevant to modern society by promoting responsible behavior and addressing actual problems that people face in the world today.
The sources discuss social problems, particularly within the context of Islamic teachings, by focusing on how certain behaviors and practices can lead to “mischief” and how these problems should be addressed [1, 2]. The sources do not explicitly define social problems, but they discuss issues that contribute to problems in society and how to resolve them. Here’s a breakdown of the key points:
Misinterpretation of Religious Texts: One of the primary sources of social problems, according to the sources, is the misinterpretation of religious texts and traditions [1, 2]. When religious texts are interpreted literally, without considering the historical, cultural, and social context, it can lead to the creation of rigid rules that do not address the actual issues. For example, the sources state that if one reads a Hadith and concludes that silk is forbidden for women, or that all music is forbidden, without considering the intent and context, then that can cause social problems [3, 4].
Focus on “Mischief”: The sources emphasize the importance of identifying and addressing the “mischief” (harm) caused by certain practices or behaviors [1, 2]. This means focusing on the negative consequences that may arise from certain actions rather than simply deeming them forbidden. The sources mention that problems related to shame, wine, and music should be resolved by addressing their potential for misuse and not by making rigid rules about them [1, 2].
Arrogance and Immodesty: The sources note that certain behaviors and practices, such as wearing silk or gold, can contribute to social problems if they lead to arrogance, immodesty, or extravagance [3-5]. The sources highlight the importance of humility and modesty in all aspects of life, including dress, speech, and conduct. The traditions about clothing and modesty are not meant to be a set of rigid rules, but should be understood in the context of avoiding arrogance and luxury [3].
Misuse of Entertainment: The sources identify the misuse of entertainment, such as music and movies, as a social problem if they are used in ways that promote immoral behavior or harmful values [4, 6, 7]. According to the sources, rather than deeming all music as forbidden, they discuss addressing the harmful aspects of music, like when it is used to encourage lustful behavior [4, 7].
Lack of Understanding: The sources also highlight that social problems arise when people lack a proper understanding of religious texts and traditions. This can lead to misinterpretations, extremism, and narrow views [1, 2]. The sources suggest that education and open discussion are crucial in addressing these problems. The sources argue that traditions should be understood and conveyed to people in a way that is sensible [8].
Importance of Discussion and Solutions: The sources emphasize resolving social problems through discussions, understanding, and finding solutions, rather than simply imposing rigid rules [1, 2, 6]. The goal is to address the root causes of problems and find ways to promote responsible behavior within the bounds of religious teachings [1, 2, 6].
Ethical Conduct: According to the sources, ethical conduct is an essential component of a healthy society [2, 5, 9]. This includes the way people speak, behave, and present themselves. The sources emphasize that religious teachings should guide not just ritual observances, but the way people live their daily lives, in a way that is just and moral [5, 9].
In summary, the sources highlight that social problems can arise from misinterpretations of religious texts, the misuse of certain practices, lack of understanding, and a failure to prioritize ethical conduct. The approach emphasized in the sources is to address these problems by focusing on the context of religious texts, by identifying and addressing the harm caused by certain practices, by promoting discussion, and by finding solutions that encourage responsible behavior within the bounds of religious teachings.
The sources suggest several approaches to moral solutions, emphasizing understanding, ethical behavior, and addressing the root causes of problems rather than simply imposing rigid rules [1, 2]. Here’s a breakdown of the key moral solutions discussed:
Contextual Understanding of Religious Texts: The sources emphasize that a key moral solution is to understand religious texts within their proper historical, cultural, and social context [1, 3]. This means avoiding literal interpretations that do not address the actual issues at hand. For example, the sources discuss how a hadith about people who will solve the issues of shame, wine, and music should not be taken to mean that all related things are permissible, but instead be understood within the broader context of ethical behavior [3].
Focus on Intent and Ethical Impact: A major moral solution is to prioritize the intent behind actions and their ethical impact [1, 4]. The sources suggest that actions should not be judged solely by their outward appearance but by whether they promote or undermine ethical principles [1]. For example, wearing silk or listening to music can be acceptable if it does not lead to arrogance or immoral behavior [4]. The focus should be on maintaining decency, avoiding arrogance, and ensuring ethical conduct in all aspects of life [2, 4].
Addressing “Mischief”: Rather than simply forbidding things, the sources emphasize the need to identify and address the “mischief” or harm caused by certain practices or behaviors [1, 2]. This involves a careful examination of the negative consequences that may arise from certain actions and finding solutions to mitigate these harms [1, 3]. For example, the sources suggest that issues related to shame, wine, and music should be addressed by discussing and resolving their potential for misuse and not by setting rigid rules about them [1, 5].
Promoting Discussion and Solutions: The sources advocate for resolving issues through discussions, understanding, and finding solutions rather than simply imposing rigid rules [1, 6]. This approach encourages open dialogue and aims to address the root causes of problems [1]. The sources highlight the importance of engaging with different perspectives and interpretations to arrive at solutions that are both ethical and practical [6].
Education and Awareness: A key moral solution lies in educating individuals about the proper interpretations of religious texts and traditions [1, 4]. This includes fostering awareness of the ethical principles that should guide behavior. By promoting an understanding of the deeper meaning of religious teachings, individuals can make informed decisions that contribute to the well-being of society [4].
Ethical Conduct in Daily Life: Moral solutions must extend to all aspects of daily life, including how people speak, behave, and present themselves [2, 7]. The sources suggest that ethical conduct is essential for a healthy society [2]. Therefore, religious teachings should guide not only rituals, but also personal behavior, social interactions, and the values people embrace [2]. The sources indicate that the goal is to promote a society that is ethical, just, and moral, with an emphasis on humility, modesty and respect [2, 4].
Balancing Individual Freedom and Social Responsibility: The sources suggest a balance between individual freedom and social responsibility [8]. While personal choices are important, they should not come at the expense of the community or violate religious principles [8]. The sources emphasize that freedom should be exercised within the boundaries of ethical conduct and in a manner that is beneficial to all. They suggest that when considering what is permitted, it should also be considered whether it harms society [6, 8].
Avoiding Extremism and Narrow Views: The sources warn against misinterpreting religious texts to justify extreme or narrow views, highlighting that such interpretations can lead to the neglect of the true spirit of Islam [4]. Therefore, a key moral solution involves promoting a balanced understanding of religious texts and traditions, which avoids rigidity and incorporates a range of viewpoints, and that considers the consequences of decisions, promoting a harmonious and compassionate society [4].
In summary, the moral solutions suggested by the sources emphasize understanding, ethical behavior, addressing harm, promoting discussion, and education. They aim to create a society that is based on justice, compassion, and moral principles [1, 2].
Summary: The passage discusses a religious tradition (hadith) that mentions people who will “solve” issues related to shame, wine, and music. The speaker argues that this doesn’t mean these things will be made universally permissible. Instead, they suggest it refers to finding solutions within the bounds of Islamic law, focusing on proper understanding rather than a literal interpretation that could lead to misinterpretations about the rules around modesty, intimacy, and what is considered forbidden.
Explanation: This passage is a complex discussion about how to interpret religious texts, particularly a hadith (a saying or tradition of the Prophet Muhammad). The speaker is concerned that some people may misinterpret the hadith, which speaks of people who will “solve” or resolve certain issues, particularly concerning shame (specifically related to private parts), as meaning these things will become universally permissible, or “halal.” The speaker rejects this literal interpretation, using the example of a car, which was gifted (the car as a metaphor for the body), that doesn’t then make it permissible to misuse it. Rather, the speaker explains that the hadith needs to be understood in the context of established Islamic law and the Quran. They argue that the “solutions” mentioned are about how to manage these issues in a way that aligns with Islamic principles. They cite examples of how even though intimacy is permitted, it must be done within the boundaries set by God. Therefore, “solving” issues around shame doesn’t mean getting rid of all restrictions, but finding legitimate ways to navigate those restrictions within the religious framework. This is similar to their understanding of divorce, where the act of divorce itself was a last resort and must be done within the parameters of the law. Essentially, the speaker is warning against taking a single hadith out of context and advocating for a careful and reasoned understanding of religious texts based on established principles.
Key terms:
Hadith: A collection of traditions containing sayings of the prophet Muhammad that, with accounts of his daily practice (the Sunna), constitute the major source of guidance for Muslims apart from the Quran.
Ummah: The whole community of Muslims bound together by ties of religion.
Haram: Forbidden or unlawful in Islam.
Halal: Permissible or lawful in Islam.
Fatwa: A formal ruling or interpretation on a point of Islamic law given by a qualified legal scholar.
Summary: This passage discusses Islamic teachings related to modesty, marital relations, and the interpretation of religious texts. It argues against overly strict interpretations that might lead to unnecessary prohibitions and emphasizes that the core of Islamic law is about justice, reason, and the protection of individual rights.
Explanation: The passage is a complex discussion about various aspects of Islamic jurisprudence, particularly focusing on issues of modesty, marriage, and interpreting religious texts. It begins by asserting that there’s no need for overly restrictive interpretations of Islamic rules, especially regarding marital relations and private matters, suggesting that such overzealousness may be rooted in caste-like thinking. The author argues that Islamic law permits intimacy within marriage, excluding anal sex, and emphasizes that Allah’s rules are reasonable, not arbitrary.
The passage critiques the tendency to focus on minor details, such as dress codes, while neglecting the broader principles of justice and mercy. It suggests that some interpretations of Islamic law are too focused on avoiding “shame” rather than on promoting good behavior and protecting human rights. The author is critical of individuals who enforce strict interpretations of religious texts on their families without proper understanding, causing unnecessary burdens. It is highlighted that the historical context of religious teachings must be considered when interpreting religious texts, and not every prohibition is meant for every person, at every time. The passage concludes by emphasizing that religious texts should be understood with reason and wisdom, not through rigid adherence to minor details. The overall message is a call for a balanced approach to Islamic teachings, emphasizing intention, purpose, and the spirit of the law over rigid literalism.
Key terms:
Ummah: The worldwide community of Muslims.
Sallallahu Alaihi Vallam: An honorific phrase used after mentioning the Prophet Muhammad, meaning “peace and blessings of Allah be upon him.”
Haram: Forbidden or unlawful in Islam.
Hudood: Boundaries or limits set by Islamic law.
Risalah Mehfil Aslam: The assembly of the Prophet’s message.
Summary: This passage discusses how to interpret religious texts, emphasizing that understanding the context and underlying intentions is more important than strictly following the literal words. It uses examples like clothing, alcohol, and other societal issues to show how interpretations of these things have changed over time and how we should address them now.
Explanation: The passage begins by addressing the idea that some people might blindly follow traditions. The author suggests that instead of just repeating stories, we need to really understand the reason and purpose behind religious teachings. For example, the passage mentions clothing and how what was considered “extravagant” in the past might not be today. The key point here is that what matters is the intention behind things, not the literal act itself.
The text also brings up the issue of alcohol. The author states that while the Quran mentions it, the way it’s understood and applied in our times must consider new societal issues. The speaker stresses the importance of considering historical context, as well as the intentions of religious text. The speaker uses examples of modern problems to further illustrate the necessity of understanding underlying purposes rather than strict word-by-word interpretations. The overarching message is that interpretation should be based on a deep understanding of scripture and its relation to the modern world, rather than just following old traditions without questioning them. Ultimately, the text argues for a thoughtful and context-aware approach to religious texts, emphasizing the importance of intention and adaptation to our times.
Key terms:
Rasul Allah: Refers to the Prophet Muhammad in Islam.
Ummah: The worldwide community of Muslims.
Mutrafin: A term referring to people who live extravagantly or luxuriously.
Hadiths: Narrations of the Prophet Muhammad’s life, teachings and sayings.
Hakeem Talab: A seeker of wisdom or knowledge, in this context, someone seeking deep understanding of religious matters.
Summary: The passage discusses how to interpret and apply religious teachings (likely Islamic) in modern contexts, particularly regarding potentially controversial topics like music and social interactions. It argues for a balanced approach that prioritizes understanding the core principles of faith over rigid adherence to specific rules, and emphasizes solving issues through discussion and consideration of intent.
Explanation: This text is essentially about how to navigate complex social and moral issues using religious guidance. The speaker emphasizes that while the Quran and Hadith (prophetic traditions) are foundational, their interpretation must be thoughtful and context-aware. The speaker is focused on the concept of finding solutions through discussion, understanding the intent of actions and not just following rules blindly. The speaker is suggesting that certain things that may be considered problematic or forbidden are not inherently bad but can be used positively if their intent is proper. For example, they discuss music and how it can be used for good or ill, and that the listener must understand the intention and be aware of negative influences. They discuss how certain behaviors like promiscuous talk and ignoring honor, family, and social status are forbidden, and that these prohibitions form the foundation of the faith. The overall message is that interpretations of religious texts should be approached thoughtfully and pragmatically, focusing on the underlying principles and goals of the faith, and not necessarily the specific, literal application of rules. The speaker suggests they are reinterpreting past traditions and making them more relevant and understandable.
Key terms:
Quran: The central religious text of Islam, believed by Muslims to be the word of God as revealed to the Prophet Muhammad.
Hadith: The collection of traditions containing sayings and actions of the Prophet Muhammad, which serve as a guide for Muslims.
Hudood-o-Sharat: Islamic legal term referring to the prescribed limits or boundaries and conditions within Islamic law.
Risalat Mahasabha: Likely a reference to a religious or scholarly assembly or gathering that addresses religious issues.
Surah Ab: Likely a reference to a specific chapter in the Quran, though the accurate chapter would be dependent on the original language.
Summary: The passage discusses the proper use of certain powerful tools or practices, emphasizing that they have inherent protections. However, these tools can be misused or overused. The speaker stresses the importance of using these tools responsibly and in moderation and does not want to be associated with those who abuse them.
Explanation: The speaker is discussing some powerful tools or practices (referred to as “Makon”), suggesting that these inherently come with protections built in by a higher power, which in the passage is referred to as Allah Ta’ala. However, the speaker notes that despite these built-in protections, these tools can be misused. The speaker notes a concern about the potential for misuse, emphasizing the need to be careful about how they are utilized and that moderation is crucial. The speaker states that they don’t want to be associated with people who misuse these tools, and that they are interested in the tools’ positive applications. The speaker alludes to past discussions, emphasizing that the correct use of these tools should always be emphasized. The speaker uses the example of technologies like radios and televisions, which a sister once called “factories of the devil,” to illustrate their point that when the use of something becomes excessive or harmful, a line needs to be drawn. The speaker also says that the foundation of morality, which is considered a gem, is based on respect. They also describe a particular person who was doing “messengership” while wearing silk, suggesting a contradiction between actions and claims. They also say that the misuse of these tools stems from a feeling of “takambur,” which in this context seems to mean arrogance.
Key terms:
Makon: This term is used to describe powerful tools or practices with inherent protections.
Allah Ta’ala: This is a common Arabic name for God in Islam.
Hudood: This term refers to the limits or boundaries set by Islamic law. In this context, it seems to refer to the limits of appropriate use of tools.
Takambur: In this context, it seems to refer to arrogance or pride.
Murtafin: This word refers to those who are respected or elevated in status.
The sources discuss the interpretation of religious texts, particularly within an Islamic context, emphasizing the importance of understanding the intent and context of teachings rather than strictly following the literal words [1-5]. The texts caution against misinterpretations that could lead to unnecessary prohibitions or the misuse of practices, and advocate for a balanced approach that aligns with core Islamic principles [1-5]. Here’s a breakdown of the key themes:
Interpretation of Religious Texts: The sources emphasize the need for a nuanced understanding of religious texts, particularly hadith (sayings and traditions of the Prophet Muhammad) [1-4]. They argue that a literal reading can lead to misinterpretations and the misuse of religious teachings [1, 2]. For example, a hadith that mentions people who will “solve” issues of shame, wine, and music does not imply that these things are to be made universally permissible. Instead, it is about finding solutions within the bounds of Islamic law [1-3].
Modesty and Marital Relations: The texts address the topic of modesty and marital relations, arguing that there is no need for overly restrictive interpretations of Islamic rules [2, 3]. Intimacy within marriage is permitted, excluding anal sex, and Allah’s rules are considered reasonable [2, 3]. The sources critique those who focus on minor details while neglecting the broader principles of justice and mercy [2].
Context and Intent: The sources repeatedly stress that religious teachings must be understood within their historical and social context [3-5]. The intent behind actions is more important than the literal act itself [3, 4]. What was considered extravagant in the past may not be today, and what is permissible must be assessed based on the current context [3, 5].
Social Issues: The texts discuss how to address various social issues, such as alcohol consumption and the use of music [4-7]. The sources suggest that instead of blindly following traditions, we need to understand the purpose behind religious teachings and the context of their application [4, 6]. They explain how even things like music can be used for good or ill, and that understanding the intention behind the music is important [4, 7]. The texts advocate for a balanced approach, considering both the religious guidance and the realities of modern life [6].
Responsibility and Moderation: The sources discuss the proper use of certain practices and tools, noting that they have inherent protections, but they can be misused if not used responsibly and with moderation [5, 8]. They highlight that excessive use can be harmful, and one should not be associated with those who abuse these tools or practices [5, 8, 9].
Discussion and Understanding: The texts promote the idea that solutions to complex issues should be found through discussion and consideration of intent rather than rigid adherence to specific rules [4, 10]. They believe that understanding the core principles of faith is more important than strict, literal application of the rules [4, 6, 7, 10].
In summary, the sources advocate for a thoughtful, context-aware, and balanced approach to religious teachings, emphasizing understanding and intent over blind adherence to the literal words.
The provided sources discuss a specific Hadith excerpt that mentions people who will “solve” issues within the Muslim community [1]. The sources interpret this hadith as referring to the resolution of complex issues related to:
Shame (Sharmgah): The sources discuss how this hadith does not mean that all things related to the private parts are permissible, but rather that there is a way to address issues of shame within the bounds of Islamic law [1, 2]. It is suggested that the hadith refers to the resolution of problems related to intimacy, particularly within marriage, while excluding unlawful acts [2]. The sources emphasize the importance of understanding the rules and limits set by Allah Ta’ala in this area [1]. The texts clarify that marital relations are permissible, with the exception of anal sex, and that the rules set by Allah are reasonable [2]. The interpretation of this aspect is that it is not about opening up forbidden practices but clarifying the permissible actions within marital relations [2].
Wine (Alcohol): The sources explain that the hadith doesn’t imply that alcohol is now permissible. Instead, it points to a need for a nuanced understanding of the issue in modern times [3, 4]. The texts note that while the Quran mentions alcohol, the way it’s understood and applied in our times must consider new societal issues [3]. The sources emphasize that the intention of the hadith is not to make alcohol permissible but to provide a framework for addressing issues related to its misuse and the wider environment where alcohol is prevalent, such as night clubs and films [3, 4]. It is important to understand that the reduction of alcohol’s normality means it is Mannu (forbidden) in every situation [4].
Music: The sources address the issue of music, stating that the hadith does not mean that all music is permissible. Instead, the hadith’s reference to music is about finding ways to address its potential misuse and the negative impacts associated with it [4, 5]. The texts indicate that music can be used positively or negatively, and the key is to understand the intent behind the music [5]. The sources also connect the misuse of music to the creation of negative thoughts and desires and emphasize that understanding how music is used in various contexts is crucial, whether in film songs, qawwalis, or other forms [5].
The sources emphasize that these issues are not to be resolved by making previously prohibited things permissible, but by understanding their correct usage, purpose, and context [1, 6]. The sources stress the importance of interpreting religious texts, like the Hadith, with a deep understanding of scripture, its historical context, and its relation to the modern world [2, 6]. The overall message is that interpretation should be thoughtful, context-aware, and focused on the underlying principles of faith rather than strict word-by-word applications of traditions [2, 4, 6, 7].
The sources interpret the Hadith regarding modesty (specifically, issues of “shame” or “Sharmgah”) not as a blanket permission to disregard Islamic rules, but as a call for a balanced and nuanced understanding of these issues within the bounds of Islamic law [1-3]. Here’s a breakdown of the interpretation:
Not a Removal of Restrictions: The author argues that the Hadith does not mean that all things related to the private parts or intimacy are now permissible [1-3]. Instead, the Hadith points to the idea that there are solutions to the problems of shame and intimacy that are in line with Islamic principles. The interpretation is not that forbidden acts become permitted but rather that there is a way to properly address issues related to intimacy and modesty within the framework of Islamic law [1, 2].
Marital Relations are Permissible: The texts clarify that intimacy within marriage is permissible, but this does not extend to practices that are specifically prohibited, such as anal sex [3]. The sources stress that Allah’s rules are not arbitrary, but rather reasonable [2, 4].
Understanding Boundaries (Hudood): The sources emphasize the importance of understanding the boundaries and conditions (Hudood-o-Sharat) set by Allah Ta’ala in matters of modesty [1, 5]. These boundaries are not meant to be overly restrictive but rather to guide behavior [2, 4]. The focus is on maintaining a balance in relationships, not allowing for inappropriate or prohibited acts [3].
Contextual Understanding: The author emphasizes that understanding the context of the Hadith is crucial [2, 3]. The discussion about “solving” issues of shame is not about overturning fundamental rules, but about finding appropriate and permissible solutions within specific contexts [1, 2].
Critique of Overly Strict Interpretations: The texts critique those who interpret religious teachings too literally or narrowly, especially when it comes to modesty [2]. The author suggests that some interpretations are overly restrictive and miss the broader purpose of Islamic teachings [2, 4]. The sources stress that such restrictive interpretations can lead to unnecessary prohibitions, and it is important to focus on the principles of justice and mercy rather than only the specific details [4, 6].
Solution Through Understanding: The author proposes that the hadith calls for a solution to issues of shame through understanding the correct applications and limits in marital relations, and not by making forbidden things permissible [3, 5]. This suggests a move away from strict legalistic interpretations towards a more nuanced approach [3].
In summary, the author interprets the Hadith regarding modesty as a call for a thoughtful and context-aware understanding of Islamic principles, permitting intimacy within marriage while excluding forbidden acts. It also emphasizes understanding the limits and boundaries set by Allah, avoiding overly strict interpretations, and finding solutions that align with broader principles of justice and mercy, rather than a complete removal of restrictions. The focus is on properly understanding the rules and limits concerning marital intimacy and modesty, and not making forbidden acts permissible [2, 3, 5].
The sources discuss a specific Hadith that mentions people who will “solve” certain issues within the Muslim community [1]. The sources interpret this hadith as referring to the resolution of complex issues related to:
Shame (Sharmgah): The sources explain that the hadith does not mean that all things related to private parts or intimacy are permissible [1-3]. Rather, it points to finding solutions to issues of shame within the boundaries of Islamic law [1-3]. This includes clarifying what is permissible within marital relations, excluding acts such as anal sex, which are specifically prohibited [3, 4]. The focus is on understanding and respecting the rules and limits set by Allah in this area [2, 3]. The interpretation emphasizes that this is about finding solutions to the problems of shame and intimacy that are in line with Islamic principles and not a removal of restrictions [3, 5].
Wine (Alcohol): The sources clarify that the hadith does not imply that alcohol is now permissible [1, 6]. Instead, it suggests a need for a nuanced understanding of the issue in modern times [6]. The sources explain that while alcohol is mentioned in the Quran, the way it is understood and applied should take into consideration the current societal issues [6]. The hadith is not about making alcohol lawful but about providing a framework for addressing issues related to its misuse and the broader environment where alcohol is prevalent, such as night clubs and films [5, 6]. The sources state that the reduction of alcohol’s normality means it is forbidden in every situation [5].
Music: The sources state that the hadith does not mean that all music is now permissible [1, 7]. Instead, the hadith is about finding ways to address its potential misuse and the negative impacts associated with it [7]. Music can be used positively or negatively, and the key is to understand the intent behind the music [7]. The sources also connect the misuse of music to the creation of negative thoughts and desires and emphasize that understanding how music is used in various contexts, whether in film songs, qawwalis, or other forms, is crucial [7]. They explain that many modern forms of entertainment use music in ways that might incite negative feelings [7, 8].
The sources emphasize that these issues are not to be resolved by making previously prohibited things permissible, but by understanding their correct usage, purpose, and context [2, 3, 5-7]. The overall message is that interpreting religious texts, like the Hadith, requires a deep understanding of the scripture, its historical context, and its relation to the modern world [2, 3, 5-8]. The focus should be on thoughtful, context-aware, and balanced interpretations based on the underlying principles of faith rather than a strict word-by-word application of traditions [2, 3, 5-8].
The author’s views on the permissibility of silk, as discussed in the sources, are nuanced and contextual, and they are connected to a larger discussion about interpreting religious texts and practices [1, 2]. Here’s a breakdown of the author’s perspective:
Not an Absolute Prohibition: The author argues that the hadith does not suggest that silk is absolutely forbidden for everyone [1]. The interpretation is not about outright prohibition, but rather about understanding the appropriate use and context of wearing silk within a society [2].
Contextual Use: The permissibility of silk is dependent on the context and the society in which it is being used [1]. The sources emphasize that during the time of the Prophet (peace be upon him), silk was considered a symbol of extravagance (“Mutrafin”) [1, 2]. It was associated with arrogance and was a status symbol for the wealthy [2]. The author notes that at that time, wearing silk could be a symbol of pride and arrogance (Takambur) [2].
Historical Context: The author argues that it is crucial to understand the historical context of the prohibition of silk for men [1, 2]. The author points out that the prohibition of silk for men during the time of the Prophet (peace be upon him) was related to its association with extravagance, arrogance, and social status. The focus was on avoiding these traits rather than the material itself [2].
Modern Considerations: The author states that the status of silk has changed in the modern world [2]. The author suggests that silk is no longer as strongly associated with extravagance as it was in the past, and many other things have taken its place as status symbols [2]. Therefore, the ruling on silk should also be understood in the context of its present-day use and significance [2].
For Women, Not the Main Issue: The author makes it clear that the discussion about silk is not mainly about women’s clothing [1]. The author argues that today, if women wear silk, it is not considered a sign of extravagance but rather a thing of pride [1]. The author points out that in the context of the hadith, the focus is on addressing the behaviors and attitudes associated with wearing silk rather than making a strict prohibition on wearing it [1].
Broader Principles: The author suggests that the hadith should be understood in light of broader principles such as avoiding arrogance and maintaining modesty, and this applies to both men and women. The author stresses that it’s not just about avoiding silk but about cultivating the right attitude and intentions [2]. The focus should be on understanding the underlying principles of faith rather than a strict word-by-word application of traditions [1, 2].
In summary, the author does not view the use of silk as being absolutely forbidden or permissible in all cases, rather that the permissibility of silk is dependent on the context, intention, and societal norms [1, 2]. The author emphasizes understanding the historical context of the prohibition and the principles behind the rules [2]. The author also explains that while silk was a status symbol of extravagance in the past and associated with arrogance, it’s status in the modern world is different. The interpretation is not about strict prohibition but about addressing the attitudes and negative traits associated with wearing silk, and not merely the material itself [1, 2].
The author expresses several concerns about the misuse of certain practices, particularly in relation to the interpretation of religious texts and their application in modern society [1-10]. These concerns revolve around the potential for misinterpreting Islamic principles, leading to actions that contradict the spirit of the teachings.
Here are the key concerns:
Misinterpretation of Hadith: The author is concerned that people might misinterpret hadith, particularly one that mentions people who will “solve” certain issues, to justify actions that are forbidden [1]. The author is concerned that people may take this hadith as a license to do as they please rather than an encouragement to engage in a thoughtful interpretation of the religious text [1]. For example, the author is concerned that people might believe that the hadith makes all things related to the private parts or intimacy permissible, or that it makes alcohol or music lawful [2, 3, 6].
Literal Interpretations: The author is wary of overly literal interpretations of religious texts, particularly when it comes to issues of modesty, alcohol, and music [1-3]. The author argues that a strict, word-for-word application of traditions without considering the context, purpose, and broader principles can lead to misunderstandings and the misuse of religious teachings [1-5]. The author points out that such interpretations can result in unnecessary restrictions and prohibitions that may not align with the true intent of the faith [2, 3].
Misuse of Permissible Things: The author is concerned that things that are permissible within certain boundaries can be misused and taken to extremes [3, 5, 8]. For example, the author notes that intimacy within marriage is permissible, but it does not mean that all sexual acts are permissible [2, 3]. The author emphasizes that permissible things can be misused when they are taken out of context or when the underlying principles are ignored [2-5].
Erosion of Modesty: The author is worried about practices that erode modesty, whether in clothing, behavior, or speech [2, 5, 8]. The author is concerned that people may misuse the concept of “solving” issues of shame to justify immodest behavior, and emphasizes the need to understand and respect the limits set by Allah [2, 3]. The author also raises concern about how modern media such as films and music, can be misused to incite negative desires [8].
Arrogance and Extravagance: The author is concerned about the potential for arrogance and extravagance when people misinterpret religious rules about things like silk [4, 5, 10]. The author notes that in the past, silk was a symbol of extravagance, but its status has changed over time [5]. The author believes that it’s important to consider the historical context and the reasons behind the prohibition to avoid misuse of the rule [4, 5, 10]. The author states that the underlying principle is to avoid behaviors associated with arrogance, rather than focus only on the material itself [4, 5, 10].
Ignoring Underlying Principles: The author emphasizes the importance of understanding the underlying principles of faith, such as justice, mercy, and modesty, rather than just focusing on the specific details or the letter of the law [2, 4, 5, 7, 8]. The author stresses that misinterpretations can occur when people do not understand the principles that guide religious teachings [2, 4, 5, 7, 8]. The author argues that a focus on the principles allows for a more thoughtful and contextual approach, which avoids misuse and misapplication [2, 4, 5, 7, 8].
Contemporary Misuses: The author is concerned with how some modern entertainment and media (such as films, songs, and qawwalis) are used to incite negative desires [6, 8]. They are worried that these forms of entertainment are being misused to promote immodest behavior and are causing people to ignore the underlying principles of the faith [8]. The author is concerned that many things which are currently popular among people are being used in a way that is against the purpose of the faith, and thus are misuses [8].
In summary, the author’s concerns center on the misapplication of religious teachings through literal interpretations, the misuse of permissible actions, the erosion of modesty, and the failure to understand the underlying principles and historical contexts. The author stresses the importance of thoughtful, context-aware, and balanced interpretations that align with the broader principles of faith to avoid misuse [1-10].
The author proposes solutions focused on understanding, interpretation, and context, rather than on strict prohibitions or permissions, when addressing the social issues mentioned in the hadith [1]. The author suggests that the problems of shame, alcohol, and music are not to be solved by simply making previously forbidden things permissible [1-3]. The solutions involve a deeper, more nuanced approach to Islamic teachings [4].
Here are the key solutions proposed by the author:
Contextual Interpretation: The author emphasizes that religious texts, like the Hadith, must be interpreted in their proper historical and social context [2-5]. This means understanding the circumstances at the time of the Prophet (peace be upon him), including the social norms, the meanings of words, and the underlying principles of faith [2-5]. For example, when interpreting the hadith, one must understand the reasons behind the initial prohibitions and not just the prohibitions themselves [2, 4].
Understanding Underlying Principles: The author stresses the importance of understanding the broader principles behind religious teachings, such as modesty, avoiding arrogance, and maintaining justice [4, 5]. These principles should guide the application of religious rules and not just the rules themselves [1-3, 6]. The author suggests that focusing on the principles allows for a more thoughtful and contextual approach to resolving issues [1-3, 6].
Nuanced Approach to “Solving” Issues: The author clarifies that the hadith’s mention of people who will “solve” issues does not mean that they will make forbidden things permissible [1-3, 6]. Instead, it means they will address these issues in a way that aligns with the principles of Islam [1-3, 6]. The author is not suggesting that the solutions will be a removal of restrictions, rather a thoughtful understanding of these restrictions and their proper place [1-3, 6].
Focus on Proper Usage and Intent: The author suggests that many things can be used positively or negatively, and the key is to understand the intent behind the action and the context in which it is being done [3, 6, 7]. For example, in the case of music, it is not inherently wrong, but it can be misused to incite negative feelings and desires [6-8]. The author argues for being mindful of the intended purpose and potential impact of such practices [6-8].
Addressing Misuse and Extremes: The author points out the need to address the misuse of permissible things [3, 4, 6]. For instance, while intimacy within marriage is permissible, the author stresses that this does not mean all sexual acts are allowed [3, 4]. The focus is on maintaining balance and moderation [3, 4].
Dialogue and Discussion: The author suggests that resolving complex issues requires discussion and dialogue within the community, and not simply rigid adherence to a literal understanding [1, 6, 8]. The author mentions that the issues should be resolved through discussions [8].
Considering Contemporary Context: The author argues that the interpretation of religious teachings should consider the modern context and the present-day use and significance of things [3-6, 8]. The author explains that some things may have different meanings or status than in the past [3-6, 8]. For example, silk may not be the symbol of extravagance it once was, and therefore, the ruling on it may need to be understood in the light of its present-day context [3-5].
Avoiding Extremism: The author’s solutions aim to strike a balance between strict adherence to tradition and the flexibility needed to address modern issues [1-3, 6]. The author warns against using the interpretation of the hadith as a justification for extremes, and instead calls for a moderate and reasoned approach [1-3, 6].
In summary, the author’s solutions are about promoting thoughtful understanding and interpretation of religious texts, considering the context and underlying principles of faith, and addressing issues with moderation and balance [1-6, 8]. The author does not propose simply removing prohibitions, but rather understanding them and applying them in a manner that is consistent with the true spirit of Islam [1-3, 6, 8].
The author’s perspective on the use of silk and alcohol is nuanced and contextual, emphasizing that these issues should not be viewed through a lens of absolute prohibition or permission. Instead, the author stresses the importance of understanding the historical and social context, the underlying principles of faith, and the intended purpose behind religious rules [1-3].
Here’s a breakdown of the author’s perspective on each:
Silk:
Not an Absolute Prohibition: The author argues that the hadith does not suggest that silk is absolutely forbidden for everyone, and that the interpretation is not about outright prohibition but rather about understanding the appropriate use and context of wearing silk within a society [4].
Contextual Use: The permissibility of silk is dependent on the context and the society in which it is being used [4]. The sources emphasize that during the time of the Prophet (peace be upon him), silk was considered a symbol of extravagance (“Mutrafin”) [4]. It was associated with arrogance and was a status symbol for the wealthy [4]. The author notes that at that time, wearing silk could be a symbol of pride and arrogance (Takambur) [5].
Historical Context: The author argues that it is crucial to understand the historical context of the prohibition of silk for men [2]. The author points out that the prohibition of silk for men during the time of the Prophet (peace be upon him) was related to its association with extravagance, arrogance, and social status [5]. The focus was on avoiding these traits rather than the material itself [4].
Modern Considerations: The author states that the status of silk has changed in the modern world [4]. The author suggests that silk is no longer as strongly associated with extravagance as it was in the past, and many other things have taken its place as status symbols [5]. Therefore, the ruling on silk should also be understood in the context of its present-day use and significance [4].
For Women, Not the Main Issue: The author makes it clear that the discussion about silk is not mainly about women’s clothing [4]. The author argues that today, if women wear silk, it is not considered a sign of extravagance but rather a thing of pride [4]. The author points out that in the context of the hadith, the focus is on addressing the behaviors and attitudes associated with wearing silk rather than making a strict prohibition on wearing it [4].
Broader Principles: The author suggests that the hadith should be understood in light of broader principles such as avoiding arrogance and maintaining modesty, and this applies to both men and women [4]. The author stresses that it’s not just about avoiding silk but about cultivating the right attitude and intentions. The focus should be on understanding the underlying principles of faith rather than a strict word-by-word application of traditions [4].
Alcohol:
Not a Simple Issue of Prohibition: The author explains that the hadith’s mention of solving the issue of wine should not be understood as making alcohol permissible [1]. Instead, it should be seen as an encouragement to address the underlying issues related to alcohol consumption in society.
Contextual Understanding: The author emphasizes that the mention of alcohol in the hadith must be understood within its specific context. It’s not simply about the law of honor or courage, but about understanding the potential for mischief [5]. The author suggests that the purpose of mentioning alcohol is not to state the law of honor or courage but to draw attention to the ways alcohol can be misused.
Modern Misuse: The author draws a parallel between alcohol and other modern issues, such as the misuse of music and media. The author is concerned with how alcohol is often associated with negative environments, like night clubs and films [6]. The author suggests that the issue with alcohol is related to the way it is being used and the negative atmosphere that it creates [6].
Focus on Intent and Use: The author emphasizes that the issue is not about the substance itself, but about the intent and use of alcohol [5, 6]. The focus should be on addressing the negative behaviors associated with alcohol consumption, rather than the substance itself [5]. The author uses an analogy that if there had been a mention of waist instead of alcohol, people would not assume that meant it was okay to commit the act of waist, and thus the same thinking should apply to alcohol. The author argues that just because something is mentioned it does not mean that it’s now permissible [6].
Underlying Principles: The author stresses that the underlying principle is to avoid actions that lead to harm, misbehavior, and negative outcomes. The author uses the example that when offering prayers, one accepts the blessings of God, and thus alcohol should also be understood in light of what one does in the context of their relationship with God [6].
Dialogue and Understanding: The author suggests that the issues related to alcohol must be discussed and addressed through dialogue within the community. The author points out that the tradition is not about making it permissible, but about creating discussions and finding ways to resolve the issues surrounding alcohol in society [6].
In summary, the author does not view the use of silk and alcohol as being absolutely forbidden or permissible in all cases, rather that their permissibility is dependent on the context, intention, and societal norms. The author emphasizes understanding the historical context of the rules and the principles behind the rules. The author also explains that while silk was a status symbol of extravagance in the past and associated with arrogance, and alcohol is often misused in modern society to create negative atmospheres, their status in the modern world is different. The interpretation is not about strict prohibition but about addressing the attitudes and negative traits associated with use, and not merely the materials or acts themselves.
The author does not view the permissibility of alcohol as a simple issue of prohibition or permission [1, 2]. Instead, the author emphasizes a contextual understanding of religious texts and principles [3, 4]. The author’s view is that the mention of alcohol in the hadith is not intended to make alcohol permissible [2], but rather to encourage a deeper understanding of the underlying issues associated with its consumption [1, 2].
Here’s a more detailed breakdown of the author’s perspective:
Not a Simple Issue of Prohibition: The author clarifies that the hadith’s mention of “solving” the issue of wine should not be interpreted as making alcohol permissible. Instead, it means that people will address the problems related to alcohol in a way that aligns with the principles of Islam [3]. The author argues that the hadith does not suggest that alcohol is permissible, but rather that its misuse needs to be addressed [1, 2].
Contextual Understanding: The author emphasizes that the mention of alcohol in the hadith must be understood within its specific context [4]. It’s not simply about the law of honor or courage, but about understanding the potential for mischief [1]. The purpose of mentioning alcohol is to draw attention to the ways it can be misused and the negative atmosphere associated with it [2].
Focus on Intent and Use: The author suggests that the issue is not about the substance itself, but about the intent and use of alcohol [1]. The author stresses the need to address the negative behaviors associated with alcohol consumption, rather than simply focusing on the substance [2]. The author uses an analogy that if there had been a mention of waist instead of alcohol, people would not assume that meant it was okay to commit the act of waist, and thus the same thinking should apply to alcohol [2]. The author argues that just because something is mentioned, it does not mean that it’s now permissible [2].
Modern Misuse: The author draws a parallel between alcohol and other modern issues, such as the misuse of music and media [5]. The author expresses concern about how alcohol is often associated with negative environments, like night clubs and films [1, 2]. The author suggests that the problem with alcohol is related to the negative atmosphere that it creates [2].
Underlying Principles: The author stresses the importance of understanding the broader principles behind religious teachings [4]. The underlying principle is to avoid actions that lead to harm, misbehavior, and negative outcomes. The author uses the example that when offering prayers, one accepts the blessings of God, and thus alcohol should also be understood in light of what one does in the context of their relationship with God [2].
Dialogue and Discussion: The author suggests that the issues related to alcohol must be discussed and addressed through dialogue within the community, rather than rigid adherence to a literal understanding [3]. The author points out that the tradition is not about making it permissible but about creating discussions and finding ways to resolve the issues surrounding alcohol in society [2].
In summary, the author does not view alcohol as simply forbidden or permissible, but rather stresses the importance of understanding its use, context, and the negative impacts associated with it [1]. The emphasis is on addressing the underlying issues and negative behaviors linked to alcohol, and not simply making it permissible [2].
The author’s views on the permissibility of silk clothing for women are nuanced and contextual, and are not a primary focus of the text [1]. The author does not offer a simple “yes” or “no” regarding its permissibility, but rather emphasizes understanding the context, intent, and societal norms surrounding its use [1]. The main focus of the discussion about silk is on the attitudes associated with it and not a strict prohibition [1-4].
Here’s a breakdown of the author’s perspective:
Not the Main Focus: The author makes it clear that the discussion about silk is not primarily about women’s clothing [1]. The primary concern in the source material is related to men’s use of silk, and the issue of extravagance, arrogance, and social status [1, 5].
Contextual Understanding: The author argues that if women wear silk, it is not considered a sign of extravagance but rather a thing of pride [1]. The author highlights that the social implications of wearing silk have changed over time [1, 6].
Shifting Societal Norms: The author notes that while silk was a status symbol and associated with extravagance in the past, it is not viewed the same way in the present day [1, 6]. The author suggests that many other things have taken its place as status symbols, so the ruling on silk should be understood in the context of its present-day use and significance [6].
Emphasis on Underlying Principles: The author stresses that the broader principles of modesty and avoiding arrogance are important to consider [1-9]. The focus is not just on the material itself but on the attitudes and behaviors associated with it [1-9]. The author emphasizes that the hadith should be understood in light of these broader principles [1-9].
Addressing Attitudes: The author’s concern is about addressing the behaviors and attitudes associated with wearing silk rather than making a strict prohibition on wearing it [1-9]. The author argues that the focus should be on understanding the underlying principles of faith rather than a strict word-by-word application of traditions [1-9].
In summary, the author’s perspective is that the use of silk by women is not the central issue being addressed in the hadith. Instead, the author is focused on broader principles, the historical context of the rules, and the importance of understanding the intended purpose of religious teachings. The author’s view is that what matters more is the context, intent and the societal implications of wearing silk rather than an absolute prohibition [1-9].
The author interprets the hadith regarding wine, music, and modesty not as a simple set of prohibitions or permissions, but as a call to understand the underlying principles and address the potential for misuse and negative consequences associated with them [1-5]. The author emphasizes the importance of contextual understanding, intent, and societal norms, rather than strict adherence to literal interpretations of the hadith [1-3].
Here’s a breakdown of the author’s interpretation:
Modesty (Shame):
The author states that the hadith mentions “solving the issues of shame” [1]. This does not mean that shameful acts become permissible [2]. Instead, it refers to addressing the root causes and negative consequences associated with actions that are considered shameful [1, 2, 6].
The author argues that when the hadith speaks of resolving issues related to private parts, it does not mean that anything related to them is now permissible [6]. Rather, it refers to resolving those issues within the boundaries of what is permissible, such as marriage and avoiding unlawful sexual relations [6, 7].
The author stresses the importance of understanding what is considered lawful within the religion and resolving issues of shame within those guidelines. The author points out that the hadith is about addressing problems related to shame, and not about making shameful actions permissible [2, 6].
Wine (Alcohol):
The hadith mentions that “some people will solve the problems of wine.” The author interprets this to mean that people will address the issues and negative behaviors associated with alcohol use, not that alcohol will be made permissible [1].
The author views the mention of wine not as an endorsement of its use, but as a way to highlight the negative atmosphere associated with it and its potential for misuse [3, 4].
The author states that the hadith is not about the substance itself, but about the potential harm and negative consequences associated with its consumption [3, 4].
The author draws a parallel between alcohol and other modern issues like music and media, highlighting how these can contribute to negative environments [4].
Music:
The author interprets the hadith as saying that “some people will solve the problems of music” [1]. This does not mean that music becomes permissible in all forms, but that the issues and misuse associated with it will be addressed [1, 8].
The author does not view the hadith’s mention of music as a blanket endorsement of its use. The author states that just because the hadith mentions music, it does not make all forms of music permissible [8].
The author highlights that music, like wine, is associated with negative environments and can lead to negative thoughts [8]. The author argues that the focus should be on understanding the underlying issues and negative behaviors related to music, rather than only thinking about its permissibility [8].
General Principles:
The author emphasizes that the hadith should be understood within a broader context, taking into account the intent behind the teachings and the societal norms [2, 7, 9].
The author stresses that the focus should be on understanding the underlying principles of faith, such as avoiding harm and negative behaviors, rather than just a literal reading of the text [2, 6, 7].
The author’s interpretation is that the hadith is meant to encourage dialogue and discussion within the community to find solutions to these issues, rather than simply making them permissible [4, 5].
The author emphasizes that the hadith is about addressing the negative uses of these things, while also not prohibiting their permitted uses [4, 5].
In summary, the author interprets the hadith about wine, music, and modesty as a guide for addressing the underlying issues associated with them, rather than simply as a set of rules about what is permitted or forbidden. The emphasis is on understanding the context, intent, and societal implications of these things, and encouraging dialogue to resolve issues in alignment with Islamic principles [1, 2, 4]. The author focuses on the negative impacts and misuse of these things and aims to resolve those problems without blanket prohibition of anything that is not strictly forbidden [3, 4, 8].
Affiliate Disclosure: This blog may contain affiliate links, which means I may earn a small commission if you click on the link and make a purchase. This comes at no additional cost to you. I only recommend products or services that I believe will add value to my readers. Your support helps keep this blog running and allows me to continue providing you with quality content. Thank you for your support!