ホーム » 2023 » 4月

月別アーカイブ: 4月 2023

2023年4月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

カテゴリー

アーカイブ

ブログ統計情報

  • 95,591 アクセス


CWinApp::m_pszRegistryKey

VC で MFC プロジェクトを作成すると,CXxxApp::InitInstance が次の様になる.
SetRegistryKey(_T(“アプリケーション ウィザードで生成されたローカル アプリケーション”));
ドキュメントにある様に,会社名などを指定する.
The registry key is usually the name of a company.


あまりやってはいけないことだろうと思うが,ドキュメントを見ていると,その設定した値 CWinApp::m_pszRegistryKey を直接変更できる.
そこのサンプルコードが… 何年も前から変わっていない.
CWinApp::m_pszRegistryKey


CWinApp::SetRegistryKey の動作は,新しいキーに変更して m_pszPforileName も変更している.
CWinApp::SetRegistryKey


MFC を使用するコンソール AP での SetRegistryKey の使用は次の所.
https://dev.mish.work/wordpress/2021/07/21/console-ap-setregistrykey/

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

[MyDNS.JP] IPアドレスの通知が …

MyDNS.JP から次の様なメールが …
[MyDNS.JP] IPアドレスの通知が8日間確認できません。
「コントロールパネル」-「外部アクセス」-「DDNS」を見ると,前回の更新が 2023/04/19 のまま.
「今すぐアップデート」を押しても変わらず.
対象のものを選択して「テスト接続」しても,エラーになってしまう.


ログを確認すると,先日 DSM の更新があり,その後うまく機能していない?
もう一つの NAS はうまく動作しているので,対象の NAS を「再起動」.
通知はされた.「今すぐアップデート」も効いている.
あとは,定期的に更新されるか …


2023/05/10
その後うまく更新(通知)できている.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

OpenMP Fatal User Error 1002

OpenMP が有効な時でも動作する様にテストしていると…
Fatal User Error 1002: A ‘#pragma omp critical’ is illegally nested in one of the same name
Fatal User Error 1002: A '#pragma omp critical' is illegally nested in one of the same name
元々あった次のコードに #pragma omp critical としたことによるもの.

tstring	Get_Self_Original	(void)
{
	static	bool	Yet = true ;
	static	tstring	Self_original ;
	if (Yet) {
		tstring	self_name = ::Get_module_name() ;
		Self_original = Get_OriginalFilename(self_name.c_str()) ;
		if (Self_original.empty()) {
			Self_original = ::Path_GetName(self_name) ;
			}
		Yet = false ;
		}
	return	Self_original ;
	}

次の様に並列処理可能にすることで動作する様にはなるが …

tstring	Get_Self_Original	(void)
{
	#ifdef	_OPENMP
	//	#pragma	omp	critical	(_Get_Self_Original_)
	#endif
	{
	#ifdef	_OPENMP
			bool	Yet = true ;
			tstring	Self_original ;
	#else
		static	bool	Yet = true ;
		static	tstring	Self_original ;
	#endif
		if (Yet) {
			tstring	self_name = ::Get_module_name() ;
			Self_original = Get_OriginalFilename(self_name.c_str()) ;
			if (Self_original.empty()) {
				Self_original = ::Path_GetName(self_name) ;
				}
			Yet = false ;
			}
		return	Self_original ;
		}
	}

以前にも同じようなことをやっていた.
https://dev.mish.work/wordpress/2010/03/31/openmp-error-1002/


2023/04/27
この関数は exe の起動直後などに一度呼ばれることを意図しているので,critical をもっと局所的に.

tstring	Get_Self_Original	(void)
{
	static	bool	Yet = true ;
	static	tstring	Self_original ;
	if (Yet) {
		#ifdef	_OPENMP
			#pragma	omp	critical	(_Get_Self_Original_)
		#endif
		{
			tstring	self_name = ::Get_module_name() ;
			Self_original = Get_OriginalFilename(self_name.c_str()) ;
			if (Self_original.empty()) {
				Self_original = ::Path_GetName(self_name) ;
				}
			Yet = false ;
			}
		}
	return	Self_original ;
	}

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

