The first step is to determine whether your program really needs admin privilege at all. Sometimes a program only runs with admin privilege, but for trivial reasons: a log file is being generated in the wrong place, for example, or a file that should be being opened for read-only access is being opened for full access. If that's the case you can fix the problem and avoid any further structural changes.
Secondly, ask whether your program needs admin privilege all the time, or only when the user performs certain actions. In the latter case, you should probably only elevate when it becomes necessary to do so; as well as meaning that the user does not need to approve the program launch on every reboot, it also helps protect the user from making an administrative change without intending to. This is particularly relevant if UAC is configured to require a password each time.
Thirdly, ask whether your program really needs a user interface. If not, then it should be a system service.
If your program really does need admin privilege all the time, and really does need a user interface, then you need to separate it into two parts, one containing the user interface and one containing the functionality that requires elevated privilege.
The user interface part should be a program that runs whenever a user logs in, just as your program does now. The elevated privilege part should be a system service.
The primary logic might belong in either part, or might also need to be split into two; it depends entirely on the context. (The system service does need to contain enough logic to ensure that the the privileged operations it is performing are safe and appropriate. It can't simply do anything the user interface part tells it to.)
These two parts can interact using whatever form of inter-process communication and/or synchronization is most convenient. You do need to be aware that they will be in different Remote Desktop sessions; for example, if you create an event object for synchronization the name must start with the Global\
prefix.
You will need to consider that more than one user may be logged in at the same time, either via Switch User or because the machine is a Remote Desktop server. This may mean that the service component needs to support multiple simultaneous clients, which affects your choice and implementation of IPC. Alternatively, the user interface component needs to detect that another instance is already running, and wait until that instance goes away before attempting to connect.
You will also need to consider how the program should react when the logged-in user does not have administrative privilege. At the moment such a user can't run your program at all, probably making the prompts even more annoying than they are to an admin user! If it is OK for the program to work as normal for a non-admin user then you don't need to do anything special. If the program should not work for a non-admin user, or if some of the functionality should be restricted, then (a) the GUI component needs to behave accordingly, by, e.g., exiting silently; and (b) the service component needs to check the context in which the GUI component is running. It is not enough for the GUI component to do the check, because the user can trick it if he or she wishes to; the service component must check too.
The easiest way to do that is probably to use GetTokenInformation with the TokenElevationType option; if the token type is TokenElevationTypeLimited or TokenElevationTypeFull, the user has administrator privilege. If the token type is TokenElevationTypeDefault, there is no split token; either the user is not an administrator, is the local Administrator account, or UAC is turned off; in this case, use CheckTokenMembership to check whether the user is in the Administrators group or not.
In some cases, it might also be sensible for certain tasks to require UAC approval, even if other tasks do not. Such tasks need not involve the service component; the GUI component can elevate itself, with the user's consent, to perform them.