dポイントプレゼントキャンペーン実施中!

「WinMainCRTStartup の オーバーライド?」
_____________________________________________________________________________________

半月前、 http://oshiete.goo.ne.jp/qa/6949019.html 「フォーカス位置を取得したい」
で質問したものですが、少しは進んだのですが、困りました。

そこで、また質問させて頂きます。


【やりたいこと】できるだけ、MFCでつくったコードは、流用しつつ、最後にフォーカスしたオブジェクト名(クラス名?)を取得したいのですが・・・、

C言語の時は、int main()からプログラムを書くと…いう、うろ覚えですが、
MFCウィザードで作ったアプリでは、

CWinApp::InitInstance(); が入り口らしく、ソースコードにも記述されていましたが、

static LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM ); が使いたいが為に
「WinMainを実行したい」と思い、次に「WinMainCRTStartup」という言葉に辿り着き、

「WinMainをカスタマイズするには、WinMainが呼び出すCWinAppのメンバ関数(例:InitInstance等)
をオーバーライドして行います。」という言葉に辿り着きました。


要は、MFC使用でWinMainをカスタマイズしたいのですが、
「"MFC利用" WinMain VC++」などの言葉で、Web検索してみたのですが、
やはり、分からないです。

Enterキー、マウスのクリック、Escキーなどのキーボードイベントも取得したいですが、
今の所は、▼ここから

  case WM_COMMAND:
    switch (LOWORD(wp)) {
      case IDC_COMBO_OK_BOX:
        if (HIWORD(wp) == EN_SETFOCUS){
          KeywordDlg KeywordDlg_CLASS;
          KeywordDlg_CLASS.LastFocus = _T("OK_BOX");
          //MessageBox(NULL ,_T("OK_BOX"),MB_YESNOCANCEL);
        }
      case IDC_COMBO_NG_BOX:
        if (HIWORD(wp) == EN_SETFOCUS){
          KeywordDlg KeywordDlg_CLASS;
          KeywordDlg_CLASS.LastFocus = _T("NG_BOX");
        }
    }
    return 0;

▲ここまでのソースが通れば、良いです。

OKは、マッチする言葉、NGは、マッチしない文字列を入れたコンボボックスを
意味しています。

LastFocusは、文字型グローバル変数です。


【↓】(1) 「VC++のダイアログベースでAPIを使う方法はありますか?」
http://detail.chiebukuro.yahoo.co.jp/qa/question …

という、回答がまさに僕のやりたいことですが、もう少し、補足が欲しいところです。
何か分かりやすく書かれたサイトや参考書はないでしょうか?
(MFCを使う事、.NETなどのランタイムを必要としないもの、VC++に限るという観点から教えて下さい)


まだ、VC買って数ヶ月程度で、MSDNで調べても分からない事もあります。


【↓】(2) これもヒントになりそうですが・・・ちょっと長いです。
http://social.msdn.microsoft.com/Forums/ja-JP/vc …

【↓】(3) これは、ずばりだと思いますが、難しいです。
http://homepage1.nifty.com/kazubon/progdoc/tcloc …


VCで、MFC利用 でつくったものは エントリポイントがWinMainCRTStartupになる事は分かりました。
またWinMainにしたときは、エラーは出ませんでした。そもそも実行されていないだけかも。

後、気になった点は、2ちゃんねるのログで・・・
/entyオプションでエントリポイントを変えなさい
WinMainCRTStartupという名前の関数を作らないとだめなのでは?
・・・と書かれていた事ぐらいです。

回りに聞く人がいなくて困っています。トライ&エラーはしてるつもりです。

オーバーライドというのは、前にここでも質問しましたが、まだ自分のモノになったとは、
言えません。(教えて下さった方すみません)



【ファイル】FileListCreator.cpp
class CFileListCreatorDlg : public CDialogEx
{
private:
  static LRESULT CALLBACK WndProc( HWND, UINT, WPARAM, LPARAM );
  LRESULT WindowProc( HWND, UINT, WPARAM, LPARAM );
  //static unsigned __stdcall Thread( void * );

