65.9K
CodeProject 正在变化。 阅读更多。
Home

45 天系列:CodeProject VC++ 论坛问答 - VI

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (14投票s)

2011年7月12日

CPOL

27分钟阅读

viewsIcon

34989

VC++ 论坛的问答集合。

引言

没什么特别的,和往常一样……这篇文章是对 Code Project Visual C++ 论坛上提出的问题和答案的汇编。此外,这是45天系列文章的第四篇。希望我能继续带来更多内容。

感谢论坛上的人们为帮助同伴解决问题所做的贡献。我修改了一些原始评论以适应/保持文章的风格(为此表示歉意)。如果我遗漏了什么,请随时给我发邮件或在底部留言。

内容 


MFC

MFC01 如何避免 MFC 扩展 DLL 中的资源冲突?
MFC02 CListControl 的最大行数和最大列数是多少?
MFC03 在列表控件中使用滚动条时,如何知道向上滚动了多少行?
MFC04 如何在单个视图中平铺多个窗口?
MFC05 如何使用 COleDataTime 查找当月的第二个星期六?
MFC06 如何删除 ListCtrl 行中显示的虚线灰色边框?
MFC07 获取当前活动子框架窗口/当前活动视图/或文档的最佳方法是什么?
MFC08 如何将 CString 值转换为十六进制?
MFC09 如何识别在树形控件中单击的 hashbutton 或项目标签?
MFC10 向量和 CArray 之间有什么重大区别?
MFC11 如何使用单个函数展开或折叠树形控件中的所有子项目?
   

WINDOW32

 
Win3201 如何解决此错误信息:“_WIN32_WINNT 设置与 _WIN32_IE 设置冲突”?
Win3202 是否有可以导入注册表文件的 API 到注册表编辑器?
Win3203 我需要对 HKEY_LOCAL_MACHINE 中的每个人拥有完全控制权限,如何通过源代码获得?
Win3204 如何删除 AllUserProfile 目录中的文件?
Win3205 如何暂停当前应用程序,直到生成的进程完成任务并退出?
Win3206 如何设置带有默认字体名称和大小的系统字体对话框?
Win3207 如何获取系统时钟样式,例如是 12 小时制还是 24 小时制?
Win3208 如何将局域网中所有 PC 的启动和关闭时间记录到文件中?
Win3209 如何在 SDI 应用程序中从框架窗口中删除最大化按钮?
Win3210 是否有其他函数可以允许在文件复制期间显示自定义进度条?
Win3211 如何获取自身的进程 ID 和内存使用情况?
Win3212 如何处理跨进程事件?
Win3213 如何确定应用程序中正在运行的线程?
Win3214 如何从资源 DLL 加载对话框?
   

通用

 
GEN01 是否有可以检索正在运行的窗口应用程序类名的应用程序?
GEN02 Linux 中是否有 UrlDownloadToFile() 的等效函数?
GEN03 PE 头中的哪个字段指示是否为有效的 PE 文件?
GEN04 从 Visual C++ 6.0 迁移到 Visual C++ .NET 时有哪些主要变化?
GEN05 如果堆上的内存被引用持有,如何删除它?
GEN06 任何 PC 中独一无二的是什么?
GEN07 如何拆分 DWORD 值?
GEN08 从 VS6 迁移到 Vs2005 时,静态库会发生什么变化?
GEN09 从 VS6 迁移到 Vs2005 时,动态链接库会发生什么变化?
GEN10 如何将泛型指针传递给带有 void** 参数的函数?
GEN11 类型转换:我总是对静态转换、动态转换和 reinterpret 转换感到困惑?请解释一下?
GEN12 如何创建一个透明窗口?
GEN13 如何在 WINDOWS 上使用 C++ 获取主板序列号?
GEN14 哪个更好?返回引用还是值?
GEN15 new 函数和 malloc 函数有什么区别?
GEN16 我创建了一个新的对话框资源。现在我想将其从项目中删除。如何操作?
GEN17 内联和宏在速度和执行方面有什么区别?
GEN18 如何将十六进制转换为 CString?
GEN19 如何避免命名空间冲突?
GEN20 如何远程读取 Windows 7 注册表?

有趣

 
FUN01 我想在互联网上搜索音乐,但我不知道该怎么做?
FUN02 不是对您问题的回答,但是当您说“请多多包涵”时……
FUN03 是否可以编写一个将今天的日期添加到 jpg 照片的代码?
FUN04 我经历过异步磁盘 I/O 不可靠。我说的对吗?
FUN05 如何比较两个音频文件?
   
   

答案

 

MFC

 
   
