language : Japanese | English

I like being alone.

Home Illust(fantasy RPG) Copic Painting Music Absolute Pitch Old Software Visual Studio C / C++ / MFC Note Diary Author


Visual Studio C / C++ / MFC Notes

Here is my notes when I maked RPG or MIDI sequencer. For basic and detailed explanation, since I've published as a book, please see the book.

Please note : This article is supplementary note of the book, so this article is not partly translated to English at now.

Break point is not hit in the current setup. Source code is different from original version.

Some version of Visual Studio has a bug that doesn't stop at the break point if you are using char code set such as Shift-JIS (Code page 932) because it can't verify the file's identity. You can resolve this probrem by saving your source code as UTF-8(Code page 65001 with signature). Ah, visual studio may see old version source code against your hope, you should compress all old source code into zip file so as not to see by visual studio directly for the safety.

But there is the other problem. UTF-8's text file has a 3byte's BOM{0xEF,0xBB,0xBF}, but some text editor or compiler can't understand this BOM. For example, Microsoft resource compiler can't understand UTF-8 text resource file (*.rc). If you use UTF-8's text file without BOM, it may be recognized as a Shift-JIS's text file. Some text editor automatically distinguish char code set, but there are a lot of char code set around the world, it may be wrong char code set especially if the text is short. Therefore, you should save only *.c, *.cpp, *.h as UTF-8.

Which do you select, C/Win32API or C++/MFC ?

If you'll use dockingable toolbar or dialogbar, document-view architecture, multiple page print preview and so on, using C++/MFC may be suitable. Writing these source code by yourself will take very long time away. Else, using C/Win32API may be suitable because we can develop with Visual Studio Express Edition which we can get without paying. But at 2015, microsoft has released Visual Studio 2015 Community Edition, with MFC without paying, as long as for individual development (including commercial development) or open-source development and so on.

Minimum Windows application

Here is the shortest windows program written in C. For detail, please see the book. This program shows a message box.

#include <tchar.h>
#include <windows.h>

int WINAPI _tWinMain 
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
  return MessageBox (NULL, _T("こんにちは、世界よ。"), 
    _T("例題"), MB_ICONEXCLAMATION | MB_OK);
}

Execution Result

How to write both ANSI and Unicode compliant application

Since all strings are treated as a Unicode UTF-16LE in the Windows XP or later, even if you are using Japanese language's Windows, you can use all kind of charactars including latin, chinese, hangul and cyrillic character by using IME-pad (including at file name). For example, notepad including in Windows can treat all kind of characters.

Windows program has two version, which is, ANSI version and Unicode version. In ANSI version, internal character is treated as 1 byte, and since how to decode is diffrent by char code set, it is easy to cause character corruption. On the other hands, in Unicode version, internal character is treated as 2 byte (Speaking exactly, sizeof(wchar_t) byte), and since how to decode is common around the world, you don't have to worry character corruption (as long as the user has installed suitable font). In the early days of Windows2000 or earlier, ANSI version was mainstream. But now, the days we can think it has no problem because working in Japanese language means also working in English language has already ended, and a lot of programs are compliant to Japanese, English, Chinese and the other language. Therefore if you have ANSI version's source code, you should move them to Unicode version.

移行期間や互換性検証の問題もあるので、ひとつのソースコードからANSI版のexeファイルとUnicode版のexeファイルが両方出来上がれば便利です。そのためには、tchar.hで定義されているマクロを活用します。例えば、TCHARは、_UNICODEが定義されている場合はwchar_tに、そうでない場合はcharに展開されます。ソースコード中にANSIもしくはUnicodeのどちらかに依存するコードが入ってはいけません。やむを得ない場合は、その部分だけ#ifdef _UNICODE #else #endifで分けます。なお、MFCのCString型はこのマクロ機構が既に組み込まれているので、どちらの場合でも気にせず使えます(リテラル文字列使用時は_T("")にする必要あり)。

Visual Studio 2005以降では、デフォルトでUnicode版のexeファイルができるようになっています。それ以前の古いVisual StudioではデフォルトでANSI版のexeファイルができるようになっています。このページでは、ANSI版とUnicode版の両方に対応するため、必ずTCHARで定義されているマクロを用い、_UNICODEの定義があるかないかで出力するexeファイルの種類を切り替えらえるようにしています。Visual Studioを使用している場合は、プロジェクト -> (プロジェクト名)のプロパティ... -> 構成プロパティ -> 全般 -> 文字セットで、「設定なし」がANSI版、「Unicode文字セットを使用する」がUnicode版となります。

