Saturday 5 December 2009

Making a .NET Dll COM-Visible

In this post, I want to explain how I created a dll in C#/.NET using Microsoft Visual Studio 2008 that could be accessed from an Excel VBA program.

In order for VBA to access it, a dll has to be COM-visible. See Wikipedia; Component Object Model for more details. However, Microsoft's description at Exposing .NET Framework Components to COM is quite hard to understand. I hope this step-by-step explanation of an actual example will be easier to follow.

Summary

I created a dll, called MdJLibrary.dll, that defines a function WidthInPoints in the class TextFunctions. This function takes three parameters - a string, a font name and a font size - and returns the width of that string in points. When built and deployed, the function in this dll can be accessed from Excel VBA.

Step 1. Create the dll in C#/.NET

A. Create a new project

In Microsoft Visual 2008, select File, then New and Project. Choose the Class Library template. Set Name to MdJLibrary. Click OK.

B. Define the required function

Here is the initial code. Note that neither the class nor the method can be specified as static (since, as we will see later, the class will have to inherit an interface). Also, the method has to be public so that it can later be exposed to COM.

using System;
using System.Drawing;
 
namespace MdJLibrary
{
/// <summary>
/// Class defining functions on text
/// </summary>
public class TextFunctions
{
/// <summary>
/// Measure the width of a given string when drawn in a font of a given size
/// </summary>
/// <param name="text">string to measure</param>
/// <param name="fontName">name of font to use</param>
/// <param name="fontSize">em-size of font (points)</param>
/// <returns>width of string in points</returns>
public int WidthInPoints(string text, string fontName, Single fontSize)
{
//create a temporary Graphics object and set the measurement units to points
Graphics g = Graphics.FromImage(new Bitmap(1, 1));
g.PageUnit = GraphicsUnit.Point;
 
//create a Font object from the given name and size
Font f = new Font(fontName, fontSize);
 
//measure the given string when drawn with this Font
SizeF stringSize = g.MeasureString(text, f);
 
//extract and return the width (rounded to nearest integer)
return Convert.ToInt32(stringSize.Width);
}
}
}


C. Sign it with a strong name

This avoids versioning problems, and increases security. All shared dlls should be signed, even if they're not going to be deployed to the Global Assembly Cache.

Open the project properties (select Project then Properties...), and select the Signing tab. Click Sign the assembly and either create or choose an existing strong name key file.

Step 2. Test it

Create a separate, small .NET program to test the dll.

I haven’t included the test program listing here. The key thing is that the solution needs to include an explicit reference to the new library.

Step 3. Make the dll COM-visible

There are several stages to this.

A. Provide an explicit interface

It is possible to generate an interface by using the ClassInterfaceAttribute to automatically expose the public methods, etc., but the recommended way is to define an explicit interface. The type of the ClassInterfaceAttribute is then set to None, so as to stop this automatic class interface being produced.

The ClassInterfaceAttribute is defined in the System.Runtime.InteropServices namespace, so add this to the list of using directives:

using System.Runtime.InteropServices;

The interface definition has to contain an exact copy of the WidthInPoints method signature, without any access modifiers:

public interface ITextFunctions
{
int WidthInPoints(string text, string fontName, Single fontSize);
}


The class definition now needs to be modified to show that it is implementing this interface, with the ClassInterfaceAttribute as described.

