none
[COM相关] 关于 Persistent Zone Identifier Object 的问题 RRS feed

  • 问题

  • 我希望能够通过编程实现类似下图中解除锁定功能.

    http://ch.sysu.edu.cn/hope/sites/liuzh/data/computer/word-protect.png

    通过简单搜索后我相信 Persistent Zone Identifier Object 可以达成这个需求.

    我的 demo 代码如下:

    	HRESULT hr = S_OK;
    	IUnknown* pUnknown = NULL;
    	IPersistFile* pIPF = NULL;
    	__try {
    		hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    		if (FAILED(hr)) {
    			printf("CoInitializeEx failed!\n");
    			__leave;
    		}
    		hr = CoCreateInstance(CLSID_PersistentZoneIdentifier, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);
    		if (FAILED(hr)) {
    			printf("CoCreateInstance failed!\n");
    			__leave;
    		}
    		hr = pUnknown->QueryInterface(IID_IPersistFile, (void**)&pIPF);
    		if (FAILED(hr)) {
    			printf("QueryInterface IID_IPersistFile failed!\n");
    			__leave;
    		}
    		hr = pIPF->Load(TEXT("E:\\Desktop\\viewer.exe"), 0);
    		if (FAILED(hr)) {
    			printf("pIPF->Load failed!\n"); 
    			__leave;
    		}
    	}
    	__finally {
    		if (pUnknown != NULL) {
    			pUnknown->Release();
    		}
    		if (pIPF != NULL) {
    			pIPF->Release();
    		}
    		CoUninitialize();
    	}
    

    在调用 IPersistFile::Load 方法时提示找不到指定文件, 我想知道是哪里错了?

    另外我想知道如何获得 IZoneIdentifier 接口, 因为 MSDN 里好像没写他的 IID? 

    看到 urlmon.h 里有声明, 但是通过 IPersistFile 或者 IUnknown 来 QueryInterface 时提示 NOINTERFACE.

    THX FOR YOUR TIME & HAPPY CHINESE NEW YEAR!


    2010年2月12日 13:30

答案

  • 先问一声新年好,呵呵,这个问题总算是没有拖到新年再解决。

    关于你这个问题,我请你检查几个方面:
    1) 确认你的路径一定要是绝对路径;
    2) 确认你的文件确实是在那个指定路径上存在;
    3) 确认你传入的路径字符串是UNICODE 内码的;

    我测试了一下,已经成功了,下面是我的代码:
    	CComPtr<IZoneIdentifier> spZoneIdenitfier = NULL;
    	CComPtr<IPersistFile> spFile = NULL;
    
    	HRESULT hr = S_OK;
    	hr = ::CoCreateInstance(CLSID_PersistentZoneIdentifier, NULL, 
    		CLSCTX_INPROC_SERVER, IID_IZoneIdentifier, (void**) &spZoneIdenitfier);
    	hr = spZoneIdenitfier->QueryInterface(IID_IPersistFile, (void **) &spFile);
    	hr = spFile->Load(TEXT("F:\\vv.chm"), STGM_READ);
    	DWORD zoneID = 0;
    	hr = spZoneIdenitfier->GetId(&zoneID);
    	hr = spZoneIdenitfier->Remove();
    	hr = spFile->Save(TEXT("F:\\vv.chm"), TRUE);
    这段代码我已经测试过了。一定是能跑的,而且也达到了你想要的目标。
    另有一个URL 供你参考:
    http://stackoverflow.com/questions/135600/reproducing-the-blocked-exe-unblock-option-in-file-properties-in-windows-2003/135924
    2010年2月13日 13:48
    版主

