Мое предпочтительное решение, как сказал Питер Лэнг, использовать Sockets. Когда ваше приложение запускается, вы можете запустить сокет сервера, прослушивающий входящие соединения на локальном хосте (плюс порт по вашему выбору). До того, как это произойдет в вашем коде, вы можете попытаться установить соединение с серверным сокетом, и если это успешно, вы знаете, что другой экземпляр уже открыт, поэтому вы можете выйти из текущего экземпляра с соответствующим сообщением.
В вашей реализации сокета сервера вы также можете добавить функциональность, которая при получении входящего соединения фактически вынуждает текущий экземпляр приложения на передний план.