问题(MFC01) 如何避免 MFC 扩展 DLL 中的资源冲突?
完整问题 多年来,我编写了许多 MFC 扩展 DLL,其中包含资源(主要是对话框),这些资源可以从主应用程序以及 DLL 本身的代码中访问。
我偶尔会遇到资源 ID 与主应用程序冲突的问题,为了避免这种情况,我总是尝试在每个 DLL 中分配不同的资源 ID 范围。这工作得很好,但管理起来越来越困难,我认为一定有更优雅的方法来确保可以访问正确的资源。
答案 我一直使用 AfxSetResourceHandle()。在访问资源之前调用一次,然后在之后重置句柄。当然,您需要知道哪个 DLL 包含您的资源。根据您的架构,这可能非常简单,或者几乎不可能。一种解决这些问题的方法是从 DLL 导出工厂函数来创建对话框等。
 
问题(MFC02) CListControl 的最大行数和最大列数是多少?
答案 由于文档没有说明最大限制,我猜它要么取决于可用的进程内存,要么取决于 int 变量可以保存的最大值,因为索引使用 int 变量指定。
 
问题(MFC03) 在列表控件中使用滚动条时,如何知道向上滚动了多少行?
答案 假设列表控件处于报表视图中,请尝试处理列表控件的 LVN_ENDSCROLL 通知。使用 GetTopIndex() 获取顶部可见项目的索引。那么多行将位于顶部。
 
问题(MFC04) 如何在单个视图中平铺多个窗口?
答案 有像 CMDIFrameWnd::MDICascadeCMDIFrameWnd::MDITile 这样的方法可以为您完成此操作。还有名为 CascadeWindowsTileWindows 的 API 可以执行相同的操作。
 
问题(MFC05) 如何使用 COleDataTime 查找当月的第二个星期六?
答案 答案#1
  1. 将 COleDataTimeSpan 对象(天)设置为一天
  2. 将 COleDataTime 对象(日期)设置为所需年份的所需月份
  3. 构建日期递增循环 (date += day)
  4. 当 (sat) != date.GetDayOfWeek() 时循环两次
  5. 现在您 (date) 位于第二个星期六
答案#2
  COleDateTime dt(year,month,1,0,0,0);
  int add = 14 - dt.GetDayOfWeek();
  dt += add;
 
 
问题(MFC06) 如何删除 ListCtrl 行中显示的虚线灰色边框?
答案 虚线构造了焦点矩形,这对于使用键盘导航 GUI 的人来说至关重要的信息。要删除它,您可以 自定义绘制[^] 列表。
 
问题(MFC07) 获取当前活动子框架窗口/当前活动视图/或文档的最佳方法是什么?
答案 这是我们在我的 MDI 应用程序中使用的方法
// First get the MDI frame window
CMDIFrameWnd* pFrame = (CMDIFrameWnd*)AfxGetApp()->m_pMainWnd;
 
// !Here we get the active MDI child window!
CMDIChildWnd* pChild = (CMDIChildWnd*)pFrame->GetActiveFrame();
 
// Alternatively you can use    
// CMDIChildWnd* pChild = pFrame->MDIGetActive();
       
// !Get the active view attached to the active MDI child window!
CMyView* pView = (CMyView*)pChild->GetActiveView();
 
// !Get the active document!
CMyDoc* pDoc = (CMyDoc*)pChild->GetActiveDocument();
 
问题(MFC08) 如何将 CString 值转换为十六进制?
答案 答案#1
ULONG nVal;
CString str = _T("0x4335");
nVal = _tcstol(str, NULL, 0)
答案#2
在 C++ 中使用标准 C++ 库的设施
#include <sstream>
int GetIntFromHexString(const char* HexString)
{
	int val;
	std::istringstream(HexString) >> std::hex >> val;
	return val;
}
 
问题(MFC09) 如何识别在树形控件中单击的 hashbutton 或项目标签?
答案 您可能需要在处理 NM_DBLCLK 通知时执行类似的操作
case NM_DBLCLK:
	// Get the double clicked item
	HTREEITEM hItem = (HTREEITEM)SendMessage(((LPNMHDR) lParam)->hwndFrom,TVM_GETNEXTITEM,TVGN_CARET,0);
	TVITEM tvi;
	tvi.mask  = TVIF_PARAM;
	tvi.hItem = hItem;
	// Get the lParam value asociated with this item 
	SendMessage(((LPNMHDR) lParam)->hwndFrom,TVM_GETITEM,0,(int)&tvi);
	break;
 
tvi.lParam 现在包含您在将项目添加到树视图时添加到项目的 lParam 值。您可以使用此值提供有关内容的信息,例如它是 hashbutton 还是项目标签。
 
问题(MFC10) 向量和 CArray 之间有什么重大区别?
答案 答案#1
这与现代性有关;MFC 集合已过时

STL 是标准模板库。
STL 更快(在发布模式下测试,而不是调试模式)并提供更大的灵活性(算法、迭代器……)

这里有一个讨论链接
http://www.codeguru.com/forum/archive/index.php/t-391319.html[^]

……坦率地说,团队会给您相同的答案。MFC 集合类仅为了向后兼容而存在。C++ 有集合类的标准,那就是标准 C++ 库。在 MFC 应用程序中使用任何标准库都没有技术障碍。我们不计划在此领域进行重大更改。

Ronald Laeremans
代理产品单元经理
Visual C++ 团队……”

答案#2

