Windows下C++/Fortran調用.exe可執行文件

軟件環境

# 操作系統 = Windows10
# 編譯鏈工具 = 
#	gcc, g++, GNU Fortran (MinGW.org GCC Build-2) 9.2.0
#	GNU Make 3.82.90	Built for i686-pc-mingw32
#	GNU ld (GNU Binutils) 2.32
#	CMake version 3.18.1

本機安裝了MinGW編譯工具,並將<安裝路徑>\MinGW\bin添加到環境變量中。

Windows下CMake編譯配置

設置項目的generator

在本機使用CMake過程中發現,默認使用NMake Makefiles作為generator,因為沒有安裝因此配置失敗。

希望設置generator為已安裝的MinGW Makefiles

Command Line

cmake .. -G "MinGW Makefiles"

CMake GUI

初次Configure時指定項目的generator。

選用MinGW Makefiles,使用默認編譯器。

如果已經指定generator後,需要修改。可以清除CMake Cache後再次ConfigureFile->Delete Cache

PreLoad.cmake

CMake設置generator是在處理CMakeLists.txt之前的,因此不能通過在CMakeLists.txt中設置CMAKE_GENERATOR達到修改默認generator的作用。

可以在項目根目錄添加PreLoad.cmake這樣的文件實現預先修改配置,內容如下。

set(CMAKE_GENERATOR "MinGW Makefiles" CACHE INTERNAL "" FORCE)

設置make

即使已為MinGW添加了環境變量,但是不能在命令行中直接使用make。gcc、g++、gfortran可以直接使用。

是因為在<安裝路徑>\MinGW\bin下,對應make的可執行程序名字為mingw32-make,在同目錄下拷貝一份,重命名為make即可使用。

示例程序

CMake

以下為CMakeLists.txt文件內容,

# name: exe_test
# date: 2021/3/14
#

cmake_minimum_required(VERSION 3.3)
project(exe_test CXX Fortran)

add_executable(hello source/hello.cpp)
add_executable(caller_cpp source/caller.cpp)
add_executable(caller_fort source/caller.f90)

設置Fortran語言的一個小問題

-->$ cmake ..
-- The CXX compiler identification is GNU 9.3.0
CMake Error: Could not find cmake module file: CMakeDetermineFORTRANCompiler.cmake
CMake Error: Error required internal CMake variable not set, cmake may not be built correctly.
Missing variable is:
CMAKE_FORTRAN_COMPILER_ENV_VAR
CMake Error: Error required internal CMake variable not set, cmake may not be built correctly.
Missing variable is:
CMAKE_FORTRAN_COMPILER
...

這個問題的原因在CMakeLists.txt中,是大小寫敏感的,Fortran只應該使用首字母大寫的形式。

# project(exe_test CXX FORTRAN)
project(exe_test CXX Fortran)

source/hello.cpp

// name: hello.cpp
// date: 2021/3/14
//

#include <iostream>
using namespace std;

int main()
{
        cout << "Aloha!" << endl;

        return 0;
}

source/caller.cpp

// name: caller.cpp
// date: 2021/3/14
//

#include <iostream>
#include <string>
#include <Windows.h>
#include <tlhelp32.h>
#include <process.h>

using namespace std;

void Launch(LPCTSTR lpApplicationName)
{
        // additional information
        STARTUPINFO si;
        PROCESS_INFORMATION pi;

        // set the size of the structures
        ZeroMemory( &si, sizeof(si) );
        si.cb = sizeof(si);
        ZeroMemory( &pi, sizeof(pi) );

        // start the program up
        CreateProcess( lpApplicationName,   // the path
                        (char*)"",        // Command line
                        NULL,           // Process handle not inheritable
                        NULL,           // Thread handle not inheritable
                        FALSE,          // Set handle inheritance to FALSE
                        0,              // No creation flags
                        NULL,           // Use parent's environment block
                        NULL,           // Use parent's starting directory
                        &si,            // Pointer to STARTUPINFO structure
                        &pi             // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
        );

        cout << "process id: " << pi.dwProcessId << ", thread id: " << pi.dwThreadId << endl;

        // Close process and thread handles.
        CloseHandle( pi.hProcess );
        CloseHandle( pi.hThread );
}

int main(int argv, char* args[])
{
        cout << "caller_cpp:" << endl;

        // 1.
        // 可以加路徑,路徑中應使用\\而非/
//      WinExec("hello.exe", SW_SHOW);

        // 2.
//      SHELLEXECUTEINFO shell = { sizeof(shell) };
//      shell.fMask = SEE_MASK_FLAG_DDEWAIT;
//      shell.lpVerb = "open";
//      shell.lpFile = "hello.exe";
//      shell.nShow = SW_SHOWNORMAL;
//      BOOL ret = ShellExecuteEx(&shell);

    	// 3.
        // ShellExecuteA
//      ShellExecute(NULL, "open", "hello.exe", NULL, NULL, SW_SHOWDEFAULT);

        // 4.
//      system("hello.exe");

        // 5.
        Launch("hello.exe");

        // 6.
//      _execv("hello.exe", args);

        return 0;
}

收集了多種方式,建議使用CreateProcess(),詳細內容查閱Microsoft Docs。

source/caller.f90

! name: caller.f90
! date: 2021/3/14
!

PROGRAM Caller_fort

        PRINT *, "Caller_fort"

        ! Fortran 2008
        call execute_command_line ("hello.exe", wait=.false.)

        !
        call system("hello.exe")

END

execute_command_lineFortran2008標準開始支持的函數。

參考資料

  1. cmake_ Selecting a generator within CMakeLists.txt – Stack Overflow
  2. Using cmake with fortran – Stack Overflow
  3. windows C_C++ 在一個程序中打開,關閉和監視其它的exe程序_lumanman_的博客-CSDN博客
  4. C++ 打開exe文件的方法(VS2008) – work hard work smart – 博客園
  5. C++ 中打開 exe 文件_楠木大哥的博客-CSDN博客
  6. C++程序中調用exe可執行文件_積累點滴,保持自我-CSDN博客_c++調用exe文件
  7. ShellExecuteA function (shellapi.h) – Win32 apps _ Microsoft Docs
  8. Creating Processes – Win32 apps _ Microsoft Docs
  9. _exec, _wexec Functions _ Microsoft Docs
  10. EXECUTE_COMMAND_LINE (The GNU Fortran Compiler)
  11. SYSTEM (The GNU Fortran Compiler)