我是靠谱客的博主 勤奋曲奇,最近开发中收集的这篇文章主要介绍Windows编程工作总结1-基础windows内核对象DLL技术COM技术TLS(Thread Local Storage),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

windows内核对象

windows下关闭句柄(HANDLE)

如何关闭句柄?

参考<<windows核心编程(第5版)>>,句柄在错误情况下返回无效的值NULL。所以,类似于指针释放,句柄关闭代码如下:

#include <windows.h>
void main()
{
//句柄初始化为NULL
HANDLE testHandle = NULL;
//创建并使用句柄的源码
......;
//关闭句柄
if (testHandle != NULL) {
CloseHandle(testHandle);
//关闭后设置为NULL
testHandle = NULL;
}
}

多次打开句柄的问题

在<<windows核心编程(第五版)>>3.2.2介绍了关闭句柄,先做一个补充。

如果两次打开一个内核对象,只需要CloseHandle()一次。具体代码如下。在MSDN上明确指出,不要CloseHandle()一个句柄两次。

#include <windows.h>
#include <cstdint>
#include <iostream>
using namespace std;
int32_t main()
{
HANDLE testHandleA = nullptr;
HANDLE testHandleB = nullptr;
const int SHARED_MEM_SIZE = 4096;
char szMemName[MAX_PATH];
int iDesktopNumber = -1; //GetDesktopNumber();
testHandleA = OpenFileMappingA(
FILE_MAP_ALL_ACCESS, FALSE, szMemName);
if (!testHandleA) {
testHandleA = CreateFileMappingA(
(HANDLE)INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE, 0,
SHARED_MEM_SIZE, szMemName);
if (!testHandleA)
cout <<"OpenFileMappingA failed"<<endl;
}
//使用共享内存的代码在这里。
testHandleA = OpenFileMappingA(
FILE_MAP_ALL_ACCESS, FALSE, szMemName);
CloseHandle(testHandleA);
//第二次关闭时,将出现错误。
CloseHandle(testHandleA);

如果两次打开两个内核对象,需要CloseHandle()两次。

#include <windows.h>
#include <cstdint>
#include <iostream>
using namespace std;
int32_t main()
{
HANDLE testHandleA = nullptr;
HANDLE testHandleB = nullptr;
const int SHARED_MEM_SIZE = 4096;
char szMemName[MAX_PATH];
int iDesktopNumber = -1; //GetDesktopNumber();
testHandleA = OpenFileMappingA(
FILE_MAP_ALL_ACCESS, FALSE, szMemName);
if (!testHandleA) {
testHandleA = CreateFileMappingA(
(HANDLE)INVALID_HANDLE_VALUE,
NULL, PAGE_READWRITE, 0,
SHARED_MEM_SIZE, szMemName);
if (!testHandleA)
cout <<"OpenFileMappingA failed"<<endl;
}
//使用共享内存的代码在这里。
testHandleB = OpenFileMappingA(
FILE_MAP_ALL_ACCESS, FALSE, szMemName);
CloseHandle(testHandleA);
//如果没有如下打开的话,可能出现内存泄漏
CloseHandle(testHandleB);
}

多次关闭句柄将释放资源

所以如果句柄资源仍然要使用,不要将其释放。所以一般来说,初始化时创建一次,结束时关闭一次,中间多次打开和关闭。

Windows资源释放没有设置nullptr

注意windows中的Release()仅仅是减少计数,释放资源而已,并没有将指针设置为nullptr。经过实际测试。所以在调用释放资源时,没有必要if(x!=nullptr),直接调用Release()即可。

DLL技术

加载dll

在使用LoadLibraryA()加载DLL库时,出现126(十进制)的错误。显式原因是DLL找不到,隐式的原因是当前DLL依赖太多,无法搜索的到。

使LoadLibraryEx(“DLL绝对路径”, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);通过指定LOAD_WITH_ALTERED_SEARCH_PATH,让系统DLL搜索顺序从DLL所在目录开始。

关于DLL加载,详见<<windows核心编程>>(第5版)。

64位Window下DLL目录

C:WindowsSystem32:用于存放64位的dll。

C:WindowsSysWOW64:存放32位的DLL,使32位的dll运行在64位的系统中。全称为32bit Windows On 64bit Windows。

上面的结论经过了dumpbin的实测。

COM技术

资料

详见https://blog.csdn.net/fly0413/article/details/114651812。

关于COM的书籍,比较古老,都已经搜集。最新的资料比如微软的官方资料: https://docs.microsoft.com/en-us/windows/win32/com/component-object-model–com–portal。

COM组件注册

关于MSDN中COM组件的注册:https://docs.microsoft.com/en-us/windows/win32/com/registering-com-servers。下面以我们使用的两种方式。

使用regsvr32注册

1、 以管理员的身份打开cmd。https://jingyan.baidu.com/article/ceb9fb10b53ab88cac2ba05b.html。右键屏幕左下角的开始菜单,选择windows powershell(管理员)即可。

2、 在cmd 打开regedit。

3、 右键HKEY_CLASSES_ROOT,点击权限设置。将权限设置为读写设置。

4、 采用regsvr32进行dll注册。

注意一定要以管理员的身份注册,否则无法注册成功。

具体的注册的示例代码详见<<COM原理与应用>>(潘爱民著)的3.6

使用系统调用DllRegisterServer()

https://blog.csdn.net/hellokandy/article/details/78396037。

MSDN中的关键字为Self-Registration,具体为: https://docs.microsoft.com/en-us/windows/win32/com/self-registration。

ComPtr

专用于COM的智能指针。比如用于DirectD3D9、DirectD3D11、DirectD3D12、DXGI的编程。

  • 微软官方资料: https://docs.microsoft.com/en-us/cpp/cppcx/wrl/comptr-class?view=msvc-170。
  • Github上的资料为: https://github.com/Microsoft/DirectXTK/wiki/ComPtr。

三个获取指针接口的区别。

  • ComPtr::GetAddressOf():获取指针的指针(T**)

  • ComPtr::Reset():对里面的实例调用Release()方法 ,然后将相关的指针置为nullptr。

  • ComPtr::ReleaseAndGetAddressOf():该方法相当于先调用Reset方法,再调用GetAddressOf方法获取T**,常用在COM组件接口的函数输出,适用于实例可能会被反复构造的情况下。

  • ComPtr::Get():该方法返回T*,常用在COM组件接口的函数输入。

重载运算符

在实际使用中,我们可以可见comptr指针直接使用->调用成员函数,这个是重载了->运算符。个人不提倡这样使用。

windows中COM接口的实现

其实现分为C和C++两个部分。其中C的实现展现了更多的底层细节,值的参考。以dxgi.h中的IDXGIObject为例说明。

//windows系统中,dxgi.h中IDXGIObject定义实现
EXTERN_C const IID IID_IDXGIObject;
#if defined(__cplusplus) && !defined(CINTERFACE)
MIDL_INTERFACE("aec22fb8-76f3-4639-9be0-28eb43a67a2e")
IDXGIObject : public IUnknown
{
public:
virtual HRESULT STDMETHODCALLTYPE SetPrivateData(
_In_
REFGUID Name,
UINT DataSize,
_In_reads_bytes_(DataSize)
const void *pData) = 0;
virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
_In_
REFGUID Name,
_In_opt_
const IUnknown *pUnknown) = 0;
virtual HRESULT STDMETHODCALLTYPE GetPrivateData(
_In_
REFGUID Name,
_Inout_
UINT *pDataSize,
_Out_writes_bytes_(*pDataSize)
void *pData) = 0;
virtual HRESULT STDMETHODCALLTYPE GetParent(
_In_
REFIID riid,
_COM_Outptr_
void **ppParent) = 0;
};
#else//C语言下接口的定义。
typedef struct IDXGIObjectVtbl
{
//C++中基类IUnknown接口的定义。
BEGIN_INTERFACE
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
IDXGIObject * This,
/* [in] */ REFIID riid,
_COM_Outptr_
void **ppvObject);
ULONG ( STDMETHODCALLTYPE *AddRef )(
IDXGIObject * This);
ULONG ( STDMETHODCALLTYPE *Release )(
IDXGIObject * This);
//当前类的接口定义。
HRESULT ( STDMETHODCALLTYPE *SetPrivateData )(
IDXGIObject * This,
_In_
REFGUID Name,
/* [in] */ UINT DataSize,
_In_reads_bytes_(DataSize)
const void *pData);
HRESULT ( STDMETHODCALLTYPE *SetPrivateDataInterface )(
IDXGIObject * This,
_In_
REFGUID Name,
_In_opt_
const IUnknown *pUnknown);
HRESULT ( STDMETHODCALLTYPE *GetPrivateData )(
IDXGIObject * This,
_In_
REFGUID Name,
_Inout_
UINT *pDataSize,
_Out_writes_bytes_(*pDataSize)
void *pData);
HRESULT ( STDMETHODCALLTYPE *GetParent )(
IDXGIObject * This,
_In_
REFIID riid,
_COM_Outptr_
void **ppParent);
END_INTERFACE
} IDXGIObjectVtbl;
interface IDXGIObject
{
CONST_VTBL struct IDXGIObjectVtbl *lpVtbl;
};
......
#endif