正如杰出的Mr. Grauss 曾经说过:“MFC 容器是垃圾”
我相信他是对的。STL 容器设计更加严格和清晰。
现在您对 CArrays 等感觉更舒适,但是一旦习惯了 STL,您就不会再回头了。
   
问题(MFC11) 如何使用单个函数展开或折叠树形控件中的所有子项目?
答案 CTreeCtrl::Expand[^] 应该能完成您想要的操作?
如果您想展开一个项目并展开所有子项目,则需要循环遍历它们。
   
 

WINDOW32

 
   
问题(WIN3201) 如何解决此错误信息:“_WIN32_WINNT 设置与 _WIN32_IE 设置冲突”?
完整问题 现在,我想将 vc 项目转换为 vs2008。当我使用 vs2008 编译此 dll 时:它抛出此错误代码
1>c:\program files\microsoft sdks\windows\v6.0a\include\sdkddkver.h(217) : fatal error C1189: #error : _WIN32_WINNT settings conflicts with _WIN32_IE setting
我在我的项目中搜索宏 "_WIN32_WINNT",但没有找到定义。
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif 
最后,我删除了 vs2008 中的新 SDK 包含路径。但它仍然失败了。
答案 看起来编译器正在混淆来自不同 SDK 版本的的文件。WINVER、_WIN32_WINNT 和 _WIN32_IE 应该被定义,以便“组件”的版本不会高于它们运行的 API 版本。请检查目录搜索的顺序(include、libraries、DLL 等应该引用各个 SDK,顺序相同)。另外,检查新的 SDK 中是否缺少任何“.h”文件(以便找到来自另一个 SDK 的旧版本)。
 
问题(WIN3202) 是否有可以导入注册表文件的 API 到注册表编辑器?
答案 RegLoadKey[^]
 
问题(WIN3203) 我需要对 HKEY_LOCAL_MACHINE 中的每个人拥有完全控制权限,如何通过源代码获得?
答案 RegSetKeySecurity[^]
 
问题(WIN3204) 如何在 AlluserProfile 目录中删除文件?
答案 GetAllUsersProfileDirectory[^] 仅提供“所有用户”文件夹的路径,它不会删除任何内容,但我假设你知道这一点,并且只是询问如何使用此函数,例如这样(未经测试,省略了错误检查)。
 
    DWORD charsNeeded; 
    GetAllUsersProfileDirectory(NULL, &charsNeeded); TCHAR *FolderPath = new 
    TCHAR[charsNeeded + 1]; GetAllUsersProfileDirectory(FolderPath, &charsNeeded);
    
... 路径现在应该在 FolderPath 缓冲区中,你可以对它做任何想做的事情...
    delete []FolderPath;
    
 
问题(WIN3205) 如何暂停当前应用程序,直到生成的进程完成任务并退出?
答案 这段代码会有帮助:
int RunAppAndWait(char *cmd)  // where "cmd" is the command line with args
{
 PROCESS_INFORMATION ProcInfo;
 STARTUPINFO StartInfo;
 int exit_status = 0;
 
 memset(&StartInfo, 0, sizeof(StartInfo));
 StartInfo.cb = sizeof(StartInfo);
 if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL, &StartInfo,  &ProcInfo))
 {
  WaitForSingleObject(ProcInfo.hProcess, INFINITE); // wait for the command to finish
  GetExitCodeProcess(ProcInfo.hProcess, (unsigned long *)&exit_status);
  CloseHandle(ProcInfo.hProcess);
  CloseHandle(ProcInfo.hThread);
  return exit_status;
 }
 
 return 1;  // non-zero = failed
}
 
问题(WIN3206) 如何设置带有默认字体名称和大小的系统字体对话框?
完整问题 当我像这样调用系统字体对话框时,如何设置默认字体名称和大小?
HFONT FAR PASCAL MyCreateFont( void ) 
{ 
    CHOOSEFONT cf; 
    LOGFONT lf; 
    HFONT hfont; 
 
 
    // Initialize members of the CHOOSEFONT structure.  
    cf.lStructSize = sizeof(CHOOSEFONT); 
    cf.hwndOwner = (HWND)NULL; 
    cf.hDC = (HDC)NULL; 
    cf.lpLogFont = &lf; 
    cf.iPointSize = 0; 
    cf.Flags = CF_SCREENFONTS; 
    cf.rgbColors = RGB(0,0,0); 
    cf.lCustData = 0L; 
    cf.lpfnHook = (LPCFHOOKPROC)NULL; 
    cf.lpTemplateName = (LPSTR)NULL; 
    cf.hInstance = (HINSTANCE) NULL; 
    cf.lpszStyle = (LPSTR)NULL; 
    cf.nFontType = SCREEN_FONTTYPE; 
    cf.nSizeMin = 0; 
    cf.nSizeMax = 0; 
 
 
    // Display the CHOOSEFONT common-dialog box.  
    // How to set the defalut font name or size before call this function??? 
    ChooseFont(&cf); 
 
 
    // Create a logical font based on the user's  
    // selection and return a handle identifying  
    // that font.   
    hfont = CreateFontIndirect(cf.lpLogFont); 
    return (hfont); 
} 
答案 如果你在 Flags 成员中设置了...标志并初始化了其他成员,ChooseFont 函数将使用与...匹配的字体初始化对话框。
 