C++ メンバ関数テンプレート

.ini に対してのアクセスは関数として用意した が,今度はレジストリ.


クラスとして実装して,基本的な動作は何とかできた.
さらに .ini と同様に,文字列としてアクセスする部分を呼出す関数をテンプレートに…
と思って書き始めたが,今まで使ってなかったのか書き方がわからない.
検索すると,次の所があり参考にさせてもらった.
メンバ関数テンプレート | Programming Place Plus C++編【言語解説】 第33章
メンバー関数テンプレート


特に通常の関数テンプレートと書き方は変わらない.

	template	<typename	T>	T   	get	(   LPCTSTR ent,const T& def)	{
		tstring	dst = ::To_tstring(def) ;
		tstring	str = this->get(ent,dst.c_str()) ;
		T   	val ;
		::string_to(str.c_str(),&val) ;
		return	val ;
		}

	template	<typename	T>	bool	set	(   LPCTSTR ent,const T& val)	{
		tstring	str = ::To_tstring(val) ;
		return	this->set(ent,str.c_str()) ;
		}

メンバ関数テンプレート

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

C++ 戻り値の異なる関数 template

先日からやっている .ini やレジストリにアクセスする関数.
MFC の CWinApp::GetProfileString , CWinApp::WriteProfileString にあたる部分は目途がついた.
それで,それらを呼出す部分.前に作成したものもそうだったが,型ごとにクラスの関数を定義していた.
レジストリにアクセスするクラス
これらをもう少し簡単にできないかと…


set の方は,特に難しい所はない(::To_tstring は,文字列に変換する関数として用意している).

template	<class T>	bool	INI_set	(LPCTSTR sec,LPCTSTR ent,const T& val)
{
	tstring	ini = ::INI_get_module_ini() ;
	tstring	str = ::To_tstring(val) ;
	return	::INI_set(ini.c_str(),sec,ent,str.c_str()) ;
	}

get の場合,文字列から変数に変換する方法をどうするか?
例えば,atoi や atof ,他にも 4 つの整数の文字列を RECT に変換するなど.
検索すると template で可能みたい だが…
よくわからなかったので,簡単な方法にした.

inline	RECT	To_RECT		(LPCTSTR str)
{
	RECT	rect	=	{	0	} ;
	{
		v_tstring	str_ary = ::String_SplitSpace(str,_T(" ,\t\r\n")) ;
		if (0 < str_ary.size())	{	rect.left	= ::ttoi4(str_ary[0]) ;	}
		if (1 < str_ary.size())	{	rect.top	= ::ttoi4(str_ary[1]) ;	}
		if (2 < str_ary.size())	{	rect.right	= ::ttoi4(str_ary[2]) ;	}
		if (3 < str_ary.size())	{	rect.bottom	= ::ttoi4(str_ary[3]) ;	}
		}
	return	rect ;
	}
inline	bool	string_to	(LPCTSTR str,RECT*  rect_)	{	*rect_ = ::To_RECT (str) ;	return	true ;	}
inline	bool	string_to	(LPCTSTR str,POINT* point)	{	*point = ::To_POINT(str) ;	return	true ;	}
inline	bool	string_to	(LPCTSTR str,SIZE*  size_)	{	*size_ = ::To_SIZE (str) ;	return	true ;	}

それぞれの型に合わせた関数を呼べるようになったので template に.

template	<class T>	T	INI_get	(LPCTSTR sec,LPCTSTR ent,const T& def)
{
	tstring	ini = ::INI_get_module_ini() ;
	tstring	dst = ::To_tstring(def) ;
	tstring	str = ::INI_get(ini.c_str(),sec,ent,dst.c_str()) ;
	T	val ;
	::string_to(str.c_str(),&val) ;
	return	val ;
	}

これで,次の様な使い方ができる様になる.