  //int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,PSTR lpCmdLine , int nCmdShow );
  void WINAPI WinMainCRTStartup(void);

};


【ファイル】FileListCreatorDlg.cpp
void WINAPI WinMainCRTStartup(void){

  HWND hwnd;
  MSG msg;
  WNDCLASS winc;

  HINSTANCE hInstance;

  hInstance = GetModuleHandle(NULL);

  winc.style    = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
  winc.lpfnWndProc  = (WNDPROC)WndProc;
  winc.cbClsExtra  = winc.cbWndExtra  = 0;
  winc.hInstance    = hInstance;
  winc.hIcon    = LoadIcon(NULL , IDI_APPLICATION);
  winc.hCursor    = LoadCursor(NULL , IDC_ARROW);
  winc.hbrBackground  = (HBRUSH)GetStockObject(WHITE_BRUSH);
  winc.lpszMenuName  = NULL;
  winc.lpszClassName  = TEXT("HELLO");

  CString szClassName = _T("TEST");
  CString szWindowText = _T("Hello,World");

  hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, szClassName, szWindowText,
0, CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);

  ShowWindow(hwnd, SW_SHOW);
  UpdateWindow(hwnd);

  while(GetMessage(&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  ExitProcess(msg.wParam);
}

【分からない所】

WndProc

に赤い波線が付いていて、
エラー  47  error C2065: 'WndProc' : 定義されていない識別子です。  
となっており、

static に変えているのに、エラーが起こります。

void WINAPI WinMainCRTStartup(void) にした時の疑問です。

_____________________________________________________________________

本題の質問ですが、MFCを利用しつつ(今まで作ったコードを残しつつ)
目的の機能が実装可能か?が、それだけでも知りたいです。教えてください!

A 回答 (3件)

子ウィンドウに対するハンドラが標準で用意されていないメッセージを処理したい場合は、PreTranslateMessageをし追加ます。


ダイアログの仮想関数追加でPreTranslateMessageを追加して、pMsgの内容から必要なメッセージだと判断した時に処理するようにします。
今回の場合は、

pMsg->message が WM_LBUTTONDOWN
pMsg->hwnd が m_xcCombo_OK_BOX.m_hWmd

と等しい場合に処理すればいいんじゃないでしょうか。
ただ、WM_LBUTTONDOWNで処理してしまうと、ドラッグで文字を選択した場合などは、問題があると思います。また、コンボボックスがWM_LBUTTONDOWNを処理してからでないと、キャレットの位置も変わっていないでしょうし。
WM_LBUTTONUPで処理を行えばいいような気がします。
    • good
    • 0
この回答へのお礼

(そのままですが・・・)
「ダイアログの仮想関数追加でPreTranslateMessageを追加 コンボボックス」
・・・で調べました。

貴重なキーワードを頂き、感謝しています。また、最後まで教えて下さり、ありがとうございました。
方向性が定まっていないで、とてつもなく面倒な事をするところでしたυ

それとは別に、一つ ミスタイプがありました。正しくは「->m_hWnd」ですね。
自分では思いつかないのに、あげ足取りですみません。

PreTranslateMessageは他にも応用が利きそうです。質問してよかったです。

以下、参考までに・・・

【KeywordDlg.h】
class KeywordDlg : public CDialogEx
{
  DECLARE_DYNAMIC(KeywordDlg)

public:
  KeywordDlg(CWnd* pParent = NULL); // 標準コンストラクター
  virtual ~KeywordDlg();
protected:
  virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV サポート
  
  // 生成された、メッセージ割り当て関数
  virtual BOOL OnInitDialog(); //追加2011.08.17
  virtual BOOL PreTranslateMessage(MSG* pMsg); //追加2011.09.02

  DECLARE_MESSAGE_MAP()

・・・
};

【KeywordDlg.cpp】
// PreTranslateMessage
BOOL KeywordDlg::PreTranslateMessage (MSG* pMsg)
{
  switch (pMsg->message)
  {
  //case WM_LBUTTONDOWN:
  //  break;
  case WM_LBUTTONUP://コンボボックスの▼が押された時のみ拾ってくれるみたいで、無くても良いみたいですυ
    //HWND m_hWnd;
    //if(pMsg->hwnd == GetDlgItem(IDC_COMBO_OK_BOX)->m_hWnd ){
    //  LastFocus = _T("OK_BOX");
    //  DWORD dwSel;
    //  if ((dwSel = ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->GetEditSel()) != CB_ERR){
    //    dwSel_OK = dwSel;
    //  }
    //  //MessageBox(_T("マウスが押されました"));
    //}
    break;
  case WM_MOUSEMOVE://カーソルの移動そのものを拾う //コンボボックスを横切っただけでも、拾われそうですが(?)、問題なしみたいです。
    if(pMsg->hwnd == GetDlgItem(IDC_COMBO_OK_BOX)->m_hWnd ){

      LastFocus = _T("OK_BOX");
        
      DWORD dwSel;

      if ((dwSel = ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->GetEditSel()) != CB_ERR){
        dwSel_OK = dwSel;
      }

      //MessageBox(_T("カーソルが移動しました"));
    }
    break;
  default:
    break;
  }


return CDialog::PreTranslateMessage (pMsg);
}

~ 一部抜粋 ~

void KeywordDlg::OnCbnEditchangeComboOkBox()
{
  LastFocus = _T("OK_BOX");

  DWORD dwSel;

  if ((dwSel = ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->GetEditSel()) != CB_ERR){
    dwSel_OK = dwSel;
  }
}

void KeywordDlg::OnCbnSetfocusComboOkBox()
{
  ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->SetEditSel(-1,-1);//選択なしで、最後の文字位置にカーソルを置く
}

void KeywordDlg::OnCbnKillfocusComboOkBox()
{
  LastFocus = _T("OK_BOX");
}

void KeywordDlg::OnBnClickedBtnAsterisk()
{
  InsertStrIntoKeywords(_T("*"));
}

void KeywordDlg::InsertStrIntoKeywords(CString str){
  CString ComboStr = _T("");

  DWORD dwSel;

  if (LastFocus == _T("OK_BOX")){
    ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->GetWindowText(ComboStr);
    dwSel = dwSel_OK;
    if (dwSel != CB_ERR){
      ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->SetWindowText(const_cast<LPTSTR>(static_cast<LPCTSTR>(ComboStr.Left(LOWORD(dwSel)) + str + ComboStr.Right(ComboStr.GetLength()-LOWORD(dwSel)))));
      dwSel_OK++; //連続して文字を挿入する場合を考慮
    }
  }else if (LastFocus == _T("NG_BOX")){
    ((CComboBox*)GetDlgItem(IDC_COMBO_NG_BOX))->GetWindowText(ComboStr);
    dwSel = dwSel_NG;
    if (dwSel != CB_ERR){
      ((CComboBox*)GetDlgItem(IDC_COMBO_NG_BOX))->SetWindowText(const_cast<LPTSTR>(static_cast<LPCTSTR>(ComboStr.Left(LOWORD(dwSel)) + str + ComboStr.Right(ComboStr.GetLength()-LOWORD(dwSel)))));
      dwSel_NG++; //連続して文字を挿入する場合を考慮
    }
  }else{
    dwSel = CB_ERR;
    return;
  }
}


(変数の追加で、カテゴリを「Control」すればいいと思うのですが、万が一今までの記述で
 不具合があるといけないので、今回は DDX_Control で設定した物は使わず、直で指定しています。)


◆Win32 メッセージ一覧
http://wisdom.sakura.ne.jp/system/winapi/windata …

たった、一文字を挿入したいだけなのに~ぃ と思いましたが、出来て良かったです!
本当にありがとうございましたm(_ _)m

しばらくして、締め切ります。

お礼日時:2011/09/02 07:55

ウィンドウハンドルがNULLってことは、m_xcCombo_OK_BOXがコントロールとうまく結びついていないということです。


DDXを使っているなら、DoDataExchange内のDDX_Controlの記述が正しいか確認してみてください。

この回答への補足

返事が遅くなってしまい、大変申しわけありません。
色々、思考錯誤したのですが、まだできません。しかし、僕のソフトにとっては
絶対にいる機能ではないです。ただ、勉強のため お付き合いくださるなら、うれしいです。

コンボボックスに文字を入力した直後に 「*」を挿入する機能はできました。
ありがとうございます。しかし、以下のようなケースでは「*」が思った位置に挿入できません。

その為、
◆コンボボックスで左マウスが押されたか?を拾いたいのですが、
(コンボボックスでWM_LBUTTONDOWNが押されたか?)

クリックメッセージハンドラ

void 【コンボボックス】.OnLButtonDown(UINT nFlags, CPoint point){}はないので、
コンボボックスの値が変わらず、マウスカーソルだけが動くときに、インデックスを
拾えないです。OnCbnEditchangeComboOkBox()の部分です。

エディットボックスならできるのかもしれませんが。


◆CComboBox クラス(でも、ちょっと難しいです↓後でやってみます。今は時間切れという感じです)
http://msdn.microsoft.com/ja-jp/library/12h9x0ch …

メモ WM_KEYDOWN メッセージと WM_CHAR メッセージを処理する場合は、コンボ ボックスのエディット コントロールとリスト ボックス コントロールをサブクラス化し、CEdit と CListBox からクラスを派生させ、その派生クラスにこれらのメッセージのハンドラーを追加する必要があります。 詳細については、 http://support.microsoft.com/default.aspx?scid=k … および「CWnd::SubclassWindow」を参照してください。


//「コンボ ボックスのエディット コントロールとリスト ボックス コントロールをサブクラス化し、CEdit と CListBox からクラスを派生させ、」という所がすぐには出来ません。

【KeywordDlg.h】

#include "afxwin.h"
// KeywordDlg ダイアログ

class KeywordDlg : public CDialogEx
{
・・・
public:
  CComboBox m_xcCombo_OK_BOX;
  CComboBox m_xcCombo_NG_BOX;

  DWORD dwSel_OK;
  DWORD dwSel_NG;
  CString LastFocus;

  void InsertStrIntoKeywords(CString str);
  afx_msg void OnBnClickedBtnAsterisk();
};


【KeywordDlg.cpp】マッチする文字を入れるOK_BOXの部分を主に載せます。
BOOL KeywordDlg::OnInitDialog(){
  //WndProc;
  LastFocus = _T("NotSelected");
  
  return true;
}

void KeywordDlg::OnCbnEditchangeComboOkBox()
{
  // TODO: ここにコントロール通知ハンドラー コードを追加します。
  LastFocus = _T("OK_BOX");

  DWORD dwSel;

  //if ((dwSel = m_xcCombo_OK_BOX.GetEditSel()) != CB_ERR){
  if ((dwSel = ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->GetEditSel()) != CB_ERR){
    dwSel_OK = dwSel;
  }

  CString tempStr;
  tempStr.Format(_T(" LOWORD(dwSel):%d \r\n HIWORD(dwSel):%d \r\n\r\n"),(int)LOWORD(dwSel),(int)HIWORD(dwSel));
  TRACE(tempStr);
//ここでは、値が変わらない&カーソルのみ動く…ということが、拾えない
}


void KeywordDlg::OnCbnSetfocusComboOkBox()
{
  // TODO: ここにコントロール通知ハンドラー コードを追加します。
  ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->SetEditSel(dwSel_OK,dwSel_OK);
}

void KeywordDlg::OnBnClickedBtnAsterisk()
{
  // TODO: ここにコントロール通知ハンドラー コードを追加します。
  InsertStrIntoKeywords(_T("*"));
}

void KeywordDlg::InsertStrIntoKeywords(CString str){

  CString ComboStr = _T("");

  DWORD dwSel;

  if (LastFocus == _T("OK_BOX")){
    ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->GetWindowText(ComboStr);
    dwSel = dwSel_OK;
    if (dwSel != CB_ERR){
      ((CComboBox*)GetDlgItem(IDC_COMBO_OK_BOX))->SetWindowText(const_cast<LPTSTR>(static_cast<LPCTSTR>(ComboStr.Left(LOWORD(dwSel)) + str + ComboStr.Right(ComboStr.GetLength()-LOWORD(dwSel)))));
    }
  }else if (LastFocus == _T("NG_BOX")){
    ((CComboBox*)GetDlgItem(IDC_COMBO_NG_BOX))->GetWindowText(ComboStr);
    dwSel = dwSel_NG;
    if (dwSel != CB_ERR){
      ((CComboBox*)GetDlgItem(IDC_COMBO_NG_BOX))->SetWindowText(const_cast<LPTSTR>(static_cast<LPCTSTR>(ComboStr.Left(LOWORD(dwSel)) + str + ComboStr.Right(ComboStr.GetLength()-LOWORD(dwSel)))));
    }
  }else{
    dwSel = CB_ERR;
    return;
  }

}


変数に入れてない、冗長で見にくいですが、お許し下さい。

後、「イベントハンドラの追加」でも#1に書かれている事はできますね。知りませんでした。

メンバ変数の追加ウィザードで、追加したときに、カテゴリを「Value」にしたのが、問題だったのか、
DDX_Controlの動作がうまく行かなかったのかもしれません。

補足日時:2011/09/01 11:03
    • good
    • 0

別に、そんな大変なことをしなくても、コンボボックスのCBN_SETFOCUSに対するハンドラを用意すればいいんじゃないですか?


数が多くて、1つ1つハンドラを作成するのが大変とかであれば、親ウィンドウ(ダイアログなど)のPreTranslateMessageで処理できるはずです。

MFCを使っていても、独自にCreateWindowでウィンドウを作成するとか、ウィンドウプロシージャを別のものにするとかはできますが、MFCの動作をよく理解していないと大変苦労します。MFCの枠組み内でできることは、利用したほうが無難です。

この回答への補足

雷マークを押して、追加するのですね。どおりて、簡単すぎてググっても
ダメなはずです。(遅くなってすみません。その他の部分をずっと考えてました。)

とりあえず、フォーカスを検知はできましたが、下のコードすら通らないです。
僕のMFCライブラリは壊れてしまったのでしょうか?


>  FileListCreator.exe!CComboBox::GetEditSel() 行 854 + 0x2d バイトC++

+  m_hWnd  0x00000000 {unused=??? }HWND__ *
+  this  0x0292ae18 {CComboBox hWnd=0x00000000}  const CComboBox * const

以下のようなエラーが出て、中断せざるを得ません。続けると、ちゃんと文字も入っているのですが、


Get,Setする所が悪さをしているように思います。
 GetEditSel()
 m_xcCombo_OK_BOX.GetWindowTextW(ComboStr);

Setするのは、他のボタンを押したときです。カーソル位置に一文字「*」などを挿入したいです。
LeftとRightを使うだけなので、カーソルが何文字目にあるか?さえ分かれば作れます。

くだらない(?)機能にお手間を取らせてすみません。


BOOL KeywordDlg::OnInitDialog(){
  //WndProc;
  LastFocus = _T("NotSelected");

  return true;
}

void KeywordDlg::OnCbnEditchangeComboOkBox()
{
  // TODO: ここにコントロール通知ハンドラー コードを追加します。
  LastFocus == _T("OK_BOX")

  DWORD dwSel;

  // Set the selection to be all characters after the current selection.
  if ((dwSel = m_xcCombo_OK_BOX.GetEditSel()) != CB_ERR){
    //m_xcCombo_OK_BOX.SetEditSel(HIWORD(dwSel), -1);
    
    //CString tempStr;
    //tempStr.Format(_T("%d"),(int)dwSel);

    //MessageBox(tempStr);

    dwSel_OK = dwSel;//マッチキーワードボックス内の文字位置をグローバル変数に退避
  }
}


途中報告でした。回答ありがとうございました。

補足日時:2011/08/31 16:29
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!