问题(WIN3207) 如何获取系统时钟样式,例如是 12 小时制还是 24 小时制?
答案 答案#1:
系统时钟没有样式,它只是一个用于计算从某个预定义日期起至今时间的计数器。但是,Windows 中使用的任何时钟显示都会访问区域设置以确定应如何显示这些时间。我不确定如何访问这些详细信息的简单方法,但你可以查看HKEY_CURRENT_USER\Control Panel\International注册表键。

答案#2:试试看。
bool GetTimeFormattedBySystem(const SYSTEMTIME& sSomeTime,
                              CString& cszReceiver)
{
  static const int iTimeStrLen(40);
  static TCHAR szResult[iTimeStrLen];
  
  cszReceiver.Empty();
 
  if (GetTimeFormat(LOCALE_USER_DEFAULT,
                    0,
                    &sSomeTime,
                    NULL,
                    szResult,
                    iTimeStrLen)) {
    cszReceiver = szResult;
  }
 
  return (0 < cszReceiver.GetLength());
}
答案#3:
你尝试过 GetLocaleInfo(LOCALE_ITIME) 吗?
 
问题(WIN3208) 如何将局域网中所有 PC 的启动和关闭时间记录到文件中?
答案 你需要逐台计算机进行操作,这些指针可能对保护启动和关闭时间有所帮助:要监视计算机启动时的情况,你需要一个服务。要关闭计算机,你需要响应 WM_ENDSESSION 消息。
 
问题(WIN3209) 如何在 SDI 应用程序中从框架窗口中删除最大化按钮?
答案 答案#1
不要设置(或删除)WS_MAXIMIZEBOX 样式。正如 Ash 所说,最大化框将以禁用状态绘制。如果你没有 WS_MAXIMIZEBOXWS_MINIMIZEBOX 样式,则根本不会绘制任何大小框。

答案#2

在你的 CreateWindowEx() 调用中调整 样式[^],以便不包含 WS_MAXIMIZEBOX 值。如果你正在使用 MFC,那么我认为你可能需要在 CMainFrame 类中的 PreCreateWindow() 方法中执行此操作。
 
问题(WIN3210) 是否有其他函数可以允许在文件复制期间显示自定义进度条?
答案 CopyFileEx[^] 支持进度回调。
 
问题(WIN3211) 如何获取自身的进程 ID 和内存使用情况?
答案 你可以使用 GetCurrentProcess[^] 函数并将其传递给 GetProcessMemoryInfo[^]。
	HANDLE hProc = GetCurrentProcess();
	PROCESS_MEMORY_COUNTERS_EX info;
	info.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
	GetProcessMemoryInfo(hProc, (PROCESS_MEMORY_COUNTERS*)&info, info.cb);
	
 
问题(WIN3212) 如何处理跨进程事件?
答案 请查看 CreateEventSetEventWaitForSingleObject 函数。
 
问题(WIN3213) 如何确定应用程序中正在运行的线程?
答案 请参阅 此处[^]。
你可以使用链接中的代码,只需将 printf 调用替换为条件检查 - 如果进程 ID 与你自己的进程 ID 匹配(通过 GetCurrentProcessId[^] 获取),即可。
 
问题(WIN3214) 如何从资源 DLL 加载对话框?
答案 首先你需要一个消息处理程序,基本上与你的主消息处理程序相同,通常是 WndProc
BOOL CALLBACK DlgProc(HWND hDlg, UINT nMessage, WPARAM wParam, LPARAM lParam) {
	switch (nMessage) { 
		case WM_INITDIALOG: 
			//This is where you do all your initialisation. This is the first place after all the controls have been created, so you can use GetDlgItem()
			return TRUE; 
 
		case WM_COMMAND:
			//This is the same as it is in the WndProc, handle button clicks, etc...
			switch (LOWORD(wParam)) {
				case IDOK: //This is usually the OK or Done button
					return TRUE; 
 
				case IDCANCEL: //This is usually the Close or Cancel button
					DestroyWindow(hDlg); //close the dialog
					return TRUE;
			}
			break;
            } 
    } 
    return FALSE; 
} 
 
然后你只需要将其作为“过程函数”使用
void DisplayDynamicDialog(HWND hParent) {
	HMODULE hModule = LoadLibrary("Resources.dll");
	if(hModule != NULL) {
		hDialog = CreateDialog(hModule, MAKEINTRESOURCE(ID_DIALOG), hParent, (DLGPROC)DlgProc); 
		ShowWindow(hDialog, SW_SHOW);
		FreeLibrary(hModule); //This might need to be called later, after the dialog has been closed since it is modeless
	}
}
最后,你提供的 ID 需要是 DLL 中定义的 ID,而不是 EXE 中定义的 ID,因此你包含的 resource.h 需要来自 DLL。为了避免混淆,我建议使用带引号的字符串名称。
 
   