{
	POINT	point = ::POINT_set(   10,   20) ;
	SIZE	size_ = :: SIZE_set( 1111,  525) ;
	RECT	rect_ = :: RECT_set(point,size_) ;
	{
		::INI_set(_T("test"),_T("rect_"),rect_) ;
		::INI_set(_T("test"),_T("point"),point) ;
		::INI_set(_T("test"),_T("size_"),size_) ;
		std::tout << ::To_tstring(::INI_get(_T("test"),_T("rect_"),rect_)) << std::endl ;
		std::tout << ::To_tstring(::INI_get(_T("test"),_T("point"),point)) << std::endl ;
		std::tout << ::To_tstring(::INI_get(_T("test"),_T("size_"),size_)) << std::endl ;
		}
	}

::INI_set ,::INI_get の動作テスト

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

C++ NonCopyable

MFC を使用しないコードに書き直していて,代入できない構造体が欲しくなった.
オリジナルのコードは 20 年以上前のもので,CRegKey が簡単には使えなかった?頃.


C++ クラス 代入できなくする」で検索.
コピー禁止を徹底させるNoncopyableクラス
More C++ Idioms/コピー禁止ミックスイン(Non-copyable Mixin)
明示的に既定された関数および削除された関数
MFC の CObject も同様と思いソースを見ると,やはり private になっている.
MFC CObject 代入の禁止
C++11 以降では =delete も使える.
関数のdefault/delete宣言
wiki C++11


これらを調べていて,次の所を見つけた.
旧時代のC言語を使うのはそろそろやめよう。
2016年、C言語はどう書くべきか (前編)
2016年、C言語はどう書くべきか (後編)
幾つかは既に意識しているが,Windows に依存する部分はなかなかできてない.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

RegOpenKeyEx REGSAM

レジストリアクセスのコードを書き直していて,::RegOpenKeyExsamDesired を調べてみた.

KEY_READ              (0x20019)	0010 0000 0000 0001 1001
KEY_WRITE             (0x20006)	0010 0000 0000 0000 0110
KEY_EXECUTE           (0x20019) 0010 0000 0000 0001 1001

KEY_QUERY_VALUE        (0x0001)	     0000 0000 0000 0001
KEY_SET_VALUE          (0x0002)	     0000 0000 0000 0010
KEY_CREATE_SUB_KEY     (0x0004)	     0000 0000 0000 0100
KEY_ENUMERATE_SUB_KEYS (0x0008)	     0000 0000 0000 1000
KEY_NOTIFY             (0x0010)	     0000 0000 0001 0000

KEY_CREATE_LINK        (0x0020)	     0000 0000 0010 0000
KEY_WOW64_64KEY        (0x0100)	     0000 0001 0000 0000
KEY_WOW64_32KEY        (0x0200)	     0000 0010 0000 0000

KEY_ALL_ACCESS        (0xF003F)	1111 0000 0000 0011 1111

ソースを見た方が早かったか?
RegOpenKeyEx REGSAM


読み込み時,KEY_READ の方が速いなどはあるのか?それともファイルアクセスなどと同じ?

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

GetMonitorInfo

2002/08 に,マルチディスプレイ対応のコードを書いている.
今回,高 DPI 対応やディスプレイ位置が変わった時などのためもう一度…


ChatGPT で.
ChatGPT  GetMonitorInfo 使い方サンプル
そのままでは VC 6 ではうまくビルドできなかったので,VC 8 で.
printf を使用しているので #include <cstdio> が必要.
そのまま実行すると 1920×1080 となる.
GetMonitorInfo
高 DPI スケール設定を「アプリケーション」とすると,3840×2160 .


2023/04/14
以前のコードを見ると,プライマリのみの情報は ::SystemParametersInfo などを使用している.
これで求められる SPI_GETWORKAREA は,::GetMonitorInfo で戻されるものと同じ?

システム     0  61 1920 1080
アプリケーション 0 122 3840 2160

::GetSystemMetrics(SM_?VIRTUALSCREEN) は,-1920 0 .
::GetSystemMetrics(SM_C?VIRTUALSCREEN) は,上をずらしているので少し異なる.

システム     3840 1210
アプリケーション 5760 2160

Z170S0  ディスプレイの配置
https://learn.microsoft.com/ja-jp/windows/win32/gdi/the-virtual-screen

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

MFC タイトルバーの変更

