named pipe from UMDF printer driver to user-mode application - driver

I'm quite new to WDK, I'm trying to create, a virtual printer driver which will send data to user application using named pipe. I'm using 'XPSDrv Driver and Filter Sample' as start. I've added new filter at the end in which I've put this client code:
HANDLE hPipe;
LPTSTR lpvMessage=TEXT("Message from UMDF!");
BOOL fSuccess = FALSE;
DWORD cbToWrite, cbWritten, dwMode;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");
hPipe = CreateFile(
lpszPipename,
//GENERIC_READ |
GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
dwMode = PIPE_READMODE_MESSAGE;
fSuccess = SetNamedPipeHandleState(
hPipe,
&dwMode,
NULL,
NULL);
if (fSuccess)
{
cbToWrite = (lstrlen(lpvMessage)+1)*sizeof(TCHAR);
fSuccess = WriteFile(
hPipe,
lpvMessage,
cbToWrite,
&cbWritten,
NULL);
}
Code works for a Console Application project, but doesn't work inside UMDF printer driver. Server is also a Console Application which is started all the time. Does someone has idea why? Or maybe you know easy way how can I debug printer drivers?
All the best,
Daniel

The reason can be found here:
There is an important difference between an empty and a nonexistent
DACL. When a DACL is empty, it contains no access control entries
(ACEs); therefore, no access rights are explicitly granted. As a
result, access to the object is implicitly denied.
When an object has no DACL (when the pDacl parameter is NULL), no
protection is assigned to the object, and all access requests are
granted.
You're passing a null pDacl, so you're making the pipe accessible to everyone.

I've added those lines before CreateNamedPipe to my server and now it works, not sure why but it's working. If someone has any idea why I would love to know that. Before that I was haveing NULL passed despite m_pSecAttrib as last CreateNamedPipe parameter.
SECURITY_ATTRIBUTES m_pSecAttrib;
SECURITY_DESCRIPTOR* m_pSecDesc;
m_pSecDesc = (SECURITY_DESCRIPTOR*)LocalAlloc(LPTR,SECURITY_DESCRIPTOR_MIN_LENGTH);
InitializeSecurityDescriptor(m_pSecDesc,SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(m_pSecDesc,TRUE,(PACL)NULL,FALSE);
m_pSecAttrib.nLength = sizeof(SECURITY_ATTRIBUTES);
m_pSecAttrib.bInheritHandle = TRUE;
m_pSecAttrib.lpSecurityDescriptor = m_pSecDesc;
Pipe[i].oOverlap.hEvent = hEvents[i];
Pipe[i].hPipeInst = CreateNamedPipe(
lpszPipename,
PIPE_ACCESS_DUPLEX |
FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_ACCEPT_REMOTE_CLIENTS |
PIPE_WAIT,
INSTANCES,
BUFSIZE*sizeof(TCHAR),
BUFSIZE*sizeof(TCHAR),
PIPE_TIMEOUT,
&m_pSecAttrib);

Related

Intercept/handle mime type/file

How do you disable the default action for .torrent files/content-type application/x-bittorrent(eg open with dialog or run program) and instead handle the data in a extension?
There are multiple ways, that all boil down to nsIMimeService/nsIHandlerService and nsIMimeInfo and setting the appropriate nsIHandlerInfo. E.g. see PDF.js making itself the handler for PDF files (by effectively disabling all handler or plugins and implementing a stream converter), or my answer on how to register a web protocol handler (not mime related but protocol related, but the handler info stuff still applies).
Depending on how you'd like to handle things, you may use the nsIHandlerApp-ervied interfaces e.g. to pass the uri (protocols) or file (mime) directly to some local or web application, or implement a full blown stream converter like PDF.js.
In theory, it would be also possible to implement new kinds of nsIHandlerApp-derived interfaces, implementing in particular launchWithURI (protocols) or launchWithFile (mime content types and file extensions (downloads)). However, this is a bit tricky as nsIHandlerService only handles the built-in interfaces.
Based on #nmaiers post this is how you do it:
This is how you do it IF the mime type already exists. If it doesn't exist I don't know how to add it, probably some register function.
For some reason the type for my torrents is application/x-download I have no idea why. If you want info on how I figured that out than I'll tell you. So in the example below I use that as file type.
When we console.log(wrappedHandlerInfo) we see that it looks like this:
so now let's do that enumerate all application handlers (i got this from here: MXR :: gApplicationsPane, and if .type == 'application/x-download' let'sbreak` so we can than play with that object.
var handlerService = Cc['#mozilla.org/uriloader/handler-service;1'].getService(Ci.nsIHandlerService);
var listOfWrappedHandlers = handlerService.enumerate();
var i = 0;
while (listOfWrappedHandlers.hasMoreElements()) {
var wrappedHandlerInfo = listOfWrappedHandlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
console.log(i, 'handler for', wrappedHandlerInfo.type, wrappedHandlerInfo);
if (wrappedHandlerInfo.type == 'application/x-download') {
break;
}
i++;
}
console.log('Listed ', i, ' handlers');
console.log('wrappedHandlerInfo=', wrappedHandlerInfo); //should be the application/x-download one as we broke the loop once it found that
now we have to set its properties then save it.
// Change and save mime handler settings.
wrappedHandlerInfo.alwaysAskBeforeHandling = false;
wrappedHandlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
handlerService.store(wrappedHandlerInfo);
I'm not sure what to change those properties too though, maybe #nmaier can advise us on that.
We see here on MXR :: nsIHandlerService.idl #L69 that store does this:
69 * Save the preferred action, preferred handler, possible handlers, and
70 * always ask properties of the given handler info object to the datastore.
71 * Updates an existing record or creates a new one if necessary.
72 *
73 * Note: if preferred action is undefined or invalid, then we assume
74 * the default value nsIHandlerInfo::useHelperApp.
75 *
76 * #param aHandlerInfo the handler info object
77 */
78 void store(in nsIHandlerInfo aHandlerInfo);
ANOTHER WAY
Ok i found an even better way, this way you don't need to loop to find the handler.
Do this:
var mimeService = Cc['#mozilla.org/mime;1'].getService(Ci.nsIMIMEService);
var CONTENT_TYPE = ''; //'application/x-download'; can leave this blank
var TYPE_EXTENSION = 'torrent';
var handlerInfo = mimeService.getFromTypeAndExtension(CONTENT_TYPE, TYPE_EXTENSION);
console.info('handlerInfo:', handlerInfo); //http://i.imgur.com/dUKox24.png
// Change and save mime handler settings.
handlerInfo.alwaysAskBeforeHandling = false;
handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
handlerService.store(handlerInfo);
This handlerInfo object is slightly different in that it has a primaryExtension attribute which holds torrent.
Problem with both ways
The problem with both ways is that, if the mime type doesn't exist, you have to register it somehow, I don't know how. Probably use mime service and some register function.
Update August 3rd 2014
I think i found a solution for the problem mentioned in bullet above (problem with both ways).
MXR :: addPossibleApplicationHandler
235 addPossibleApplicationHandler: function(aNewHandler) {
236 var possibleApps = this.possibleApplicationHandlers.enumerate();
237 while (possibleApps.hasMoreElements()) {
238 if (possibleApps.getNext().equals(aNewHandler))
239 return;
240 }
241 this.possibleApplicationHandlers.appendElement(aNewHandler, false);
242 },
243
This is code for addPossibleApplicationHandler, we probably just need to copy that and edit somehow.
Update August 3rd 2014
Ok this is how to add protocol handler (it only adds a nsIWebAppHandler but im sure to add a local meaning a nsIAppHandler it should be similar just no need for uri param:
https://gist.github.com/Noitidart/2faaac70c62bc13e7773#add-a-handler-to-a-protocol
Info on functions available in nsIMIMEService: MXR :: nsIMIMEService.idl

Hiding custom ItemProperties from print. Interop.Outlook

I have written an Outlook plugin that basically allows emails being received through Outlook to be linked with a website so that the email can also be view in the communications feature of the website. I store additional details within the ItemProperties of a MailItem, these details are basically things like the id of the user the email relates to within a website.
The problem I'm having is any ItemProperties I add to a MailItem are being printed when the email is printed. Does anyone know how to exclude custom ItemProperties when printing an email?
Here is the code that is creating the custom ItemProperty:
// Try and access the required property.
Microsoft.Office.Interop.Outlook.ItemProperty property = mailItem.ItemProperties[name];
// Required property doesnt exist so we'll create it on the fly.
if (property == null) property = mailItem.ItemProperties.Add(name, Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText);
// Set the value.
property.Value = value;
I'm working on Outlook extension and sometimes ago we had the same issue.
One of our team members found a solution. You can create some method which is responsible for disable printing. You can see peace of our code below:
public void DisablePrint()
{
long printablePropertyFlag = 0x4; // PDO_PRINT_SAVEAS
string printablePropertyCode = "[DispID=107]";
Type customPropertyType = _customProperty.GetType();
// Get current flags.
object rawFlags = customPropertyType.InvokeMember(printablePropertyCode , BindingFlags.GetProperty, null, _customProperty, null);
long flags = long.Parse(rawFlags.ToString());
// Remove printable flag.
flags &= ~printablePropertyFlag;
object[] newParameters = new object[] { flags };
// Set current flags.
customPropertyType.InvokeMember(printablePropertyCode, BindingFlags.SetProperty, null, _customProperty, newParameters);
}
Make sure that _customProperty it is your property which you created by the following code: mailItem.ItemProperties.Add(name,Microsoft.Office.Interop.Outlook.OlUserPropertyType.olText);
On the low (Extended MAPI) level, each user property definition has a flag that determines whether it is printable (namely, PDO_PRINT_SAVEAS). That flag however is not exposed through the Outlook Object Model.
You can either parse the user properties blob and manually set that flag (user properties blob format is documented, and you can see it in OutlookSpy (I am its author) if you click the IMessage button) or you can use Redemption (I am also its author) and its RDOUserProperty.Printable property.
The following script (VB) will reset the printable property for all user propeties of the currently selected message:
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set Msg = Session.GetMessageFromID(Application.ActiveExplorer.Selection(1).EntryID)
for each prop in Msg.UserProperties
Debug.Print prop.Name
prop.Printable = false
next
Msg.Save

Error using WASAPI with PortAudio on Win7

I'm trying to use PortAudio and libsndfile to play .wav files in exclusive mode on my Windows 7 machine, but I'm getting
error number -9984 "Incompatible host API specific stream info" .
I've filled out the PaWasapiStreamInfo struct as follows:
struct PaWasapiStreamInfo wasapiInfo ;
wasapiInfo.size = sizeof(PaWasapiStreamInfo);
wasapiInfo.hostApiType = paWASAPI;
wasapiInfo.version = 1;
wasapiInfo.flags = paWinWasapiExclusive;
wasapiInfo.channelMask = NULL;
wasapiInfo.hostProcessorOutput = NULL;
wasapiInfo.hostProcessorInput = NULL;
wasapiInfo.threadPriority = eThreadPriorityProAudio;
Then assigning the hostApiSpecificStreamInfo parameter and opening the stream via Pa_OpenStream as follows:
/* stereo or mono */
out_param.channelCount = sfinfo.channels;
out_param.sampleFormat = paInt16;
out_param.suggestedLatency = _GetDeviceInfo(out_param.device)->defaultLowOutputLatency;
out_param.hostApiSpecificStreamInfo = (&wasapiInfo);
err = Pa_OpenStream(&stream, NULL, &out_param, sfinfo.samplerate,
paFramesPerBufferUnspecified, paClipOff,
output_cb, file);
Have I missed a step?
Thanks,
Tyler
The technique you used to run the stream in exclusive mode worked for me. It may be the case that you're not opening a stream on a WASAPI device. Depending on your system configuration you may have DirectSound and WMME devices as well. The following code will verify whether the device referenced by index deviceIndexis a WASAPI device or not:
bool isWasapi = Pa_GetHostApiInfo(Pa_GetDeviceInfo(deviceIndex)->hostApi)->type == paWASAPI;
You also need to specify the same index in the out_param struct:
out_param.device = deviceIndex;
You did couple things I did not. In your example you tried to set the thread priority, but PortAudio documentation states that the following line:
wasapiInfo.threadPriority = eThreadPriorityProAudio;
will have no effect because you didn't not set the paWinWasapiThreadPriority bit in wasapiInfo.flags. By the same rule it is unnecessary to explicitly set the other varaibles to null. To fix this set wasapiInfo.flags as follows:
wasapiInfo.flags = (paWinWasapiExclusive|paWinWasapiThreadPriority)
This should enable exclusive mode and cause the threadPriority variable to take effect.

Can't find pin in DirectShow filter by name/ID despite it being the ID returned by QueryPinInfo

I'm having a weird problem while developing my DirectShow application. I am using Delphi 6 with the DSPACK DirectShow component library. One of the IBaseFilter instances doesn't seem to recognize a pin that it owns when I try to find the pin in the filter using it's TPinInfo.achName property (_PinInfo). (Note, in this case it is the IBaseFilter created by the TSampleGrabber component that is exhibiting this weird behavior).
The sequence of events, encapsulated in the code sample below is this:
Find the first available input pin in the IBaseFilter instance. In the code below this is the pin passed to testPinInfo().
Execute QueryPinInfo() on the returned pin to get that information. The returned information shows the pin's achName as 'Input'.
Try to find a pin named 'Input' in the very same IBaseFilter instance using IBaseFilter.findPin().
Get NIL back indicating a pin could not be found with that name. This in my opinion is a really strange condition (error).
Does anyone know what kind of conditions could cause this scenario? I don't think it's a memory corruption problem because the data structures involved look fine when I inspect them in the debugger. Is it possible that some IBaseFilter implementations neglect to implement the FindPin() method properly?
Here's the code below:
procedure testPinInfo(intfInputPin: IPin);
var
intfTestPin: IPin;
pinInfo_input: TPinInfo;
begin
intfTestPin := nil;
// Get the pin information.
ZeroMemory(#pinInfo_input, SizeOf(pinInfo_input));
intfInputPin.QueryPinInfo(pinInfo_input);
// Now immediately turn around and try to find the pin in the filter that
// owns it, using the name found in pinInfo_input
pinInfo_input.pFilter.FindPin(pinInfo_input.achName, intfTestPin);
// >>> intfTestPin is NIL (unassigned). This is an error.
end;
Don't use FindPin, you always have better ways to do it. Look for unconnected pin of desired direction with the media type of interest. If you look for preview/capture pins specifically, you always have an option to use IKsPropertySet interface to unambiguously identify the pins you need.
I had a similar issue to this so I made my own version of FindPin :-
HRESULT GraphControl::FindPinByName(IBaseFilter* pFilter,LPCWSTR pName,IPin** ppPin)
{
HRESULT hr = E_FAIL;
IEnumPins* pEnum = NULL;
IPin* pPin = NULL;
DWORD pFetched = 0;
PIN_INFO pinInfo = {0};
// Create a pin enumerator
if(FAILED(pFilter->EnumPins(&pEnum)))
return E_FAIL;
// Get the first instance
hr = pEnum->Next(1,&pPin,&pFetched);
while( hr == S_OK )
{
pPin->QueryPinInfo(&pinInfo);
// Compare the names
if (wcscmp(pName,pinInfo.achName) == 0 )
{
// pin names match so use this one and exit
*ppPin = pPin;
break;
}
SAFE_RELEASE(pinInfo.pFilter);
SAFE_RELEASE(pPin);
hr = pEnum->Next(1,&pPin,&pFetched);
}
SAFE_RELEASE(pinInfo.pFilter);
SAFE_RELEASE(pEnum);
// if the pPin address is null we didnt find a pin with the wanted name
if(&*pPin == NULL)
hr = VFW_E_NOT_FOUND;
return hr;
}
For FindPin you need the corresponding Id, check QueryId(). For Input it's usually "In".

Adding a mapped drive with WNetAddConnection2 is not accessible

I'm trying to map a drive using WNetAddCOnnection2 but there's something not quite right. The code that I am using from pinvoke.net and seems to work at first. If I am stepping through the code I get a 0 for a response and I am able to use System.IO.Directory.GetFiles() to inspect the new mapped drive which leads me to believe that credentials are fine.
The problem is that the drive is not available outside of the application. When I type net use from a command prompt I see the drive listed like this:
Unavailable L: \\<server>\<share> Microsoft Windows Network
When I try to access the drive I get either:
The system cannot find the drive specified.
or
The system cannot find the path specified.
Any help would be greatly appreciated.
Here's the nutshell of the code in question:
NETRESOURCE res = new NETRESOURCE();
res.iScope = RESOURCE_GLOBALNET;
res.iType = RESOURCETYPE_DISK;
res.iDisplayType = RESOURCEDISPLAYTYPE_SHARE;
res.iUsage = RESOURCEUSAGE_CONNECTABLE;
res.sRemoteName = share;
res.sLocalName = drive;
res.sProvider = null;
int iFlags = 0;
iFlags = CONNECT_UPDATE_PROFILE;
int iResult = WNetAddConnection2( ref res, psPassword, psUsername, iFlags );
The iResult always ends up equaling 0.
MSDN articles that may assist:
* WNetAddConnection2 - [http://msdn.microsoft.com/en-us/library/aa385413%28VS.85%29.aspx][1]
* NETRESOURCE - [http://msdn.microsoft.com/en-us/library/aa385353%28VS.85%29.aspx][2]
I reckon your problem is the display type where "res.iDisplayType = RESOURCEDISPLAYTYPE_SHARE". Perhaps try changing to a value of "0" (RESOURCEDISPLAYTYPE_GENERIC). So for example, what I generally use to map drives appears:
With Res
.dwScope = RES_SCOPE_GLOBALNET 'value 2
.dwType = RES_TYPE_DISK 'value of 1
.dwUsage = RES_USE_CONNECT 'value of 1
.localName = "x:" 'leave blank for no drive
.RemoteName = "\\\"
End With
lRes = WNetAddConnection2(Res, sPassword, sDomain & "\" & sPassword, RES_CNN_UPDATE_PROFILE)
If lRes = 0 Then
'Success
Else
'Error
End If
Always check your connections before & after calls from command prompt:
1a) From system making connection, list current connections:
net use
1b) From system connected too, list current sessions:
net session
To disconnect session, use API 'WNetCancelConnection2', my code following from above:
sServer = "\\\"
lRes = WNetCancelConnection2(sServer, RES_CNN_UPDATE_PROFILE, True)
If lRes `> 0 Then
'Success
Else
'Error
End If
Alternatively, simply making connections using 'net' command:
1) To map a drive letter:
net use `: \\`\` /user:`\` `
2) To map an IPC connection:
net use \\`\` /user:`\` `
Disconnecting using 'net' command:
1) Disconnecting mapped drive:
net use `: /delete
2) Disconnecting server share:
net use \\`\` /delete

Resources