--EDITED--
Since the question was clarified, I will edit this anwer to provide a better answer.
First, it is going to be almost impossible to provide just a plain name to a C++ function. The tooling just doesn't work this way. The linker would just totally ignore non C++ names when trying to make import libraries.
The best option is to export the functions as extern "C". Nothing is gained by exporting a C++ function as a plain name, but the ability for the compiler to detect this is lost. Using extern"C" will get the library close while allowing the compiler to detect attempts at overloading a function. The ability to overload a function is lost when the name mangling is removed, so at that point it is better to just use extern"C". There is also the fact that C++ name mangling is not standardised. This means that the name mangling scheme can and has changed.
When extern "C" is used, surprisingly, the goal has been achieved already. When Visual C++ exports a C function from a DLL, a __cdecl function will be exported from the DLL using its plain name.
Dump of file testlib.dll
File Type: DLL
Section contains the following exports for testlib.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
2 number of functions
2 number of names
ordinal hint RVA name
1 0 00001010 ?fun2@@YAXXZ
2 1 00001000 fun1
Summary
2000 .data
1000 .fptable
7000 .rdata
1000 .reloc
D000 .text
Notice how fun1 does not have the leading _. Whats more, the import library is automatically set up to allow the linker find the entry.
Archive member name at 6E6: testlib.dll/
FFFFFFFFFFFFFFFF time/date
uid
gid
0 mode
26 size
correct header end
Version : 0
Machine : 14C (x86)
TimeDateStamp: D77D3838
SizeOfData : 00000012
DLL name : testlib.dll
Symbol name : _fun1
Type : code
Name type : no prefix
Hint : 1
Name : fun1
Notice how Symbol name is _fun1, but the Name is fun1. The symbol name is the name that the compiler generates and looks for, but Name is the name exported from the DLL. So just by using extern "C", the goal has been achieved. The original function definition and export was:
extern"C"
void fun1()
{
}
#ifdef _M_IX86
#pragma comment(linker, "/export:_fun1")
#else
#pragma comment(linker, "/export:fun1")
#endif
Notice how, for x86 builds, I am explicitly exporting the mangled name of the function.
If for some reason a plain name for a C++ exported name is needed, there are a couple of methods available, but it is required to export the mangled C++ name if link time dynamic linking is required (this is where the import library (.lib) file is used). It is impossible to associate a C++ mangled name with a plain symbol exported from a DLL. If a module definition like:
EXPORTS
?fun1@@YAAXXZ=fun1
is used, lib.exe seems to assume that the plain name to the right is actually mangled, or it ignores the name. This module definition is required to construct an import library that makes the association between the C++ mangled name and the DLL export so the linker is able to use it. There is no issue exporting the same function twice as long as each export is uniquely named.
#ifdef _M_IX86
#pragma comment(linker, "/export:_fun1")
#pragma comment(linker, "/export:?fun2@@YAXXZ")
#pragma comment(linker, "/export:fun2=?fun2@@YAXXZ,PRIVATE")
#else
//For anything currently supported but x64, C exports do not
//use mangling for __cdecl, __vectorcall still mangles though
#pragma comment(linker, "/export:fun1")
//Even though in this case the x86 C++ name is the same, do not
//assume that it will always be the same.
#pragma comment(linker, "/export:?fun2@@YAXXZ")
#pragma comment(linker, "/export:fun2=?fun2@@YAXXZ,PRIVATE")
#endif
The private on the plain name definition of fun2 only tells the linker not to put a reference to it in the import library, so fun2 will still be visible as an export for the DLL.
If there is a desire to keep the DLL exports simple, then the only option is to use a forwarder DLL. The plain names can be placed into a separate DLL:
LIBRARY testlibforward.dll
EXPORTS
fun1=testlib.fun1
fun2=testlib.?fun2@@YAXXZ
This helps split things between libraries, but in the end the C++ name still has to be exported. If you wish to access the plain name then the forwarder library can be used. In this case, Windows will automatically load testlib.dll and use ?fun2@@YAXXZ when fun2 is used in testlibforwarder.dll.
--Original Post--
The question is unclear, so I will make some assumptions here.
For names such as ?Func4@@YAXXZ, these are C++ mangled names. If the intent is to export a C++ function then function overloading has to be taken into account. For function overloading to work, the C++ mangled names must be exported.
If there is no intent of making a function overloadable, then putting the function definition into an extern "C" block helps. C++ name mangling isn't standardised and can change, so using the C name mangling is better because it is relatively fixed. Also, as a note, _Func or _main is only used for __cdecl for x86 C name mangling, x64 will use Func or main.
If the intent is to export the C name as a plain funcion name, the documentation for the linker /EXPORT option states:
#pragma comment(linker, "/export:PlainFuncName=_PlainFuncName@4")
BOOL CALLBACK PlainFuncName( Things * lpParams)
as an example. The thing to note is that the plain name comes first, this is the name that is exported from the library, where the decorated name comes second.So, for x86 builds of the DLL only,
extern "C"
void Func4();
#pragma comment(linker, "/export:Func4=_Func4")
Appears to be what you wish to do.
However, parsing the question a bit more, if the intent is to export the function from the DLL using the plain name, but have it available in the import library in a form that is usable by the linker, then the solution must be external to the project.
To use kernel32.dll as an example,
File Type: DLL
Section contains the following exports for KERNEL32.dll
00000000 characteristics
F503DEE8 time date stamp
0.00 version
1 ordinal base
1643 number of functions
1643 number of names
ordinal hint RVA name
4 0 AcquireSRWLockExclusive (forwarded to NTDLL.RtlAcquireSRWLockExclusive)
5 1 AcquireSRWLockShared (forwarded to NTDLL.RtlAcquireSRWLockShared)
6 2 0002F240 ActivateActCtx
7 3 0001CE50 ActivateActCtxWorker
8 4 0001F8D0 ActivatePackageVirtualizationContext
9 5 000546B0 AddAtomA
10 6 00016090 AddAtomW
The first fer entries of the x86 bit DLL (more explicitly the WoW64 x86 DLL) show all of the functions exported from the DLL using the plain names. Some are forwarded, but that is a discussion for another time. However, if we look at the kernel32.lib entry for AddAtomA,
Archive member name at 291E6: KERNEL32.dll/
FFFFFFFFFFFFFFFF time/date
uid
gid
0 mode
2D size
correct header end
Version : 0
Machine : 14C (x86)
TimeDateStamp: C5B79BF5
SizeOfData : 00000019
DLL name : KERNEL32.dll
Symbol name : _AddAtomA@4
Type : code
Name type : undecorate
Hint : 5
Name : AddAtomA
Notice how the Symbol name entry is _AddAtomA@4? This is the __stdcall mangled name for the function, and this is what the compiler and linker is looking for.
However, if we look at the entry for my previous example library in TestLib.lib:
Archive member name at 628: TestLib.dll/
FFFFFFFFFFFFFFFF time/date
uid
gid
0 mode
25 size
correct header end
Version : 0
Machine : 14C (x86)
TimeDateStamp: F9115131
SizeOfData : 00000011
DLL name : TestLib.dll
Symbol name : fun1
Type : code
Name type : name
Hint : 0
Name : fun1
Notice how the Symbol name entry is fun1? Which is the unmangled name. For this to be usable by the linker, the import library must be created in a way to make that link again. This requires creating a separate module definition file.
NAME TestLib.dll
EXPORTS
fun1=fun1
The weird fun1=fun1 thing is needed because the linker attempts to do some name mangling. But if this module definition file is made into an import library, then it will produce:
Archive member name at 62C: TestLib.dll/
66DC6E87 time/date Sat Sep 7 16:17:27 2024
uid
gid
0 mode
26 size
correct header end
Version : 0
Machine : 14C (x86)
TimeDateStamp: 66DC6E87 Sat Sep 7 16:17:27 2024
SizeOfData : 00000012
DLL name : TestLib.dll
Symbol name : _fun1
Type : code
Name type : no prefix
Hint : 0
Name : fun1
Notice that the Symbol name is _fun1, and the Name is fun1. So if the intent is to export a function using a non default name, then the link must be made visible to the linker in the import library.