通用

 
   
问题(GEN01) 是否有可以检索正在运行的窗口应用程序类名的应用程序?
答案 spy++ 应用程序可以获取类名,更多信息请参阅 MSDN[^]。
 
问题(GEN02) Linux 中是否有 UrlDownloadToFile() 的等效函数?
答案 它可能不太容易使用,但 CURL 网站上有一个简单的 HTTP[^] 客户端,大约 6 行代码。打开一个文件进行写入,并使用 CURLOPT_WRITEDATA 和 snip snip,鲍勃就是你的阿姨。
#include stdio.h
#include curl
 
int main(void)
{
  CURL *curl = curl_easy_init();
  FILE *output_to = fopen( "output.txt", "w" );
 
  if( curl && output_to )
  {
    curl_easy_setopt(curl, CURLOPT_URL,       "curl.haxx.se" );
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, output_to      );
    curl_easy_perform(curl);
  }
 
  if( curl ) curl_easy_cleanup(curl);
  if( output_to ) fclose( output_to );
}
将这些内容打包到一个函数中,就完成了...
 
问题(GEN03) PE 头中的哪个字段指示是否为有效的 PE 文件?
答案 你可以参考这个 维基百科链接[^] 了解更多关于 PE 头的信息。
 
问题(GEN04) 从 Visual C++ 6.0 迁移到 Visual C++ .NET 时有哪些主要变化?
答案 我知道它有点旧,但是人们仍然在使用它,请参考这个 链接[^]。
 
问题(GEN05) 如果堆上的内存被引用持有,如何删除它?
完整问题 如果我可以使用 delete 关键字删除使用 new 关键字在堆上分配的内存?
class A
{
} ;
A &a1 = * (new A ) ;
如何释放被引用持有的内存?
答案 delete &a1 正是ta需要做的。
它工作得很好。听起来可能有点不寻常,但取决于 a1 的使用方式:如果 a1 参与到泛型代码中重载运算符的算术表达式中,它可能不是指针,并且 *a1 可能不适合期望引用的模板函数。

泛型编程改变了很多“传统规则”。(注意:你也可以有一个可为空的引用,例如 *(A*)0,可以像 !&a 一样进行检查)。
 
问题(GEN06) 任何 PC 中独一无二的是什么?
答案 有许多不同的方案用于实现这种类型的保护,通常称为 HWID(硬件 ID)。通常,在应用转换/哈希例程之前,会收集有关各种硬件的信息。这种方法的一个明显缺点是,如果用户的任何硬件发生变化,生成的 HWID 也会发生变化 - 导致软件认为它现在是在另一台机器上(好吧,从某种意义上说,是这样 - 但不完全是)。

微软采用了一种技术,即在运行时存储并比较从_每个_设备收集的信息。只要一定比例的 ID 保持不变,软件就会继续运行。请记住,Windows 95(或大约那个时候 - 也许是 XP,我不记得了)在早期遇到了一些问题,用户报告说 Windows 在小升级后拒绝运行 - 这可能是简单的兼容性问题,但如果我记忆正确,其中很大一部分是由于简单的 HWID 身份验证失败造成的。

我相信微软为此类想法拥有专利 - 允许一定比例的硬件更改,但仍通过身份验证。ZBrush3 是一款实施类似方案的软件 - 但具有“1 更改 = 身份验证失败”的限制。你可能会发现一些有趣的结果 此处[^]。

你也会在地下破解/黑客网站上找到很多关于击败这些方案的信息 - 绝对值得阅读,以确定如何不实施这个想法。(Tuts4You 是一个不错的网站)。
 
问题(GEN07) 如何拆分 DWORD 值?
完整问题 我有一个 DWORD 数据项,我想将其用作 24 位值和一个 8 位值。获取和设置 DWORD 与两个其他变量之间有什么好的方法?
答案 是的,不要进行复杂的数学表达式,即使编译器会对其进行优化。重新阅读之前的答案,并根据哪个部分是 8 位,哪个部分是 24 位进行调整。无论哪种情况,只需使用简单的移位 AND 和 OR 运算符即可
DWORD dwValue = (bits8 << 24) | bits24;
// or
DWORD dwValue = (bits24 << 8) | bits8;
以及拆分
bits8 = dwValue & 0xFF;
bits24 = dwValue >> 8;
// or
bits24 = dwValue & 0xFFFFFF;
bits8 = dwValue >> 24;
 
问题(GEN08) 从 VS6 迁移到 Vs2005 时,静态库会发生什么变化?
完整问题 如果使用旧版本的 VS6.0 构建了任何静态库,那么它将无法与使用 VS2005 构建的应用程序正常工作。我们需要使用与应用程序构建的相同编译器重新构建它。这是真的吗?为什么在静态库的情况下会发生这种情况?
答案 答案#1:是的。静态库几乎永远不兼容不同版本的 MS 编译器。STL 和 MFC 在不同版本之间会发生变化,接口会中断,成员会移动等等。

