概述
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)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复