2011年6月18日 星期六

Visual C++ MFC視窗模式下呼叫gnuplot

研究這的目的,是要在Visual C++視窗模式下呼叫gnuplot

也順便解決了呼叫新版gnuplot會出現的問題:
Timeout: gnuplot is not ready


原本只會用C的popen()來呼叫gnuplot,或用Visual C++的_popen()。
這種方法在console(控制台,黑黑的視窗)模式下是可行的。
但在VC++的視窗模式下,是無法使用_popen()的。

因為popen()是Unix系統的pipe function,Windows系統雖有模擬,但使用上有些限制( ex. 視窗程式不能用 )


因為在視窗程式不能用_popen(),只好用在console模式下寫另一只程式Callgnuplot
然後再用視窗程式呼叫Callgnuplot讀檔畫圖

system( "Callgnuplot.exe" )

但這種方式會出現黑色console視窗,有礙觀瞻
就算Callgnuplot裡加上隱藏視窗,還是會閃一下黑色視窗


Using popen():

    →指令傳遞方向

    My Program ( console )
       ↓
       ↓ popen() function
       ↓
    pgnuplot.exe
       ↓
       ↓ pipe function of Windows
       ↓
    wgnuplot.exe ( GUI )


此方法適用於:
舊版gnuplot
compiler: mingw32-gcc
compiler: VC++ console 模式 (popen要改成_popen)

無法用於 VC++ 視窗模式

系統: Windows XP           gnuplot版本: Version 4.4 patchlevel 0-rc1 (gp44rc1win32)
/*******************************************************************/
#include <stdio.h>
int main( void )
{
    FILE *command;

    //command = popen( "gnuplot", "w" ); // XP可用,Vista不行
    command = popen( "pgnuplot", "w" ); // XP Vista 皆可

    fprintf( command, "plot x**2\n" );
    fflush( command ); // 清空pipe緩衝區裡的指令,把指令傳到 pgnuplot,此時圖才會畫出來

    //為了慢慢欣賞畫出的圖,這裡要放暫停的指令

    pclose( command ); // 清除pipe緩衝區裡的指令,此時gnuplot就被關掉了

    return 0;
}
/*******************************************************************/
以上程式修改自:
Appendix B. Call gnuplot from C program


用popen呼叫新版gnuplot( Version 4.4 patchlevel 3 )( gp443win32 )
每次都會出現Timeout: gnuplot is not ready,且畫不出圖
我曾試著在popen之後加上Sleep,還是失敗



後來找到pgnuplot的原始碼,從中找到用Windows的pipe function呼叫gnuplot的方式
於是視窗程式也可以直接呼叫gnuplot!!!
順便也解決了呼叫新版gnuplot會出現的問題:

Timeout: gnuplot is not ready

Using pipe function of Windows:

    →指令傳遞方向

    My Program ( console or 視窗 )
       ↓
       ↓ pipe function of Windows
       ↓
    wgnuplot.exe ( GUI )


/*******************************************************************/
#include <stdio.h>
#include <windows.h>

#define PROGNAME "wgnuplot.exe"
#define TEXTCLASS "wgnuplot_text"

/* GLOBAL Variables */
HWND hwndParent = NULL;
HWND hwndText = NULL;

PROCESS_INFORMATION piProcInfo;
STARTUPINFO         siStartInfo;

/* CRS: Callback for the EnumThreadWindows function */
BOOL CALLBACK
cbGetTextWindow(HWND  hwnd, LPARAM  lParam)
{
    /* save the value of the parent window */
    hwndParent = hwnd;
    /* check to see if it has a child text window */
    hwndText = FindWindowEx(hwnd, NULL, TEXTCLASS, NULL);

    /* if the text window was found, stop looking */
    return (hwndText == NULL);
}

/* sends a string to the specified window */
/* CRS: made this into a function call */
void
PostString(HWND hwnd, char *pc)
{
    while(*pc) {
    PostMessage(hwnd, WM_CHAR, (unsigned char) *pc, 1L);
    /* CRS: should add a check of return code on PostMessage. If 0, the
      message que was full and the message wasn't posted. */
    pc++;
    }
}

int
main ()
{
    char    psGnuplotCommandLine[MAX_PATH] = PROGNAME;

    BOOL    bSuccess;

    /* CRS: initialize the STARTUPINFO and call CreateProcess(). */
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.lpReserved = NULL;
    siStartInfo.lpReserved2 = NULL;
    siStartInfo.cbReserved2 = 0;
    siStartInfo.lpDesktop = NULL;
    siStartInfo.dwFlags = STARTF_USESHOWWINDOW;
    siStartInfo.wShowWindow = SW_SHOWMINIMIZED;

    //如果不加下面這兩行, 會Error:998
    ZeroMemory(&piProcInfo,   sizeof(piProcInfo) ); 
    ZeroMemory(&siStartInfo,   sizeof(siStartInfo) );

    bSuccess = CreateProcess(
                NULL,                   /* pointer to name of executable module   */
                psGnuplotCommandLine,   /* pointer to command line string         */
                NULL,                   /* pointer to process security attributes */
                NULL,                   /* pointer to thread security attributes  */
                FALSE,                  /* handle inheritance flag                */
                0,                      /* creation flags                         */
                NULL,                   /* pointer to new environment block       */
                NULL,                   /* pointer to current directory name      */
                &siStartInfo,           /* pointer to STARTUPINFO                 */
                &piProcInfo             /* pointer to PROCESS_INFORMATION         */
                );

    /* if CreateProcess() failed, print a warning and exit. */
    if (! bSuccess) {
        fprintf(stderr,"ERROR %u: Couldn't execute: \"%s\"\n",
        GetLastError(), psGnuplotCommandLine);
        exit(EXIT_FAILURE);
    }

    Sleep(1000); // 這行是我加的,不加的話,新版的gnuplot會失敗

    if (WaitForInputIdle(piProcInfo.hProcess, 1000)) {
    fprintf(stderr, "Timeout: gnuplot is not ready\n");
    exit(EXIT_FAILURE);
    }

    /* CRS: get the HWND of the parent window and text windows */
    EnumThreadWindows(piProcInfo.dwThreadId, cbGetTextWindow, 0);

    /* CRS: free the process and thread handles */
    CloseHandle(piProcInfo.hProcess);
    CloseHandle(piProcInfo.hThread);

    //下面3行是我隨意寫的,你可以改成你想要的指令傳給gnuplot
    PostString(hwndText, "plot x**2\n");
    Sleep(10000);
    PostString(hwndText, "\nexit\n");

    return EXIT_SUCCESS;
}

/*******************************************************************/
以上程式修改自:


沒有留言:

張貼留言