One primary driver of the unknowability of software estimation, is that the customer doesn't actually know, and perhaps CANNOT actually know, what they really want or need until they see something that isn't it. No amount of asking beforehand will bring out this information, and complaining afterwards that they didn't say that doesn't accomplish anything except souring the relationship.
Bill by the hour, and absolutely refuse to bill by the project. It's the only way.