ダイアログベースであれば C…Dlg::OnInitDialog() に次の様なコードを追加.

	{
		tstring	str_title = ::GetWindowText(this->GetSafeHwnd()) ;
		       	str_title+= _T(" ") + ::Get_ModuleVersion() + ::Get_BuildStrMSC() ;
		SetWindowText(str_title.c_str()) ;
		}

SDI や MDI の場合は,
MainFrm.h に OnUpdateFrameTitle を追加.

	virtual void OnUpdateFrameTitle (BOOL bAddToTitle);

MainFrm.cpp に次の様な OnUpdateFrameTitle を追加.

//	SDI
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
	CFrameWnd::OnUpdateFrameTitle(bAddToTitle) ;
	{
		tstring	str_title = ::GetWindowText(this->GetSafeHwnd()) ;
		       	str_title+= _T(" ") + ::Get_ModuleVersion() + ::Get_BuildStrMSC() ;
		SetWindowText(str_title.c_str()) ;
		}
	}
//	MDI
void CMainFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
	CMDIFrameWnd::OnUpdateFrameTitle(bAddToTitle) ;
	{
		tstring	str_title = ::GetWindowText(this->GetSafeHwnd()) ;
		       	str_title = ::MDI_Add_VerBuildStr(str_title.c_str()) ;
		SetWindowText(str_title.c_str()) ;
		}
	}

S_asZ のタイトルバーにバージョンなどの付加

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

高 DPI CToolBar のリサイズ

CToolBar を使用した 自前のコード
CDialogBar や CStatusBar はスケーリングしてくれるのに,CToolBar は対応しない?
以前は設定などで変更可能なコードにしていたが,DPI を求める方法に.

	if (newExtendSize == CSize(0,0)) {
		double	dpi_s = ::GetDPI_scale(toolBar->GetSafeHwnd()) ;  // ::GetDpiForWindow()/96.
		if (dpi_s > 1) {
			CToolBarCtrl&	tbCtrl = toolBar->GetToolBarCtrl() ;
			CSize	orgSize = tbCtrl.GetButtonSize() ;
			CSize	newSize = orgSize ;
			newSize.cx = int(orgSize.cx*dpi_s) ;
			newSize.cy = int(orgSize.cy*dpi_s) ;
			newExtendSize.cx = newSize.cx - orgSize.cx ;
			newExtendSize.cy = newSize.cy - orgSize.cy ;
			resizeType = 'R' ;
			}
		}

あとは既存コードの,ビットマップのリサイズと CToolBar::SetSizes を呼出せば良さそう.


CMFCToolBar は DPI を正しく処理しているみたいで,その部分のコード.
…\VC\…\atlmfc\src\mfc\afxtoolbar.cpp の CMFCToolBar::LoadToolBarEx .
CMFCToolBar::LoadToolBarEx  SetSizes


まだ幾つか問題はあるが,今回の対応部分としてはこれで良さそう.
ToolBar::Resize のコードを変更


更にツールバー上のコンボボックスのスケーリングは次の様にした.

	{
		int		width = 50 ;
		{
			double	dpi_s = 1. ;
			    	dpi_s = ::GetDPI_scale(AfxGetMainWnd()->GetSafeHwnd()) ;
			    	dpi_s = ::GetDPI_scale(    m_wndToolBar.GetSafeHwnd()) ;
			    	dpi_s = ::GetDPI_scale(           this->GetSafeHwnd()) ;
			if (dpi_s > 1.) {
				width = int(width*dpi_s) ;
				}
			}
		m_wndToolBar.SetButtonInfo  (19,ID_COMBO_ANGLE,TBBS_SEPARATOR,width) ;
		CRect	rectCombo ;
		m_wndToolBar.GetItemRect    (19,&rectCombo) ;
		rectCombo.top = 1 ;
		rectCombo.bottom = rectCombo.top + 300 ;
		if (!m_ComboAngle.Create(CBS_DROPDOWN | WS_VSCROLL | WS_VISIBLE,rectCombo,&m_wndToolBar,ID_COMBO_ANGLE)) {
			TRACE0("Failed to ComboBox\n");
			return -1;
			}
		m_ComboAngle.SetFont(m_wndToolBar.GetFont()) ;
		m_ComboAngle.AddString(_T(" 0.")) ;
		m_ComboAngle.AddString(_T("10.")) ;
	//	...
		m_ComboAngle.AddString(_T("90.")) ;
		}
Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