Example macros for both ANSI and Unicode defined in tchar.h

Macro _UNICODE is undefined _UNICODE is defined description
_tmain main wmain The main functin of Console application.
_tWinMain WinMain wWinMain The main functin of Windows application.
TCHAR char wchar_t The minimum unit of a character. In ANSI, 1~2bytes/char, and the character table is diffrent from char code set such as Latin-1, Shift-JIS, Chinese simplified, Hungle, and so on), maybe cause character corruption. In Unicode, (wchar_t)bytes/char, and character table is common around the world.
LPTSTR char* wchar_t* A pointer to string.
LPCTSTR const char* const wchar_t* A pointer to const string.
_T("") "" L"" A literal string. If without L, it is treated as char, else, treated as wchar_t.
_tfprintf fprintf _wfprintf Show string on the console. How to specify strings is diffrent between const char* and const wchar_t*.
_tfopen fopen _wfopen Open file. How to specify file name and mode is diffrent between const char* and const wchar_t*.
_tcslen strlen wcslen Get the length null terminerted string. strlen is 1 byte unit, and wcslen is sizeof (wchar_t) bytes unit.
_tcsncmp strncmp wcsncmp Compare two strings.
_tcsncpy strncpy wcsncpy Copy string.
_sntprintf _snprintf _snwprintf Format string.
_ttoi atoi _wtoi Convert to integer from string.
_ttof atof _wtof Convert to double from string.
_fputts fputs fputws ファイルに文字列書き込み。
_fgetts fgets fgetws ファイルから文字列を読み取り。
_ftprintf fprintf fwprintf ファイルに文字列を書き込み。
_ftscanf fscanf fwscanf ファイルから文字列を読み取り。

How to write both ANSI and Unicode compliant DLL

By writing following macro in the header file of DLL, if DLL's user defines UNICODE, GetHelloWorld is automatically extracted to GetHelloWorldW, else, it is extracted to GetHelloWorldA. DLL's user don't need mind which their code is unicode or not, they don't need to divide code, they only have to use GetHelloWorld. As well, to write following macro and divide the function code is required only when the function's parameter or return value contains string or a pointer to string.

TestDLL.def

;LIBRARY TestDLL
EXPORTS 
  GetHelloWorldA
  GetHelloWorldW

TestDLL.h

#ifndef _TESTDLL_H_
#define _TESTDLL_H_

char* __stdcall GetHelloWorldA (char* pBuf, long lLen);
wchar_t* __stdcall GetHelloWorldW (wchar_t* pBuf, long lLen);
#ifdef UNICODE
#define GetHelloWorld GetHelloWorldW
#else
#define GetHelloWorld GetHelloWorldA
#endif

#endif

TestDLL.c

#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "TestDLL.h"

char* __stdcall GetHelloWorldA (char* pBuf, long lLen) {
  memset (pBuf, 0, lLen);
  strncpy (pBuf, "HelloWorld", lLen - 1);
  return pBuf;
}

wchar_t* __stdcall GetHelloWorldW (wchar_t* pBuf, long lLen) {
  memset (pBuf, 0, lLen * sizeof(wchar_t));
  wcsncpy (pBuf, L"HelloWorld", lLen - 1);
  return pBuf;
}

How to use ini file in MFC for avoiding registory pollution

It depends on which use registory or local ini file by the project, here I mention about how to use ini file in MFC application. At first, in the CMyApp::InitInstance function, delete CWinApp::m_pszProfileName's buffer, and then, allocate to this pointer a buffer for ini file name with full path, and then, copy the ini file name here. After this, the target of LoadStdProfileSetting, GetProfileString or WriteProfileString is fixed to this ini file.

