I can get this code to run in the interactive environment but it crashes when I run the code from debugger or from the .exe file
Forgot the error:
FatalExecutionEngineError was detected!
The runtime has encountered a fatal error. The address of the error was at 0x6c9781b0, on thread 0x1104. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.
Using .net 4.5
open System.IO
open System.Runtime.InteropServices
open System.Text
[<DllImport("kernel32.dll",CharSet = CharSet.Auto, SetLastError=true)>]
extern uint32 GetShortPathName(
[<MarshalAs(UnmanagedType.LPWStr)>] string longpath,
[<MarshalAs(UnmanagedType.LPWStr)>] StringBuilder shortpath,
[<MarshalAs(UnmanagedType.U4)>] uint32 item)
let MakeShortName(longPath : string) =
let sb = StringBuilder()
let currPath = longPath
let item = 1024u
// let blah = ""
//win32 assigns shortPath
let blah32 = GetShortPathName(currPath, sb, item)
sb.ToString()
[<EntryPoint>]
let main argv =
let path = #"C:\dev\shortName\shortName"
let shorty = MakeShortName path
printfn "%s" shorty
let x = System.Console.ReadKey()
0
Interactive env
$ (me alt+entering the above two functions)
val GetShortPathName : string * StringBuilder * uint32 -> uint32
val MakeShortName : string -> string
$ MakeShortName #"C:\dev\shortName\shortName";;
val it : string = "C:\dev\SHORTN~1\SHORTN~1"
Fixed it.
I just suck # win 32
Removed marshals
See solution:
open System.IO
open System.Runtime.InteropServices
open System.Text
[<DllImport("kernel32.dll",CharSet = CharSet.Auto, SetLastError=true)>]
extern int GetShortPathName(
string longpath,
StringBuilder shortpath,
int item)
let MakeShortName(longPath : string) =
let sb = StringBuilder()
let currPath = longPath
let item = 1024
// let blah = ""
//win32 assigns shortPath
let blah32 = GetShortPathName(currPath, sb, item)
sb.ToString()
[<EntryPoint>]
let main argv =
let path = #"C:\dev\shortName\shortName"
let shorty = MakeShortName path
printfn "%s" shorty
let x = System.Console.ReadKey()
0
Win32 functions may expect a pre-allocated buffer of the maximum size. This code fixed the crash, for me:
let MakeShortName(longPath : string) =
let maxSize = uint32 <| longPath.Length + 16
let sb = new StringBuilder("", int32 maxSize)
let len = GetShortPathName(longPath, sb, maxSize)
sb.ToString(0, int32 len)
Related
On my adventure to learn Rust I decided to try and print to the cli contents of the clipboard. I've done this before in Swift so thought I would have much issues in Rust.
However I'm having a hard time printing the contents of the returned NSArray. I've spent a few hours playing around with different functions but haven't made much progress.
The Swift code I have that works:
import Foundation
import AppKit
let pasteboard = NSPasteboard.general
func reload() -> [String]{
var clipboardItems: [String] = []
for element in pasteboard.pasteboardItems! {
if let str = element.string(forType: NSPasteboard.PasteboardType(rawValue: "public.utf8-plain-text")) {
clipboardItems.append(str)
}
}
return clipboardItems;
}
// Access the item in the clipboard
while true {
let firstClipboardItem = reload()
print(firstClipboardItem);
sleep(1);
}
Here is the Rust code:
use cocoa::appkit::{NSApp, NSPasteboard, NSPasteboardReading, NSPasteboardTypeString};
use cocoa::foundation::NSArray;
fn main() {
unsafe {
let app = NSApp();
let pid = NSPasteboard::generalPasteboard(app);
let changec = pid.changeCount();
let pid_item = pid.pasteboardItems();
if pid_item.count() != 0 {
let items = &*pid_item.objectAtIndex(0);
println!("{:?}", items);
}
println!("{:?}", *pid.stringForType(NSPasteboardTypeString));
}
}
The code above produces: *<NSPasteboardItem: 0x6000021a3de0>*
EDIT:
I've made a little progress but stuck on one last bit. I've managed to get the first UTF8 char out of the clipboard.
The issue I have is if I copy the text: World the system will loop the correct amount of times for the word length but will only print the first letter, in this case W. Output below:
TEXT 'W'
TEXT 'W'
TEXT 'W'
TEXT 'W'
TEXT 'W'
The bit I'm trying to get my head around is how to move to the next i8. I can't seem to find a way to point to the next i8.
The NSString function UTF8String() returns *const i8. I'm scratching my head with how one would walk the text.
use cocoa::appkit::{NSApp, NSPasteboard, NSPasteboardTypeString};
use cocoa::foundation::{NSArray, NSString};
fn main() {
unsafe {
let app = NSApp();
let pid = NSPasteboard::generalPasteboard(app);
let changec = pid.changeCount();
let nsarray_ptr = pid.pasteboardItems();
if nsarray_ptr.count() != 0 {
for i in 0..NSArray::count(nsarray_ptr) {
let raw_item_ptr = NSArray::objectAtIndex(nsarray_ptr, i);
let itm = raw_item_ptr.stringForType(NSPasteboardTypeString);
for u in 0..itm.len() {
let stri = itm.UTF8String();
println!("TEXT {:?}", *stri as u8 as char);
}
}
}
}
}
To everyone who's looked/commented on this so far thank you.
After reading some tests provided by cocoa I figured out what I needed to do.
The code below prints the contents of the clipboard. Thanks to those who pointed me in the right direction.
use cocoa::appkit::{NSApp, NSPasteboard, NSPasteboardTypeString};
use cocoa::foundation::{NSArray, NSString};
use std::{str, slice};
fn main() {
unsafe {
let app = NSApp();
let pid = NSPasteboard::generalPasteboard(app);
let nsarray_ptr = pid.pasteboardItems();
if nsarray_ptr.count() != 0 {
for i in 0..NSArray::count(nsarray_ptr) {
let raw_item_ptr = NSArray::objectAtIndex(nsarray_ptr, i);
let itm = raw_item_ptr.stringForType(NSPasteboardTypeString);
let stri = itm.UTF8String() as *const u8;
let clipboard = str::from_utf8(slice::from_raw_parts(stri, itm.len()))
.unwrap();
println!("{}", clipboard);
}
}
}
}
I'm porting a small application I wrote for keybindings to .net core and I've run across an instance where the same code behaves differently. I'm calling the SendInput function in F# with this declaration
open System.Runtime.InteropServices
[<StructLayout(LayoutKind.Sequential)>]
type private MOUSEINPUT = struct
val dx: int32
val dy:int32
val mouseData:uint32
val dwFlags: uint32
val time: uint32
val dwExtraInfo: int
new(_dx, _dy, _mouseData, _dwFlags, _time, _dwExtraInfo) = {dx=_dx; dy=_dy; mouseData=_mouseData; dwFlags=_dwFlags; time=_time; dwExtraInfo=_dwExtraInfo}
end
[<StructLayout(LayoutKind.Sequential)>]
type private KEYBDINPUT = struct
val wVk: uint16
val wScan: uint16
val dwFlags: uint32
val time: uint32
val dwExtraInfo:int
new(_wVk, _wScan, _dwFlags, _time, _dwExtraInfo) = {wVk =_wVk; wScan = _wScan; dwFlags = _dwFlags; time = _time; dwExtraInfo = _dwExtraInfo}
end
[<StructLayout(LayoutKind.Sequential)>]
type private HARDWAREINPUT = struct
val uMsg: uint32
val wParamL: uint16
val wParamH: uint16
new(_uMsg, _wParamL, _wParamH) = {uMsg = _uMsg; wParamL = _wParamL; wParamH = _wParamH}
end
[<StructLayout(LayoutKind.Explicit)>]
type private LPINPUT = struct
[<FieldOffset(0)>]
val mutable ``type``:int // 1 is keyboard
[<FieldOffset(4)>]
val mutable mi : MOUSEINPUT
[<FieldOffset(4)>]
val mutable ki : KEYBDINPUT
[<FieldOffset(4)>]
val mutable hi : HARDWAREINPUT
end
module private NativeMethods =
[<DllImport("user32.dll", SetLastError=true)>]
extern uint32 SendInput(uint32 nInputs, LPINPUT* pInputs, int cbSize)
let appSignature = 0xA8969
let private createPressInput (code: int) =
let mutable input = LPINPUT()
input.``type`` <- InputModes.INPUT_KEYBOARD
input.ki <- KEYBDINPUT(uint16 code, uint16 0, Dwords.KEYEVENTF_KEYDOWN, uint32 0, appSignature)
input
let pressKey (code: int) =
let input = createPressInput code
NativeMethods.SendInput(uint32 1, &&input, Marshal.SizeOf(input)) |> ignore
The same code works in a .net framework application that I created in visual studio. Now, the output of Marshal.GetLastWin32ErrorCode() is 87 which apparently means ERROR_INVALID_PARAMETER -- not very helpful. I'm new to .net and F# so I'm not sure what could be different in this context. I admit, even getting this binding code was mostly trial and error as well.
I'd appreciate any info that could help me debug this.
UPDATE: I have a workaround that I'm not satisfied with. I can't explain why this works just yet -- I need to read more about how marshaling works. With this, the Marshal.GetLastWin32ErrorCode() is 5, access denied. It still send the key so I'm not sure what that error is supposed to mean. That said, here it is. Splitting out the union from the struct that I was using into a dedicated union type, making that union type LayoutKind.Explicit, and making at least one of the fields FieldOffset(1) (but not the field I care about) gets key presses working. Other combinations of field offsets result in something that works but doesn't actually press keys, which I assume means that its marshaled in a way that results in no visible key presses.
[<StructLayout(LayoutKind.Explicit)>]
type private InputUnion = struct
[<FieldOffset(0)>]
val mutable ki : KEYBDINPUT
[<FieldOffset(1)>]
val mutable mi : MOUSEINPUT
[<FieldOffset(1)>]
val mutable hi : HARDWAREINPUT
end
[<StructLayout(LayoutKind.Sequential)>]
type private LPINPUT = struct
val ``type``:int // 1 is keyboard
val u: InputUnion
new(_type, _u) = {``type`` = _type; u = _u}
end
I ended up opening a bug on this.
https://github.com/dotnet/runtime/issues/1515
It wasn't an issue in .net. Apparently, my .net framework app was running as 32bit. I had declared the dwExtraInfo field as an int, which was fine for 32bit. My .net core app was running as 64bit and I should have used a UIntPtr to handle the differences between platforms for the length of that field. Updated to this code and it works.
[<StructLayout(LayoutKind.Sequential)>]
type private MOUSEINPUT = struct
val dx: int32
val dy:int32
val mouseData:uint32
val dwFlags: uint32
val time: uint32
val dwExtraInfo: UIntPtr
new(_dx, _dy, _mouseData, _dwFlags, _time, _dwExtraInfo) = {dx=_dx; dy=_dy; mouseData=_mouseData; dwFlags=_dwFlags; time=_time; dwExtraInfo=_dwExtraInfo}
end
[<StructLayout(LayoutKind.Sequential)>]
type private KEYBDINPUT = struct
val wVk: uint16
val wScan: uint16
val dwFlags: uint32
val time: uint32
val dwExtraInfo: UIntPtr
new(_wVk, _wScan, _dwFlags, _time, _dwExtraInfo) = {wVk =_wVk; wScan = _wScan; dwFlags = _dwFlags; time = _time; dwExtraInfo = _dwExtraInfo}
end
[<StructLayout(LayoutKind.Sequential)>]
type private HARDWAREINPUT = struct
val uMsg: uint32
val wParamL: uint16
val wParamH: uint16
new(_uMsg, _wParamL, _wParamH) = {uMsg = _uMsg; wParamL = _wParamL; wParamH = _wParamH}
end
[<StructLayout(LayoutKind.Explicit)>]
type private InputUnion = struct
[<FieldOffset(0)>]
val mutable mi : MOUSEINPUT
[<FieldOffset(0)>]
val mutable ki : KEYBDINPUT
[<FieldOffset(0)>]
val mutable hi : HARDWAREINPUT
end
[<StructLayout(LayoutKind.Sequential)>]
type private LPINPUT = struct
val mutable ``type``: int // 1 is keyboard
val mutable u: InputUnion
end
The main differences are the dwExtraInfo field definitions. Also, there is an explicit type for the union now instead of having it all rolled into a single LPINPUT. That let's me avoid having to specify the field offset for the 3 optional fields which also differs by platform.
I'm trying to convert a program from Python (2.7, 32-bit) to Go (1.12.5, 32bit), and failing miserably with an access violation. The program calls a function in a 32-bit dll (ehlapi32.dll).
The following clips of the Python code appear to work perfectly (I'm not affirming that they are correct!):
class ehlapi32:
hllDll = "C:\Program Files (x86)\IBM\Personal Communications\EHLAPI32.dll"
hllDll = ctypes.WinDLL(hllDll)
hllApiProto = ctypes.WINFUNCTYPE(ctypes.c_int,ctypes.c_void_p,ctypes.c_void_p,
ctypes.c_void_p,ctypes.c_void_p)
hllApiParams = (1, "p1", 0), (1, "p2", 0), (1, "p3",0), (1, "p4",0),
hllApi = hllApiProto (("HLLAPI", hllDll), hllApiParams)
pFunConnect = ctypes.c_int(1)
...
def connect(self):
shlSession = self.session + b'\0\0\0'
pText = ctypes.c_char_p (shlSession)
pLength = ctypes.c_int (len(shlSession))
pReturn = ctypes.c_int (13)
ehlapi32.hllApi (ctypes.byref (ehlapi32.pFunConnect), pText,
ctypes.byref (pLength), ctypes.byref (pReturn))
Now here's what I'm trying as the Go equivalent. This fails:
// ElFunc: EHLLAPI Functions
type ElFunc uint32
const (
eConnect ElFunc = 1 // Connect to a terminal
)
...
// Session: a 3270 session handle
type Session struct {
SessId string
SysId string
Entry string
Result []string
Headers int
Footers int
Rows int
Cols int
Scroll int
Proc *syscall.LazyProc
}
...
func (s Session) Connect() uint32 {
// Load EHLLAPI DLL
dll := syscall.NewLazyDLL(dllName)
s.Proc = dll.NewProc(dllProc)
// Connect to session
rc := s.ecall(eConnect, s.SessId, 1, 0)
if rc == 0 {
defer s.Disconnect()
}
...
// ecall: provides simplified EHLLAPI DLL calls
func (s Session) ecall(fun ElFunc, buffer string, count uint32, ps uint32) uint32 {
rc := ps
_, _, _ = s.Proc.Call(
uintptr(unsafe.Pointer(uintptr(uint32(fun)))),
uintptr(unsafe.Pointer(&buffer)),
uintptr(unsafe.Pointer(uintptr(count))),
uintptr(unsafe.Pointer(uintptr(rc))),
)
return rc
}
The failure looks like an access violation at s.Proc.Call in ecall().
I realise that syscall is deprecated; I have also tried with golang.org/x/sys/windows, without success. I have no constraints as to which I use.
I'll confess I'm way out of my depth here.
first of all i'm sorry for my english.
I've one question about windows WMI and how to add a local port to shared printer. I've this script:
Set objWMIService = GetObject("winmgmts:")
Set objNewPort = objWMIService.Get _
("Win32_TCPIPPrinterPort").SpawnInstance_
objNewPort.Name = "Ricoh3300C"
objNewPort.Protocol = 2
objNewPort.HostAddress = "XXX.XXX.X.XXX"
objNewPort.PortNumber = "9100"
objNewPort.SNMPEnabled = False
objNewPort.Put_
With this i can add a printer with IP address but i want to add a printer in samba server with an address like "\\XXX.XXX.X.XXX\printerColor". I've lost a lot of time in google trying to find an script and all that i've seen is for TCPIP ports. I wan't to do it but in local port.
I've tried to use this script with prnadmin.dll and no luck.
function PortAdd(strPort, portType)
on error resume next
dim oMaster
dim oPort
dim iResult
set oMaster = CreateObject("PrintMaster.PrintMaster.1")
set oPort = CreateObject("Port.Port.1")
iResult = kErrorFailure
oPort.PortName = strPort
oPort.PortType = portType
oMaster.PortAdd oPort
if Err = 0 then
iResult = kErrorSuccess
else
wscript.echo "Error: 0x" & Hex(Err.Number) & ". " & Err.Description
end if
PortAdd = iResult
end function
I get this error:
Error: 0x1A8. Se requiere un objeto
in english is like
Error: 0x1A8. An object is required
How can i fix that error or what script can i use to add a local port?. Thanks in advance.
I forgot to say that i want to do it with normal user without admin access. The first script works fine in that users but is for TCPIP.
Consider using XcvData, e.g.
private static void AddPort(string portName)
{
var def = new PRINTER_DEFAULTS();
def.pDatatype = null;
def.pDevMode = IntPtr.Zero;
def.DesiredAccess = 1; //Server Access Administrator
IntPtr hPrinter = IntPtr.Zero;
int n = OpenPrinter(",XcvMonitor Local Port", ref hPrinter, def);
if (n == 0)
throw new Exception("Local Port monitor has not been opened.");
if (!portName.EndsWith("\0"))
portName += "\0";
// .NET strings are formed by 2-byte characters
var size = (uint) (portName.Length*2);
IntPtr portPtr = Marshal.AllocHGlobal((int) size);
Marshal.Copy(portName.ToCharArray(), 0, portPtr, portName.Length);
uint needed, xcvResult;
XcvData(hPrinter, "AddPort", portPtr, size, IntPtr.Zero, 0, out needed, out xcvResult);
ClosePrinter(hPrinter);
Marshal.FreeHGlobal(portPtr);
}
[DllImport("winspool.drv", EntryPoint = "XcvDataW", SetLastError = true)]
private static extern bool XcvData(
IntPtr hXcv,
[MarshalAs(UnmanagedType.LPWStr)] string pszDataName,
IntPtr pInputData,
uint cbInputData,
IntPtr pOutputData,
uint cbOutputData,
out uint pcbOutputNeeded,
out uint pwdStatus);
Here's the code using GetOpenFileNameW:
import core.sys.windows.windows;
import std.stdio, std.string, std.utf;
pragma(lib, "comdlg32");
// Fill in some missing holes in core.sys.windows.windows.
extern (Windows) DWORD CommDlgExtendedError();
enum OFN_FILEMUSTEXIST = 0x001000;
void main()
{
auto buf = new wchar[1024];
OPENFILENAMEW ofn;
ofn.lStructSize = ofn.sizeof;
ofn.lpstrFile = buf.ptr;
ofn.nMaxFile = buf.length;
ofn.lpstrInitialDir = null;
ofn.Flags = OFN_FILEMUSTEXIST;
BOOL retval = GetOpenFileNameW(&ofn);
if (retval == 0) {
// Get 0x3002 for W and 0x0002 for A. ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms646916(v=vs.85).aspx )
throw new Exception(format("GetOpenFileName failure: 0x%04X.", CommDlgExtendedError()));
}
writeln(buf);
}
This results in FNERR_INVALIDFILENAME, but I don't see any non-optional strings that I haven't filled in. And here's the code (only differences shown) for GetOpenFileNameA:
auto buf = new char[1024];
OPENFILENAMEA ofn;
// ...
BOOL retval = GetOpenFileNameA(&ofn);
This results in CDERR_INITIALIZATION, and the only elaboration MSDN gives me is
The common dialog box function failed during initialization.
This error often occurs when sufficient memory is not available.
This is on Windows 7 64 bit, DMD v2.059.
buf has to be zeroed completely. The problem here is that wchar.init == wchar.max (for error detection reasons), so your array is essentially 1024 instances of wchar.max. A simple buf[] = 0; should fix that.