TLS(Thread Local Storage)

<<windows核心编程(第5版)>>的第21章说明了TLS的用法,但其背景和使用方式没有说明。

windows进程内的堆内存,对所有进程都是可读写的,线程A和线程B都可以对用malloc分配的内存进行读写。不过线程A对堆内存的写入可能对线程B造成影响,为了避免这种情况,可以使堆内存X只能在线程A中读写,堆内存Y仅仅在线程B中读写。则堆内存A或堆内存B,就是所谓的TLS。

MSDN(https://docs.microsoft.com/en-us/windows/win32/procthread/thread-local-storage)相关示例代码注释如下。

#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
DWORD dwTlsIndex;
VOID ErrorExit(LPCSTR message);
VOID CommonFunc(VOID)
{
LPVOID lpvData;
// Retrieve a data pointer for the current thread.
lpvData = TlsGetValue(dwTlsIndex);
if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
ErrorExit("TlsGetValue error");
// Use the data stored for the current thread.
printf("common: thread %d: lpvData=%lxn",
GetCurrentThreadId(), lpvData);
Sleep(5000);
}
DWORD WINAPI ThreadFunc(VOID)
{
LPVOID lpvData;
// Initialize the TLS index for this thread.
lpvData = (LPVOID)LocalAlloc(LPTR, 256);
//在线程中为TLS设置数据
if (!TlsSetValue(dwTlsIndex, lpvData))
ErrorExit("TlsSetValue error");
printf("thread %d: lpvData=%lxn", GetCurrentThreadId(), lpvData);
CommonFunc();
// Release the dynamic memory before the thread returns.
//在线程中读取TLS数据。
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != 0)
LocalFree((HLOCAL)lpvData);
return 0;
}
int main(VOID)
{
DWORD IDThread;
HANDLE hThread[THREADCOUNT];
int i;
//Allocate a TLS index.在主线程中分配。
//相同的索引,在不同的线程中都有对应的TLS。
if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)
ErrorExit("TlsAlloc failed");
// Create multiple threads.
for (i = 0; i < THREADCOUNT; i++)
{
hThread[i] = CreateThread(NULL, // default security attributes
0,
// use default stack size
(LPTHREAD_START_ROUTINE)ThreadFunc, // thread function
NULL,
// no thread function argument
0,
// use default creation flags
&IDThread);
// returns thread identifier
// Check the return value for success.
if (hThread[i] == NULL)
ErrorExit("CreateThread errorn");
}
for (i = 0; i < THREADCOUNT; i++)
WaitForSingleObject(hThread[i], INFINITE);
//在主线程中释放
TlsFree(dwTlsIndex);
return 0;
}
VOID ErrorExit(LPCSTR message)
{
fprintf(stderr, "%sn", message);
ExitProcess(0);
}

最后

以上就是勤奋曲奇为你收集整理的Windows编程工作总结1-基础windows内核对象DLL技术COM技术TLS(Thread Local Storage)的全部内容,希望文章能够帮你解决Windows编程工作总结1-基础windows内核对象DLL技术COM技术TLS(Thread Local Storage)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(47)

评论列表共有 0 条评论

立即
投稿
返回
顶部