[ClassInterface(ClassInterfaceType.None)]
public class TextFunctions : ITextFunctions
{
...


B. Define a default constructor

Define a default constructor for the class, so that COM clients can create objects.

Microsoft Visual Studio 2008 creates public default constructors automatically, but I prefer to make this explicit:

//default constructor
public TextFunctions() { }


C. Mark the methods to be made COM-visible

Perhaps the easiest way of doing this is to make the whole assembly COM-visible with the ComVisibleAttribute: In the project properties, on the Application tab, click Assembly Information... and check Make assembly Com-visible. Note that this changes the ComVisible setting in the AssemblyInfo.cs file (under Properties in Solution Explorer) to true:

[assembly: ComVisible(true)]

If there are any methods that you don't want to make COM-visible you they need to be marked explicitly as [ComVisible(false)].

However, the recommended alternative is to leave the assembly setting unchanged and to explicitly add [ComVisible(true)] to each class and method that you want to expose as COM-visible:

[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class TextFunctions : ITextFunctions
{
...
 
[ComVisible(true)]
public int WidthInPoints(string text, string fontName, Single fontSize)
{
...


D. Identify the dll with a unique class id

Microsoft Visual Studio 2008 does this automatically by generating a unique GUID for the assembly. This can be seen in AssemblyInfo.cs:

[assembly: Guid("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")]

Here is the finished code:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
 
namespace MdJLibrary
{
public interface ITextFunctions
{
int WidthInPoints(string text, string fontName, Single fontSize);
}
 
/// <summary>
/// Class defining functions on text
/// </summary>
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class TextFunctions : ITextFunctions
{
//default constructor
public TextFunctions() { }
 
/// <summary>
/// Measure the width of a given string when drawn in a font of a given size
/// </summary>
/// <param name="text">string to measure</param>
/// <param name="fontName">name of font to use</param>
/// <param name="fontSize">em-size of font (points)</param>
/// <returns>width of string in points</returns>
[ComVisible(true)]
public int WidthInPoints(string text, string fontName, Single fontSize)
{
//create a temporary Graphics object and set the measurement units to points
Graphics g = Graphics.FromImage(new Bitmap(1, 1));
g.PageUnit = GraphicsUnit.Point;
 
//create a Font object from the given name and size
Font f = new Font(fontName, fontSize);
 
//measure the given string when drawn with this Font
SizeF stringSize = g.MeasureString(text, f);
 
//extract and return the width (rounded to nearest integer)
return Convert.ToInt32(stringSize.Width);
}
}
}


Step 4. Package and deploy the assembly

A. Register the component for COM interoperation

In the project properties, on the Build tab, check the Register for COM interop option.

B. Deploy the dll

A convenient way of doing this is to use the assembly registration tool, regasm.

On the Build Events tab, add the following line to the post-build event:
%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\regasm $(TargetFileName) /tlb:$(TargetName).lib

%SystemRoot%\Microsoft.NET\Framework\v2.0.50727 is the default installation path for regasm in v2.0 of the .NET Framework, which is the latest version available.

regasm registers all the public classes contained in the dll, and generates and registers the type library (.lib). You can use the /reglib option to generate a .reg file that contains the registry entries instead of making the changes directly to the registry.

Step 5. Use the dll

To complete the picture, here's how to use the functions provided by the dll in Excel.

A. Add a reference to the dll

In the Visual Basic Editor window, select Tools then References....

The new library name should appear in the Available References list. Select it and click OK.

B. Create an object of the required type and call the function

In a VBA module, call CreateObject to create and return a reference to a new instance of the class defined in the dll, and then call the appropriate method. In this case:

Dim obj As Object
Dim width As Integer
 
Set obj = CreateObject("MdJLibrary.TextFunctions")
width = obj.WidthInPoints("Mike", "Arial", 10)

Friday 20 November 2009

Don't Worry 14 - Pray About It

14. Pray about it

If we can worry about something then surely we can pray about it. Commit your way to God. Ask God for wisdom and help. Ask that his will be done. But don’t prattle, going over the same things again and again; this can be a particular temptation when one's mind is in a turmoil. When you pray, do not keep babbling like pagans, for they think they will be heard because of their many words. Do not be like them, for your Father knows what you need before you ask him. (Matthew 6:7)

Any concern too small to be turned into a prayer is too small to be made into a burden. (Corrie ten Boom)

Do not be anxious about anything, but in everything, by prayer and petition, with thanksgiving, present your requests to God. And the peace of God, which transcends all understanding, will guard your hearts and your minds in Christ Jesus. (Philippians 4:6-7)

We should pray specifically:

... that we will not succumb to temptation when it comes. This is how you should pray: Our Father in heaven ... lead us not into temptation, but deliver us from the evil one. (Matthew 6:9-13)

... for God to meet our needs. Give us today our daily bread. (Matthew 6:11)

... that we will have wisdom in all circumstances. If any of you lacks wisdom, he should ask God, who gives generously to all without finding fault; and it will be given him. (James 1:5)

Friday 18 September 2009

Who Made God?

From Professor Edgar Andrews’ forthcoming book Who made God?:

It’s a question commonly posed by those who would banish the very ideas of God and "creation". Richard Dawkins asks it repeatedly, in various ways, in his best-selling book The God Delusion. The logic runs something like this.

If God exists, then presumably he created everything (why else would we need him?) But if God exists, who made him? And since no one can answer that question, it does nothing to solve the riddle of the universe to say "God made it". We simply push the mystery one step further back and that is a pointless exercise.

No one can doubt that atheists regard their "unanswerable question" — "Who made God?" — as a formidable weapon in their war against faith, but there is more to the question than meets the eye and it crops up in a surprising variety of philosophical contexts.

So let’s look briefly at three such contexts — the "we made God" hypothesis, the "improbability of God" calculation, and the "unanswerable question" dilemma.

... missing out discussion about the first two ...

The third context in which "Who made God?" appears is the most obvious one. The question is deemed unanswerable because the only realistic reply is "no-one made God". And if no-one made God, then he can’t be there, can he? After all, for every effect there must be a cause. An effect that has no cause must be imaginary.

Once again, in their enthusiasm to prove their point, the proponents of this argument entangle physics with metaphysics. Cause and effect do indeed reign supreme in the physical realm — both science and normal life would be impossible unless they did. But why should they operate in the same manner in a spiritual realm (if such exists)?

We have a choice. Firstly, we can assert a priori that there is no such thing as a spiritual realm — that nothing exists that is not physical and open to scientific investigation. On this basis we can proceed to claim, with some logical justification, that every possible effect must have a cause, because that is how the physical world works.

But what we cannot do is use this claim to disprove the existence of God on the grounds that he doesn’t have a cause! Why not? Because our argument would be completely circular. We begin by assuming that no spiritual realm exists and conclude by "proving" our initial assumption. Big deal.

So let’s try to find a different route through the maze, this time without cheating. To avoid assuming at the outset what we want to prove, we must start by allowing that there might indeed be a spiritual realm.

Because cause and effect is only proven for the physical world, we can no longer insist that they are relevant to the origin of a spiritual entity like God. Therefore God doesn’t have to have a cause — he can be the ultimate uncaused cause, a being whom no-one made.

Sunday 6 September 2009

Don't Worry 13 - Get Your Thinking Straight (C)

13. Get your thinking straight: See the problem in perspective and be realistic

What’s the worst that can happen? And what is really likely to happen? I once prepared a Bible study about worrying — and then caught myself worrying about how it would be received! Whether it would be really boring and unhelpful for those attending, whether it would be too short or too long, and so on. Forcing myself to think realistically I realised that it would probably be at least of some use to them, because considering these things had helped me. It might be a bit boring, but I felt sure they could live with that! It didn't really matter how long it was. And even if I should completely dry up, or they found it too obvious or even completely wrong — I knew that they would be kind and supportive because we were brothers and sisters in Christ.

Somehow our devils are never quite what we expect when we meet them face to face. (Nelson DeMille)

I think Jacob fell into the trap of getting things out of perspective and losing a sense of reality, when he returned to meet Esau after 15 years or more with Laban. Perhaps there was some excuse for him because the last he’d heard from Esau were words of hate. Esau held a grudge against Jacob because of the blessing his father had given him. He said to himself, "The days of mourning for my father are near; then I will kill my brother Jacob." (Genesis 27:41)  But Jacob had God’s promise: Then the LORD said to Jacob, "Go back to the land of your fathers and to your relatives, and I will be with you." (Genesis 31:3)

We read in Genesis 32:7-8, In great fear and distress Jacob divided the people who were with him into two groups, and the flocks and herds and camels as well. He thought, "If Esau comes and attacks one group, the group that is left may escape." At least he then prayed — but remember then how he sent his family and gifts of animals ahead of him to pacify Esau.

In the end, though, all his fears were unfounded. But Esau ran to meet Jacob and embraced him; he threw his arms around his neck and kissed him. And they wept. (Genesis 33:4)

Sunday 23 August 2009

Why was Jesus Baptized?

In or around December 26 AD, Jesus Christ was baptized by John the Baptist in the Jordan river. (See, for example, Mark 1:9.)

But we know that Jesus was sinless. So why was he baptized – since baptism is a sign of repentance, a symbol of the need to be cleansed from sin? Here are six possible reasons:

1. To obey. He submitted himself in perfect obedience to God’s will. When John objected to baptizing Jesus, Jesus replied (Matthew 3:15) that it is proper for us to do this to fulfil all righteousness. That is, he was eager to do everything just as God the Father willed.

2. Because of sin. Jesus demonstrated his resolve to identify with those he came to save and to take their guilt upon him. Jesus did have sin — his people's — which is the reason he died. Isaiah 53:6, We all, like sheep, have gone astray, each of us has turned to his own way; and the LORD has laid on him the iniquity of us all.

3. Because of John the Baptist. He validated John's ministry. By being baptized by John, Jesus publicly acknowledged that John’s ministry was from God, and encouraged John to continue in that ministry.

4. To fulfil prophecy. The promise that God had given John the Baptist had to be fulfilled so that John was able to proclaim Christ more clearly. John says (John 1:33), I would not have known him, except that the one who sent me to baptize with water told me, 'The man on whom you see the Spirit come down and remain is he who will baptize with the Holy Spirit.'

5. For revelation. When he was baptized, the Holy Spirit descended on him in the form of a dove, and the Father spoke from heaven, publicly revealing him to be the Christ, the Son of God. John says (John 1:31), the reason I came baptizing with water was that he might be revealed to Israel.

6. As an example. Christ has commanded the church to baptize believers as a public confession of their faith. It is appropriate for him, as head of the church, to be baptized ahead of his people.

Jesus came from heaven to lay down his life for his people. His baptism is part of the fulfilment of that task. At the same time he was publicly revealed for the first time as the Christ, and encouraged John in his ministry.

Tuesday 16 June 2009

Love and Marriage

'Child,' said the Director, 'it is not a question of how you or I look on marriage but how my Masters look on it.'

'Someone said they were very old-fashioned. But –'

'That was a joke. They are not old-fashioned; but they are very, very old.'

'They would never think of finding out first whether Mark and I believed in the ideas of marriage?'

'Well – no,' said the Director with a curious smile. 'No. Quite definitely they wouldn't think of doing that.'

'And it would make no difference to them what a marriage was actually like – whether it was a success? Whether the woman loved her husband?'

Jane had not exactly intended to say this: much less to say it in the cheaply pathetic tone which, it now seemed to her, she had used. Hating herself, and fearing the Director's silence, she added, 'But I suppose you will say I oughtn't to have told you that.'

'My dear child,' said the Directory, 'you have been telling me that ever since your husband was mentioned.'

'Does it make no difference?'

'I suppose,' said the Director, 'it would depend on how he lost your love.'

Jane was silent. Though she could not tell the Director the truth, and indeed did not know it herself, yet when she tried to explore her inarticulate grievance against Mark, a novel sense of her own injustice, and even of pity for her husband, arose in her mind. And her heart sank, for now it seemed to her that this conversation, to which she had vaguely looked for some sort of deliverance from all problems, was in fact involving her in new ones.

'It was not his fault,' she said at last. 'I suppose our marriage was just a mistake.'

The Director said nothing.

'What would you – what would the people you are talking of – say about a case like that?'

'I will tell you if you really want to know,' said the Director.

'Please,' said Jane reluctantly.

'They would say,' he answered, 'that you do not fail in obedience through lack of love, but have lost love because you never attempted obedience.'

from That Hideous Strength, by C. S. Lewis.

Tuesday 9 June 2009

We Need Free Speech

The Christian Institute is alerting people to the fact that the Government is seeking to remove the free speech clause from the homosexual hatred law via the Coroners and Justice Bill that is currently going through Parliament. The protection that this clause gives was included on the instigation of Lord Waddington, and was approved by Parliament only last year.

This law needs free speech protection, just like the law relating to the incitement of religious hatred. I am dismayed by many things that are said against my faith; but I recognise that, in a strong democracy, there should be freedom to say such things. In the same way, homosexual people should recognise that others may not share their opinions and are entitled to discuss and publish differing viewpoints. No group of people should be allowed to silence those who disagree with them.

The historic, orthodox Christian belief is that the practice of homosexuality is sinful. Christians should be allowed to retain the freedom to say what they believe in a democracy.

Here's just one example of the way things are moving. How long before it will be illegal to speak openly at all in the name of Christ?

Monday 25 May 2009

How Far is the Horizon?

I'm not sure why this has become an interest for me in recent weeks. Partly, I think, because I've been (re-)reading Patrick O'Brian's Aubrey/Maturin series of historical novels about the English Navy during the first part of the 19th century.

Anyway, it's fairly easy to work out with some basic maths if you know the radius of the earth.

Assume the earth is a sphere, with centre C; and imagine that you are looking at the horizon, H, from point E, which is a distance h above the earth:



The distance to the horizon is d and the radius of the earth is r – which NASA says is on average 6,371 km, approximately 3,959 miles.. Since ∠EHC is a right angle, we know from Pythagoras' Theorem that:


... which can be rearranged to find d:


That's all very well and good, but is not very useful. Let's define dm to be the distance to the horizon in miles, and df, hf and rf to be the measurements in feet, respectively, of the distance to the horizon, our height above the earth and the radius of the earth. Then, because there are 5,280 (3 x 1,760) feet in a mile:


Substituting in the actual radius of the earth (3,959 x 5,280 feet) and rearranging the formula a bit, we get:


Now, it so happens that 3,959 is almost exactly three-quarters of 5,280, so:


And, because the first term is so small compared with the second, we can approximate even further, and say:


So, if you're 6 ft tall, the distance to the horizon when you're standing on a beach as the water's edge is about 3 miles. If you were on the deck of Captain Aubrey's Surprise, say 18 ft above sea level, you'd be able to see over 5 miles; but at the top of the mast, which was probably 80 ft above sea level, you'd be able to see over twice as far.

In fact, this approximate calculation is remarkable accurate. At altitudes up to about 20,000 ft the difference between the "exact" distance given by equation (A) above and the approximate equation (B) is only about 0.01% - just a few yards.

Saturday 9 May 2009

Don't Worry 12 - Get Your Thinking Straight (B)

Another way for Christians to avoid worry.

12. Get your thinking straight: get the facts right

We need to train our minds to think rationally. Sometimes we worry about impossibilities … things that can never happen. As a simple example, it’s not much use - at least in this country - worrying that our child will meet a stampeding elephant on the way to school. And even if things are not impossible, they’re often extremely improbable. We need to research the facts carefully. If it’s futile to worry - how much more futile is it to worry about something that will never happen or that is not based on facts.

I am an old man and have known a great many troubles, but most of them never happened. (Mark Twain)

How much pain they have cost us, the evils which have never happened. (Thomas Jefferson)

Remember the two on the road to Emmaus after Jesus’ death. They were depressed, worried and confused. When Jesus met with them they stood still, their faces downcast (Luke 24:18) ... and explained to him:

[Jesus] was a prophet, powerful in word and deed before God and all the people. The chief priests and our rulers handed him over to be sentenced to death, and they crucified him; but we had hoped that he was the one who was going to redeem Israel. And what is more, it is the third day since all this took place. (Luke 24:19-21) ... and now it’s all gone wrong!

Jesus had to take them back to first principles, show them the facts ... especially the fact of his resurrection. It was then that:

their eyes were opened and they recognized him, and he disappeared from their sight. They asked each other, "Were not our hearts burning within us while he talked with us on the road and opened the Scriptures to us?" (Luke 24:31:32)

Monday 4 May 2009

Flat Earth? Never!

Part of a quote from David Warren by Post-Darwinist. In the same post, she also helpfully refers to Myth of the Flat Earth by Jeffrey Burton Russell.

One of the constants in the long clash between Scientism & Christianity has been the repetition & elaboration by the Scienticists of quite incredible myths & lies, that are still used in our public schools, media, &c, to mock & slander Christians & Christianity. I know about the potency of these myths & lies, for I myself was taught many of them, in school, saw them endlessly repeated in the press, heard them repeated by all liberal adults, & actually believed several of them until I came to riper years, & began to realize that public atheism requires the defence of a "bodyguard of lies." (And you will find them all repeated uncritically in Dawkins, Hitchens, Sam Harris, & all the current bestselling atheist tracts.)

...

Among the most universally taught & least subtle, lies taught to this day, is that Christians throughout the Middle Ages believed the world was flat; & that the Church taught this, & defended it as religious doctrine against Copernicus, Kepler, & Galileo. Yet the Church never taught the earth was flat, nor did any educated person, Christian or otherwise, ever believe it.

Indeed, the old Ptolemaic system -- universally accepted by intelligent Christians, Heretics, Atheists, Jews, Muslims, & even Hindus until the age of Copernicus -- was very clear on the fact that the earth was a sphere.

Friday 24 April 2009

What is an Evangelical Christian?

Here's some of what I would have liked to say to the man on the train who asked me what I believed as an "evangelical Christian", instead of the rather lame, "Well, um, er, I try to follow what the Bible says, er, ..."

Yes, I do try to follow what the Bible says, but only because I believe it to be the Word of God: that is, God's revelation of himself to man. The words it contains were written down by God-inspired individuals, as he worked in and influenced their lives. I didn't come to that conclusion overnight, but became convinced because of what it says about Jesus Christ, especially his death and resurrection; what it claims about itself; its internal harmony; its historical accuracy; and its insights into the human condition.

This has implications. It can be relied upon. What it says is true, in an absolute sense. It would be stupid to ignore it.

Its basic message, and hence the Christian message, can perhaps be summarized like this:

There is a God. He created me and everything else in the universe. Because he is the creator he has certain claims on his creation, particularly man.

Man rebels against these claims. He wants to be independent of God, to be the centre of his own universe. He has broken God's law; he is therefore subject to God's judgment.

God has provided a way of escape in the person of the God-man Jesus Christ. Jesus lived perfectly in accord with God's law. Even though he was put to death, he was raised to life again.

And (this is the crucial bit). Jesus died for his people - he took the punishment that they deserve. If I turn from my old way of life and trust in what Christ has done, if I am joined to him, then I no longer have to face the punishment that I deserve. I am washed clean, made new, born again. I am made right with God and can be confident of being raised to life with Jesus.

That is good news!

Wednesday 11 March 2009

Forgive When They're Not Sorry?

Are Christians called to forgive everyone who does them wrong?

Ben Midgeley addresses this question in a thoughtful piece in this month's Grace Magazine.

...
I think the expectations upon victims can be unreasonable. I think the question we ought to ask is, "If God grants repentance, would you be prepared to forgive?"
...
Jesus taught us to pray, "Forgive us our sins as we forgive those that sin against us." The little word "as" must mean "in the same way", not "at the same time". The way we obtain forgiveness is through faith, repentance and our claim in the atoning blood, so we cannot deny anybody else who seeks forgiveness in the same way. The willingness to forgive must be expressed and the actual forgiveness can come into operation when the right conditions prevail. The possibility of forgiveness remains there while simultaneously emphasizing the necessity of repentance first
...
Reconciliation in any meaningful sense is impossible without repentance. Therefore, forgiveness ... is out of reach too without repentance as a component. However we need a safeguard against bitterness and a thirst for revenge that will fill the vacuum until forgiveness can be made effective. I like to think of this as "interim government" and I would call it mercy.

Mercy, as a disposition, stands in the place of of forgiveness until the conditions for forgiveness become possible. At the moment of repentance, forgiveness, which is the payload that mercy carries, is discharged and the person experiences not simply "not getting what they deserve" - mercy, but "getting what they don't deserve" - forgiveness. We should make a decision not to harm the offender, but to do him good, to be unaffected in that sense by his sinful actions. Then, should the offender ever come to appreciate that the door has been left open to him, one is ready, even glad to forgive. ... It is provisional forgiveness, a readiness to forgive, a willingness, a hope expressed ...
...
Did God forgive me before the world began, or at Calvary, or when I was saved? Certainly he predestined me to experience his forgiveness, then he made actual provision for it, but it wasn't until I repented and believed that I knew the joy of my sins forgiven ...
...
In summary - especially to those who struggle to forgive, or feel pressured to do so, or feel guilty if they can't, I would just ask, "Are you willing to forgive if that person repents?" I think this is at the heart of what "Father forgive them" means: a genuine willingness to forgive, and leaving the rest with God.

Friday 6 February 2009

Cake

You will probably have seen the result of an "out of office" message appearing on a signpost in Swansea.

On a similar theme, this is what happens when you try to speed up the supply line just a bit too much:


"We just cut and paste from the e-mail to the program we use for printing the edible images; we are usually in such a hurry that we really don't have time to check, and if we do the customers yell at us for bothering them."

Incidentally, I wonder what you are supposed to do when you suddenly add to a blog that you haven't updated for over 3 months? Are you supposed to apologise? (But then that assumes that someone is out there, hanging on your every word.) Create a special post to draw attention to the fact and give some kind of explanation? Just carry on as normal and pretend nothing has happened? Add a postscript to the first new post, but not actually tell anyone the reason for the interlude? (Yep, that seems like a good idea...)