In summary: I searched MSDN and thought to create my own SetValue but I can't find any sourcecode for it. It's not in a DLL, its like InitPropVariantFromString, it's in a header file, but I can't find it. :( If you can please just show me htat C++ code I'll take it from there :)
Bottom stuff is to show the efforts I put on it.
This is the tiny C++ code I'm converting:
OnCreate(HWND hwnd, LPCREATESTRUCT lpcs)
{
IPropertyStore *pps;
HRESULT hr = SHGetPropertyStoreForWindow(hwnd, IID_PPV_ARGS(&pps));
if (SUCCEEDED(hr)) {
IPropertyStore_SetValue(pps, PKEY_AppUserModel_ID, L"Contoso.Scratch");
}
}
I have translated this to:
var ppv = ctypes.voidptr_t(0);
var pps = new IID();
var HR_pps = CLSIDFromString('{886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99}', pps.address());
if (!checkHRESULT(HR_pps)) { throw new Error('checkHRESULT error'); }
var hr = SHGetPropertyStoreForWindow(hwnd, pps.address(), ppv.address());
if (!checkHRESULT(hr)) { throw new Error('checkHRESULT error'); }
var pszValue = ctypes.jschar.array()('Contoso.Scratch'); // PCWSTR
IPropertyStore_SetValue(pps.address(), PKEY_AppUserModel_ID, pszValue.address());
This IPropertyStore_SetValue I converted from this C++:
HRESULT IPropertyStore_SetValue(IPropertyStore *pps, REFPROPERTYKEY pkey, PCWSTR pszValue)
{
PROPVARIANT var;
HRESULT hr = InitPropVariantFromString(pszValue, &var);
if (SUCCEEDED(hr)) {
hr = pps->SetValue(pkey, var);
PropVariantClear(&var);
}
return hr;
}
I got it to:
var struct_PROPVARIANT = ctypes.StructType('PROPVARIANT', [
{'fntud': struct_GUID}, // GUID
{'pid': ctypes.unsigned_long}, // DWORD
// comment from loomo on union :: union not supported by js-ctypes https://bugzilla.mozilla.org/show_bug.cgi?id=535378 "You can always typecast pointers, at least as long as you know which type is the biggest"
// so now in my case i know that InitPropVariantFromString is looking to se the pwszVal so forget all the other crap in the union and just leave this one
{'pwszVal': new ctypes.PointerType(ctypes.jschar)} // LPWSTR
]);
var IPropertyStore_SetValue = function(pps /** IPopertyStore pointer **/, pkey /** PROPERTYKEY **/, pszValue /** PCWSTR **/) {
var v = new struct_PROPVARIANT(); // PROPVARIANT
var rez = InitPropVariantFromString(pszValue, v.address());
if (rez) {
console.info('pps.SetValue', pps.SetValue);
pps.SetValue(pkey, v);
} else {
throw new Error('failed InitPropVariantFromString');
}
return true;
}
So this required me to write InitPropVariantFromString which I did:
function InitPropVariantFromString(string /** PCWSTR **/, propvarPtr /** PROPVARIANT pointer **/) {
var hr = SHStrDup(string, propvarPtr.contents.pwszVal.address());
if (!checkHRESULT(hr)) {
//PropVariantInit(propvarPtr); //can skip this, it just does a memset
}
return true;
}
A SMALL ASIDE ON MY SHSTRDUP DILEMMA (not a question just showing i worked hard and through unexplainable weirdness got it to work)
So this required me to do SHStrDup which I thought would take two
arguments of ctypes.jschar.ptr as it is first argument of LPCTSTR
and second arg of LPTSTR.
For the first argument, i was passing the PCWSTR of recall
ctypes.jschar.array()('Contoso.Scratch'). So when I the first arg
definition set to ctypes.jschar.ptr it wouldn't work use it throws
this error: expected type pointer, got
ctypes.jschar.array(16).ptr(ctypes.UInt64("0x2855b560"))
So I change in the defintion from ctypes.jschar.ptr to
ctypes.voidptr_t in defintion so then it worked for this argument
but then threw error on second argument recall the
propvarPtr.contents.pwszVal.address():
ctypes.jschar.ptr.ptr(ctypes.UInt64("0x20fdb3b4")),
So then I just ended up setting both arguments of SHStrDup to
ctypes.voidptr_t and it worked. Don't know why but it worked. And
propvarPtr.contents.pwszVal is definitely getting populated, because
when I console.log it it shows: "propvarPtr.contents.pwszVal" CData {
contents: "C" }.
So the final SHStrDup defintion looked like this:
/* http://msdn.microsoft.com/en-us/library/windows/desktop/bb759924%28v=vs.85%29.aspx
* HRESULT SHStrDup(
* __in_ LPCTSTR pszSource,
* __out_ LPTSTR *ppwsz
* );
*/
var SHStrDup = shlwapi.declare('SHStrDupW', ctypes.winapi_abi, ctypes.long, // HRESULT
ctypes.voidptr_t, // LPCTSTR // should be ctypes.jschar.ptr OR ctypes.char.ptr // im trying to pass PCWSTR here, which is `ctypes.jschar.array()('blah blah').address()`
ctypes.voidptr_t // LPTSTR // should be ctypes.jschar.ptr OR ctypes.char.ptr // im trying to pass address to struct_PROPVARIANT.pwszVal which is new ctypes.PointerType(ctypes.jschar)
);
So now all that is passing and I end up back in IPropertyStore_SetValue in this if:
var rez = InitPropVariantFromString(pszValue, v.address());
if (rez) {
console.info('pps.SetValue', pps.SetValue);
pps.SetValue(pkey, v);
} else {
throw new Error('failed InitPropVariantFromString');
}
Now rez is true, so then it now tries pps.SetValue and this is where it goes kaput and I can't figure out how to fix it. :(
I searched MSDN and thought to create my own SetValue but I can't find any sourcecode for it. It's not in a DLL, its like InitPropVariantFromString, it's in a header file, but I can't find it. :( If you can please just show me htat C++ code I'll take it from there :)
Solved, I had to use VTBL: https://github.com/Noitidart/_scratchpad/blob/master/IPropertyStore%20COM%20jsctypes.js
The order of the defintions in the Vtbl matter big time. So make sure its correct if you want to do something with Vtbls
var IPropertyStoreVtbl = new ctypes.StructType('IPropertyStoreVtbl');
var IPropertyStore = new ctypes.StructType('IPropertyStore', [{
'lpVtbl': IPropertyStoreVtbl.ptr
}]);
this.IPropertyStorePtr = new ctypes.PointerType(IPropertyStore);
IPropertyStoreVtbl.define(
[{ //start inherit from IUnknown
'QueryInterface': ctypes.FunctionType(ctypes.stdcall_abi,
this.HRESULT, [
IPropertyStore.ptr,
this.REFIID, // riid
this.VOIDPTR // **ppvObject
]).ptr
}, {
'AddRef': ctypes.FunctionType(ctypes.stdcall_abi,
this.ULONG, [
IPropertyStore.ptr
]).ptr
}, {
'Release': ctypes.FunctionType(ctypes.stdcall_abi,
this.ULONG, [
IPropertyStore.ptr
]).ptr
}, { //end inherit from IUnknown //start IPropertyStore
'GetCount': ctypes.FunctionType(ctypes.stdcall_abi,
this.HRESULT, [
IPropertyStore.ptr,
this.DWORD.ptr // *cProps
]).ptr
}, {
'GetAt': ctypes.FunctionType(ctypes.stdcall_abi,
this.HRESULT, [
IPropertyStore.ptr,
this.DWORD, // iProp
this.PROPERTYKEY.ptr //*pkey
]).ptr
}, {
'GetValue': ctypes.FunctionType(ctypes.stdcall_abi,
this.HRESULT, [
IPropertyStore.ptr,
this.REFPROPERTYKEY, // key
this.PROPVARIANT.ptr // *pv
]).ptr
}, {
'SetValue': ctypes.FunctionType(ctypes.stdcall_abi,
this.HRESULT, [
IPropertyStore.ptr,
this.REFPROPERTYKEY, // key
this.REFPROPVARIANT // propvar
]).ptr
}, {
'Commit': ctypes.FunctionType(ctypes.stdcall_abi,
this.HRESULT, [
IPropertyStore.ptr
]).ptr
}]
);
Then to get the IPropertyStore of a window:
var ppsPtr = new ostypes.IPropertyStorePtr();
var hr_SHGetPropertyStoreForWindow = _dec('SHGetPropertyStoreForWindow')(cHwnd, IID_IPropertyStore.address(), ppsPtr.address());
checkHRESULT(hr_SHGetPropertyStoreForWindow, 'SHGetPropertyStoreForWindow');
var pps = ppsPtr.contents.lpVtbl.contents;
now you can use IPropertyStore :: SetValue and IPropertyStore :: Release but make sure to pass the ppsPtr as first argument to them then the remaining arguments are what you would expect: var hr = pps.SetValue(ppsPtr, firstArgOf_SetValue, secondArgOf_SetValue)
EDIT:
adding copy paste runnable script from scratchpad.
Copy and paste this: http://pastebin.mozilla.org/8429999
this will do the equivalent of this XPCOM:
var win = Services.wm.getMostRecentWindow(null);
Cc["#mozilla.org/windows-taskbar;1"].getService(Ci.nsIWinTaskbar).setGroupIdForWindow(win, 'Contoso.Scratch')
Yes its long but the good thing about it is, there is no XPCOM to set RelaunchCommand and RelaunchIconResource etc. Thats why js-ctypes is needed.
Related
I'm trying to create a wgl context according to the tutorial at https://www.khronos.org/opengl/wiki/Creating_an_OpenGL_Context_(WGL). For whatever reason, wglCreateContext returns null, and GetLastError returns 6, or Invalid Handle. I have used the tutorial before, and it worked just fine.
I'm trying to create a dummy context to a hidden window so that I can create another context for the user window. What's going on?
The Windows API is referred through the winAPI identifier, and wgl is a global struct containing function pointers to OpenGL.
struct WglContext
{
winAPI.HDC hdc;
winAPI.HGLRC handle;
}
__gshared Win32GL wgl;
///WGL-specific global data.
struct Win32GL
{
import oswald : OsWindow, WindowConfig, WindowError;
OsWindow helperWindow;
winAPI.HINSTANCE instance;
PFN_wglCreateContext createContext;
PFN_wglDeleteContext deleteContext;
PFN_wglGetProcAddress getProcAddress;
PFN_wglMakeCurrent makeCurrent;
PFN_wglCreateContextAttribsARB createContextAttribsARB;
PFN_wglGetExtensionStringARB getExtensionStringARB;
PFN_wglGetExtensionStringEXT getExtensionStringEXT;
bool extensionsAreLoaded;
static void initialize()
{
if (wgl.instance !is null)
return; //The library has already been initialized
WindowConfig cfg;
cfg.title = "viewport_gl_helper";
cfg.hidden = true;
auto windowError = OsWindow.createNew(cfg, &wgl.helperWindow);
if (windowError != WindowError.NoError)
{
import std.conv : to;
assert(false, "Failed to create helper window: " ~ windowError.to!string);
}
wgl.instance = winAPI.LoadLibrary("opengl32.dll");
if (wgl.instance is null)
assert(false, "Viweport failed to load opengl32.dll");
wgl.bind(cast(void**)&wgl.createContext, "wglCreateContext\0");
wgl.bind(cast(void**)&wgl.deleteContext, "wglDeleteContext\0");
wgl.bind(cast(void**)&wgl.getProcAddress, "wglGetProcAddress\0");
wgl.bind(cast(void**)&wgl.makeCurrent, "wglMakeCurrent\0");
}
static void terminate()
{
if (!wgl.instance)
return;
winAPI.FreeLibrary(wgl.instance);
wgl.helperWindow.destroy();
}
void bind(void** func, in string name)
{
*func = winAPI.GetProcAddress(this.instance, name.ptr);
}
}
struct WglContext
{
winAPI.HDC hdc;
winAPI.HGLRC handle;
}
WglContext wglCreateTmpContext()
{
assert(wgl.instance);
winAPI.PIXELFORMATDESCRIPTOR pfd;
pfd.nSize = winAPI.PIXELFORMATDESCRIPTOR.sizeof;
pfd.nVersion = 1;
pfd.dwFlags = winAPI.PFD_DRAW_TO_WINDOW | winAPI.PFD_SUPPORT_OPENGL | winAPI.PFD_DOUBLEBUFFER;
pfd.iPixelType = winAPI.PFD_TYPE_RGBA;
pfd.cColorBits = 24;
auto hdc = winAPI.GetDC(wgl.helperWindow.platformData.handle);
const pixelFormat = winAPI.ChoosePixelFormat(hdc, &pfd);
if (winAPI.SetPixelFormat(hdc, pixelFormat, &pfd) == winAPI.FALSE)
assert(false, "Failed to set pixel format for temp context");
writeln(hdc);
writeln(pixelFormat);
winAPI.HGLRC context = wgl.createContext(hdc);
if (context is null)
{
import std.conv: to;
assert(false, "Failed to create temp context: Error Code " ~ winAPI.GetLastError().to!string);
}
if (wgl.makeCurrent(hdc, context) == winAPI.FALSE)
{
wgl.deleteContext(context);
assert(false, "Failed to make temp context current");
}
return WglContext(hdc, context);
}
void main()
{
wgl.initialize(); //Fetches function pointers, and loads opengl32.dll
auto context = wglCreateTmpContext();
wglDeleteContext(context); //Delegates to wgl.deleteContext
wgl.terminate(); //Unloads opengl32.dll and nulls function pointers
}
I know nothing on that winAPI you use. Anyhow, I'm sure of:
You must:
Create a window and get its HWND handle (Windows definition of
HWND, see below). Tipically the window uses WS_CLIPCHILDREN | WS_CLIPSIBLINGS
style; but try also the WS_OVERLAPPEDWINDOW as in the link you
posted.
Verify you get a valid HWND, and a valid HDC from it.
HWND and HDC are defined as *void. I would not trust on auto.
There is a file on mydesktop parent.lock which is a text file. I want the PIDs that are locking this file. So I managed to list out all handles in use. Code striked out below.
I want to use CreateFile to open parent.lock on my desktop to get a handle to it, as I don't know the process which has it open I cannot use DuplicateHandle. Is this possible?
GetFullPathFinal is not an option for me for a couple reasons. One being its only Vista+.
I listed out all handles with NtQuerySystemInformation(SystemHandleInformation.... and it returned 54000 handles (does this number sound right?)
There is a file on mydesktop parent.lock which is a text file. I want the PIDs that are locking this file. But I am not able to figure out the file handle for the parent.lock file on my desktop.
This is how I enumerate handles:
Cu.import("resource://gre/modules/ctypes.jsm");
var lib_ntdll = ctypes.open("ntdll.dll");
var lib_kernel32 = ctypes.open("kernel32.dll");
var STATUS_BUFFER_TOO_SMALL = 0xC0000023>>0;
var STATUS_INFO_LENGTH_MISMATCH = 0xC0000004>>0;
var SystemHandleInformation = 16;
var UNICODE_STRING = new ctypes.StructType("UNICODE_STRING", [
{'Length': ctypes.unsigned_short}, //USHORT
{'MaximumLength': ctypes.unsigned_short}, //USHORT
{'Buffer': ctypes.jschar.ptr} ]); //PWSTR
//https://github.com/tjguk/winsys/blob/5f11b308171382046ff0f67ef3129e47e9fee06c/random/file_handles.py#L100
var SYSTEM_HANDLE_TABLE_ENTRY_INFO = new ctypes.StructType('SYSTEM_HANDLE_TABLE_ENTRY_INFO', [ //typedef struct _TagHANDLEINFO
{'UniqueProcessId': ctypes.unsigned_short}, //USHORT dwPid; //UniqueProcessId
{'CreatorBackTraceIndex': ctypes.unsigned_short}, //USHORT CreatorBackTraceIndex; //CreatorBackTraceIndex
{'ObjectTypeIndex': ctypes.unsigned_long}, //BYTE ObjType; //ObjectTypeIndex UCHAR
{'HandleAttributes': ctypes.unsigned_long}, //BYTE HandleAttributes; //im not sure if byte should be unsigned_long, maybe unsigned_char //HandleAttributes UCHAR
{'HandleValue': ctypes.unsigned_short}, //USHORT HndlOffset; //HandleValue USHORT
{'Object': ctypes.void_t.ptr}, //DWORD dwKeObject; //Object PVOID
{'GrantedAccess': ctypes.unsigned_long} //ULONG GrantedAccess; //GrantedAccess ULONG
]); //HANDLEINFO, PHANDLEINFO;
var SYSTEM_HANDLE_INFORMATION = new ctypes.StructType('SYSTEM_HANDLE_INFORMATION', [
{'NumberOfHandles': ctypes.unsigned_long},
{'Handles': ctypes.ArrayType(SYSTEM_HANDLE_TABLE_ENTRY_INFO, 5)}
]);
var NtQuerySystemInformation = lib_ntdll.declare("NtQuerySystemInformation",
ctypes.winapi_abi,
ctypes.long, // return //NTSTATUS
ctypes.int, // SystemInformationClass //SYSTEM_INFORMATION_CLASS
ctypes.void_t.ptr, // SystemInformation //PVOID
ctypes.unsigned_long, // SystemInformationLength //ULONG
ctypes.unsigned_long.ptr); // ReturnLength //PULONG
/* http://msdn.microsoft.com/en-us/library/ms633499%28v=vs.85%29.aspx
* HWND WINAPI FindWindow(
* __in_opt LPCTSTR lpClassName,
* __in_opt LPCTSTR lpWindowName
* );
*/
// NOT SUPPORTED BY WINXP so just doing this to test and then later will figure out how to get handle to path name then look in here
var GetFinalPathNameByHandle = lib_kernel32.declare('GetFinalPathNameByHandleW', ctypes.winapi_abi, ctypes.uint32_t, //DWORD
ctypes.unsigned_short, // HANDLE
ctypes.void_t.ptr, // LPTSTR
ctypes.uint32_t, // DWORD
ctypes.uint32_t // DWORD
);
function enumHandles() {
var res = {};
var _enumBufSize = new ctypes.unsigned_long(0x4000);
var buffer = ctypes.char.array(_enumBufSize.value)();
while (true) {
var status = NtQuerySystemInformation(SystemHandleInformation, buffer,
_enumBufSize, _enumBufSize.address());
if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) {
buffer = ctypes.char.array(_enumBufSize.value)();
} else break;
}
if (status < 0) return null;
var proc = ctypes.cast(buffer.addressOfElement(0), SYSTEM_HANDLE_INFORMATION.ptr).contents;
for (var i=0; i<proc.Handles.length; i++) {
//console.log('i:', proc.Handles[i].HandleValue);
//var nbuff = ctypes.jschar.array()(32); //nbuff.length == 32
//var ret = GetFinalPathNameByHandle(proc.Handles[i].HandleValue, nbuff.address(), nbuff.length, 0);
//console.log(nbuff.readString()); //always blank i have no idea why
if (res[proc.Handles[i].HandleValue]) {
res[proc.Handles[i].HandleValue].push(proc.Handles[i]);
//console.error('multiple handlevalue occourance for handle value of ', res[proc.Handles[i].HandleValue])
} else {
res[proc.Handles[i].HandleValue] = [proc.Handles[i]]
}
}
return res;
}
var allHandles = enumHandles();
console.log('enumHandles:', Object.keys(allHandles).length, allHandles);
lib_ntdll.close();
lib_kernel32.close();
NtQuerySystemInformation(SystemHandleInformation, ...) returns an array of SYSTEM_HANDLE_INFORMATION items, not SYSTEM_HANDLE_TABLE_ENTRY_INFO items. See Enumerating the processes referencing an object and HOWTO: Enumerate handles.
If you already have an open HANDLE to a file/directory path, such as from CreateFile(), then you should already have the original path that was used to open the HANDLE. To find other handles to the same path, you can:
loop through the array looking for all entries whose Handle field matches the HANDLE you already have, if you have one.
loop through the array looking for entries whose ObjectTypeNumber field is HANDLE_TYPE_FILE. For each Handle, use DuplicateHandle() to make it accessible to your app's process space, then use either NtQueryInformationFile(..., FileNameInformation) or GetFinalPathNameByHandle() to query its path, which you can then compare to the path you are looking for (see File handle operations demo).
My plugin code crashes when I call the NPN_GetValue. Basically I created a scriptable object which has a 'getDevice' method that can return a device array to JavaScript. Below is the code snippet.
static bool mainNPObjectInvoke(NPObject *obj, NPIdentifier identifier, const NPVariant *args, uint32_t argCount, NPVariant *result)
{
printf("create main object");
MainNPObject *mainObject = (MainNPObject *)obj;
if (identifier == methodIdentifiers[METHOD_ID_GET_DEVICES])
{
NPObject *windowObj = NULL;
browser->getvalue(mainObject->npp, NPNVWindowNPObject, &windowObj);
// it crashed here
....
}
}
I created the MainNPObject instance with below method.
NPObject *createMainNPObject(NPP npp)
{
MainNPObject *object = (MainNPObject *)browser->createobject(npp, &mainNPClass);
object->npp = npp;
theMainObject = object;
return (NPObject *)object;
}
The createMainNPObject is called in the plugin function I provided to browser.
NPError NPP_GetValue(NPP instance, NPPVariable variable, void *value)
{
PluginObject *obj = instance->pdata;
switch (variable) {
case NPPVpluginCoreAnimationLayer:
if (!obj->rootLayer)
setupLayerHierarchy(obj);
*(CALayer **)value = obj->rootLayer;
return NPERR_NO_ERROR;
case NPPVpluginScriptableNPObject:
if (!obj->mainObject)
{
obj->mainObject = createMainNPObject(instance);
}
....
}
And the allocate function is as below.
static NPObject *mainNPObjectAllocate(NPP npp, NPClass *class)
{
initializeIdentifiers();
MainNPObject *mainObject = malloc(sizeof(MainNPObject));
mainObject->deviceManager = [[DeviceManager alloc] init];
return (NPObject *)mainObject;
}
Definition of MainNPObject:
typedef struct
{
NPObject *npobject;
NPP npp;
DeviceManager *deviceManager;
} MainNPObject;
By debugging the code, I found that the system raised an EXC_BAD_ACCESS when calling the browser->getValue and it looks like the npp pointer is invalid.
0x00007fff83f82dab <+0019> je 0x7fff83f82db9 <_ZN6WebKit14NetscapePlugin7fromNPPEP4_NPP+33>
0x00007fff83f82dad <+0021> incl 0x8(%rax)
Can someone help me out?
Thanks!
Hmm; not seeing anything obvious. Try adding another parameter (an int?) to your structure and set it during allocate or immediately afterwords, then later on check to see if it's still the value you set before you call getvalue. See if your struct is somehow getting corrupt. That happened to me once when I was casting the NPObject funny in a non-obvious way.
I'm working on a drag and drop problem now and trying to get the PIDLs of the items being dragged from windows shell to my application.
The code below can get correct PIDLs if the dragged item is 'My Computer' or 'Control Panel' itself, but it doesn't work when the dragged item is an item in the 'Control Panel'.
What's wrong with my code?
#define GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
#define GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
STGMEDIUM medium;
UINT fmt = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
FORMATETC fe= {fmt, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
HRESULT GETDATA_RESULT = pDataObject->GetData(&fe, &medium);
if (SUCCEEDED(GETDATA_RESULT))
{
LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
LPCITEMIDLIST pidlFolder = GetPIDLFolder(pida);
int n = pida->cidl; // the n is always correct
if( n > 0 )
{
LPCITEMIDLIST pidlItem = GetPIDLItem(pida, 0);
// the pidlItem is wrong when the dragged object is an item in 'Control Panel'
}
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
Any idea? Thanks
Zach#Shine
If I D&D Mouse, Network Connections and Fonts I get the following output in my test app:
0 Mouse | sfgao=4 hr=0
1 ::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\::{7007ACC7-3202-11D1-AAD2-00805FC1270E} | sfgao=20000004 hr=0
2 C:\WINDOWS\Fonts | sfgao=60000004 hr=0
The Network Connections and Fonts pidls can be converted to fully qualified shell paths while Mouse only returns a relative path/displayname.
This makes sense if you check the documentation for IShellFolder::GetDisplayNameOf:
...They do not guarantee that
IShellFolder will return the requested
form of the name. If that form is not
available, a different one might be
returned. In particular, there is no
guarantee that the name returned by
the SHGDN_FORPARSING flag will be
successfully parsed by
IShellFolder::ParseDisplayName. There
are also some combinations of flags
that might cause the
GetDisplayNameOf/ParseDisplayName
round trip to not return the original
identifier list. This occurrence is
exceptional, but you should check to
be sure.
It is clear that when dealing with controlpanel items, you need to keep the pidl around and only use GetDisplayNameOf for display strings in your UI.
(IShellLink::SetIDList on the Mouse pidl will create a working shortcut so the pidl is valid)
void OnDrop(IDataObject*pDO)
{
STGMEDIUM medium;
UINT fmt = RegisterClipboardFormat(CFSTR_SHELLIDLIST);
FORMATETC fe= {fmt, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
HRESULT hr = pDO->GetData(&fe, &medium);
if (SUCCEEDED(hr))
{
LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
if (pida)
{
LPCITEMIDLIST pidlFolder = GetPIDLFolder(pida);
for (UINT i=0; i<pida->cidl; ++i)
{
LPCITEMIDLIST pidlItem = GetPIDLItem(pida,i);
LPITEMIDLIST pidlAbsolute = ILCombine(pidlFolder,pidlItem);
if (pidlAbsolute)
{
IShellFolder*pParentSF;
hr= SHBindToParent(pidlAbsolute,IID_IShellFolder,(void**)&pParentSF,&pidlItem);
if (SUCCEEDED(hr))
{
STRRET str;
hr= pParentSF->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &str);
if (SUCCEEDED(hr))
{
TCHAR szName[MAX_PATH];
hr= StrRetToBuf(&str,pidlItem,szName,MAX_PATH);
if (SUCCEEDED(hr))
{
SFGAOF sfgao = SFGAO_FOLDER|SFGAO_FILESYSTEM|SFGAO_HASSUBFOLDER|SFGAO_CANLINK;
hr= pParentSF->GetAttributesOf(1,&pidlItem,&sfgao);
TRACE(_T("%u %s | sfgao=%X hr=%X\n"),i,szName,sfgao,hr);
CreateTestShortcut(pidlAbsolute);
}
}
pParentSF->Release();
}
ILFree(pidlAbsolute);
}
}
GlobalUnlock(medium.hGlobal);
}
ReleaseStgMedium(&medium);
}
}
How can I destroy a pointer to CFindReplaceDialog object properly?
For example I have class:
class CjumpView : public CRichEditView
{
CFindReplceDialog *m_pFRDlg;
// ...
};
CjumpView::CjumpView()
: m_pFRDlg(NULL)
{
}
CjumpView::~CjumpView()
{
if(m_pFRDlg != NULL)
{
m_pFRDlg->DestroyWindow();
delete(m_pFRDlg);
}
}
void CjumpView::OnEditFind()
{
if(m_pFRDlg == NULL)
{
const bool fShowFind = true;
m_pFRDlg = new CFindReplaceDialog();
m_pFRDlg->Create(fShowFind, m_sFind, NULL, NULL, this)
}
}
LRESULT CjumpView::OnFind(WPARAM, LPARAM lParam)
{
LPFINDREPLACE lpFindReplace = reinterpret_cast<LPFINDREPLACE>(lParam);
if(lpFindReplace->Flags & FR_DIALOGTERM)
{
m_pFRDlg->DestroyWindow();
delete(m_pFRDlg);
m_pFRDlg = NULL;
return NULL;
}
lpFindReplace->Flags = 0;
return NULL;
}
In Visual Studio CFindReplaceDialog help article it is said that objects of this class should be created in heap using new operator. Then Create member function should be called.
My OnFind function reacts on closing of this Find dialog. It calls DestroyWindow() function, the tries to delete dialog object.
But when I try to call DestoyWindow() in OnFind() or in destructor I have an exception:
Access violation at address...
How to destroy this dialog and to delete pointer?
If someone's interested as me on this problem, the solution is simple.
Actually you don't have to delete CFindReplaceDislog* pointer after it has been used.
After it's closed, it receives WM_NCDESTROY message. And as far as it's derived from CWnd, CWnd::OnNcDestroy() handler is invoked. In the last line it calls PostNcDestroy() which does nothing in CWnd, but overriden in CFindReplaceDialog. There it deletes 'this' pointer like so:
void CFindReplaceDialog::PostNcDestroy()
{
ASSERT(m_hWnd == NULL);
delete this;
}
So, you don't have to invoke delete(m_pFRDlg) anywhere.