drawlog が更新されない?

Web サーバ管理用の,アクセスログの表示が更新されなくなった.
DS220  Web サーバへのアクセスログ
/tmp/ がいっぱいになったかと思い,解放したが変わらず.
それならと思い NAS を再起動したが,それでも変わらない.


不正なアクセスにより,ログにゴミが入ったみたいで,そこで表示データの生成が止まっていた.
ログに "\0" が入っている

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

高 DPI テスト exe

先日の exe をテストしていると…
TToPA.exe を「システム(拡張)」で実行


高 DPI をテストするために,次の様なコードの exe を作成.

void CT_aesDlg::OnDropFiles(HDROP hDropInfo) 
{
	v_tstring	drop_files = ::DropFilesTo(hDropInfo) ;
	for (size_t index=0 ; index<drop_files.size() ; index++) {
		tstring	drop_file = drop_files[index] ;
		tstring	ext = ::Path_GetExtLow(drop_file) ;
		if (ext != _T("exe"))	{	continue ;	}
		{
			S_Exec	se ;
			se.SetFile(drop_file.c_str()) ;
			se.Execute() ;
			}
		}
	CDialog::OnDropFiles(hDropInfo);
	}

この exe に,他の exe をドロップして起動すると,「高 DPI スケール設定」が引き継がれる.
「高 DPI スケール設定」
これらの設定は,次の所に持っている?

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers
   A	~ HIGHDPIAWARE	
   S	~ DPIUNAWARE	
   E	~ GDIDPISCALING DPIUNAWARE	

HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers


それぞれを実行すると表示が異なることはわかるが…
HiDPI テスト exe
左から,「システム」,「システム(拡張)」,「アプリケーション」.


「システム」と「システム(拡張)」を区別する方法がわからない.
また「システム(拡張)」.exe で,何かの情報の取得が違っていて 表示が正しくない ものと思う.


::GetDeviceCaps の情報の表示は dc.DrawText(str,rect,DT_LEFT) としている.
この時,表示するフォントを指定していないため,「アプリケーション」では小さくなってしまう.
CFont::CreatePointFontIndirect
CFont::CreatePointFont などを呼ぶことで対応可能.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.

tcsncpy_s Buffer is too small

先日更新した ツール をテストしていると,フォントによりアプリケーションエラー?となってしまう.
デバッガで追いかけると,
—————————
Microsoft Visual C++ Runtime Library
—————————
Debug Assertion Failed!
Program: c:\Temp\i_Tools\TToPA\Debug.120\TToPA.exe
File: f:\dd\vctools\crt\crtw32\h\tcsncpy_s.inl
Line: 62
Expression: (L"Buffer is too small" && 0)
For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.
(Press Retry to debug the application)
—————————
中止(A) 再試行(R) 無視(I)
—————————
tcsncpy_s Buffer is too small
CRichEditCtrl を使用した CHARFORMATW の szFaceName の指定がうまくなかった.
::TcsNCpy(cf.szFaceName,LF_FACESIZE-1,faceName,LF_FACESIZE-1) ;
::TcsNCpy は,幾つかの環境でビルドできる様にしたもので,今回の場合は ::tcsncpy_s と同様もの.
faceName に与えたものが L"Bahnschrift Light SemiCondensed" で,コピー先のバッファが足りないため.
正しくは,
::TcsNCpy(cf.szFaceName,LF_FACESIZE-0,faceName,LF_FACESIZE-1) ;


これらの動作は,LOGFONT の時にもよく利用する.
既存のコードを検索してみると,LF_FACESIZE を正しく指定できていた.
::TcsCpy(lf.lfFaceName,LF_FACESIZE,tstring(faceName).substr(0,LF_FACESIZE-1).c_str()) ;
与えるデータは LF_FACESIZE-1 としている.

Is this 投稿 useful? Useful Useless 0 of 0 people say this 投稿 is useful.