BOOL CExampleApp::InitInstance () {

  // (中略)

  // EXEファイルのフルパス名を取得
  TCHAR szPath[_MAX_PATH + 1];
  TCHAR szDrive[_MAX_DRIVE + 1];
  TCHAR szDir[_MAX_DIR + 1];
  TCHAR szFileName[_MAX_FNAME + 1];
  TCHAR szExt[_MAX_EXT + 1];
  memset (szPath, 0, sizeof (szPath));
  memset (szDrive, 0, sizeof (szDrive));
  memset (szDir, 0, sizeof (szDir));
  memset (szFName, 0, sizeof (szFName));
  memset (szExt, 0, sizeof (szExt));
  ::GetModuleFileName (this->m_hInstance, szPath, _MAX_PATH);
  _tsplitpath (szPath, szDrive, szDir, szFileName, szExt);
  m_strExeFilePath.Format (_T("%s%s"), szDrive, szDir);
  if (m_strExeFilePath.Right (11) == _T("\\.x64\\Debug\\")) {
    m_strExeFilePath = m_strExeFilePath.Left (m_strExeFilePath.GetLength () - 10);
  }
  else if (m_strExeFilePath.Right (13) == _T("\\.x64\\Release\\")) {
    m_strExeFilePath = m_strExeFilePath.Left (m_strExeFilePath.GetLength () - 12);
  }
  else if (m_strExeFilePath.Right (7) == _T("\\Debug\\")) {
    m_strExeFilePath = m_strExeFilePath.Left (m_strExeFilePath.GetLength () - 6);
  }
  else if (m_strExeFilePath.Right (9) == _T("\\Release\\")) {
    m_strExeFilePath = m_strExeFilePath.Left (m_strExeFilePath.GetLength () - 8);
  }

  // INIファイルのフルパス名の変更
  if (m_pszProfileName) {
    delete ((void*)m_pszProfileName);
    m_pszProfileName = NULL;
  }
  m_pszProfileName = new TCHAR[MAX_PATH + 1];
  if (m_pszProfileName == NULL) {
    AfxMessageBox (_T("メモリ不足です。"), MB_ICONSTOP);
    return FALSE;
  }
  memset ((void*)m_pszProfileName, 0, sizeof (TCHAR) * (MAX_PATH + 1));
    _sntprintf ((TCHAR*)m_pszProfileName, MAX_PATH, 
    _T("%s%s"), m_strExeFilePath, _T("Example.ini"));

  // (中略)

  return TRUE;
}

A problem of GetPrivateProfileString / WritePrivateProfileString

These function including in Win32API is very useful, but it has a problem that these function ignores the trailing space characters in the string, so it cuase a bug whenever read or write string which ends with space characters. If someone may restore a string with trailing space characters, you need to make your special GetPrivateProfileString and WritePrivateProfileString by yourself.

また、UTF-8のiniファイルは、ラテン文字・漢字・ハングル文字・キリル文字のGetPrivateProfileString/WritePrivateProfileStringがうまくいかないようです。しかし、UTF-16LE(BOM付き)ではうまくいくようです。この点は環境にもよりますので、各々動作確認してください。

VirtualStoreの問題にも気をつける必要があります。WindowsVista以降、Windowsのユーザーアカウント制御(UAC)機能により、c:\program files, c:\program files(x86), c:\windowsの中には、ファイルに書き込みをすることはできません。iniファイルばかりでなくゲームのセーブデータなど全ファイルについて、書き込みモードのfopenは、virtualstoreフォルダ上で行われます。UACをユーザーに解除させること、あるいはプログラム的に無効化することは可能ですが、セキュリティ上問題があります。

A note of fgets / fputs

初歩的な問題ですが、rtモードでfopenしたテキストファイルからfgetsすると、その文字列には最後に"\r\n"や"\r"や"\n"がくっついてきます。そのため、必要文字列をファイルから正しく獲得するには、最後に引っ付いているこれらを'\0'で置き換えるのを忘れないように注意します。一方、fputsでは"\r\n"や"\r"や"\n"はつかないので自前でつける必要があります。

A probrem of strcpy_s / wcscpy_s

使用領域をゼロで初期化して使っても、文字列の最後のヌル文字以降のゼロ初期化が不定値になるようです。したがって、これの続きにstrcatなどを使うと、たちまちバグとなります。既に自分でメモリを管理しているならば、strcpy / wcscpyのままにすることをお勧めします。

A probrem of mbstowcs_s / wcstombs_s

上記の問題に加え、途中で変換不能文字があった場合の動作が未定義です。また、改行コード\r,\nの変換が正しくないようです。既に正常に動作しているのであれば、mbstowcs / wcstombsのままにすることをお勧めします。

How to read / write UTF-16LE or UTF-8's text file (only for Unicode version)