答案#2:如果你的库或 DLL 具有 C 接口,并且你的编程语言可以使用具有 C 接口的库或 DLL,那么你可能可以使用不同版本的编译器。这对于你手动加载的 DLL 尤其如此。
 
问题(GEN09) 从 VS6 迁移到 Vs2005 时,动态链接库会发生什么变化?
完整问题 如果 Gen08 为真,那么是否需要使用 VS2005 重新构建 DLL?如果不是,为什么使用旧版本编译器构建的 DLL 可以与使用较新版本编译器构建的应用程序正常工作?
答案 答案#1:这取决于你的 DLL 在其导出的接口中使用哪些内容。

答案#2:
如果你的库或 DLL 是用 C++ 编写的,请确保使用相同版本的编译器编译你的应用程序。名称重整、对象布局和许多其他内容会随着编译器版本的变化而变化。另外,请确保使用尽可能多的相同编译器开关,以避免任何奇怪的东西偷偷进入你的构建中。因此,这意味着你可以从 VC 6 C 库中使用 VC2010 C++ 应用程序(我需要从 DEF 文件生成新的导入库),但无法使用甚至 VC++2008 的静态库。
 
问题(GEN10) 如何将泛型指针传递给带有 void** 参数的函数?
完整问题
int CreateArray(void** data, unsigned int* size)
{
//allocates some array and assigns it to data
}
 
int main()
{
unsigned char* data = 0;
unsigned int size;
int res = CreateArray(&data, &size);
}
 
是否可以避免在 main 函数中使用 void* pData 并直接传递数据指针?
答案
int res = CreateArray((void **) &data, &size);
 
问题(GEN11) 类型转换:我总是对静态转换、动态转换和重新解释转换感到困惑?请解释一下?
答案 static_castreinterpret_cast 的工作方式与经典的 C 风格转换相同,但它们用于两种不同的上下文
  • static_cast 用于在两个相关类型之间进行转换,例如从一个数字类型到另一个数字类型。转换可能会导致数据丢失,但没有转换运算符代码无法编译;编译器可能会生成警告。
    char a, b;
    int x = 123;
    double y = 123.456;
    a = static_cast(x); // From int to char
    b = static_cast(y); // From double to char
    
  • reinterpret_cast 用于在两个不相关类型之间进行转换,通常是指针,例如从指向结构的指针到指向 char 的指针。没有转换运算符代码无法编译;编译器会生成错误。
    POINT pt = { 10, 21 };
    unsigned char *pb = reinterpret_cast(&pt);
    // Print to screen the content of the structure in raw format
    for(int i = 0; i < sizeof(POINT); i++)
       printf("%02X  ", pb[i]);
       
最后,dynamic_cast 是一个多态转换运算符;它仅与指针一起使用,其主要属性是,如果转换可能,它将返回指针值,否则它将返回 NULL。当使用继承时很有用。
 
问题(GEN12) 如何创建一个透明窗口?
答案 http://msdn.microsoft.com/en-us/library/ms997507.aspx[^]
 
问题(GEN13) 如何在 WINDOWS 上使用 C++ 获取主板序列号?
答案 你可以利用 WMI 的服务来获取主板的序列号,Win32_MotherboardDevice 类的 DeviceID 属性可以获取序列号,并且 此处[^] 是在 C++ 中使用 WMI 的示例。
 
问题(GEN14) 哪个更好?返回引用还是值?
完整问题 你认为以下哪种方式会生成更快的查询类变量的方式 - 如果有任何差异的话?
//--- Method A ----
class CMyClass
{
protected:
  int m_myVar;
 
public:
  int GetVar() const { return m_myVar; }
};
//---- Method B ----
  ...
  const int &GetVar() const { return m_myVar; }
  ...
//--- Method C ----
  ,,,
  inline int GetVar() const { return m_myVar; }
  ...
//---- Method D ----
  ...
  inline const int &GetVar() const { return m_myVar; }
  ...
  
答案 答案#1
在你的示例中,你返回一个基本数据类型(一个整数),那么通过引用返回和通过值返回几乎相同:例如,在 32 位环境中,通过引用返回意味着将 m_myVar 的地址加载到 eax 寄存器中,而通过值返回意味着将 m_myVar 的内容加载到该寄存器中。
内联版本的函数可能会更好,因为编译器可以更好地优化 CPU 寄存器的使用,并生成稍微更高效的代码。如果你的方法返回类或结构体,情况可能会有所不同:按值返回意味着你的方法应该在栈上分配一个临时对象并调用其拷贝构造函数。因此,在大多数情况下,按引用返回更好。

回答#2
实际上,从长远来看,按值返回可能更快,但这取决于你测量的内容。对于基本数据类型,实际的返回方式区别不大,但当你使用返回的值时,如果它是按引用返回的,则存在额外的间接层需要解析。当然,编译器优化以及你可能对结果进行的其它操作都会影响整体性能,因此事先确定并非易事。然而,在大多数情况下,我认为差异不会很大。
 