全部回复

  • 先问一声新年好,呵呵,这个问题总算是没有拖到新年再解决。

    关于你这个问题,我请你检查几个方面:
    1) 确认你的路径一定要是绝对路径;
    2) 确认你的文件确实是在那个指定路径上存在;
    3) 确认你传入的路径字符串是UNICODE 内码的;

    我测试了一下,已经成功了,下面是我的代码:
    	CComPtr<IZoneIdentifier> spZoneIdenitfier = NULL;
    	CComPtr<IPersistFile> spFile = NULL;
    
    	HRESULT hr = S_OK;
    	hr = ::CoCreateInstance(CLSID_PersistentZoneIdentifier, NULL, 
    		CLSCTX_INPROC_SERVER, IID_IZoneIdentifier, (void**) &spZoneIdenitfier);
    	hr = spZoneIdenitfier->QueryInterface(IID_IPersistFile, (void **) &spFile);
    	hr = spFile->Load(TEXT("F:\\vv.chm"), STGM_READ);
    	DWORD zoneID = 0;
    	hr = spZoneIdenitfier->GetId(&zoneID);
    	hr = spZoneIdenitfier->Remove();
    	hr = spFile->Save(TEXT("F:\\vv.chm"), TRUE);
    这段代码我已经测试过了。一定是能跑的,而且也达到了你想要的目标。
    另有一个URL 供你参考:
    http://stackoverflow.com/questions/135600/reproducing-the-blocked-exe-unblock-option-in-file-properties-in-windows-2003/135924
    2010年2月13日 13:48
    版主
  • 感谢回复, 没想到在大年夜能得到关注, 真好 ^^

    我试了下

    IZoneIdentifier* pIZId = NULL;
    HRESULT hr = S_OK;
    hr = CoCreateInstance(CLSID_PersistentZoneIdentifier, NULL, CLSCTX_INPROC_SERVER, IID_IZoneIdentifier, (void**)&pIZId);
    if (FAILED(hr)) {
    	printf("CoCreateInstance failed!\n");
    }

    这段提示失败 @@, 错误是 E_NOINTERFACE, 编译没错. 我没用 ATL, 请问是不是必须用 ATL 呀?

    E:\\Desktop\\viewer.exe 是本地绝对路径, 且文件的确存在, 默认 Unicode 编译所以 TEXT 宏应该也是没问题的.

    虎年快乐
    2010年2月13日 14:27
  • 应该没用ATL 效果也是一样的。我使用ATL 是因为我对智能指针情有独钟。 你发帖子的时候不是可以正确执行CoCreateInstance 吗?
    2010年2月13日 14:44
    版主
  • 应该没用ATL 效果也是一样的。我使用ATL 是因为我对智能指针情有独钟。 你发帖子的时候不是可以正确执行CoCreateInstance 吗?
    是的, 不过我一开始是查询 IUnknown 而不是 IZoneIdentifier, 原本也是参照你给的那个链接里说的方法, 所以先查了 IUnknown, 然后通过 IUnknown 查 IPersistFile, 接着打算再查 IZoneIdentifier 的...

    结果在 IPersistFile::Load 这里卡住, 提示找不到文件, 可是明明那个文件就在那儿啊 @@, 然后就郁闷的要死.

    同时也试过查 IZoneIdentifier, 但都提示没有这个接口, 可是编译没错应该么啥问题啊, 也很郁闷...

    我只导入了 urlmon.h, 是不是也要导入 urlmon.idl ? idl 这个该怎么导入呀? COM 菜鸟, 问题多了点. 谢谢~

    ============== 追加 ==============

    Solution 是新建的 VC2008 SP1 中的 Win32 Console, 除了默认外还导入这些:

    // TODO: reference additional headers your program requires here
    #include <Windows.h>
    #include <objbase.h>
    #include <UrlMon.h>
    #include <ObjIdl.h>
    2010年2月13日 14:50
  • http://d.namipan.com/d/e7f26384a596dcb5ae5bf22fa6b6502d1bbd6b93c3000100

    这里是 Solution 打包, 继续寻求使用 SDK 完成的办法. 期待指教.
    2010年2月14日 9:59
  • 我在我的机器上调试过了,你的代码没有问题呀。
    先把调试好的贴出来给你:
    int _tmain(int argc, _TCHAR* argv[])
    {
    	printf("***** Security Zone Test *****\n\n");
    	HRESULT hr = S_OK;
    	IUnknown* pUnknown = NULL;
    	IPersistFile* pIPF = NULL;
    	IZoneIdentifier* pIZId = NULL;
    	__try {
    		hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    		if (FAILED(hr)) {
    			printf("CoInitializeEx failed!\n");
    			__leave;
    		}
    		hr = CoCreateInstance(CLSID_PersistentZoneIdentifier, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknown);
    		if (FAILED(hr)) {
    			printf("CoCreateInstance failed!\n");
    			__leave;
    		}
    		hr = pUnknown->QueryInterface(IID_IPersistFile, (void**)&pIPF);
    		if (FAILED(hr)) {
    			printf("QueryInterface IID_IPersistFile failed!\n");
    			__leave;
    		}
    		hr = pIPF->Load(TEXT("F:\\vv.chm"), 0);
    		if (FAILED(hr)) {
    			printf("pIPF->Load failed!\n"); 
    			__leave;
    		}
    		pUnknown->QueryInterface(IID_IZoneIdentifier, (void**) &pIZId);
    		// DWORD dwZone = 0L;
    		// hr = pIZId->GetId(&dwZone);
    		hr = pIZId->Remove();
    		hr = pIPF->Save(TEXT("F:\\vv.chm"), TRUE);
    	}
    	__finally {
    		if (pUnknown != NULL) {
    			pUnknown->Release();
    		}
    		if (pIPF != NULL) {
    			pIPF->Release();
    		}
    		CoUninitialize();
    	}
    	system("pause");
    	return 0;
    }
    
    2010年2月14日 11:13
    版主
  • 十分感谢! 其实我也想不通我的代码就啥大问题... 就是调试运行的时候总是 pIPF->Load failed!, 看 hr 是 系统找不到文件.

    既然在大大那里没问题, 看来我需要在环境里找找原因了.

    thx again, 大过年的还能这样上心帮助我这样的菜鸟, 赞!
    2010年2月14日 13:39
  • 重新看了一下, 找到篇很好的文章.

    http://weblogs.asp.net/kennykerr/archive/2008/09/12/visual-c-in-short-unblock-downloaded-applications.aspx

    其中比较具体的提到了 Zone ID 的储存方式是以 NTFS 数据流的形式附加在文件上的, 并演示了一种通过 DeleteFile 直接操作数据流来实现类似功能的方法.

    所以我想是不是对于附加的 NTFS 流已经被删除的文件无法通过 Persistent Zone Identifier Object 来操作. 试了下果然如此, 桌面上的 viewer.exe 因为并没有附加这个数据流所以在 IPersistFile::Load 时会提示找不到文件. 从网络上下载一个可以在文件属性标签里看到解除锁定按钮的文件就可以打开了, 不再提示找不到指定文件.

    不过这又衍生出另一个问题, 如果想将一个本地文件添加上 security zone 信息的话应该怎么做? 通过 CreateFile 先创建附加的数据流吗? 同时也需要向 Michael Lee2 大牛求证下, 如果为你代码中的 F:\\vv.chm 文件先手动执行解除锁定后能否再通过 IPersistFile::Load 加载并重新设置 security zone 信息?
    2010年2月14日 14:04
  • 我测试了一下啊,确实如你所说,如果一个文件已经是被信任的了,那么再次用IPersistFile Load 这个文件时,就会报告“系统找不到指定文件”。
    2010年2月15日 13:09
    版主