UTF-16LEのテキストファイルをwchar_t配列に読み書きするには、fopen時に、modeの引数のところを、"rt, ccs=UTF-16LE" もしくは "wt, ccs=UTF-16LE" と指定します。UTF-8のテキストファイルをwchar_t配列に読み書きするには、fopen時に、modeの引数のところを、"rt, ccs=UTF-8" もしくは "wt, ccs=UTF-8" と指定します。mode引数にccsを何も指定しないとANSI仕様で読み書きするので、wchar_t配列への読み書きはうまくいきません。

CStdioFileをUnicodeに対応させる方法

CStdioFileはUnicodeに対応していないのでfopenとfcloseだけ手動でする必要があります。

FILE* pFile = _tfopen (strFileName, _T("rt, ccs=UTF-8"));
if (pFile == NULL) {
 /* エラー時の処理 */
}
CStdioFile theStdioFile (pFile);

...

fclose (pFile);

How to support multiple language in C/Win32 application - satellite DLL and I18N (Internationalization)

サテライトDLLとは、リソースのみを含むDLLです。プログラムを日本語・英語・中国語などに対応させるため、言語に依存したリソース(ビットマップ、アイコン、カーソル、ツールバー、メニュー、アクセラレーター、ダイアログ、文字列など)は専用のDLLに分離して記述します。言語別にサテライトDLLを用意し(例:ExampleJpn.dll, ExampleEnu.dll, ExampleChs.dll)、アプリケーションの初期化時に、所定言語のサテライトDLLを読み込みます。実行時にはサテライトDLLから文字列などを読み込みます。アプリケーションの終了時にはそのサテライトDLLを解放します。resource.hで各言語間のリソースIDを共通にしておけば、基本的にソースコードは言語に影響しません。

次の例は、上記の最小のWindowsアプリケーション(Hello World)を国際化した例です。プロジェクトは4つ必要です。ひとつは、Example.exe製作用で、Example.cとresource.hを使用。ひとつは、ExampleEnu.dll製作用で、ExampleEnu.rcとresource.hを使用。ひとつは、ExampleJpn.dll製作用で、ExampleJpn.rcとresource.hを使用。ひとつは、ExampleChs制作用で、ExampleChs.rcとresource.hを使用。resource.hは全プロジェクト共通なので、#includeでは相対パスを指定しておいてください。サテライトDLLでは、プロジェクトのプロパティで、全般→構成の種類を「ダイナミックリンクライブラリ(DLL)」にするとともに、リンカ→詳細→エントリポイントなしを「はい(/NOENTRY)」に設定してください。デバッグ版とリリース版の両方設定するのを忘れないでください。

reosurce.h (UTF-8, BOM付, 1行目はBOMを分離するため空行かコメントとする)


#define IDS_APPNAME              0x2001
#define IDS_MESSAGE              0x2002

ExampleEnu.rc (UTF-16LE, BOM付)

#include <windows.h>
#include "..\\resource.h"

STRINGTABLE DISCARDABLE {
  IDS_APPNAME            "Example"
  IDS_MESSAGE            "Hello World !"
}

ExampleJpn.rc (UTF-16LE, BOM付)

#include <windows.h>
#include "..\\resource.h"

STRINGTABLE DISCARDABLE {
  IDS_APPNAME            "例題"
  IDS_MESSAGE            "こんにちは、世界よ。"
}

ExampleChs.rc (UTF-16LE, BOM付)

#include <windows.h>
#include "..\\resource.h"

STRINGTABLE DISCARDABLE {
  IDS_APPNAME            "例题"
  IDS_MESSAGE            "您好,世界。"
}

Example.c (UTF-8, BOM付)

#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include "resource.h"

HINSTANCE g_hInstance;
HANDLE g_hResource;
TCHAR g_szExeFilePath[256];
TCHAR g_szIniFileName[256];
TCHAR g_szLanguage[256];
TCHAR g_szAppName[256];
TCHAR g_szMessage[256];