问题(GEN15) new 函数和 malloc 函数有什么区别?
答案 回答#1
简短回答:new 提供了对象初始化(通过调用构造函数)和清理(delete 调用析构函数)的机会,而 malloc 仅提供裸分配功能。
简短建议:在进行 C++ 开发时,始终使用 new/delete 方法。

回答#2
new 创建新对象,malloc 只是为你保留一块内存并给你一个指向它的指针。所以当你说
foo *p = new foo;
 
你是在要求编译器生成创建 foo(无论是什么)的原始实例的代码,并告诉你它在程序的地址空间中的位置。通常这意味着:- 编译器生成代码以保留一块内存来存储对象 - 它调用 foo 的适当构造函数并在该内存块上运行它。另一方面,当你说(在 C 中)
foo *p = malloc( sizeof( foo ) );
 
你是在说“给我一块大小恰好为 foo 的字节,我将把它当作一个 foo 来处理”。其中可能包含任何垃圾数据。如果你想将其初始化为任何合理的状态,你必须手动进行。顺便说一句,这就是你为什么会发现 C 程序员会立即 memset 分配的内存块或使用 calloc 的原因——它将内存置于已知状态。你还会发现许多优秀的 C 程序员将分配和初始化捆绑到函数中,并将其命名为“construct_foo”以保持操作的原子性。

顺便说一句,你在 C++ 中可以插入到对象创建序列过程中的地方有很多,但很少(即,我所知道的没有)可以修改 C 中 malloc 的工作方式。在 C++ 中,你可以修改内存的保留方式(通过重载 operator new 或使用 placement new),如果分配失败该怎么办(set_new_handler),以及对象如何初始化(通过编写构造函数)。

最后一点。如果 new 失败,它会抛出一个异常并回滚任何已经完成的初始化或内存保留。异常将是 bad_alloc(如果内存保留失败),或者来自构造函数的异常。如果 malloc 失败,它将返回一个 NULL 指针,这是使用 C 中的初始化函数的另一个好理由——你可以在一个地方处理所有错误。
 
问题(GEN16) 我创建了一个新的对话框资源。现在我想将其从项目中删除。如何操作?
答案 只需在资源视图中选择该对话框 (IDD_DIALOG1),然后按删除按钮。就这样了...
 
问题(GEN17) 内联和宏在速度和执行方面有什么区别?
答案 回答#1
有两种方法可以让函数内联展开
  • 显式地在其定义中使用 inline 关键字
  • 在类的体部定义函数
在两种情况下,这都取决于编译器的编写者。如果他们的成本/效益分析认为内联该函数没有好处,编译器就不会这样做。 [同样,编译器和链接器串通跨模块进行内联也没有理由——VC++ 已经这样做了很多年...]

我个人只在编写模板时才使用内联函数,否则我不会费心,因此可能或可能不会内联的函数的大小是别人的问题。同样,对于宏——我只在没有其他编程语言选项时才使用它们。内联函数通常更好,因为它们由编译器处理,并且可以为你提供比预处理器更有意义的错误消息。从执行速度的角度来看,在一般情况下无法确定。

回答#2
宏由预处理器处理,它只是文本替换。因为 C 语言没有 const 关键字,所以程序员使用宏来定义常量。以及定义的函数。头文件可以使用 #define 来定义一个宏,以禁止在一个项目中多次包含同一个头文件。现在 Visual Studio 2005 可以使用 #pragma once 代替它。

预处理器无法检查宏的类型,因此由于宏而发生一些错误。
inline 可以让函数定义在类内部。

因为一个函数被调用,它需要执行跳转、压栈、弹栈操作。所以如果某个函数简单地处理数据并且被频繁调用,你可以将该函数定义为内联函数,以减少调用方法所用的时间。内联函数将在函数调用位置被处理器展开。编译后,你找不到内联函数的名称。

我使用内联函数来计算结构体的大小。
关于速度,我没有测试哪个更快。但 C++ 建议不要使用宏。它经常会导致错误。
 
问题(GEN18) 如何将十六进制转换为 CString?
答案 你可以使用 std::ostringstream[^] 轻松地做到这一点。
这里有一个小例子给你
#include <iostream>
#include <string>
#include <iomanip>
#include <sstream>

