You generate the game map once it's known where the player clicked. During generation you take into account that the first clicked cell has to be mine-free. In general, when distributing mines, the algorithm has to take into account already that there must be N mines in the field, so avoid instances where it assigns two mines to the same field twice. You treat the starting field as just another "mine" whose position is hardcoded.
A simple algorithm would be to re-do the map generation until there is no mine collision, but with larger numbers of mines, the collision chance increases, so your runtime will go up superlinearly. A simple but very effective improvement is to keep the positions of the mines up until the collision and then only change the seed for subsequent mines. Then your algorithm should in fact scale much better. You can also use different methods, like assigning indices in a certain way so that invalid states are avoided completely. A bit more complicated but way "cleaner".
There is an even simpler approach: for maps with K total cells there would be always K-1 unclicked cells and a single clicked cell after the first click, so you can generate K-1 cells and insert a "gap" corresponding to the first clicked cell. This works because every unclicked cell has an equal probability to become a mine.
This is what actually happens in the Windows original (it doesn't look for a random empty square, it tries them in reading order starting from the top left).
Well you have all the cells in an array so that's easy enough. Pick a cell at random until you find an empty one, which won't take long. There's no regeneration, just moving one mine to an easy to find place.
A simple algorithm would be to re-do the map generation until there is no mine collision, but with larger numbers of mines, the collision chance increases, so your runtime will go up superlinearly. A simple but very effective improvement is to keep the positions of the mines up until the collision and then only change the seed for subsequent mines. Then your algorithm should in fact scale much better. You can also use different methods, like assigning indices in a certain way so that invalid states are avoided completely. A bit more complicated but way "cleaner".