/* EXEファイルのあるパスを設定 */
void InitExeFilePath () {
  TCHAR szPath[_MAX_PATH + 1];
  TCHAR szDrive[_MAX_DRIVE + 1];
  TCHAR szDir[_MAX_DIR + 1];
  TCHAR szFName[_MAX_FNAME + 1];
  TCHAR szExt[_MAX_EXT + 1];
  long lLen = 0;
  memset (szPath, 0, sizeof (szPath));
  memset (szDrive, 0, sizeof (szDrive));
  memset (szDir, 0, sizeof (szDir));
  memset (szFName, 0, sizeof (szFName));
  memset (szExt, 0, sizeof (szExt));

  /* EXEファイル名をフルパスで取得 */
  GetModuleFileName (g_hInstance, szPath, _MAX_PATH);

  /* EXEファイルのあるディレクトリ名を設定 */
  _tsplitpath (szPath, szDrive, szDir, szFName, szExt);
  _sntprintf (g_szExeFilePath, 255, _T("%s%s"), szDrive, szDir);
  lLen = _tcslen (g_szExeFilePath);
  if (lLen >= 11 &&
    _tcsncmp (&g_szExeFilePath[lLen - 11], 
    _T("\\.x64\\Debug\\"), 11) == 0) {
    g_szExeFilePath[lLen - 11] = _T('\0');
  }
  else if (lLen >= 13 &&
    _tcsncmp (&g_szExeFilePath[lLen - 13], 
    _T("\\.x64\\Release\\"), 13) == 0) {
    g_szExeFilePath[lLen - 13] = _T('\0');
  }
  else if (lLen >= 7 &&
    _tcsncmp (&g_szExeFilePath[lLen - 7], 
    _T("\\Debug\\"), 7) == 0) {
    g_szExeFilePath[lLen - 7] = _T('\0');
  }
  else if (lLen >= 9 &&
    _tcsncmp (&g_szExeFilePath[lLen - 9], 
    _T("\\Release\\"), 9) == 0) {
    g_szExeFilePath[lLen - 9] = _T('\0');
  }
}


/* リソースDLLを読み込む */
BOOL LoadResourceDLL () {
  if (_tcscmp (g_szLanguage, _T("English")) == 0) {
    g_hResource = LoadLibrary (_T("ExampleEnu.dll"));
    if (g_hResource == NULL) {
      MessageBox (NULL, _T("ExampleEnu.dll Load Failed!"), 
      _T("Example"), MB_ICONSTOP);
      return FALSE;
    }
  }
  else if (_tcscmp (g_szLanguage, _T("Chinese")) == 0) {
    g_hResource = LoadLibrary (_T("ExampleChs.dll"));
    if (g_hResource == NULL) {
      MessageBox (NULL, _T("ExampleChs.dll Load Failed!"), 
      _T("Example"), MB_ICONSTOP);
      return FALSE;
    }
  }
  else {
    g_hResource = LoadLibrary (_T("ExampleJpn.dll"));
    if (g_hResource == NULL) {
      MessageBox (NULL, _T("ExampleJpn.dll Load Failed!"), 
      _T("Example"), MB_ICONSTOP);
      return FALSE;
    }
  }
  return TRUE;
}

/* リソースDLLを解放する */
void FreeResourceDLL () {
  if (g_hResource) {
    FreeLibrary (g_hResource);
  }
  g_hResource = NULL;
}

/* メイン関数 */
int WINAPI _tWinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
  g_hInstance = hInstance;

  /* exeファイルのあるパスを調べる */
  InitExeFilePath ();

  /* iniファイルから言語設定を読み込む */
  _sntprintf (g_szIniFileName, 255, 
  _T("%s\\%s"), g_szExeFilePath, _T("Example.ini"));
  GetPrivateProfileString (_T("Language"), _T("Language"), _T("Japanese"),
  g_szLanguage, 255, g_szIniFileName);

  /* 言語別サテライトDLLの読み込み */
  if (LoadResourceDLL () == FALSE) {
    return 0;
  }

  /* 本論 */
  LoadString (g_hResource, IDS_APPNAME, g_szAppName, 255);
  LoadString (g_hResource, IDS_MESSAGE, g_szMessage, 255);
  MessageBox (NULL, g_szMessage, g_szAppName, MB_ICONEXCLAMATION | MB_OK);

  /* 言語別サテライトDLLの解放 */
  FreeResourceDLL ();
  return 1;
}

Example.ini (UTF-16LE, BOM付, 1行目はBOMを分離するため空行とする)


[Language]
Language=Chinese

Execution Result

Language=English

Language=English

Language=Japanese

Language=Japanese

Language=Chinese

Language=Chinese

How to support multiple language in C++/MFC application - MFC's satellite DLL and I18N (Internationalization)