int main()
{	
	// Test numbers - num1 declared as HEX,  num2 as DEC
	const unsigned int num1 = 0x1234, num2 = 1234;
 
	// We will use std::ostringstream for the conversion
	std::ostringstream oss;
	
	// First num to Hex
        // "0x" is optional if you don't need it you can use "oss << std::hex << num1;"
	oss << "0x" << std::hex << num1; 
	std::string strFirstNumHex = oss.str();
        // Clear the content of the string buffer for later use
	oss.str(std::string());
 
	// Second num to Hex
        // "0x" is optional if you don't need it you can use "oss << std::hex << num2;"
	oss << "0x" << std::hex << num2;
	std::string strSecondNumHex = oss.str();
        // Clear the content of the string buffer for later use
	oss.str(std::string());
 
	// First num to Dec
	oss << std::dec << num1;
	std::string strFirstNumDec = oss.str();
        // Clear the content of the string buffer for later use
	oss.str(std::string());
 
	// Second num to Dec
	oss << std::dec << num2;
	std::string strSecondNumDec = oss.str();
	
	// This line prints: Numbers in HEX: 0x1234, 0x4d2       Numbers in DEC: 4660, 1234
	std::cout << "Numbers in HEX: " << strFirstNumHex << ", " << strSecondNumHex << "\t"
	          << "Numbers in DEC: " << strFirstNumDec << ", " << strSecondNumDec;
 
	std::cin.get();
	return 0;
}
如上例所示,strFirstNumHex strSecondNumHex 包含测试整数的十六进制字符串表示形式(strFirstNumHex = 0x1234strSecondNumHex = 0x4d2)。strFirstNumDec 和 strSecondNumDec 包含测试整数的十进制字符串表示形式。

如果你不需要十六进制字符串的前缀“0x”,你可以从上述示例的指定行中删除此前缀。我希望这有帮助。

注意:为了清除流缓冲区的内容,我通常使用:oss.str("");,但它会破坏代码块的格式,所以我将其更改为 oss.str(std::string());。
 
问题(GEN19) 如何避免命名空间冲突?
完整问题 我正在使用两个 DLL,它们各自包含具有相同名称的两个命名空间。如果我们将这两个 DLL 导入到一个新类中,并尝试从这两个 DLL 的任何一个命名空间中访问一个变量,是否会发生命名空间冲突?如果是,如何避免?
答案 rename_namespace 属性
rename_namespace("NewName")
NewName: 命名空间的新的名称。rename_namespace 属性用于重命名包含类型库内容的命名空间。它接受一个参数,NewName,它指定命名空间的新的名称。
要删除命名空间,请使用 no_namespace 属性。

了解更多 这里[^]
 
问题(GEN20) 如何远程读取 Windows 7 注册表?
答案 确保远程 XP 机器没有通过 Windows 服务 MMC 在本地禁用远程注册表服务,然后你可以使用基本的注册表函数来读取注册表值!
   

有趣

   
问题(FUN01) 我想在互联网上搜索音乐,但我不知道该怎么做?
完整问题 我想完成可以从互联网上下载音乐文件的函数,例如:我想搜索名为“yesterday once more”的歌曲,然后我可以获得许多可以下载这首歌的链接。所以对我来说搜索歌曲和获取它的 URL 会很困难。有人能告诉我如何完成这项任务吗?
答案 尝试使用搜索引擎。
 
问题(FUN02) 不是对您问题的回答,但是当您说“请多多包涵”时……
答案 不是对你问题的回答,但当你说“请多多包涵”时,听起来你是在要求读者和你坦诚相见。
你需要使用“bear”这个拼写,它指的是大型毛茸茸的熊科动物以及携带某物。这是一个容易犯的错误——我认识的许多美国人总是说他们支持持枪权,如果你使用防晒霜的话,这没什么争议。
 
问题(FUN03) 是否可以编写一个将今天的日期添加到 jpg 照片的代码?
答案
  1. 读取不带日期的 JPG
  2. 将日期文本绘制到图像上
  3. 将图像保存为新的 JPG
  4. 等待 23.9999999 小时
  5. 转到 1
有无数种方法可以做到 1、2 和 3。
问题(FUN04) 我经历过异步磁盘 I/O 不可靠。我说的对吗?
答案 已知它偶尔会返回既不是 1 也不是 0 的位。
   
问题(FUN05) 如何比较两个音频文件?
答案 我想我以前听过这个问题。我想知道我该如何比较它们
   

专家

以上问题由以下专家解答(按字母顺序排列):-
  • Aescleal
  • Alain Rist
  • CPallini
  • DavidCrow
  • Iain Clarke
  • J_E_D_I
  • Jackson2010
  • Mark Salsbery  
  • Maxwell Chen
  • Michael Dunn
  • Michael Schubert
  • 迈克·奥尼尔
  • Moak
  • Naveen
  • Nibu babu thomas
  • Niklas Lindquist
  • Nuri Ismail
  • PJ Arends
  • Rajesh R Subramanian
  • Rajkumar R
  • Randor
  • Richard MacCutchan
  • Roger Stoltz
  • SandipG
  • Sauro
  • Stephen Hewitt
  • ThatsAlok
  • Thaddeus Jones
  • Viti
  • cmk
  • cofi++
  • emilio_grv
  • led mike
  • «_Superman_»

   

特别感谢

  • 我的父母、我的妻子和 Parkhi!
  • 所有活跃的贡献者/Visual C++ 论坛 [CodeProject] 的成员,没有他们,这篇文章就不会问世。

本系列的其他文章

历史

  • 2011 年 7 月 12 日:发布于 CodeProject。
  • 2011 年 5 月 11 日:开始撰写本文。
 
© . All rights reserved.