MFCを用いた場合、上述の対応に加え、MFCで定義しているリソース(例:"1から127までの数字を入力してください。")も日本語・英語・中国語などに対応させる必要があります。MFC内部では、mfcXXloc.dllという名前のdllを既定のサテライトDLLとして読み込むため、ひとつのパッケージにつきひとつの言語しか対応できません。解決方法として、MFCに付属の言語別サテライトDLLをすべて用意しておき、アプリケーションの初期化時に、リソースハンドルにターゲットのサテライトDLLをLoadLibraryし、すでに割り当てられているAfxGetMuduleState()->m_appLangDLLをFreeLibraryし、そのメンバ変数に先ほどのリソースハンドルを代入します。MFC以外の部分のサテライトDLLは、C/Win32APIの場合と同様な処理ですが、そのリソースハンドルをAfxSetResourceHandleする必要があります。アプリケーションの終了時にはそのリソースハンドルをFreeLibraryします。

// リソースDLLの読み込み
BOOL CExampleApp::LoadResourceDLL () {
  if (m_strLanguage == _T("English")) {
    m_hMFCResourceDLL = ::LoadLibrary (_T("MFC90Enu.dll"));
    if (m_hMFCResourceDLL == NULL) {
      ::MessageBox (NULL, _T("MFC90Enu.dll Load failed!"), 
      _T("Example"), MB_ICONEXCLAMATION);
      return FALSE;
    }
    m_hResourceDLL = ::LoadLibrary (_T("ExampleEnu.dll"));
    if (m_hResourceDLL == NULL) {
      ::MessageBox (NULL, _T("ExampleEnu.dll Load failed!"), 
      _T("Example"), MB_ICONEXCLAMATION);
      return FALSE;
    }
  }
  else if (m_strLanguage == _T("Chinese")) {
    m_hMFCResourceDLL = ::LoadLibrary (_T("MFC90Chs.dll"));
    if (m_hMFCResourceDLL == NULL) {
      ::MessageBox (NULL, _T("MFC90Enu.dll Load failed!"), 
      _T("Example"), MB_ICONEXCLAMATION);
      return FALSE;
    }
    m_hResourceDLL = ::LoadLibrary (_T("ExampleChs.dll"));
    if (m_hResourceDLL == NULL) {
      ::MessageBox (NULL, _T("ExampleChs.dll Load failed!"), 
      _T("Example"), MB_ICONEXCLAMATION);
      return FALSE;
    }
  }
  else {
    m_hMFCResourceDLL = ::LoadLibrary (_T("MFC90Jpn.dll"));
    if (m_hMFCResourceDLL == NULL) {
      ::MessageBox (NULL, _T("MFC90Jpn.dll Load failed!"), 
      _T("Example"), MB_ICONEXCLAMATION);
      return FALSE;
    }
    m_hResourceDLL = ::LoadLibrary (_T("ExampleJpn.dll"));
    if (m_hResourceDLL == NULL) {
      ::MessageBox (NULL, _T("ExampleJpn.dll Load failed!"), 
      _T("Example"), MB_ICONEXCLAMATION);
      return FALSE;
    }
  }

  // MFCの言語別リソースDLLはDLLMainで勝手に設定されてしまうので、
  // いったん解放してから指定したDLLを設定しなおす。
  AFX_MODULE_STATE* pState = AfxGetModuleState ();
  if (pState->m_appLangDLL) {
    ::FreeLibrary (pState->m_appLangDLL);
    pState->m_appLangDLL = NULL;
  }
  pState->m_appLangDLL = m_hMFCResourceDLL;

  // Exampleの言語別リソースDLLを設定する
  AfxSetResourceHandle (m_hResourceDLL);
  return TRUE;
}

// リソースDLLの開放
void CExampleApp::FreeResourceDLL () {
  if (m_hResourceDLL) {
    ::FreeLibrary (m_hResourceDLL);
  }
  m_hResource = NULL;
}

Execution Result

Language=English

Language=English

Language=Japanese

Language=Japanese

Language=Chinese

Language=Chinese

Extra : How to select network algorithm

リプライが必要な時点ですでにTCPを選択するべきです。自作のリプライ機構を実装してもTCPのパフォーマンスには適いません。

Extra : How to put dll file in 64bit Windows


(C)2000-2024 kuzu all rights reserved.